From c669beb798e273dd3d44cfa6a7a95ff90eba7209 Mon Sep 17 00:00:00 2001 From: Serguei Katkov Date: Thu, 14 Jan 2016 10:30:21 +0600 Subject: [PATCH 001/204] Fast ART x86_64 interpretator Introduce the ART x86_64 fast interpreter. Change-Id: I3649698eb251ac8acc98851969f9445f60d17b02 Signed-off-by: Serguei Katkov --- runtime/Android.mk | 3 +- runtime/interpreter/interpreter.cc | 4 +- runtime/interpreter/mterp/config_x86_64 | 516 +- runtime/interpreter/mterp/out/mterp_x86_64.S | 11955 ++++++++++++++++ runtime/interpreter/mterp/rebuild.sh | 2 +- runtime/interpreter/mterp/x86_64/alt_stub.S | 17 + runtime/interpreter/mterp/x86_64/bincmp.S | 28 + runtime/interpreter/mterp/x86_64/bindiv.S | 34 + .../interpreter/mterp/x86_64/bindiv2addr.S | 35 + .../interpreter/mterp/x86_64/bindivLit16.S | 27 + runtime/interpreter/mterp/x86_64/bindivLit8.S | 25 + runtime/interpreter/mterp/x86_64/binop.S | 17 + runtime/interpreter/mterp/x86_64/binop1.S | 19 + runtime/interpreter/mterp/x86_64/binop2addr.S | 19 + runtime/interpreter/mterp/x86_64/binopLit16.S | 19 + runtime/interpreter/mterp/x86_64/binopLit8.S | 18 + runtime/interpreter/mterp/x86_64/binopWide.S | 10 + .../interpreter/mterp/x86_64/binopWide2addr.S | 11 + runtime/interpreter/mterp/x86_64/cvtfp_int.S | 27 + runtime/interpreter/mterp/x86_64/entry.S | 70 + runtime/interpreter/mterp/x86_64/fallback.S | 3 + runtime/interpreter/mterp/x86_64/footer.S | 166 + runtime/interpreter/mterp/x86_64/fpcmp.S | 35 + runtime/interpreter/mterp/x86_64/fpcvt.S | 17 + runtime/interpreter/mterp/x86_64/header.S | 289 + runtime/interpreter/mterp/x86_64/invoke.S | 17 + .../interpreter/mterp/x86_64/op_add_double.S | 1 + .../mterp/x86_64/op_add_double_2addr.S | 1 + .../interpreter/mterp/x86_64/op_add_float.S | 1 + .../mterp/x86_64/op_add_float_2addr.S | 1 + runtime/interpreter/mterp/x86_64/op_add_int.S | 1 + .../mterp/x86_64/op_add_int_2addr.S | 1 + .../mterp/x86_64/op_add_int_lit16.S | 1 + .../mterp/x86_64/op_add_int_lit8.S | 1 + .../interpreter/mterp/x86_64/op_add_long.S | 1 + .../mterp/x86_64/op_add_long_2addr.S | 1 + runtime/interpreter/mterp/x86_64/op_aget.S | 24 + .../mterp/x86_64/op_aget_boolean.S | 1 + .../interpreter/mterp/x86_64/op_aget_byte.S | 1 + .../interpreter/mterp/x86_64/op_aget_char.S | 1 + .../interpreter/mterp/x86_64/op_aget_object.S | 16 + .../interpreter/mterp/x86_64/op_aget_short.S | 1 + .../interpreter/mterp/x86_64/op_aget_wide.S | 1 + runtime/interpreter/mterp/x86_64/op_and_int.S | 1 + .../mterp/x86_64/op_and_int_2addr.S | 1 + .../mterp/x86_64/op_and_int_lit16.S | 1 + .../mterp/x86_64/op_and_int_lit8.S | 1 + .../interpreter/mterp/x86_64/op_and_long.S | 1 + .../mterp/x86_64/op_and_long_2addr.S | 1 + runtime/interpreter/mterp/x86_64/op_aput.S | 23 + .../mterp/x86_64/op_aput_boolean.S | 1 + .../interpreter/mterp/x86_64/op_aput_byte.S | 1 + .../interpreter/mterp/x86_64/op_aput_char.S | 1 + .../interpreter/mterp/x86_64/op_aput_object.S | 13 + .../interpreter/mterp/x86_64/op_aput_short.S | 1 + .../interpreter/mterp/x86_64/op_aput_wide.S | 1 + .../mterp/x86_64/op_array_length.S | 12 + .../interpreter/mterp/x86_64/op_check_cast.S | 13 + .../interpreter/mterp/x86_64/op_cmp_long.S | 17 + .../interpreter/mterp/x86_64/op_cmpg_double.S | 1 + .../interpreter/mterp/x86_64/op_cmpg_float.S | 1 + .../interpreter/mterp/x86_64/op_cmpl_double.S | 1 + .../interpreter/mterp/x86_64/op_cmpl_float.S | 1 + runtime/interpreter/mterp/x86_64/op_const.S | 4 + .../interpreter/mterp/x86_64/op_const_16.S | 4 + runtime/interpreter/mterp/x86_64/op_const_4.S | 7 + .../interpreter/mterp/x86_64/op_const_class.S | 10 + .../mterp/x86_64/op_const_high16.S | 5 + .../mterp/x86_64/op_const_string.S | 10 + .../mterp/x86_64/op_const_string_jumbo.S | 10 + .../interpreter/mterp/x86_64/op_const_wide.S | 4 + .../mterp/x86_64/op_const_wide_16.S | 4 + .../mterp/x86_64/op_const_wide_32.S | 4 + .../mterp/x86_64/op_const_wide_high16.S | 5 + .../interpreter/mterp/x86_64/op_div_double.S | 1 + .../mterp/x86_64/op_div_double_2addr.S | 1 + .../interpreter/mterp/x86_64/op_div_float.S | 1 + .../mterp/x86_64/op_div_float_2addr.S | 1 + runtime/interpreter/mterp/x86_64/op_div_int.S | 1 + .../mterp/x86_64/op_div_int_2addr.S | 1 + .../mterp/x86_64/op_div_int_lit16.S | 1 + .../mterp/x86_64/op_div_int_lit8.S | 1 + .../interpreter/mterp/x86_64/op_div_long.S | 1 + .../mterp/x86_64/op_div_long_2addr.S | 1 + .../mterp/x86_64/op_double_to_float.S | 1 + .../mterp/x86_64/op_double_to_int.S | 1 + .../mterp/x86_64/op_double_to_long.S | 1 + .../mterp/x86_64/op_fill_array_data.S | 9 + .../mterp/x86_64/op_filled_new_array.S | 17 + .../mterp/x86_64/op_filled_new_array_range.S | 1 + .../mterp/x86_64/op_float_to_double.S | 1 + .../mterp/x86_64/op_float_to_int.S | 1 + .../mterp/x86_64/op_float_to_long.S | 1 + runtime/interpreter/mterp/x86_64/op_goto.S | 19 + runtime/interpreter/mterp/x86_64/op_goto_16.S | 19 + runtime/interpreter/mterp/x86_64/op_goto_32.S | 22 + runtime/interpreter/mterp/x86_64/op_if_eq.S | 1 + runtime/interpreter/mterp/x86_64/op_if_eqz.S | 1 + runtime/interpreter/mterp/x86_64/op_if_ge.S | 1 + runtime/interpreter/mterp/x86_64/op_if_gez.S | 1 + runtime/interpreter/mterp/x86_64/op_if_gt.S | 1 + runtime/interpreter/mterp/x86_64/op_if_gtz.S | 1 + runtime/interpreter/mterp/x86_64/op_if_le.S | 1 + runtime/interpreter/mterp/x86_64/op_if_lez.S | 1 + runtime/interpreter/mterp/x86_64/op_if_lt.S | 1 + runtime/interpreter/mterp/x86_64/op_if_ltz.S | 1 + runtime/interpreter/mterp/x86_64/op_if_ne.S | 1 + runtime/interpreter/mterp/x86_64/op_if_nez.S | 1 + runtime/interpreter/mterp/x86_64/op_iget.S | 27 + .../mterp/x86_64/op_iget_boolean.S | 1 + .../mterp/x86_64/op_iget_boolean_quick.S | 1 + .../interpreter/mterp/x86_64/op_iget_byte.S | 1 + .../mterp/x86_64/op_iget_byte_quick.S | 1 + .../interpreter/mterp/x86_64/op_iget_char.S | 1 + .../mterp/x86_64/op_iget_char_quick.S | 1 + .../interpreter/mterp/x86_64/op_iget_object.S | 1 + .../mterp/x86_64/op_iget_object_quick.S | 14 + .../interpreter/mterp/x86_64/op_iget_quick.S | 18 + .../interpreter/mterp/x86_64/op_iget_short.S | 1 + .../mterp/x86_64/op_iget_short_quick.S | 1 + .../interpreter/mterp/x86_64/op_iget_wide.S | 1 + .../mterp/x86_64/op_iget_wide_quick.S | 1 + .../interpreter/mterp/x86_64/op_instance_of.S | 21 + .../interpreter/mterp/x86_64/op_int_to_byte.S | 1 + .../interpreter/mterp/x86_64/op_int_to_char.S | 1 + .../mterp/x86_64/op_int_to_double.S | 1 + .../mterp/x86_64/op_int_to_float.S | 1 + .../interpreter/mterp/x86_64/op_int_to_long.S | 8 + .../mterp/x86_64/op_int_to_short.S | 1 + .../mterp/x86_64/op_invoke_direct.S | 1 + .../mterp/x86_64/op_invoke_direct_range.S | 1 + .../mterp/x86_64/op_invoke_interface.S | 8 + .../mterp/x86_64/op_invoke_interface_range.S | 1 + .../mterp/x86_64/op_invoke_static.S | 2 + .../mterp/x86_64/op_invoke_static_range.S | 1 + .../mterp/x86_64/op_invoke_super.S | 8 + .../mterp/x86_64/op_invoke_super_range.S | 1 + .../mterp/x86_64/op_invoke_virtual.S | 8 + .../mterp/x86_64/op_invoke_virtual_quick.S | 1 + .../mterp/x86_64/op_invoke_virtual_range.S | 1 + .../x86_64/op_invoke_virtual_range_quick.S | 1 + runtime/interpreter/mterp/x86_64/op_iput.S | 20 + .../mterp/x86_64/op_iput_boolean.S | 1 + .../mterp/x86_64/op_iput_boolean_quick.S | 1 + .../interpreter/mterp/x86_64/op_iput_byte.S | 1 + .../mterp/x86_64/op_iput_byte_quick.S | 1 + .../interpreter/mterp/x86_64/op_iput_char.S | 1 + .../mterp/x86_64/op_iput_char_quick.S | 1 + .../interpreter/mterp/x86_64/op_iput_object.S | 10 + .../mterp/x86_64/op_iput_object_quick.S | 9 + .../interpreter/mterp/x86_64/op_iput_quick.S | 13 + .../interpreter/mterp/x86_64/op_iput_short.S | 1 + .../mterp/x86_64/op_iput_short_quick.S | 1 + .../interpreter/mterp/x86_64/op_iput_wide.S | 14 + .../mterp/x86_64/op_iput_wide_quick.S | 12 + .../mterp/x86_64/op_long_to_double.S | 1 + .../mterp/x86_64/op_long_to_float.S | 1 + .../interpreter/mterp/x86_64/op_long_to_int.S | 2 + .../mterp/x86_64/op_monitor_enter.S | 11 + .../mterp/x86_64/op_monitor_exit.S | 15 + runtime/interpreter/mterp/x86_64/op_move.S | 13 + runtime/interpreter/mterp/x86_64/op_move_16.S | 12 + .../mterp/x86_64/op_move_exception.S | 5 + .../interpreter/mterp/x86_64/op_move_from16.S | 11 + .../interpreter/mterp/x86_64/op_move_object.S | 1 + .../mterp/x86_64/op_move_object_16.S | 1 + .../mterp/x86_64/op_move_object_from16.S | 1 + .../interpreter/mterp/x86_64/op_move_result.S | 11 + .../mterp/x86_64/op_move_result_object.S | 1 + .../mterp/x86_64/op_move_result_wide.S | 5 + .../interpreter/mterp/x86_64/op_move_wide.S | 8 + .../mterp/x86_64/op_move_wide_16.S | 7 + .../mterp/x86_64/op_move_wide_from16.S | 6 + .../interpreter/mterp/x86_64/op_mul_double.S | 1 + .../mterp/x86_64/op_mul_double_2addr.S | 1 + .../interpreter/mterp/x86_64/op_mul_float.S | 1 + .../mterp/x86_64/op_mul_float_2addr.S | 1 + runtime/interpreter/mterp/x86_64/op_mul_int.S | 1 + .../mterp/x86_64/op_mul_int_2addr.S | 8 + .../mterp/x86_64/op_mul_int_lit16.S | 1 + .../mterp/x86_64/op_mul_int_lit8.S | 1 + .../interpreter/mterp/x86_64/op_mul_long.S | 1 + .../mterp/x86_64/op_mul_long_2addr.S | 8 + .../interpreter/mterp/x86_64/op_neg_double.S | 1 + .../interpreter/mterp/x86_64/op_neg_float.S | 1 + runtime/interpreter/mterp/x86_64/op_neg_int.S | 1 + .../interpreter/mterp/x86_64/op_neg_long.S | 1 + .../interpreter/mterp/x86_64/op_new_array.S | 18 + .../mterp/x86_64/op_new_instance.S | 13 + runtime/interpreter/mterp/x86_64/op_nop.S | 1 + runtime/interpreter/mterp/x86_64/op_not_int.S | 1 + .../interpreter/mterp/x86_64/op_not_long.S | 1 + runtime/interpreter/mterp/x86_64/op_or_int.S | 1 + .../mterp/x86_64/op_or_int_2addr.S | 1 + .../mterp/x86_64/op_or_int_lit16.S | 1 + .../interpreter/mterp/x86_64/op_or_int_lit8.S | 1 + runtime/interpreter/mterp/x86_64/op_or_long.S | 1 + .../mterp/x86_64/op_or_long_2addr.S | 1 + .../mterp/x86_64/op_packed_switch.S | 27 + .../interpreter/mterp/x86_64/op_rem_double.S | 14 + .../mterp/x86_64/op_rem_double_2addr.S | 15 + .../interpreter/mterp/x86_64/op_rem_float.S | 14 + .../mterp/x86_64/op_rem_float_2addr.S | 15 + runtime/interpreter/mterp/x86_64/op_rem_int.S | 1 + .../mterp/x86_64/op_rem_int_2addr.S | 1 + .../mterp/x86_64/op_rem_int_lit16.S | 1 + .../mterp/x86_64/op_rem_int_lit8.S | 1 + .../interpreter/mterp/x86_64/op_rem_long.S | 1 + .../mterp/x86_64/op_rem_long_2addr.S | 1 + runtime/interpreter/mterp/x86_64/op_return.S | 15 + .../mterp/x86_64/op_return_object.S | 1 + .../interpreter/mterp/x86_64/op_return_void.S | 9 + .../mterp/x86_64/op_return_void_no_barrier.S | 7 + .../interpreter/mterp/x86_64/op_return_wide.S | 13 + .../interpreter/mterp/x86_64/op_rsub_int.S | 2 + .../mterp/x86_64/op_rsub_int_lit8.S | 1 + runtime/interpreter/mterp/x86_64/op_sget.S | 25 + .../mterp/x86_64/op_sget_boolean.S | 1 + .../interpreter/mterp/x86_64/op_sget_byte.S | 1 + .../interpreter/mterp/x86_64/op_sget_char.S | 1 + .../interpreter/mterp/x86_64/op_sget_object.S | 1 + .../interpreter/mterp/x86_64/op_sget_short.S | 1 + .../interpreter/mterp/x86_64/op_sget_wide.S | 1 + runtime/interpreter/mterp/x86_64/op_shl_int.S | 1 + .../mterp/x86_64/op_shl_int_2addr.S | 1 + .../mterp/x86_64/op_shl_int_lit8.S | 1 + .../interpreter/mterp/x86_64/op_shl_long.S | 1 + .../mterp/x86_64/op_shl_long_2addr.S | 1 + runtime/interpreter/mterp/x86_64/op_shr_int.S | 1 + .../mterp/x86_64/op_shr_int_2addr.S | 1 + .../mterp/x86_64/op_shr_int_lit8.S | 1 + .../interpreter/mterp/x86_64/op_shr_long.S | 1 + .../mterp/x86_64/op_shr_long_2addr.S | 1 + .../mterp/x86_64/op_sparse_switch.S | 1 + runtime/interpreter/mterp/x86_64/op_sput.S | 17 + .../mterp/x86_64/op_sput_boolean.S | 1 + .../interpreter/mterp/x86_64/op_sput_byte.S | 1 + .../interpreter/mterp/x86_64/op_sput_char.S | 1 + .../interpreter/mterp/x86_64/op_sput_object.S | 10 + .../interpreter/mterp/x86_64/op_sput_short.S | 1 + .../interpreter/mterp/x86_64/op_sput_wide.S | 15 + .../interpreter/mterp/x86_64/op_sub_double.S | 1 + .../mterp/x86_64/op_sub_double_2addr.S | 1 + .../interpreter/mterp/x86_64/op_sub_float.S | 1 + .../mterp/x86_64/op_sub_float_2addr.S | 1 + runtime/interpreter/mterp/x86_64/op_sub_int.S | 1 + .../mterp/x86_64/op_sub_int_2addr.S | 1 + .../interpreter/mterp/x86_64/op_sub_long.S | 1 + .../mterp/x86_64/op_sub_long_2addr.S | 1 + runtime/interpreter/mterp/x86_64/op_throw.S | 10 + .../interpreter/mterp/x86_64/op_unused_3e.S | 1 + .../interpreter/mterp/x86_64/op_unused_3f.S | 1 + .../interpreter/mterp/x86_64/op_unused_40.S | 1 + .../interpreter/mterp/x86_64/op_unused_41.S | 1 + .../interpreter/mterp/x86_64/op_unused_42.S | 1 + .../interpreter/mterp/x86_64/op_unused_43.S | 1 + .../interpreter/mterp/x86_64/op_unused_79.S | 1 + .../interpreter/mterp/x86_64/op_unused_7a.S | 1 + .../interpreter/mterp/x86_64/op_unused_f4.S | 1 + .../interpreter/mterp/x86_64/op_unused_fa.S | 1 + .../interpreter/mterp/x86_64/op_unused_fb.S | 1 + .../interpreter/mterp/x86_64/op_unused_fc.S | 1 + .../interpreter/mterp/x86_64/op_unused_fd.S | 1 + .../interpreter/mterp/x86_64/op_unused_fe.S | 1 + .../interpreter/mterp/x86_64/op_unused_ff.S | 1 + .../interpreter/mterp/x86_64/op_ushr_int.S | 1 + .../mterp/x86_64/op_ushr_int_2addr.S | 1 + .../mterp/x86_64/op_ushr_int_lit8.S | 1 + .../interpreter/mterp/x86_64/op_ushr_long.S | 1 + .../mterp/x86_64/op_ushr_long_2addr.S | 1 + runtime/interpreter/mterp/x86_64/op_xor_int.S | 1 + .../mterp/x86_64/op_xor_int_2addr.S | 1 + .../mterp/x86_64/op_xor_int_lit16.S | 1 + .../mterp/x86_64/op_xor_int_lit8.S | 1 + .../interpreter/mterp/x86_64/op_xor_long.S | 1 + .../mterp/x86_64/op_xor_long_2addr.S | 1 + runtime/interpreter/mterp/x86_64/shop2addr.S | 19 + runtime/interpreter/mterp/x86_64/sseBinop.S | 9 + .../interpreter/mterp/x86_64/sseBinop2Addr.S | 10 + runtime/interpreter/mterp/x86_64/unop.S | 22 + runtime/interpreter/mterp/x86_64/unused.S | 4 + runtime/interpreter/mterp/x86_64/zcmp.S | 24 + 282 files changed, 14216 insertions(+), 260 deletions(-) create mode 100644 runtime/interpreter/mterp/out/mterp_x86_64.S create mode 100644 runtime/interpreter/mterp/x86_64/alt_stub.S create mode 100644 runtime/interpreter/mterp/x86_64/bincmp.S create mode 100644 runtime/interpreter/mterp/x86_64/bindiv.S create mode 100644 runtime/interpreter/mterp/x86_64/bindiv2addr.S create mode 100644 runtime/interpreter/mterp/x86_64/bindivLit16.S create mode 100644 runtime/interpreter/mterp/x86_64/bindivLit8.S create mode 100644 runtime/interpreter/mterp/x86_64/binop.S create mode 100644 runtime/interpreter/mterp/x86_64/binop1.S create mode 100644 runtime/interpreter/mterp/x86_64/binop2addr.S create mode 100644 runtime/interpreter/mterp/x86_64/binopLit16.S create mode 100644 runtime/interpreter/mterp/x86_64/binopLit8.S create mode 100644 runtime/interpreter/mterp/x86_64/binopWide.S create mode 100644 runtime/interpreter/mterp/x86_64/binopWide2addr.S create mode 100644 runtime/interpreter/mterp/x86_64/cvtfp_int.S create mode 100644 runtime/interpreter/mterp/x86_64/entry.S create mode 100644 runtime/interpreter/mterp/x86_64/fallback.S create mode 100644 runtime/interpreter/mterp/x86_64/footer.S create mode 100644 runtime/interpreter/mterp/x86_64/fpcmp.S create mode 100644 runtime/interpreter/mterp/x86_64/fpcvt.S create mode 100644 runtime/interpreter/mterp/x86_64/header.S create mode 100644 runtime/interpreter/mterp/x86_64/invoke.S create mode 100644 runtime/interpreter/mterp/x86_64/op_add_double.S create mode 100644 runtime/interpreter/mterp/x86_64/op_add_double_2addr.S create mode 100644 runtime/interpreter/mterp/x86_64/op_add_float.S create mode 100644 runtime/interpreter/mterp/x86_64/op_add_float_2addr.S create mode 100644 runtime/interpreter/mterp/x86_64/op_add_int.S create mode 100644 runtime/interpreter/mterp/x86_64/op_add_int_2addr.S create mode 100644 runtime/interpreter/mterp/x86_64/op_add_int_lit16.S create mode 100644 runtime/interpreter/mterp/x86_64/op_add_int_lit8.S create mode 100644 runtime/interpreter/mterp/x86_64/op_add_long.S create mode 100644 runtime/interpreter/mterp/x86_64/op_add_long_2addr.S create mode 100644 runtime/interpreter/mterp/x86_64/op_aget.S create mode 100644 runtime/interpreter/mterp/x86_64/op_aget_boolean.S create mode 100644 runtime/interpreter/mterp/x86_64/op_aget_byte.S create mode 100644 runtime/interpreter/mterp/x86_64/op_aget_char.S create mode 100644 runtime/interpreter/mterp/x86_64/op_aget_object.S create mode 100644 runtime/interpreter/mterp/x86_64/op_aget_short.S create mode 100644 runtime/interpreter/mterp/x86_64/op_aget_wide.S create mode 100644 runtime/interpreter/mterp/x86_64/op_and_int.S create mode 100644 runtime/interpreter/mterp/x86_64/op_and_int_2addr.S create mode 100644 runtime/interpreter/mterp/x86_64/op_and_int_lit16.S create mode 100644 runtime/interpreter/mterp/x86_64/op_and_int_lit8.S create mode 100644 runtime/interpreter/mterp/x86_64/op_and_long.S create mode 100644 runtime/interpreter/mterp/x86_64/op_and_long_2addr.S create mode 100644 runtime/interpreter/mterp/x86_64/op_aput.S create mode 100644 runtime/interpreter/mterp/x86_64/op_aput_boolean.S create mode 100644 runtime/interpreter/mterp/x86_64/op_aput_byte.S create mode 100644 runtime/interpreter/mterp/x86_64/op_aput_char.S create mode 100644 runtime/interpreter/mterp/x86_64/op_aput_object.S create mode 100644 runtime/interpreter/mterp/x86_64/op_aput_short.S create mode 100644 runtime/interpreter/mterp/x86_64/op_aput_wide.S create mode 100644 runtime/interpreter/mterp/x86_64/op_array_length.S create mode 100644 runtime/interpreter/mterp/x86_64/op_check_cast.S create mode 100644 runtime/interpreter/mterp/x86_64/op_cmp_long.S create mode 100644 runtime/interpreter/mterp/x86_64/op_cmpg_double.S create mode 100644 runtime/interpreter/mterp/x86_64/op_cmpg_float.S create mode 100644 runtime/interpreter/mterp/x86_64/op_cmpl_double.S create mode 100644 runtime/interpreter/mterp/x86_64/op_cmpl_float.S create mode 100644 runtime/interpreter/mterp/x86_64/op_const.S create mode 100644 runtime/interpreter/mterp/x86_64/op_const_16.S create mode 100644 runtime/interpreter/mterp/x86_64/op_const_4.S create mode 100644 runtime/interpreter/mterp/x86_64/op_const_class.S create mode 100644 runtime/interpreter/mterp/x86_64/op_const_high16.S create mode 100644 runtime/interpreter/mterp/x86_64/op_const_string.S create mode 100644 runtime/interpreter/mterp/x86_64/op_const_string_jumbo.S create mode 100644 runtime/interpreter/mterp/x86_64/op_const_wide.S create mode 100644 runtime/interpreter/mterp/x86_64/op_const_wide_16.S create mode 100644 runtime/interpreter/mterp/x86_64/op_const_wide_32.S create mode 100644 runtime/interpreter/mterp/x86_64/op_const_wide_high16.S create mode 100644 runtime/interpreter/mterp/x86_64/op_div_double.S create mode 100644 runtime/interpreter/mterp/x86_64/op_div_double_2addr.S create mode 100644 runtime/interpreter/mterp/x86_64/op_div_float.S create mode 100644 runtime/interpreter/mterp/x86_64/op_div_float_2addr.S create mode 100644 runtime/interpreter/mterp/x86_64/op_div_int.S create mode 100644 runtime/interpreter/mterp/x86_64/op_div_int_2addr.S create mode 100644 runtime/interpreter/mterp/x86_64/op_div_int_lit16.S create mode 100644 runtime/interpreter/mterp/x86_64/op_div_int_lit8.S create mode 100644 runtime/interpreter/mterp/x86_64/op_div_long.S create mode 100644 runtime/interpreter/mterp/x86_64/op_div_long_2addr.S create mode 100644 runtime/interpreter/mterp/x86_64/op_double_to_float.S create mode 100644 runtime/interpreter/mterp/x86_64/op_double_to_int.S create mode 100644 runtime/interpreter/mterp/x86_64/op_double_to_long.S create mode 100644 runtime/interpreter/mterp/x86_64/op_fill_array_data.S create mode 100644 runtime/interpreter/mterp/x86_64/op_filled_new_array.S create mode 100644 runtime/interpreter/mterp/x86_64/op_filled_new_array_range.S create mode 100644 runtime/interpreter/mterp/x86_64/op_float_to_double.S create mode 100644 runtime/interpreter/mterp/x86_64/op_float_to_int.S create mode 100644 runtime/interpreter/mterp/x86_64/op_float_to_long.S create mode 100644 runtime/interpreter/mterp/x86_64/op_goto.S create mode 100644 runtime/interpreter/mterp/x86_64/op_goto_16.S create mode 100644 runtime/interpreter/mterp/x86_64/op_goto_32.S create mode 100644 runtime/interpreter/mterp/x86_64/op_if_eq.S create mode 100644 runtime/interpreter/mterp/x86_64/op_if_eqz.S create mode 100644 runtime/interpreter/mterp/x86_64/op_if_ge.S create mode 100644 runtime/interpreter/mterp/x86_64/op_if_gez.S create mode 100644 runtime/interpreter/mterp/x86_64/op_if_gt.S create mode 100644 runtime/interpreter/mterp/x86_64/op_if_gtz.S create mode 100644 runtime/interpreter/mterp/x86_64/op_if_le.S create mode 100644 runtime/interpreter/mterp/x86_64/op_if_lez.S create mode 100644 runtime/interpreter/mterp/x86_64/op_if_lt.S create mode 100644 runtime/interpreter/mterp/x86_64/op_if_ltz.S create mode 100644 runtime/interpreter/mterp/x86_64/op_if_ne.S create mode 100644 runtime/interpreter/mterp/x86_64/op_if_nez.S create mode 100644 runtime/interpreter/mterp/x86_64/op_iget.S create mode 100644 runtime/interpreter/mterp/x86_64/op_iget_boolean.S create mode 100644 runtime/interpreter/mterp/x86_64/op_iget_boolean_quick.S create mode 100644 runtime/interpreter/mterp/x86_64/op_iget_byte.S create mode 100644 runtime/interpreter/mterp/x86_64/op_iget_byte_quick.S create mode 100644 runtime/interpreter/mterp/x86_64/op_iget_char.S create mode 100644 runtime/interpreter/mterp/x86_64/op_iget_char_quick.S create mode 100644 runtime/interpreter/mterp/x86_64/op_iget_object.S create mode 100644 runtime/interpreter/mterp/x86_64/op_iget_object_quick.S create mode 100644 runtime/interpreter/mterp/x86_64/op_iget_quick.S create mode 100644 runtime/interpreter/mterp/x86_64/op_iget_short.S create mode 100644 runtime/interpreter/mterp/x86_64/op_iget_short_quick.S create mode 100644 runtime/interpreter/mterp/x86_64/op_iget_wide.S create mode 100644 runtime/interpreter/mterp/x86_64/op_iget_wide_quick.S create mode 100644 runtime/interpreter/mterp/x86_64/op_instance_of.S create mode 100644 runtime/interpreter/mterp/x86_64/op_int_to_byte.S create mode 100644 runtime/interpreter/mterp/x86_64/op_int_to_char.S create mode 100644 runtime/interpreter/mterp/x86_64/op_int_to_double.S create mode 100644 runtime/interpreter/mterp/x86_64/op_int_to_float.S create mode 100644 runtime/interpreter/mterp/x86_64/op_int_to_long.S create mode 100644 runtime/interpreter/mterp/x86_64/op_int_to_short.S create mode 100644 runtime/interpreter/mterp/x86_64/op_invoke_direct.S create mode 100644 runtime/interpreter/mterp/x86_64/op_invoke_direct_range.S create mode 100644 runtime/interpreter/mterp/x86_64/op_invoke_interface.S create mode 100644 runtime/interpreter/mterp/x86_64/op_invoke_interface_range.S create mode 100644 runtime/interpreter/mterp/x86_64/op_invoke_static.S create mode 100644 runtime/interpreter/mterp/x86_64/op_invoke_static_range.S create mode 100644 runtime/interpreter/mterp/x86_64/op_invoke_super.S create mode 100644 runtime/interpreter/mterp/x86_64/op_invoke_super_range.S create mode 100644 runtime/interpreter/mterp/x86_64/op_invoke_virtual.S create mode 100644 runtime/interpreter/mterp/x86_64/op_invoke_virtual_quick.S create mode 100644 runtime/interpreter/mterp/x86_64/op_invoke_virtual_range.S create mode 100644 runtime/interpreter/mterp/x86_64/op_invoke_virtual_range_quick.S create mode 100644 runtime/interpreter/mterp/x86_64/op_iput.S create mode 100644 runtime/interpreter/mterp/x86_64/op_iput_boolean.S create mode 100644 runtime/interpreter/mterp/x86_64/op_iput_boolean_quick.S create mode 100644 runtime/interpreter/mterp/x86_64/op_iput_byte.S create mode 100644 runtime/interpreter/mterp/x86_64/op_iput_byte_quick.S create mode 100644 runtime/interpreter/mterp/x86_64/op_iput_char.S create mode 100644 runtime/interpreter/mterp/x86_64/op_iput_char_quick.S create mode 100644 runtime/interpreter/mterp/x86_64/op_iput_object.S create mode 100644 runtime/interpreter/mterp/x86_64/op_iput_object_quick.S create mode 100644 runtime/interpreter/mterp/x86_64/op_iput_quick.S create mode 100644 runtime/interpreter/mterp/x86_64/op_iput_short.S create mode 100644 runtime/interpreter/mterp/x86_64/op_iput_short_quick.S create mode 100644 runtime/interpreter/mterp/x86_64/op_iput_wide.S create mode 100644 runtime/interpreter/mterp/x86_64/op_iput_wide_quick.S create mode 100644 runtime/interpreter/mterp/x86_64/op_long_to_double.S create mode 100644 runtime/interpreter/mterp/x86_64/op_long_to_float.S create mode 100644 runtime/interpreter/mterp/x86_64/op_long_to_int.S create mode 100644 runtime/interpreter/mterp/x86_64/op_monitor_enter.S create mode 100644 runtime/interpreter/mterp/x86_64/op_monitor_exit.S create mode 100644 runtime/interpreter/mterp/x86_64/op_move.S create mode 100644 runtime/interpreter/mterp/x86_64/op_move_16.S create mode 100644 runtime/interpreter/mterp/x86_64/op_move_exception.S create mode 100644 runtime/interpreter/mterp/x86_64/op_move_from16.S create mode 100644 runtime/interpreter/mterp/x86_64/op_move_object.S create mode 100644 runtime/interpreter/mterp/x86_64/op_move_object_16.S create mode 100644 runtime/interpreter/mterp/x86_64/op_move_object_from16.S create mode 100644 runtime/interpreter/mterp/x86_64/op_move_result.S create mode 100644 runtime/interpreter/mterp/x86_64/op_move_result_object.S create mode 100644 runtime/interpreter/mterp/x86_64/op_move_result_wide.S create mode 100644 runtime/interpreter/mterp/x86_64/op_move_wide.S create mode 100644 runtime/interpreter/mterp/x86_64/op_move_wide_16.S create mode 100644 runtime/interpreter/mterp/x86_64/op_move_wide_from16.S create mode 100644 runtime/interpreter/mterp/x86_64/op_mul_double.S create mode 100644 runtime/interpreter/mterp/x86_64/op_mul_double_2addr.S create mode 100644 runtime/interpreter/mterp/x86_64/op_mul_float.S create mode 100644 runtime/interpreter/mterp/x86_64/op_mul_float_2addr.S create mode 100644 runtime/interpreter/mterp/x86_64/op_mul_int.S create mode 100644 runtime/interpreter/mterp/x86_64/op_mul_int_2addr.S create mode 100644 runtime/interpreter/mterp/x86_64/op_mul_int_lit16.S create mode 100644 runtime/interpreter/mterp/x86_64/op_mul_int_lit8.S create mode 100644 runtime/interpreter/mterp/x86_64/op_mul_long.S create mode 100644 runtime/interpreter/mterp/x86_64/op_mul_long_2addr.S create mode 100644 runtime/interpreter/mterp/x86_64/op_neg_double.S create mode 100644 runtime/interpreter/mterp/x86_64/op_neg_float.S create mode 100644 runtime/interpreter/mterp/x86_64/op_neg_int.S create mode 100644 runtime/interpreter/mterp/x86_64/op_neg_long.S create mode 100644 runtime/interpreter/mterp/x86_64/op_new_array.S create mode 100644 runtime/interpreter/mterp/x86_64/op_new_instance.S create mode 100644 runtime/interpreter/mterp/x86_64/op_nop.S create mode 100644 runtime/interpreter/mterp/x86_64/op_not_int.S create mode 100644 runtime/interpreter/mterp/x86_64/op_not_long.S create mode 100644 runtime/interpreter/mterp/x86_64/op_or_int.S create mode 100644 runtime/interpreter/mterp/x86_64/op_or_int_2addr.S create mode 100644 runtime/interpreter/mterp/x86_64/op_or_int_lit16.S create mode 100644 runtime/interpreter/mterp/x86_64/op_or_int_lit8.S create mode 100644 runtime/interpreter/mterp/x86_64/op_or_long.S create mode 100644 runtime/interpreter/mterp/x86_64/op_or_long_2addr.S create mode 100644 runtime/interpreter/mterp/x86_64/op_packed_switch.S create mode 100644 runtime/interpreter/mterp/x86_64/op_rem_double.S create mode 100644 runtime/interpreter/mterp/x86_64/op_rem_double_2addr.S create mode 100644 runtime/interpreter/mterp/x86_64/op_rem_float.S create mode 100644 runtime/interpreter/mterp/x86_64/op_rem_float_2addr.S create mode 100644 runtime/interpreter/mterp/x86_64/op_rem_int.S create mode 100644 runtime/interpreter/mterp/x86_64/op_rem_int_2addr.S create mode 100644 runtime/interpreter/mterp/x86_64/op_rem_int_lit16.S create mode 100644 runtime/interpreter/mterp/x86_64/op_rem_int_lit8.S create mode 100644 runtime/interpreter/mterp/x86_64/op_rem_long.S create mode 100644 runtime/interpreter/mterp/x86_64/op_rem_long_2addr.S create mode 100644 runtime/interpreter/mterp/x86_64/op_return.S create mode 100644 runtime/interpreter/mterp/x86_64/op_return_object.S create mode 100644 runtime/interpreter/mterp/x86_64/op_return_void.S create mode 100644 runtime/interpreter/mterp/x86_64/op_return_void_no_barrier.S create mode 100644 runtime/interpreter/mterp/x86_64/op_return_wide.S create mode 100644 runtime/interpreter/mterp/x86_64/op_rsub_int.S create mode 100644 runtime/interpreter/mterp/x86_64/op_rsub_int_lit8.S create mode 100644 runtime/interpreter/mterp/x86_64/op_sget.S create mode 100644 runtime/interpreter/mterp/x86_64/op_sget_boolean.S create mode 100644 runtime/interpreter/mterp/x86_64/op_sget_byte.S create mode 100644 runtime/interpreter/mterp/x86_64/op_sget_char.S create mode 100644 runtime/interpreter/mterp/x86_64/op_sget_object.S create mode 100644 runtime/interpreter/mterp/x86_64/op_sget_short.S create mode 100644 runtime/interpreter/mterp/x86_64/op_sget_wide.S create mode 100644 runtime/interpreter/mterp/x86_64/op_shl_int.S create mode 100644 runtime/interpreter/mterp/x86_64/op_shl_int_2addr.S create mode 100644 runtime/interpreter/mterp/x86_64/op_shl_int_lit8.S create mode 100644 runtime/interpreter/mterp/x86_64/op_shl_long.S create mode 100644 runtime/interpreter/mterp/x86_64/op_shl_long_2addr.S create mode 100644 runtime/interpreter/mterp/x86_64/op_shr_int.S create mode 100644 runtime/interpreter/mterp/x86_64/op_shr_int_2addr.S create mode 100644 runtime/interpreter/mterp/x86_64/op_shr_int_lit8.S create mode 100644 runtime/interpreter/mterp/x86_64/op_shr_long.S create mode 100644 runtime/interpreter/mterp/x86_64/op_shr_long_2addr.S create mode 100644 runtime/interpreter/mterp/x86_64/op_sparse_switch.S create mode 100644 runtime/interpreter/mterp/x86_64/op_sput.S create mode 100644 runtime/interpreter/mterp/x86_64/op_sput_boolean.S create mode 100644 runtime/interpreter/mterp/x86_64/op_sput_byte.S create mode 100644 runtime/interpreter/mterp/x86_64/op_sput_char.S create mode 100644 runtime/interpreter/mterp/x86_64/op_sput_object.S create mode 100644 runtime/interpreter/mterp/x86_64/op_sput_short.S create mode 100644 runtime/interpreter/mterp/x86_64/op_sput_wide.S create mode 100644 runtime/interpreter/mterp/x86_64/op_sub_double.S create mode 100644 runtime/interpreter/mterp/x86_64/op_sub_double_2addr.S create mode 100644 runtime/interpreter/mterp/x86_64/op_sub_float.S create mode 100644 runtime/interpreter/mterp/x86_64/op_sub_float_2addr.S create mode 100644 runtime/interpreter/mterp/x86_64/op_sub_int.S create mode 100644 runtime/interpreter/mterp/x86_64/op_sub_int_2addr.S create mode 100644 runtime/interpreter/mterp/x86_64/op_sub_long.S create mode 100644 runtime/interpreter/mterp/x86_64/op_sub_long_2addr.S create mode 100644 runtime/interpreter/mterp/x86_64/op_throw.S create mode 100644 runtime/interpreter/mterp/x86_64/op_unused_3e.S create mode 100644 runtime/interpreter/mterp/x86_64/op_unused_3f.S create mode 100644 runtime/interpreter/mterp/x86_64/op_unused_40.S create mode 100644 runtime/interpreter/mterp/x86_64/op_unused_41.S create mode 100644 runtime/interpreter/mterp/x86_64/op_unused_42.S create mode 100644 runtime/interpreter/mterp/x86_64/op_unused_43.S create mode 100644 runtime/interpreter/mterp/x86_64/op_unused_79.S create mode 100644 runtime/interpreter/mterp/x86_64/op_unused_7a.S create mode 100644 runtime/interpreter/mterp/x86_64/op_unused_f4.S create mode 100644 runtime/interpreter/mterp/x86_64/op_unused_fa.S create mode 100644 runtime/interpreter/mterp/x86_64/op_unused_fb.S create mode 100644 runtime/interpreter/mterp/x86_64/op_unused_fc.S create mode 100644 runtime/interpreter/mterp/x86_64/op_unused_fd.S create mode 100644 runtime/interpreter/mterp/x86_64/op_unused_fe.S create mode 100644 runtime/interpreter/mterp/x86_64/op_unused_ff.S create mode 100644 runtime/interpreter/mterp/x86_64/op_ushr_int.S create mode 100644 runtime/interpreter/mterp/x86_64/op_ushr_int_2addr.S create mode 100644 runtime/interpreter/mterp/x86_64/op_ushr_int_lit8.S create mode 100644 runtime/interpreter/mterp/x86_64/op_ushr_long.S create mode 100644 runtime/interpreter/mterp/x86_64/op_ushr_long_2addr.S create mode 100644 runtime/interpreter/mterp/x86_64/op_xor_int.S create mode 100644 runtime/interpreter/mterp/x86_64/op_xor_int_2addr.S create mode 100644 runtime/interpreter/mterp/x86_64/op_xor_int_lit16.S create mode 100644 runtime/interpreter/mterp/x86_64/op_xor_int_lit8.S create mode 100644 runtime/interpreter/mterp/x86_64/op_xor_long.S create mode 100644 runtime/interpreter/mterp/x86_64/op_xor_long_2addr.S create mode 100644 runtime/interpreter/mterp/x86_64/shop2addr.S create mode 100644 runtime/interpreter/mterp/x86_64/sseBinop.S create mode 100644 runtime/interpreter/mterp/x86_64/sseBinop2Addr.S create mode 100644 runtime/interpreter/mterp/x86_64/unop.S create mode 100644 runtime/interpreter/mterp/x86_64/unused.S create mode 100644 runtime/interpreter/mterp/x86_64/zcmp.S diff --git a/runtime/Android.mk b/runtime/Android.mk index e9f7add1af..624f7d78ab 100644 --- a/runtime/Android.mk +++ b/runtime/Android.mk @@ -291,7 +291,8 @@ LIBART_TARGET_SRC_FILES_x86 := \ # Note that the fault_handler_x86.cc is not a mistake. This file is # shared between the x86 and x86_64 architectures. LIBART_SRC_FILES_x86_64 := \ - interpreter/mterp/mterp_stub.cc \ + interpreter/mterp/mterp.cc \ + interpreter/mterp/out/mterp_x86_64.S \ arch/x86_64/context_x86_64.cc \ arch/x86_64/entrypoints_init_x86_64.cc \ arch/x86_64/jni_entrypoints_x86_64.S \ diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc index 4fd3c78f44..c77daa04bd 100644 --- a/runtime/interpreter/interpreter.cc +++ b/runtime/interpreter/interpreter.cc @@ -241,7 +241,7 @@ static std::ostream& operator<<(std::ostream& os, const InterpreterImplKind& rhs } #if !defined(__clang__) -#if (defined(__arm__) || defined(__i386__) || defined(__aarch64__)) +#if (defined(__arm__) || defined(__i386__) || defined(__aarch64__) || defined(__x86_64__)) // TODO: remove when all targets implemented. static constexpr InterpreterImplKind kInterpreterImplKind = kMterpImplKind; #else @@ -249,7 +249,7 @@ static constexpr InterpreterImplKind kInterpreterImplKind = kComputedGotoImplKin #endif #else // Clang 3.4 fails to build the goto interpreter implementation. -#if (defined(__arm__) || defined(__i386__) || defined(__aarch64__)) +#if (defined(__arm__) || defined(__i386__) || defined(__aarch64__) || defined(__x86_64__)) static constexpr InterpreterImplKind kInterpreterImplKind = kMterpImplKind; #else static constexpr InterpreterImplKind kInterpreterImplKind = kSwitchImplKind; diff --git a/runtime/interpreter/mterp/config_x86_64 b/runtime/interpreter/mterp/config_x86_64 index a002dc2873..1d7eb038bb 100644 --- a/runtime/interpreter/mterp/config_x86_64 +++ b/runtime/interpreter/mterp/config_x86_64 @@ -19,6 +19,10 @@ handler-style computed-goto handler-size 128 +function-type-format FUNCTION_TYPE(%s) +function-size-format SIZE(%s,%s) +global-name-format SYMBOL(%s) + # source for alternate entry stub asm-alt-stub x86_64/alt_stub.S @@ -36,262 +40,262 @@ op-start x86_64 # (override example:) op OP_SUB_FLOAT_2ADDR arm-vfp # (fallback example:) op OP_SUB_FLOAT_2ADDR FALLBACK - op op_nop FALLBACK - op op_move FALLBACK - op op_move_from16 FALLBACK - op op_move_16 FALLBACK - op op_move_wide FALLBACK - op op_move_wide_from16 FALLBACK - op op_move_wide_16 FALLBACK - op op_move_object FALLBACK - op op_move_object_from16 FALLBACK - op op_move_object_16 FALLBACK - op op_move_result FALLBACK - op op_move_result_wide FALLBACK - op op_move_result_object FALLBACK - op op_move_exception FALLBACK - op op_return_void FALLBACK - op op_return FALLBACK - op op_return_wide FALLBACK - op op_return_object FALLBACK - op op_const_4 FALLBACK - op op_const_16 FALLBACK - op op_const FALLBACK - op op_const_high16 FALLBACK - op op_const_wide_16 FALLBACK - op op_const_wide_32 FALLBACK - op op_const_wide FALLBACK - op op_const_wide_high16 FALLBACK - op op_const_string FALLBACK - op op_const_string_jumbo FALLBACK - op op_const_class FALLBACK - op op_monitor_enter FALLBACK - op op_monitor_exit FALLBACK - op op_check_cast FALLBACK - op op_instance_of FALLBACK - op op_array_length FALLBACK - op op_new_instance FALLBACK - op op_new_array FALLBACK - op op_filled_new_array FALLBACK - op op_filled_new_array_range FALLBACK - op op_fill_array_data FALLBACK - op op_throw FALLBACK - op op_goto FALLBACK - op op_goto_16 FALLBACK - op op_goto_32 FALLBACK - op op_packed_switch FALLBACK - op op_sparse_switch FALLBACK - op op_cmpl_float FALLBACK - op op_cmpg_float FALLBACK - op op_cmpl_double FALLBACK - op op_cmpg_double FALLBACK - op op_cmp_long FALLBACK - op op_if_eq FALLBACK - op op_if_ne FALLBACK - op op_if_lt FALLBACK - op op_if_ge FALLBACK - op op_if_gt FALLBACK - op op_if_le FALLBACK - op op_if_eqz FALLBACK - op op_if_nez FALLBACK - op op_if_ltz FALLBACK - op op_if_gez FALLBACK - op op_if_gtz FALLBACK - op op_if_lez FALLBACK - op_unused_3e FALLBACK - op_unused_3f FALLBACK - op_unused_40 FALLBACK - op_unused_41 FALLBACK - op_unused_42 FALLBACK - op_unused_43 FALLBACK - op op_aget FALLBACK - op op_aget_wide FALLBACK - op op_aget_object FALLBACK - op op_aget_boolean FALLBACK - op op_aget_byte FALLBACK - op op_aget_char FALLBACK - op op_aget_short FALLBACK - op op_aput FALLBACK - op op_aput_wide FALLBACK - op op_aput_object FALLBACK - op op_aput_boolean FALLBACK - op op_aput_byte FALLBACK - op op_aput_char FALLBACK - op op_aput_short FALLBACK - op op_iget FALLBACK - op op_iget_wide FALLBACK - op op_iget_object FALLBACK - op op_iget_boolean FALLBACK - op op_iget_byte FALLBACK - op op_iget_char FALLBACK - op op_iget_short FALLBACK - op op_iput FALLBACK - op op_iput_wide FALLBACK - op op_iput_object FALLBACK - op op_iput_boolean FALLBACK - op op_iput_byte FALLBACK - op op_iput_char FALLBACK - op op_iput_short FALLBACK - op op_sget FALLBACK - op op_sget_wide FALLBACK - op op_sget_object FALLBACK - op op_sget_boolean FALLBACK - op op_sget_byte FALLBACK - op op_sget_char FALLBACK - op op_sget_short FALLBACK - op op_sput FALLBACK - op op_sput_wide FALLBACK - op op_sput_object FALLBACK - op op_sput_boolean FALLBACK - op op_sput_byte FALLBACK - op op_sput_char FALLBACK - op op_sput_short FALLBACK - op op_invoke_virtual FALLBACK - op op_invoke_super FALLBACK - op op_invoke_direct FALLBACK - op op_invoke_static FALLBACK - op op_invoke_interface FALLBACK - op op_return_void_no_barrier FALLBACK - op op_invoke_virtual_range FALLBACK - op op_invoke_super_range FALLBACK - op op_invoke_direct_range FALLBACK - op op_invoke_static_range FALLBACK - op op_invoke_interface_range FALLBACK - op_unused_79 FALLBACK - op_unused_7a FALLBACK - op op_neg_int FALLBACK - op op_not_int FALLBACK - op op_neg_long FALLBACK - op op_not_long FALLBACK - op op_neg_float FALLBACK - op op_neg_double FALLBACK - op op_int_to_long FALLBACK - op op_int_to_float FALLBACK - op op_int_to_double FALLBACK - op op_long_to_int FALLBACK - op op_long_to_float FALLBACK - op op_long_to_double FALLBACK - op op_float_to_int FALLBACK - op op_float_to_long FALLBACK - op op_float_to_double FALLBACK - op op_double_to_int FALLBACK - op op_double_to_long FALLBACK - op op_double_to_float FALLBACK - op op_int_to_byte FALLBACK - op op_int_to_char FALLBACK - op op_int_to_short FALLBACK - op op_add_int FALLBACK - op op_sub_int FALLBACK - op op_mul_int FALLBACK - op op_div_int FALLBACK - op op_rem_int FALLBACK - op op_and_int FALLBACK - op op_or_int FALLBACK - op op_xor_int FALLBACK - op op_shl_int FALLBACK - op op_shr_int FALLBACK - op op_ushr_int FALLBACK - op op_add_long FALLBACK - op op_sub_long FALLBACK - op op_mul_long FALLBACK - op op_div_long FALLBACK - op op_rem_long FALLBACK - op op_and_long FALLBACK - op op_or_long FALLBACK - op op_xor_long FALLBACK - op op_shl_long FALLBACK - op op_shr_long FALLBACK - op op_ushr_long FALLBACK - op op_add_float FALLBACK - op op_sub_float FALLBACK - op op_mul_float FALLBACK - op op_div_float FALLBACK - op op_rem_float FALLBACK - op op_add_double FALLBACK - op op_sub_double FALLBACK - op op_mul_double FALLBACK - op op_div_double FALLBACK - op op_rem_double FALLBACK - op op_add_int_2addr FALLBACK - op op_sub_int_2addr FALLBACK - op op_mul_int_2addr FALLBACK - op op_div_int_2addr FALLBACK - op op_rem_int_2addr FALLBACK - op op_and_int_2addr FALLBACK - op op_or_int_2addr FALLBACK - op op_xor_int_2addr FALLBACK - op op_shl_int_2addr FALLBACK - op op_shr_int_2addr FALLBACK - op op_ushr_int_2addr FALLBACK - op op_add_long_2addr FALLBACK - op op_sub_long_2addr FALLBACK - op op_mul_long_2addr FALLBACK - op op_div_long_2addr FALLBACK - op op_rem_long_2addr FALLBACK - op op_and_long_2addr FALLBACK - op op_or_long_2addr FALLBACK - op op_xor_long_2addr FALLBACK - op op_shl_long_2addr FALLBACK - op op_shr_long_2addr FALLBACK - op op_ushr_long_2addr FALLBACK - op op_add_float_2addr FALLBACK - op op_sub_float_2addr FALLBACK - op op_mul_float_2addr FALLBACK - op op_div_float_2addr FALLBACK - op op_rem_float_2addr FALLBACK - op op_add_double_2addr FALLBACK - op op_sub_double_2addr FALLBACK - op op_mul_double_2addr FALLBACK - op op_div_double_2addr FALLBACK - op op_rem_double_2addr FALLBACK - op op_add_int_lit16 FALLBACK - op op_rsub_int FALLBACK - op op_mul_int_lit16 FALLBACK - op op_div_int_lit16 FALLBACK - op op_rem_int_lit16 FALLBACK - op op_and_int_lit16 FALLBACK - op op_or_int_lit16 FALLBACK - op op_xor_int_lit16 FALLBACK - op op_add_int_lit8 FALLBACK - op op_rsub_int_lit8 FALLBACK - op op_mul_int_lit8 FALLBACK - op op_div_int_lit8 FALLBACK - op op_rem_int_lit8 FALLBACK - op op_and_int_lit8 FALLBACK - op op_or_int_lit8 FALLBACK - op op_xor_int_lit8 FALLBACK - op op_shl_int_lit8 FALLBACK - op op_shr_int_lit8 FALLBACK - op op_ushr_int_lit8 FALLBACK - op op_iget_quick FALLBACK - op op_iget_wide_quick FALLBACK - op op_iget_object_quick FALLBACK - op op_iput_quick FALLBACK - op op_iput_wide_quick FALLBACK - op op_iput_object_quick FALLBACK - op op_invoke_virtual_quick FALLBACK - op op_invoke_virtual_range_quick FALLBACK - op op_iput_boolean_quick FALLBACK - op op_iput_byte_quick FALLBACK - op op_iput_char_quick FALLBACK - op op_iput_short_quick FALLBACK - op op_iget_boolean_quick FALLBACK - op op_iget_byte_quick FALLBACK - op op_iget_char_quick FALLBACK - op op_iget_short_quick FALLBACK - op_unused_f3 FALLBACK - op_unused_f4 FALLBACK - op_unused_f5 FALLBACK - op_unused_f6 FALLBACK - op_unused_f7 FALLBACK - op_unused_f8 FALLBACK - op_unused_f9 FALLBACK - op_unused_fa FALLBACK - op_unused_fb FALLBACK - op_unused_fc FALLBACK - op_unused_fd FALLBACK - op_unused_fe FALLBACK - op_unused_ff FALLBACK + # op op_nop FALLBACK + # op op_move FALLBACK + # op op_move_from16 FALLBACK + # op op_move_16 FALLBACK + # op op_move_wide FALLBACK + # op op_move_wide_from16 FALLBACK + # op op_move_wide_16 FALLBACK + # op op_move_object FALLBACK + # op op_move_object_from16 FALLBACK + # op op_move_object_16 FALLBACK + # op op_move_result FALLBACK + # op op_move_result_wide FALLBACK + # op op_move_result_object FALLBACK + # op op_move_exception FALLBACK + # op op_return_void FALLBACK + # op op_return FALLBACK + # op op_return_wide FALLBACK + # op op_return_object FALLBACK + # op op_const_4 FALLBACK + # op op_const_16 FALLBACK + # op op_const FALLBACK + # op op_const_high16 FALLBACK + # op op_const_wide_16 FALLBACK + # op op_const_wide_32 FALLBACK + # op op_const_wide FALLBACK + # op op_const_wide_high16 FALLBACK + # op op_const_string FALLBACK + # op op_const_string_jumbo FALLBACK + # op op_const_class FALLBACK + # op op_monitor_enter FALLBACK + # op op_monitor_exit FALLBACK + # op op_check_cast FALLBACK + # op op_instance_of FALLBACK + # op op_array_length FALLBACK + # op op_new_instance FALLBACK + # op op_new_array FALLBACK + # op op_filled_new_array FALLBACK + # op op_filled_new_array_range FALLBACK + # op op_fill_array_data FALLBACK + # op op_throw FALLBACK + # op op_goto FALLBACK + # op op_goto_16 FALLBACK + # op op_goto_32 FALLBACK + # op op_packed_switch FALLBACK + # op op_sparse_switch FALLBACK + # op op_cmpl_float FALLBACK + # op op_cmpg_float FALLBACK + # op op_cmpl_double FALLBACK + # op op_cmpg_double FALLBACK + # op op_cmp_long FALLBACK + # op op_if_eq FALLBACK + # op op_if_ne FALLBACK + # op op_if_lt FALLBACK + # op op_if_ge FALLBACK + # op op_if_gt FALLBACK + # op op_if_le FALLBACK + # op op_if_eqz FALLBACK + # op op_if_nez FALLBACK + # op op_if_ltz FALLBACK + # op op_if_gez FALLBACK + # op op_if_gtz FALLBACK + # op op_if_lez FALLBACK + # op op_unused_3e FALLBACK + # op op_unused_3f FALLBACK + # op op_unused_40 FALLBACK + # op op_unused_41 FALLBACK + # op op_unused_42 FALLBACK + # op op_unused_43 FALLBACK + # op op_aget FALLBACK + # op op_aget_wide FALLBACK + # op op_aget_object FALLBACK + # op op_aget_boolean FALLBACK + # op op_aget_byte FALLBACK + # op op_aget_char FALLBACK + # op op_aget_short FALLBACK + # op op_aput FALLBACK + # op op_aput_wide FALLBACK + # op op_aput_object FALLBACK + # op op_aput_boolean FALLBACK + # op op_aput_byte FALLBACK + # op op_aput_char FALLBACK + # op op_aput_short FALLBACK + # op op_iget FALLBACK + # op op_iget_wide FALLBACK + # op op_iget_object FALLBACK + # op op_iget_boolean FALLBACK + # op op_iget_byte FALLBACK + # op op_iget_char FALLBACK + # op op_iget_short FALLBACK + # op op_iput FALLBACK + # op op_iput_wide FALLBACK + # op op_iput_object FALLBACK + # op op_iput_boolean FALLBACK + # op op_iput_byte FALLBACK + # op op_iput_char FALLBACK + # op op_iput_short FALLBACK + # op op_sget FALLBACK + # op op_sget_wide FALLBACK + # op op_sget_object FALLBACK + # op op_sget_boolean FALLBACK + # op op_sget_byte FALLBACK + # op op_sget_char FALLBACK + # op op_sget_short FALLBACK + # op op_sput FALLBACK + # op op_sput_wide FALLBACK + # op op_sput_object FALLBACK + # op op_sput_boolean FALLBACK + # op op_sput_byte FALLBACK + # op op_sput_char FALLBACK + # op op_sput_short FALLBACK + # op op_invoke_virtual FALLBACK + # op op_invoke_super FALLBACK + # op op_invoke_direct FALLBACK + # op op_invoke_static FALLBACK + # op op_invoke_interface FALLBACK + # op op_return_void_no_barrier FALLBACK + # op op_invoke_virtual_range FALLBACK + # op op_invoke_super_range FALLBACK + # op op_invoke_direct_range FALLBACK + # op op_invoke_static_range FALLBACK + # op op_invoke_interface_range FALLBACK + # op op_unused_79 FALLBACK + # op op_unused_7a FALLBACK + # op op_neg_int FALLBACK + # op op_not_int FALLBACK + # op op_neg_long FALLBACK + # op op_not_long FALLBACK + # op op_neg_float FALLBACK + # op op_neg_double FALLBACK + # op op_int_to_long FALLBACK + # op op_int_to_float FALLBACK + # op op_int_to_double FALLBACK + # op op_long_to_int FALLBACK + # op op_long_to_float FALLBACK + # op op_long_to_double FALLBACK + # op op_float_to_int FALLBACK + # op op_float_to_long FALLBACK + # op op_float_to_double FALLBACK + # op op_double_to_int FALLBACK + # op op_double_to_long FALLBACK + # op op_double_to_float FALLBACK + # op op_int_to_byte FALLBACK + # op op_int_to_char FALLBACK + # op op_int_to_short FALLBACK + # op op_add_int FALLBACK + # op op_sub_int FALLBACK + # op op_mul_int FALLBACK + # op op_div_int FALLBACK + # op op_rem_int FALLBACK + # op op_and_int FALLBACK + # op op_or_int FALLBACK + # op op_xor_int FALLBACK + # op op_shl_int FALLBACK + # op op_shr_int FALLBACK + # op op_ushr_int FALLBACK + # op op_add_long FALLBACK + # op op_sub_long FALLBACK + # op op_mul_long FALLBACK + # op op_div_long FALLBACK + # op op_rem_long FALLBACK + # op op_and_long FALLBACK + # op op_or_long FALLBACK + # op op_xor_long FALLBACK + # op op_shl_long FALLBACK + # op op_shr_long FALLBACK + # op op_ushr_long FALLBACK + # op op_add_float FALLBACK + # op op_sub_float FALLBACK + # op op_mul_float FALLBACK + # op op_div_float FALLBACK + # op op_rem_float FALLBACK + # op op_add_double FALLBACK + # op op_sub_double FALLBACK + # op op_mul_double FALLBACK + # op op_div_double FALLBACK + # op op_rem_double FALLBACK + # op op_add_int_2addr FALLBACK + # op op_sub_int_2addr FALLBACK + # op op_mul_int_2addr FALLBACK + # op op_div_int_2addr FALLBACK + # op op_rem_int_2addr FALLBACK + # op op_and_int_2addr FALLBACK + # op op_or_int_2addr FALLBACK + # op op_xor_int_2addr FALLBACK + # op op_shl_int_2addr FALLBACK + # op op_shr_int_2addr FALLBACK + # op op_ushr_int_2addr FALLBACK + # op op_add_long_2addr FALLBACK + # op op_sub_long_2addr FALLBACK + # op op_mul_long_2addr FALLBACK + # op op_div_long_2addr FALLBACK + # op op_rem_long_2addr FALLBACK + # op op_and_long_2addr FALLBACK + # op op_or_long_2addr FALLBACK + # op op_xor_long_2addr FALLBACK + # op op_shl_long_2addr FALLBACK + # op op_shr_long_2addr FALLBACK + # op op_ushr_long_2addr FALLBACK + # op op_add_float_2addr FALLBACK + # op op_sub_float_2addr FALLBACK + # op op_mul_float_2addr FALLBACK + # op op_div_float_2addr FALLBACK + # op op_rem_float_2addr FALLBACK + # op op_add_double_2addr FALLBACK + # op op_sub_double_2addr FALLBACK + # op op_mul_double_2addr FALLBACK + # op op_div_double_2addr FALLBACK + # op op_rem_double_2addr FALLBACK + # op op_add_int_lit16 FALLBACK + # op op_rsub_int FALLBACK + # op op_mul_int_lit16 FALLBACK + # op op_div_int_lit16 FALLBACK + # op op_rem_int_lit16 FALLBACK + # op op_and_int_lit16 FALLBACK + # op op_or_int_lit16 FALLBACK + # op op_xor_int_lit16 FALLBACK + # op op_add_int_lit8 FALLBACK + # op op_rsub_int_lit8 FALLBACK + # op op_mul_int_lit8 FALLBACK + # op op_div_int_lit8 FALLBACK + # op op_rem_int_lit8 FALLBACK + # op op_and_int_lit8 FALLBACK + # op op_or_int_lit8 FALLBACK + # op op_xor_int_lit8 FALLBACK + # op op_shl_int_lit8 FALLBACK + # op op_shr_int_lit8 FALLBACK + # op op_ushr_int_lit8 FALLBACK + # op op_iget_quick FALLBACK + # op op_iget_wide_quick FALLBACK + # op op_iget_object_quick FALLBACK + # op op_iput_quick FALLBACK + # op op_iput_wide_quick FALLBACK + # op op_iput_object_quick FALLBACK + # op op_invoke_virtual_quick FALLBACK + # op op_invoke_virtual_range_quick FALLBACK + # op op_iput_boolean_quick FALLBACK + # op op_iput_byte_quick FALLBACK + # op op_iput_char_quick FALLBACK + # op op_iput_short_quick FALLBACK + # op op_iget_boolean_quick FALLBACK + # op op_iget_byte_quick FALLBACK + # op op_iget_char_quick FALLBACK + # op op_iget_short_quick FALLBACK + op op_invoke_lambda FALLBACK + # op op_unused_f4 FALLBACK + op op_capture_variable FALLBACK + op op_create_lambda FALLBACK + op op_liberate_variable FALLBACK + op op_box_lambda FALLBACK + op op_unbox_lambda FALLBACK + # op op_unused_fa FALLBACK + # op op_unused_fb FALLBACK + # op op_unused_fc FALLBACK + # op op_unused_fd FALLBACK + # op op_unused_fe FALLBACK + # op op_unused_ff FALLBACK op-end # common subroutines for asm diff --git a/runtime/interpreter/mterp/out/mterp_x86_64.S b/runtime/interpreter/mterp/out/mterp_x86_64.S new file mode 100644 index 0000000000..70f47fe837 --- /dev/null +++ b/runtime/interpreter/mterp/out/mterp_x86_64.S @@ -0,0 +1,11955 @@ +/* + * This file was generated automatically by gen-mterp.py for 'x86_64'. + * + * --> DO NOT EDIT <-- + */ + +/* File: x86_64/header.S */ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + Art assembly interpreter notes: + + First validate assembly code by implementing ExecuteXXXImpl() style body (doesn't + handle invoke, allows higher-level code to create frame & shadow frame. + + Once that's working, support direct entry code & eliminate shadow frame (and + excess locals allocation. + + Some (hopefully) temporary ugliness. We'll treat rFP as pointing to the + base of the vreg array within the shadow frame. Access the other fields, + dex_pc_, method_ and number_of_vregs_ via negative offsets. For now, we'll continue + the shadow frame mechanism of double-storing object references - via rFP & + number_of_vregs_. + + */ + +/* +x86_64 ABI general notes: + +Caller save set: + rax, rdx, rcx, rsi, rdi, r8-r11, st(0)-st(7) +Callee save set: + rbx, rbp, r12-r15 +Return regs: + 32-bit in eax + 64-bit in rax + fp on xmm0 + +First 8 fp parameters came in xmm0-xmm7. +First 6 non-fp parameters came in rdi, rsi, rdx, rcx, r8, r9. +Other parameters passed on stack, pushed right-to-left. On entry to target, first +param is at 8(%esp). Traditional entry code is: + +Stack must be 16-byte aligned to support SSE in native code. + +If we're not doing variable stack allocation (alloca), the frame pointer can be +eliminated and all arg references adjusted to be esp relative. +*/ + +/* +Mterp and x86_64 notes: + +Some key interpreter variables will be assigned to registers. + + nick reg purpose + rSELF rbp pointer to ThreadSelf. + rPC r12 interpreted program counter, used for fetching instructions + rFP r13 interpreted frame pointer, used for accessing locals and args + rINSTw bx first 16-bit code of current instruction + rINSTbl bl opcode portion of instruction word + rINSTbh bh high byte of inst word, usually contains src/tgt reg names + rIBASE r14 base of instruction handler table + rREFS r15 base of object references in shadow frame. + +Notes: + o High order 16 bits of ebx must be zero on entry to handler + o rPC, rFP, rINSTw/rINSTbl valid on handler entry and exit + o eax and ecx are scratch, rINSTw/ebx sometimes scratch + +Macros are provided for common operations. Each macro MUST emit only +one instruction to make instruction-counting easier. They MUST NOT alter +unspecified registers or condition codes. +*/ + +/* + * This is a #include, not a %include, because we want the C pre-processor + * to expand the macros into assembler assignment statements. + */ +#include "asm_support.h" + +/* + * Handle mac compiler specific + */ +#if defined(__APPLE__) + #define MACRO_LITERAL(value) $(value) + #define FUNCTION_TYPE(name) + #define SIZE(start,end) + // Mac OS' symbols have an _ prefix. + #define SYMBOL(name) _ ## name +#else + #define MACRO_LITERAL(value) $value + #define FUNCTION_TYPE(name) .type name, @function + #define SIZE(start,end) .size start, .-end + #define SYMBOL(name) name +#endif + +/* Frame size must be 16-byte aligned. + * Remember about 8 bytes for return address + */ +#define FRAME_SIZE 56 + +/* Frame diagram while executing ExecuteMterpImpl, high to low addresses */ +#define IN_ARG3 %rcx +#define IN_ARG2 %rdx +#define IN_ARG1 %rsi +#define IN_ARG0 %rdi +#define CALLER_RP (FRAME_SIZE + 0) +/* Spill offsets relative to %esp */ +#define RBX_SPILL (FRAME_SIZE - 8) +#define RBP_SPILL (FRAME_SIZE - 16) +#define R12_SPILL (FRAME_SIZE - 24) +#define R13_SPILL (FRAME_SIZE - 32) +#define R14_SPILL (FRAME_SIZE - 40) +#define R15_SPILL (FRAME_SIZE - 48) +/* Out Arg offsets, relative to %esp */ +#define OUT_ARG3 %rcx +#define OUT_ARG2 %rdx +#define OUT_ARG1 %rsi +#define OUT_ARG0 %rdi +#define OUT_32_ARG3 %ecx +#define OUT_32_ARG2 %edx +#define OUT_32_ARG1 %esi +#define OUT_32_ARG0 %edi +#define OUT_FP_ARG1 %xmm1 +#define OUT_FP_ARG0 %xmm0 + +/* During bringup, we'll use the shadow frame model instead of rFP */ +/* single-purpose registers, given names for clarity */ +#define rSELF %rbp +#define rPC %r12 +#define rFP %r13 +#define rINST %ebx +#define rINSTq %rbx +#define rINSTw %bx +#define rINSTbh %bh +#define rINSTbl %bl +#define rIBASE %r14 +#define rREFS %r15 + +/* + * Instead of holding a pointer to the shadow frame, we keep rFP at the base of the vregs. So, + * to access other shadow frame fields, we need to use a backwards offset. Define those here. + */ +#define OFF_FP(a) (a - SHADOWFRAME_VREGS_OFFSET) +#define OFF_FP_NUMBER_OF_VREGS OFF_FP(SHADOWFRAME_NUMBER_OF_VREGS_OFFSET) +#define OFF_FP_DEX_PC OFF_FP(SHADOWFRAME_DEX_PC_OFFSET) +#define OFF_FP_LINK OFF_FP(SHADOWFRAME_LINK_OFFSET) +#define OFF_FP_METHOD OFF_FP(SHADOWFRAME_METHOD_OFFSET) +#define OFF_FP_RESULT_REGISTER OFF_FP(SHADOWFRAME_RESULT_REGISTER_OFFSET) +#define OFF_FP_DEX_PC_PTR OFF_FP(SHADOWFRAME_DEX_PC_PTR_OFFSET) +#define OFF_FP_CODE_ITEM OFF_FP(SHADOWFRAME_CODE_ITEM_OFFSET) +#define OFF_FP_SHADOWFRAME (-SHADOWFRAME_VREGS_OFFSET) + +/* + * + * The reference interpreter performs explicit suspect checks, which is somewhat wasteful. + * Dalvik's interpreter folded suspend checks into the jump table mechanism, and eventually + * mterp should do so as well. + */ +#define MTERP_SUSPEND 0 + +/* + * "export" the PC to dex_pc field in the shadow frame, f/b/o future exception objects. Must + * be done *before* something throws. + * + * It's okay to do this more than once. + * + * NOTE: the fast interpreter keeps track of dex pc as a direct pointer to the mapped + * dex byte codes. However, the rest of the runtime expects dex pc to be an instruction + * offset into the code_items_[] array. For effiency, we will "export" the + * current dex pc as a direct pointer using the EXPORT_PC macro, and rely on GetDexPC + * to convert to a dex pc when needed. + */ +.macro EXPORT_PC + movq rPC, OFF_FP_DEX_PC_PTR(rFP) +.endm + +/* + * Refresh handler table. + * IBase handles uses the caller save register so we must restore it after each call. + * Also it is used as a result of some 64-bit operations (like imul) and we should + * restore it in such cases also. + * + */ +.macro REFRESH_IBASE + movq THREAD_CURRENT_IBASE_OFFSET(rSELF), rIBASE +.endm + +/* + * Refresh rINST. + * At enter to handler rINST does not contain the opcode number. + * However some utilities require the full value, so this macro + * restores the opcode number. + */ +.macro REFRESH_INST _opnum + movb rINSTbl, rINSTbh + movb $\_opnum, rINSTbl +.endm + +/* + * Fetch the next instruction from rPC into rINSTw. Does not advance rPC. + */ +.macro FETCH_INST + movzwq (rPC), rINSTq +.endm + +/* + * Remove opcode from rINST, compute the address of handler and jump to it. + */ +.macro GOTO_NEXT + movzx rINSTbl,%eax + movzbl rINSTbh,rINST + shll MACRO_LITERAL(7), %eax + addq rIBASE, %rax + jmp *%rax +.endm + +/* + * Advance rPC by instruction count. + */ +.macro ADVANCE_PC _count + leaq 2*\_count(rPC), rPC +.endm + +/* + * Advance rPC by instruction count, fetch instruction and jump to handler. + */ +.macro ADVANCE_PC_FETCH_AND_GOTO_NEXT _count + ADVANCE_PC \_count + FETCH_INST + GOTO_NEXT +.endm + +/* + * Get/set the 32-bit value from a Dalvik register. + */ +#define VREG_ADDRESS(_vreg) (rFP,_vreg,4) +#define VREG_REF_ADDRESS(_vreg) (rREFS,_vreg,4) + +.macro GET_VREG _reg _vreg + movl (rFP,\_vreg,4), \_reg +.endm + +/* Read wide value. */ +.macro GET_WIDE_VREG _reg _vreg + movq (rFP,\_vreg,4), \_reg +.endm + +.macro SET_VREG _reg _vreg + movl \_reg, (rFP,\_vreg,4) + movl MACRO_LITERAL(0), (rREFS,\_vreg,4) +.endm + +/* Write wide value. reg is clobbered. */ +.macro SET_WIDE_VREG _reg _vreg + movq \_reg, (rFP,\_vreg,4) + xorq \_reg, \_reg + movq \_reg, (rREFS,\_vreg,4) +.endm + +.macro SET_VREG_OBJECT _reg _vreg + movl \_reg, (rFP,\_vreg,4) + movl \_reg, (rREFS,\_vreg,4) +.endm + +.macro GET_VREG_HIGH _reg _vreg + movl 4(rFP,\_vreg,4), \_reg +.endm + +.macro SET_VREG_HIGH _reg _vreg + movl \_reg, 4(rFP,\_vreg,4) + movl MACRO_LITERAL(0), 4(rREFS,\_vreg,4) +.endm + +.macro CLEAR_REF _vreg + movl MACRO_LITERAL(0), (rREFS,\_vreg,4) +.endm + +.macro CLEAR_WIDE_REF _vreg + movl MACRO_LITERAL(0), (rREFS,\_vreg,4) + movl MACRO_LITERAL(0), 4(rREFS,\_vreg,4) +.endm + +/* File: x86_64/entry.S */ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * Interpreter entry point. + */ + + .text + .global SYMBOL(ExecuteMterpImpl) + FUNCTION_TYPE(ExecuteMterpImpl) + +/* + * On entry: + * 0 Thread* self + * 1 code_item + * 2 ShadowFrame + * 3 JValue* result_register + * + */ + +SYMBOL(ExecuteMterpImpl): + .cfi_startproc + + /* Allocate frame */ + subq $FRAME_SIZE, %rsp + .cfi_adjust_cfa_offset FRAME_SIZE + + /* Spill callee save regs */ + movq %rbx, RBX_SPILL(%rsp) + movq %rbp, RBP_SPILL(%rsp) + movq %r12, R12_SPILL(%rsp) + movq %r13, R13_SPILL(%rsp) + movq %r14, R14_SPILL(%rsp) + movq %r15, R15_SPILL(%rsp) + + /* Remember the return register */ + movq IN_ARG3, SHADOWFRAME_RESULT_REGISTER_OFFSET(IN_ARG2) + + /* Remember the code_item */ + movq IN_ARG1, SHADOWFRAME_CODE_ITEM_OFFSET(IN_ARG2) + + /* set up "named" registers */ + movl SHADOWFRAME_NUMBER_OF_VREGS_OFFSET(IN_ARG2), %eax + leaq SHADOWFRAME_VREGS_OFFSET(IN_ARG2), rFP + leaq (rFP, %rax, 4), rREFS + movl SHADOWFRAME_DEX_PC_OFFSET(IN_ARG2), %eax + leaq CODEITEM_INSNS_OFFSET(IN_ARG1), rPC + leaq (rPC, %rax, 2), rPC + EXPORT_PC + + /* Starting ibase */ + movq IN_ARG0, rSELF + REFRESH_IBASE + + /* start executing the instruction at rPC */ + FETCH_INST + GOTO_NEXT + /* NOTE: no fallthrough */ + + + .global SYMBOL(artMterpAsmInstructionStart) + FUNCTION_TYPE(SYMBOL(artMterpAsmInstructionStart)) +SYMBOL(artMterpAsmInstructionStart) = .L_op_nop + .text + +/* ------------------------------ */ + .balign 128 +.L_op_nop: /* 0x00 */ +/* File: x86_64/op_nop.S */ + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + +/* ------------------------------ */ + .balign 128 +.L_op_move: /* 0x01 */ +/* File: x86_64/op_move.S */ + /* for move, move-object, long-to-int */ + /* op vA, vB */ + movl rINST, %eax # eax <- BA + andb $0xf, %al # eax <- A + shrl $4, rINST # rINST <- B + GET_VREG %edx, rINSTq + .if 0 + SET_VREG_OBJECT %edx, %rax # fp[A] <- fp[B] + .else + SET_VREG %edx, %rax # fp[A] <- fp[B] + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + +/* ------------------------------ */ + .balign 128 +.L_op_move_from16: /* 0x02 */ +/* File: x86_64/op_move_from16.S */ + /* for: move/from16, move-object/from16 */ + /* op vAA, vBBBB */ + movzwq 2(rPC), %rax # eax <- BBBB + GET_VREG %edx, %rax # edx <- fp[BBBB] + .if 0 + SET_VREG_OBJECT %edx, rINSTq # fp[A] <- fp[B] + .else + SET_VREG %edx, rINSTq # fp[A] <- fp[B] + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_move_16: /* 0x03 */ +/* File: x86_64/op_move_16.S */ + /* for: move/16, move-object/16 */ + /* op vAAAA, vBBBB */ + movzwq 4(rPC), %rcx # ecx <- BBBB + movzwq 2(rPC), %rax # eax <- AAAA + GET_VREG %edx, %rcx + .if 0 + SET_VREG_OBJECT %edx, %rax # fp[A] <- fp[B] + .else + SET_VREG %edx, %rax # fp[A] <- fp[B] + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + +/* ------------------------------ */ + .balign 128 +.L_op_move_wide: /* 0x04 */ +/* File: x86_64/op_move_wide.S */ + /* move-wide vA, vB */ + /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */ + movl rINST, %ecx # ecx <- BA + sarl $4, rINST # rINST <- B + andb $0xf, %cl # ecx <- A + GET_WIDE_VREG %rdx, rINSTq # rdx <- v[B] + SET_WIDE_VREG %rdx, %rcx # v[A] <- rdx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + +/* ------------------------------ */ + .balign 128 +.L_op_move_wide_from16: /* 0x05 */ +/* File: x86_64/op_move_wide_from16.S */ + /* move-wide/from16 vAA, vBBBB */ + /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */ + movzwl 2(rPC), %ecx # ecx <- BBBB + GET_WIDE_VREG %rdx, %rcx # rdx <- v[B] + SET_WIDE_VREG %rdx, rINSTq # v[A] <- rdx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_move_wide_16: /* 0x06 */ +/* File: x86_64/op_move_wide_16.S */ + /* move-wide/16 vAAAA, vBBBB */ + /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */ + movzwq 4(rPC), %rcx # ecx<- BBBB + movzwq 2(rPC), %rax # eax<- AAAA + GET_WIDE_VREG %rdx, %rcx # rdx <- v[B] + SET_WIDE_VREG %rdx, %rax # v[A] <- rdx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + +/* ------------------------------ */ + .balign 128 +.L_op_move_object: /* 0x07 */ +/* File: x86_64/op_move_object.S */ +/* File: x86_64/op_move.S */ + /* for move, move-object, long-to-int */ + /* op vA, vB */ + movl rINST, %eax # eax <- BA + andb $0xf, %al # eax <- A + shrl $4, rINST # rINST <- B + GET_VREG %edx, rINSTq + .if 1 + SET_VREG_OBJECT %edx, %rax # fp[A] <- fp[B] + .else + SET_VREG %edx, %rax # fp[A] <- fp[B] + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_move_object_from16: /* 0x08 */ +/* File: x86_64/op_move_object_from16.S */ +/* File: x86_64/op_move_from16.S */ + /* for: move/from16, move-object/from16 */ + /* op vAA, vBBBB */ + movzwq 2(rPC), %rax # eax <- BBBB + GET_VREG %edx, %rax # edx <- fp[BBBB] + .if 1 + SET_VREG_OBJECT %edx, rINSTq # fp[A] <- fp[B] + .else + SET_VREG %edx, rINSTq # fp[A] <- fp[B] + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_move_object_16: /* 0x09 */ +/* File: x86_64/op_move_object_16.S */ +/* File: x86_64/op_move_16.S */ + /* for: move/16, move-object/16 */ + /* op vAAAA, vBBBB */ + movzwq 4(rPC), %rcx # ecx <- BBBB + movzwq 2(rPC), %rax # eax <- AAAA + GET_VREG %edx, %rcx + .if 1 + SET_VREG_OBJECT %edx, %rax # fp[A] <- fp[B] + .else + SET_VREG %edx, %rax # fp[A] <- fp[B] + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + + +/* ------------------------------ */ + .balign 128 +.L_op_move_result: /* 0x0a */ +/* File: x86_64/op_move_result.S */ + /* for: move-result, move-result-object */ + /* op vAA */ + movq OFF_FP_RESULT_REGISTER(rFP), %rax # get pointer to result JType. + movl (%rax), %eax # r0 <- result.i. + .if 0 + SET_VREG_OBJECT %eax, rINSTq # fp[A] <- fp[B] + .else + SET_VREG %eax, rINSTq # fp[A] <- fp[B] + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + +/* ------------------------------ */ + .balign 128 +.L_op_move_result_wide: /* 0x0b */ +/* File: x86_64/op_move_result_wide.S */ + /* move-result-wide vAA */ + movq OFF_FP_RESULT_REGISTER(rFP), %rax # get pointer to result JType. + movq (%rax), %rdx # Get wide + SET_WIDE_VREG %rdx, rINSTq # v[AA] <- rdx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + +/* ------------------------------ */ + .balign 128 +.L_op_move_result_object: /* 0x0c */ +/* File: x86_64/op_move_result_object.S */ +/* File: x86_64/op_move_result.S */ + /* for: move-result, move-result-object */ + /* op vAA */ + movq OFF_FP_RESULT_REGISTER(rFP), %rax # get pointer to result JType. + movl (%rax), %eax # r0 <- result.i. + .if 1 + SET_VREG_OBJECT %eax, rINSTq # fp[A] <- fp[B] + .else + SET_VREG %eax, rINSTq # fp[A] <- fp[B] + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_move_exception: /* 0x0d */ +/* File: x86_64/op_move_exception.S */ + /* move-exception vAA */ + movl THREAD_EXCEPTION_OFFSET(rSELF), %eax + SET_VREG_OBJECT %eax, rINSTq # fp[AA] <- exception object + movl $0, THREAD_EXCEPTION_OFFSET(rSELF) + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + +/* ------------------------------ */ + .balign 128 +.L_op_return_void: /* 0x0e */ +/* File: x86_64/op_return_void.S */ + .extern MterpThreadFenceForConstructor + call SYMBOL(MterpThreadFenceForConstructor) + testl $(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(rSELF) + jz 1f + movq rSELF, OUT_ARG0 + call SYMBOL(MterpSuspendCheck) +1: + xorq %rax, %rax + jmp MterpReturn + +/* ------------------------------ */ + .balign 128 +.L_op_return: /* 0x0f */ +/* File: x86_64/op_return.S */ +/* + * Return a 32-bit value. + * + * for: return, return-object + */ + /* op vAA */ + .extern MterpThreadFenceForConstructor + call SYMBOL(MterpThreadFenceForConstructor) + testl $(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(rSELF) + jz 1f + movq rSELF, OUT_ARG0 + call SYMBOL(MterpSuspendCheck) +1: + GET_VREG %eax, rINSTq # eax <- vAA + jmp MterpReturn + +/* ------------------------------ */ + .balign 128 +.L_op_return_wide: /* 0x10 */ +/* File: x86_64/op_return_wide.S */ +/* + * Return a 64-bit value. + */ + /* return-wide vAA */ + .extern MterpThreadFenceForConstructor + call SYMBOL(MterpThreadFenceForConstructor) + testl $(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(rSELF) + jz 1f + movq rSELF, OUT_ARG0 + call SYMBOL(MterpSuspendCheck) +1: + GET_WIDE_VREG %rax, rINSTq # eax <- v[AA] + jmp MterpReturn + +/* ------------------------------ */ + .balign 128 +.L_op_return_object: /* 0x11 */ +/* File: x86_64/op_return_object.S */ +/* File: x86_64/op_return.S */ +/* + * Return a 32-bit value. + * + * for: return, return-object + */ + /* op vAA */ + .extern MterpThreadFenceForConstructor + call SYMBOL(MterpThreadFenceForConstructor) + testl $(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(rSELF) + jz 1f + movq rSELF, OUT_ARG0 + call SYMBOL(MterpSuspendCheck) +1: + GET_VREG %eax, rINSTq # eax <- vAA + jmp MterpReturn + + +/* ------------------------------ */ + .balign 128 +.L_op_const_4: /* 0x12 */ +/* File: x86_64/op_const_4.S */ + /* const/4 vA, #+B */ + movsbl rINSTbl, %eax # eax <-ssssssBx + movl $0xf, rINST + andl %eax, rINST # rINST <- A + sarl $4, %eax + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + +/* ------------------------------ */ + .balign 128 +.L_op_const_16: /* 0x13 */ +/* File: x86_64/op_const_16.S */ + /* const/16 vAA, #+BBBB */ + movswl 2(rPC), %ecx # ecx <- ssssBBBB + SET_VREG %ecx, rINSTq # vAA <- ssssBBBB + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_const: /* 0x14 */ +/* File: x86_64/op_const.S */ + /* const vAA, #+BBBBbbbb */ + movl 2(rPC), %eax # grab all 32 bits at once + SET_VREG %eax, rINSTq # vAA<- eax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + +/* ------------------------------ */ + .balign 128 +.L_op_const_high16: /* 0x15 */ +/* File: x86_64/op_const_high16.S */ + /* const/high16 vAA, #+BBBB0000 */ + movzwl 2(rPC), %eax # eax <- 0000BBBB + sall $16, %eax # eax <- BBBB0000 + SET_VREG %eax, rINSTq # vAA <- eax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_const_wide_16: /* 0x16 */ +/* File: x86_64/op_const_wide_16.S */ + /* const-wide/16 vAA, #+BBBB */ + movswq 2(rPC), %rax # rax <- ssssBBBB + SET_WIDE_VREG %rax, rINSTq # store + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_const_wide_32: /* 0x17 */ +/* File: x86_64/op_const_wide_32.S */ + /* const-wide/32 vAA, #+BBBBbbbb */ + movslq 2(rPC), %rax # eax <- ssssssssBBBBbbbb + SET_WIDE_VREG %rax, rINSTq # store + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + +/* ------------------------------ */ + .balign 128 +.L_op_const_wide: /* 0x18 */ +/* File: x86_64/op_const_wide.S */ + /* const-wide vAA, #+HHHHhhhhBBBBbbbb */ + movq 2(rPC), %rax # rax <- HHHHhhhhBBBBbbbb + SET_WIDE_VREG %rax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 5 + +/* ------------------------------ */ + .balign 128 +.L_op_const_wide_high16: /* 0x19 */ +/* File: x86_64/op_const_wide_high16.S */ + /* const-wide/high16 vAA, #+BBBB000000000000 */ + movzwq 2(rPC), %rax # eax <- 0000BBBB + salq $48, %rax # eax <- BBBB0000 + SET_WIDE_VREG %rax, rINSTq # v[AA+0] <- eax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_const_string: /* 0x1a */ +/* File: x86_64/op_const_string.S */ + /* const/string vAA, String@BBBB */ + EXPORT_PC + movzwq 2(rPC), OUT_ARG0 # OUT_ARG0 <- BBBB + movq rINSTq, OUT_ARG1 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG2 + movq rSELF, OUT_ARG3 + call SYMBOL(MterpConstString) # (index, tgt_reg, shadow_frame, self) + testb %al, %al + jnz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_const_string_jumbo: /* 0x1b */ +/* File: x86_64/op_const_string_jumbo.S */ + /* const/string vAA, String@BBBBBBBB */ + EXPORT_PC + movl 2(rPC), OUT_32_ARG0 # OUT_32_ARG0 <- BBBB + movq rINSTq, OUT_ARG1 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG2 + movq rSELF, OUT_ARG3 + call SYMBOL(MterpConstString) # (index, tgt_reg, shadow_frame, self) + testb %al, %al + jnz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + +/* ------------------------------ */ + .balign 128 +.L_op_const_class: /* 0x1c */ +/* File: x86_64/op_const_class.S */ + /* const/class vAA, Class@BBBB */ + EXPORT_PC + movzwq 2(rPC), OUT_ARG0 # eax <- OUT_ARG0 + movq rINSTq, OUT_ARG1 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG2 + movq rSELF, OUT_ARG3 + call SYMBOL(MterpConstClass) # (index, tgt_reg, shadow_frame, self) + testb %al, %al + jnz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_monitor_enter: /* 0x1d */ +/* File: x86_64/op_monitor_enter.S */ +/* + * Synchronize on an object. + */ + /* monitor-enter vAA */ + EXPORT_PC + GET_VREG OUT_32_ARG0, rINSTq + movq rSELF, OUT_ARG1 + call SYMBOL(artLockObjectFromCode) # (object, self) + testq %rax, %rax + jnz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + +/* ------------------------------ */ + .balign 128 +.L_op_monitor_exit: /* 0x1e */ +/* File: x86_64/op_monitor_exit.S */ +/* + * Unlock an object. + * + * Exceptions that occur when unlocking a monitor need to appear as + * if they happened at the following instruction. See the Dalvik + * instruction spec. + */ + /* monitor-exit vAA */ + EXPORT_PC + GET_VREG OUT_32_ARG0, rINSTq + movq rSELF, OUT_ARG1 + call SYMBOL(artUnlockObjectFromCode) # (object, self) + testq %rax, %rax + jnz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + +/* ------------------------------ */ + .balign 128 +.L_op_check_cast: /* 0x1f */ +/* File: x86_64/op_check_cast.S */ +/* + * Check to see if a cast from one class to another is allowed. + */ + /* check-cast vAA, class@BBBB */ + EXPORT_PC + movzwq 2(rPC), OUT_ARG0 # OUT_ARG0 <- BBBB + leaq VREG_ADDRESS(rINSTq), OUT_ARG1 + movq OFF_FP_METHOD(rFP), OUT_ARG2 + movq rSELF, OUT_ARG3 + call SYMBOL(MterpCheckCast) # (index, &obj, method, self) + testb %al, %al + jnz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_instance_of: /* 0x20 */ +/* File: x86_64/op_instance_of.S */ +/* + * Check to see if an object reference is an instance of a class. + * + * Most common situation is a non-null object, being compared against + * an already-resolved class. + */ + /* instance-of vA, vB, class@CCCC */ + EXPORT_PC + movzwl 2(rPC), OUT_32_ARG0 # OUT_32_ARG0 <- CCCC + movl rINST, %eax # eax <- BA + sarl $4, %eax # eax <- B + leaq VREG_ADDRESS(%rax), OUT_ARG1 # Get object address + movq OFF_FP_METHOD(rFP), OUT_ARG2 + movq rSELF, OUT_ARG3 + call SYMBOL(MterpInstanceOf) # (index, &obj, method, self) + movsbl %al, %eax + cmpq $0, THREAD_EXCEPTION_OFFSET(rSELF) + jnz MterpException + andb $0xf, rINSTbl # rINSTbl <- A + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_array_length: /* 0x21 */ +/* File: x86_64/op_array_length.S */ +/* + * Return the length of an array. + */ + movl rINST, %eax # eax <- BA + sarl $4, rINST # rINST <- B + GET_VREG %ecx, rINSTq # ecx <- vB (object ref) + testl %ecx, %ecx # is null? + je common_errNullObject + andb $0xf, %al # eax <- A + movl MIRROR_ARRAY_LENGTH_OFFSET(%rcx), rINST + SET_VREG rINST, %rax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + +/* ------------------------------ */ + .balign 128 +.L_op_new_instance: /* 0x22 */ +/* File: x86_64/op_new_instance.S */ +/* + * Create a new instance of a class. + */ + /* new-instance vAA, class@BBBB */ + EXPORT_PC + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG0 + movq rSELF, OUT_ARG1 + REFRESH_INST 34 + movq rINSTq, OUT_ARG2 + call SYMBOL(MterpNewInstance) + testb %al, %al # 0 means an exception is thrown + jz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_new_array: /* 0x23 */ +/* File: x86_64/op_new_array.S */ +/* + * Allocate an array of objects, specified with the array class + * and a count. + * + * The verifier guarantees that this is an array class, so we don't + * check for it here. + */ + /* new-array vA, vB, class@CCCC */ + EXPORT_PC + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG0 + movq rPC, OUT_ARG1 + REFRESH_INST 35 + movq rINSTq, OUT_ARG2 + movq rSELF, OUT_ARG3 + call SYMBOL(MterpNewArray) + testb %al, %al # 0 means an exception is thrown + jz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_filled_new_array: /* 0x24 */ +/* File: x86_64/op_filled_new_array.S */ +/* + * Create a new array with elements filled from registers. + * + * for: filled-new-array, filled-new-array/range + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, type@BBBB */ + .extern MterpFilledNewArray + EXPORT_PC + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG0 + movq rPC, OUT_ARG1 + movq rSELF, OUT_ARG2 + call SYMBOL(MterpFilledNewArray) + testb %al, %al # 0 means an exception is thrown + jz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + +/* ------------------------------ */ + .balign 128 +.L_op_filled_new_array_range: /* 0x25 */ +/* File: x86_64/op_filled_new_array_range.S */ +/* File: x86_64/op_filled_new_array.S */ +/* + * Create a new array with elements filled from registers. + * + * for: filled-new-array, filled-new-array/range + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, type@BBBB */ + .extern MterpFilledNewArrayRange + EXPORT_PC + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG0 + movq rPC, OUT_ARG1 + movq rSELF, OUT_ARG2 + call SYMBOL(MterpFilledNewArrayRange) + testb %al, %al # 0 means an exception is thrown + jz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + + +/* ------------------------------ */ + .balign 128 +.L_op_fill_array_data: /* 0x26 */ +/* File: x86_64/op_fill_array_data.S */ + /* fill-array-data vAA, +BBBBBBBB */ + EXPORT_PC + movl 2(rPC), %ecx # ecx <- BBBBbbbb + leaq (rPC,%rcx,2), OUT_ARG1 # OUT_ARG1 <- PC + BBBBbbbb*2 + GET_VREG OUT_32_ARG0, rINSTq # OUT_ARG0 <- vAA (array object) + call SYMBOL(MterpFillArrayData) # (obj, payload) + testb %al, %al # 0 means an exception is thrown + jz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + +/* ------------------------------ */ + .balign 128 +.L_op_throw: /* 0x27 */ +/* File: x86_64/op_throw.S */ +/* + * Throw an exception object in the current thread. + */ + /* throw vAA */ + EXPORT_PC + GET_VREG %eax, rINSTq # eax<- vAA (exception object) + testb %al, %al + jz common_errNullObject + movq %rax, THREAD_EXCEPTION_OFFSET(rSELF) + jmp MterpException + +/* ------------------------------ */ + .balign 128 +.L_op_goto: /* 0x28 */ +/* File: x86_64/op_goto.S */ +/* + * Unconditional branch, 8-bit offset. + * + * The branch distance is a signed code-unit offset, which we need to + * double to get a byte offset. + */ + /* goto +AA */ + movsbq rINSTbl, %rax # rax <- ssssssAA + addq %rax, %rax # rax <- AA * 2 + leaq (rPC, %rax), rPC + FETCH_INST + jg 1f # AA * 2 > 0 => no suspend check +#if MTERP_SUSPEND + REFRESH_IBASE +#else + jmp MterpCheckSuspendAndContinue +#endif +1: + GOTO_NEXT + +/* ------------------------------ */ + .balign 128 +.L_op_goto_16: /* 0x29 */ +/* File: x86_64/op_goto_16.S */ +/* + * Unconditional branch, 16-bit offset. + * + * The branch distance is a signed code-unit offset, which we need to + * double to get a byte offset. + */ + /* goto/16 +AAAA */ + movswq 2(rPC), %rax # rax <- ssssAAAA + addq %rax, %rax # rax <- AA * 2 + leaq (rPC, %rax), rPC + FETCH_INST + jg 1f # AA * 2 > 0 => no suspend check +#if MTERP_SUSPEND + REFRESH_IBASE +#else + jmp MterpCheckSuspendAndContinue +#endif +1: + GOTO_NEXT + +/* ------------------------------ */ + .balign 128 +.L_op_goto_32: /* 0x2a */ +/* File: x86_64/op_goto_32.S */ +/* + * Unconditional branch, 32-bit offset. + * + * The branch distance is a signed code-unit offset, which we need to + * double to get a byte offset. + * + * Because we need the SF bit set, we'll use an adds + * to convert from Dalvik offset to byte offset. + */ + /* goto/32 +AAAAAAAA */ + movslq 2(rPC), %rax # rax <- AAAAAAAA + addq %rax, %rax # rax <- AA * 2 + leaq (rPC, %rax), rPC + FETCH_INST + jg 1f # AA * 2 > 0 => no suspend check +#if MTERP_SUSPEND + REFRESH_IBASE +#else + jmp MterpCheckSuspendAndContinue +#endif +1: + GOTO_NEXT + +/* ------------------------------ */ + .balign 128 +.L_op_packed_switch: /* 0x2b */ +/* File: x86_64/op_packed_switch.S */ +/* + * Handle a packed-switch or sparse-switch instruction. In both cases + * we decode it and hand it off to a helper function. + * + * We don't really expect backward branches in a switch statement, but + * they're perfectly legal, so we check for them here. + * + * for: packed-switch, sparse-switch + */ + /* op vAA, +BBBB */ + movslq 2(rPC), OUT_ARG0 # rcx <- BBBBbbbb + leaq (rPC,OUT_ARG0,2), OUT_ARG0 # rcx <- PC + BBBBbbbb*2 + GET_VREG OUT_32_ARG1, rINSTq # eax <- vAA + call SYMBOL(MterpDoPackedSwitch) + addl %eax, %eax + movslq %eax, %rax + leaq (rPC, %rax), rPC + FETCH_INST + jg 1f +#if MTERP_SUSPEND + REFRESH_IBASE +#else + jmp MterpCheckSuspendAndContinue +#endif +1: + GOTO_NEXT + +/* ------------------------------ */ + .balign 128 +.L_op_sparse_switch: /* 0x2c */ +/* File: x86_64/op_sparse_switch.S */ +/* File: x86_64/op_packed_switch.S */ +/* + * Handle a packed-switch or sparse-switch instruction. In both cases + * we decode it and hand it off to a helper function. + * + * We don't really expect backward branches in a switch statement, but + * they're perfectly legal, so we check for them here. + * + * for: packed-switch, sparse-switch + */ + /* op vAA, +BBBB */ + movslq 2(rPC), OUT_ARG0 # rcx <- BBBBbbbb + leaq (rPC,OUT_ARG0,2), OUT_ARG0 # rcx <- PC + BBBBbbbb*2 + GET_VREG OUT_32_ARG1, rINSTq # eax <- vAA + call SYMBOL(MterpDoSparseSwitch) + addl %eax, %eax + movslq %eax, %rax + leaq (rPC, %rax), rPC + FETCH_INST + jg 1f +#if MTERP_SUSPEND + REFRESH_IBASE +#else + jmp MterpCheckSuspendAndContinue +#endif +1: + GOTO_NEXT + + +/* ------------------------------ */ + .balign 128 +.L_op_cmpl_float: /* 0x2d */ +/* File: x86_64/op_cmpl_float.S */ +/* File: x86_64/fpcmp.S */ +/* + * Compare two floating-point values. Puts 0, 1, or -1 into the + * destination register based on the results of the comparison. + * + * int compare(x, y) { + * if (x == y) { + * return 0; + * } else if (x < y) { + * return -1; + * } else if (x > y) { + * return 1; + * } else { + * return nanval ? 1 : -1; + * } + * } + */ + /* op vAA, vBB, vCC */ + movzbq 3(rPC), %rcx # ecx<- CC + movzbq 2(rPC), %rax # eax<- BB + movss VREG_ADDRESS(%rax), %xmm0 + xor %eax, %eax + ucomiss VREG_ADDRESS(%rcx), %xmm0 + jp .Lop_cmpl_float_nan_is_neg + je .Lop_cmpl_float_finish + jb .Lop_cmpl_float_less +.Lop_cmpl_float_nan_is_pos: + addb $1, %al + jmp .Lop_cmpl_float_finish +.Lop_cmpl_float_nan_is_neg: +.Lop_cmpl_float_less: + movl $-1, %eax +.Lop_cmpl_float_finish: + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_cmpg_float: /* 0x2e */ +/* File: x86_64/op_cmpg_float.S */ +/* File: x86_64/fpcmp.S */ +/* + * Compare two floating-point values. Puts 0, 1, or -1 into the + * destination register based on the results of the comparison. + * + * int compare(x, y) { + * if (x == y) { + * return 0; + * } else if (x < y) { + * return -1; + * } else if (x > y) { + * return 1; + * } else { + * return nanval ? 1 : -1; + * } + * } + */ + /* op vAA, vBB, vCC */ + movzbq 3(rPC), %rcx # ecx<- CC + movzbq 2(rPC), %rax # eax<- BB + movss VREG_ADDRESS(%rax), %xmm0 + xor %eax, %eax + ucomiss VREG_ADDRESS(%rcx), %xmm0 + jp .Lop_cmpg_float_nan_is_pos + je .Lop_cmpg_float_finish + jb .Lop_cmpg_float_less +.Lop_cmpg_float_nan_is_pos: + addb $1, %al + jmp .Lop_cmpg_float_finish +.Lop_cmpg_float_nan_is_neg: +.Lop_cmpg_float_less: + movl $-1, %eax +.Lop_cmpg_float_finish: + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_cmpl_double: /* 0x2f */ +/* File: x86_64/op_cmpl_double.S */ +/* File: x86_64/fpcmp.S */ +/* + * Compare two floating-point values. Puts 0, 1, or -1 into the + * destination register based on the results of the comparison. + * + * int compare(x, y) { + * if (x == y) { + * return 0; + * } else if (x < y) { + * return -1; + * } else if (x > y) { + * return 1; + * } else { + * return nanval ? 1 : -1; + * } + * } + */ + /* op vAA, vBB, vCC */ + movzbq 3(rPC), %rcx # ecx<- CC + movzbq 2(rPC), %rax # eax<- BB + movsd VREG_ADDRESS(%rax), %xmm0 + xor %eax, %eax + ucomisd VREG_ADDRESS(%rcx), %xmm0 + jp .Lop_cmpl_double_nan_is_neg + je .Lop_cmpl_double_finish + jb .Lop_cmpl_double_less +.Lop_cmpl_double_nan_is_pos: + addb $1, %al + jmp .Lop_cmpl_double_finish +.Lop_cmpl_double_nan_is_neg: +.Lop_cmpl_double_less: + movl $-1, %eax +.Lop_cmpl_double_finish: + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_cmpg_double: /* 0x30 */ +/* File: x86_64/op_cmpg_double.S */ +/* File: x86_64/fpcmp.S */ +/* + * Compare two floating-point values. Puts 0, 1, or -1 into the + * destination register based on the results of the comparison. + * + * int compare(x, y) { + * if (x == y) { + * return 0; + * } else if (x < y) { + * return -1; + * } else if (x > y) { + * return 1; + * } else { + * return nanval ? 1 : -1; + * } + * } + */ + /* op vAA, vBB, vCC */ + movzbq 3(rPC), %rcx # ecx<- CC + movzbq 2(rPC), %rax # eax<- BB + movsd VREG_ADDRESS(%rax), %xmm0 + xor %eax, %eax + ucomisd VREG_ADDRESS(%rcx), %xmm0 + jp .Lop_cmpg_double_nan_is_pos + je .Lop_cmpg_double_finish + jb .Lop_cmpg_double_less +.Lop_cmpg_double_nan_is_pos: + addb $1, %al + jmp .Lop_cmpg_double_finish +.Lop_cmpg_double_nan_is_neg: +.Lop_cmpg_double_less: + movl $-1, %eax +.Lop_cmpg_double_finish: + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_cmp_long: /* 0x31 */ +/* File: x86_64/op_cmp_long.S */ +/* + * Compare two 64-bit values. Puts 0, 1, or -1 into the destination + * register based on the results of the comparison. + */ + /* cmp-long vAA, vBB, vCC */ + movzbq 2(rPC), %rdx # edx <- BB + movzbq 3(rPC), %rcx # ecx <- CC + GET_WIDE_VREG %rdx, %rdx # rdx <- v[BB] + xorl %eax, %eax + xorl %edi, %edi + addb $1, %al + movl $-1, %esi + cmpq VREG_ADDRESS(%rcx), %rdx + cmovl %esi, %edi + cmovg %eax, %edi + SET_VREG %edi, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_if_eq: /* 0x32 */ +/* File: x86_64/op_if_eq.S */ +/* File: x86_64/bincmp.S */ +/* + * Generic two-operand compare-and-branch operation. Provide a "revcmp" + * fragment that specifies the *reverse* comparison to perform, e.g. + * for "if-le" you would use "gt". + * + * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le + */ + /* if-cmp vA, vB, +CCCC */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # rcx <- A + GET_VREG %eax, %rcx # eax <- vA + cmpl VREG_ADDRESS(rINSTq), %eax # compare (vA, vB) + movl $2, %eax # assume not taken + jne 1f + movswq 2(rPC),%rax # Get signed branch offset +1: + addq %rax, %rax # rax <- AA * 2 + leaq (rPC, %rax), rPC + FETCH_INST + jg 2f # AA * 2 > 0 => no suspend check +#if MTERP_SUSPEND + REFRESH_IBASE +#else + jmp MterpCheckSuspendAndContinue +#endif +2: + GOTO_NEXT + + +/* ------------------------------ */ + .balign 128 +.L_op_if_ne: /* 0x33 */ +/* File: x86_64/op_if_ne.S */ +/* File: x86_64/bincmp.S */ +/* + * Generic two-operand compare-and-branch operation. Provide a "revcmp" + * fragment that specifies the *reverse* comparison to perform, e.g. + * for "if-le" you would use "gt". + * + * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le + */ + /* if-cmp vA, vB, +CCCC */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # rcx <- A + GET_VREG %eax, %rcx # eax <- vA + cmpl VREG_ADDRESS(rINSTq), %eax # compare (vA, vB) + movl $2, %eax # assume not taken + je 1f + movswq 2(rPC),%rax # Get signed branch offset +1: + addq %rax, %rax # rax <- AA * 2 + leaq (rPC, %rax), rPC + FETCH_INST + jg 2f # AA * 2 > 0 => no suspend check +#if MTERP_SUSPEND + REFRESH_IBASE +#else + jmp MterpCheckSuspendAndContinue +#endif +2: + GOTO_NEXT + + +/* ------------------------------ */ + .balign 128 +.L_op_if_lt: /* 0x34 */ +/* File: x86_64/op_if_lt.S */ +/* File: x86_64/bincmp.S */ +/* + * Generic two-operand compare-and-branch operation. Provide a "revcmp" + * fragment that specifies the *reverse* comparison to perform, e.g. + * for "if-le" you would use "gt". + * + * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le + */ + /* if-cmp vA, vB, +CCCC */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # rcx <- A + GET_VREG %eax, %rcx # eax <- vA + cmpl VREG_ADDRESS(rINSTq), %eax # compare (vA, vB) + movl $2, %eax # assume not taken + jge 1f + movswq 2(rPC),%rax # Get signed branch offset +1: + addq %rax, %rax # rax <- AA * 2 + leaq (rPC, %rax), rPC + FETCH_INST + jg 2f # AA * 2 > 0 => no suspend check +#if MTERP_SUSPEND + REFRESH_IBASE +#else + jmp MterpCheckSuspendAndContinue +#endif +2: + GOTO_NEXT + + +/* ------------------------------ */ + .balign 128 +.L_op_if_ge: /* 0x35 */ +/* File: x86_64/op_if_ge.S */ +/* File: x86_64/bincmp.S */ +/* + * Generic two-operand compare-and-branch operation. Provide a "revcmp" + * fragment that specifies the *reverse* comparison to perform, e.g. + * for "if-le" you would use "gt". + * + * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le + */ + /* if-cmp vA, vB, +CCCC */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # rcx <- A + GET_VREG %eax, %rcx # eax <- vA + cmpl VREG_ADDRESS(rINSTq), %eax # compare (vA, vB) + movl $2, %eax # assume not taken + jl 1f + movswq 2(rPC),%rax # Get signed branch offset +1: + addq %rax, %rax # rax <- AA * 2 + leaq (rPC, %rax), rPC + FETCH_INST + jg 2f # AA * 2 > 0 => no suspend check +#if MTERP_SUSPEND + REFRESH_IBASE +#else + jmp MterpCheckSuspendAndContinue +#endif +2: + GOTO_NEXT + + +/* ------------------------------ */ + .balign 128 +.L_op_if_gt: /* 0x36 */ +/* File: x86_64/op_if_gt.S */ +/* File: x86_64/bincmp.S */ +/* + * Generic two-operand compare-and-branch operation. Provide a "revcmp" + * fragment that specifies the *reverse* comparison to perform, e.g. + * for "if-le" you would use "gt". + * + * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le + */ + /* if-cmp vA, vB, +CCCC */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # rcx <- A + GET_VREG %eax, %rcx # eax <- vA + cmpl VREG_ADDRESS(rINSTq), %eax # compare (vA, vB) + movl $2, %eax # assume not taken + jle 1f + movswq 2(rPC),%rax # Get signed branch offset +1: + addq %rax, %rax # rax <- AA * 2 + leaq (rPC, %rax), rPC + FETCH_INST + jg 2f # AA * 2 > 0 => no suspend check +#if MTERP_SUSPEND + REFRESH_IBASE +#else + jmp MterpCheckSuspendAndContinue +#endif +2: + GOTO_NEXT + + +/* ------------------------------ */ + .balign 128 +.L_op_if_le: /* 0x37 */ +/* File: x86_64/op_if_le.S */ +/* File: x86_64/bincmp.S */ +/* + * Generic two-operand compare-and-branch operation. Provide a "revcmp" + * fragment that specifies the *reverse* comparison to perform, e.g. + * for "if-le" you would use "gt". + * + * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le + */ + /* if-cmp vA, vB, +CCCC */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # rcx <- A + GET_VREG %eax, %rcx # eax <- vA + cmpl VREG_ADDRESS(rINSTq), %eax # compare (vA, vB) + movl $2, %eax # assume not taken + jg 1f + movswq 2(rPC),%rax # Get signed branch offset +1: + addq %rax, %rax # rax <- AA * 2 + leaq (rPC, %rax), rPC + FETCH_INST + jg 2f # AA * 2 > 0 => no suspend check +#if MTERP_SUSPEND + REFRESH_IBASE +#else + jmp MterpCheckSuspendAndContinue +#endif +2: + GOTO_NEXT + + +/* ------------------------------ */ + .balign 128 +.L_op_if_eqz: /* 0x38 */ +/* File: x86_64/op_if_eqz.S */ +/* File: x86_64/zcmp.S */ +/* + * Generic one-operand compare-and-branch operation. Provide a "revcmp" + * fragment that specifies the *reverse* comparison to perform, e.g. + * for "if-le" you would use "gt". + * + * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez + */ + /* if-cmp vAA, +BBBB */ + cmpl $0, VREG_ADDRESS(rINSTq) # compare (vA, 0) + movl $2, %eax # assume branch not taken + jne 1f + movswq 2(rPC),%rax # fetch signed displacement +1: + addq %rax, %rax # eax <- AA * 2 + leaq (rPC, %rax), rPC + FETCH_INST + jg 2f # AA * 2 > 0 => no suspend check +#if MTERP_SUSPEND + REFRESH_IBASE +#else + jmp MterpCheckSuspendAndContinue +#endif +2: + GOTO_NEXT + + +/* ------------------------------ */ + .balign 128 +.L_op_if_nez: /* 0x39 */ +/* File: x86_64/op_if_nez.S */ +/* File: x86_64/zcmp.S */ +/* + * Generic one-operand compare-and-branch operation. Provide a "revcmp" + * fragment that specifies the *reverse* comparison to perform, e.g. + * for "if-le" you would use "gt". + * + * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez + */ + /* if-cmp vAA, +BBBB */ + cmpl $0, VREG_ADDRESS(rINSTq) # compare (vA, 0) + movl $2, %eax # assume branch not taken + je 1f + movswq 2(rPC),%rax # fetch signed displacement +1: + addq %rax, %rax # eax <- AA * 2 + leaq (rPC, %rax), rPC + FETCH_INST + jg 2f # AA * 2 > 0 => no suspend check +#if MTERP_SUSPEND + REFRESH_IBASE +#else + jmp MterpCheckSuspendAndContinue +#endif +2: + GOTO_NEXT + + +/* ------------------------------ */ + .balign 128 +.L_op_if_ltz: /* 0x3a */ +/* File: x86_64/op_if_ltz.S */ +/* File: x86_64/zcmp.S */ +/* + * Generic one-operand compare-and-branch operation. Provide a "revcmp" + * fragment that specifies the *reverse* comparison to perform, e.g. + * for "if-le" you would use "gt". + * + * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez + */ + /* if-cmp vAA, +BBBB */ + cmpl $0, VREG_ADDRESS(rINSTq) # compare (vA, 0) + movl $2, %eax # assume branch not taken + jge 1f + movswq 2(rPC),%rax # fetch signed displacement +1: + addq %rax, %rax # eax <- AA * 2 + leaq (rPC, %rax), rPC + FETCH_INST + jg 2f # AA * 2 > 0 => no suspend check +#if MTERP_SUSPEND + REFRESH_IBASE +#else + jmp MterpCheckSuspendAndContinue +#endif +2: + GOTO_NEXT + + +/* ------------------------------ */ + .balign 128 +.L_op_if_gez: /* 0x3b */ +/* File: x86_64/op_if_gez.S */ +/* File: x86_64/zcmp.S */ +/* + * Generic one-operand compare-and-branch operation. Provide a "revcmp" + * fragment that specifies the *reverse* comparison to perform, e.g. + * for "if-le" you would use "gt". + * + * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez + */ + /* if-cmp vAA, +BBBB */ + cmpl $0, VREG_ADDRESS(rINSTq) # compare (vA, 0) + movl $2, %eax # assume branch not taken + jl 1f + movswq 2(rPC),%rax # fetch signed displacement +1: + addq %rax, %rax # eax <- AA * 2 + leaq (rPC, %rax), rPC + FETCH_INST + jg 2f # AA * 2 > 0 => no suspend check +#if MTERP_SUSPEND + REFRESH_IBASE +#else + jmp MterpCheckSuspendAndContinue +#endif +2: + GOTO_NEXT + + +/* ------------------------------ */ + .balign 128 +.L_op_if_gtz: /* 0x3c */ +/* File: x86_64/op_if_gtz.S */ +/* File: x86_64/zcmp.S */ +/* + * Generic one-operand compare-and-branch operation. Provide a "revcmp" + * fragment that specifies the *reverse* comparison to perform, e.g. + * for "if-le" you would use "gt". + * + * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez + */ + /* if-cmp vAA, +BBBB */ + cmpl $0, VREG_ADDRESS(rINSTq) # compare (vA, 0) + movl $2, %eax # assume branch not taken + jle 1f + movswq 2(rPC),%rax # fetch signed displacement +1: + addq %rax, %rax # eax <- AA * 2 + leaq (rPC, %rax), rPC + FETCH_INST + jg 2f # AA * 2 > 0 => no suspend check +#if MTERP_SUSPEND + REFRESH_IBASE +#else + jmp MterpCheckSuspendAndContinue +#endif +2: + GOTO_NEXT + + +/* ------------------------------ */ + .balign 128 +.L_op_if_lez: /* 0x3d */ +/* File: x86_64/op_if_lez.S */ +/* File: x86_64/zcmp.S */ +/* + * Generic one-operand compare-and-branch operation. Provide a "revcmp" + * fragment that specifies the *reverse* comparison to perform, e.g. + * for "if-le" you would use "gt". + * + * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez + */ + /* if-cmp vAA, +BBBB */ + cmpl $0, VREG_ADDRESS(rINSTq) # compare (vA, 0) + movl $2, %eax # assume branch not taken + jg 1f + movswq 2(rPC),%rax # fetch signed displacement +1: + addq %rax, %rax # eax <- AA * 2 + leaq (rPC, %rax), rPC + FETCH_INST + jg 2f # AA * 2 > 0 => no suspend check +#if MTERP_SUSPEND + REFRESH_IBASE +#else + jmp MterpCheckSuspendAndContinue +#endif +2: + GOTO_NEXT + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_3e: /* 0x3e */ +/* File: x86_64/op_unused_3e.S */ +/* File: x86_64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + jmp MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_3f: /* 0x3f */ +/* File: x86_64/op_unused_3f.S */ +/* File: x86_64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + jmp MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_40: /* 0x40 */ +/* File: x86_64/op_unused_40.S */ +/* File: x86_64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + jmp MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_41: /* 0x41 */ +/* File: x86_64/op_unused_41.S */ +/* File: x86_64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + jmp MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_42: /* 0x42 */ +/* File: x86_64/op_unused_42.S */ +/* File: x86_64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + jmp MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_43: /* 0x43 */ +/* File: x86_64/op_unused_43.S */ +/* File: x86_64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + jmp MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_aget: /* 0x44 */ +/* File: x86_64/op_aget.S */ +/* + * Array get, 32 bits or less. vAA <- vBB[vCC]. + * + * for: aget, aget-boolean, aget-byte, aget-char, aget-short, aget-wide + * + */ + /* op vAA, vBB, vCC */ + movzbq 2(rPC), %rax # eax <- BB + movzbq 3(rPC), %rcx # ecx <- CC + GET_VREG %eax, %rax # eax <- vBB (array object) + GET_VREG %ecx, %rcx # ecx <- vCC (requested index) + testl %eax, %eax # null array object? + je common_errNullObject # bail if so + cmpl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ecx + jae common_errArrayIndex # index >= length, bail. + .if 0 + movq MIRROR_INT_ARRAY_DATA_OFFSET(%rax,%rcx,8), %rax + SET_WIDE_VREG %rax, rINSTq + .else + movl MIRROR_INT_ARRAY_DATA_OFFSET(%rax,%rcx,4), %eax + SET_VREG %eax, rINSTq + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_aget_wide: /* 0x45 */ +/* File: x86_64/op_aget_wide.S */ +/* File: x86_64/op_aget.S */ +/* + * Array get, 32 bits or less. vAA <- vBB[vCC]. + * + * for: aget, aget-boolean, aget-byte, aget-char, aget-short, aget-wide + * + */ + /* op vAA, vBB, vCC */ + movzbq 2(rPC), %rax # eax <- BB + movzbq 3(rPC), %rcx # ecx <- CC + GET_VREG %eax, %rax # eax <- vBB (array object) + GET_VREG %ecx, %rcx # ecx <- vCC (requested index) + testl %eax, %eax # null array object? + je common_errNullObject # bail if so + cmpl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ecx + jae common_errArrayIndex # index >= length, bail. + .if 1 + movq MIRROR_WIDE_ARRAY_DATA_OFFSET(%rax,%rcx,8), %rax + SET_WIDE_VREG %rax, rINSTq + .else + movq MIRROR_WIDE_ARRAY_DATA_OFFSET(%rax,%rcx,8), %eax + SET_VREG %eax, rINSTq + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_aget_object: /* 0x46 */ +/* File: x86_64/op_aget_object.S */ +/* + * Array object get. vAA <- vBB[vCC]. + * + * for: aget-object + */ + /* op vAA, vBB, vCC */ + movzbq 2(rPC), %rax # rax <- BB + movzbq 3(rPC), %rcx # rcx <- CC + GET_VREG OUT_32_ARG0, %rax # eax <- vBB (array object) + GET_VREG OUT_32_ARG1, %rcx # ecx <- vCC (requested index) + EXPORT_PC + call SYMBOL(artAGetObjectFromMterp) # (array, index) + cmpq $0, THREAD_EXCEPTION_OFFSET(rSELF) + jnz MterpException + SET_VREG_OBJECT %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_aget_boolean: /* 0x47 */ +/* File: x86_64/op_aget_boolean.S */ +/* File: x86_64/op_aget.S */ +/* + * Array get, 32 bits or less. vAA <- vBB[vCC]. + * + * for: aget, aget-boolean, aget-byte, aget-char, aget-short, aget-wide + * + */ + /* op vAA, vBB, vCC */ + movzbq 2(rPC), %rax # eax <- BB + movzbq 3(rPC), %rcx # ecx <- CC + GET_VREG %eax, %rax # eax <- vBB (array object) + GET_VREG %ecx, %rcx # ecx <- vCC (requested index) + testl %eax, %eax # null array object? + je common_errNullObject # bail if so + cmpl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ecx + jae common_errArrayIndex # index >= length, bail. + .if 0 + movq MIRROR_BOOLEAN_ARRAY_DATA_OFFSET(%rax,%rcx,8), %rax + SET_WIDE_VREG %rax, rINSTq + .else + movzbl MIRROR_BOOLEAN_ARRAY_DATA_OFFSET(%rax,%rcx,1), %eax + SET_VREG %eax, rINSTq + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_aget_byte: /* 0x48 */ +/* File: x86_64/op_aget_byte.S */ +/* File: x86_64/op_aget.S */ +/* + * Array get, 32 bits or less. vAA <- vBB[vCC]. + * + * for: aget, aget-boolean, aget-byte, aget-char, aget-short, aget-wide + * + */ + /* op vAA, vBB, vCC */ + movzbq 2(rPC), %rax # eax <- BB + movzbq 3(rPC), %rcx # ecx <- CC + GET_VREG %eax, %rax # eax <- vBB (array object) + GET_VREG %ecx, %rcx # ecx <- vCC (requested index) + testl %eax, %eax # null array object? + je common_errNullObject # bail if so + cmpl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ecx + jae common_errArrayIndex # index >= length, bail. + .if 0 + movq MIRROR_BYTE_ARRAY_DATA_OFFSET(%rax,%rcx,8), %rax + SET_WIDE_VREG %rax, rINSTq + .else + movsbl MIRROR_BYTE_ARRAY_DATA_OFFSET(%rax,%rcx,1), %eax + SET_VREG %eax, rINSTq + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_aget_char: /* 0x49 */ +/* File: x86_64/op_aget_char.S */ +/* File: x86_64/op_aget.S */ +/* + * Array get, 32 bits or less. vAA <- vBB[vCC]. + * + * for: aget, aget-boolean, aget-byte, aget-char, aget-short, aget-wide + * + */ + /* op vAA, vBB, vCC */ + movzbq 2(rPC), %rax # eax <- BB + movzbq 3(rPC), %rcx # ecx <- CC + GET_VREG %eax, %rax # eax <- vBB (array object) + GET_VREG %ecx, %rcx # ecx <- vCC (requested index) + testl %eax, %eax # null array object? + je common_errNullObject # bail if so + cmpl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ecx + jae common_errArrayIndex # index >= length, bail. + .if 0 + movq MIRROR_CHAR_ARRAY_DATA_OFFSET(%rax,%rcx,8), %rax + SET_WIDE_VREG %rax, rINSTq + .else + movzwl MIRROR_CHAR_ARRAY_DATA_OFFSET(%rax,%rcx,2), %eax + SET_VREG %eax, rINSTq + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_aget_short: /* 0x4a */ +/* File: x86_64/op_aget_short.S */ +/* File: x86_64/op_aget.S */ +/* + * Array get, 32 bits or less. vAA <- vBB[vCC]. + * + * for: aget, aget-boolean, aget-byte, aget-char, aget-short, aget-wide + * + */ + /* op vAA, vBB, vCC */ + movzbq 2(rPC), %rax # eax <- BB + movzbq 3(rPC), %rcx # ecx <- CC + GET_VREG %eax, %rax # eax <- vBB (array object) + GET_VREG %ecx, %rcx # ecx <- vCC (requested index) + testl %eax, %eax # null array object? + je common_errNullObject # bail if so + cmpl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ecx + jae common_errArrayIndex # index >= length, bail. + .if 0 + movq MIRROR_SHORT_ARRAY_DATA_OFFSET(%rax,%rcx,8), %rax + SET_WIDE_VREG %rax, rINSTq + .else + movswl MIRROR_SHORT_ARRAY_DATA_OFFSET(%rax,%rcx,2), %eax + SET_VREG %eax, rINSTq + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_aput: /* 0x4b */ +/* File: x86_64/op_aput.S */ +/* + * Array put, 32 bits or less. vBB[vCC] <- vAA. + * + * for: aput, aput-boolean, aput-byte, aput-char, aput-short, aput-wide + * + */ + /* op vAA, vBB, vCC */ + movzbq 2(rPC), %rax # rax <- BB + movzbq 3(rPC), %rcx # rcx <- CC + GET_VREG %eax, %rax # eax <- vBB (array object) + GET_VREG %ecx, %rcx # ecx <- vCC (requested index) + testl %eax, %eax # null array object? + je common_errNullObject # bail if so + cmpl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ecx + jae common_errArrayIndex # index >= length, bail. + .if 0 + GET_WIDE_VREG rINSTq, rINSTq + .else + GET_VREG rINST, rINSTq + .endif + movl rINST, MIRROR_INT_ARRAY_DATA_OFFSET(%rax,%rcx,4) + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_aput_wide: /* 0x4c */ +/* File: x86_64/op_aput_wide.S */ +/* File: x86_64/op_aput.S */ +/* + * Array put, 32 bits or less. vBB[vCC] <- vAA. + * + * for: aput, aput-boolean, aput-byte, aput-char, aput-short, aput-wide + * + */ + /* op vAA, vBB, vCC */ + movzbq 2(rPC), %rax # rax <- BB + movzbq 3(rPC), %rcx # rcx <- CC + GET_VREG %eax, %rax # eax <- vBB (array object) + GET_VREG %ecx, %rcx # ecx <- vCC (requested index) + testl %eax, %eax # null array object? + je common_errNullObject # bail if so + cmpl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ecx + jae common_errArrayIndex # index >= length, bail. + .if 1 + GET_WIDE_VREG rINSTq, rINSTq + .else + GET_VREG rINST, rINSTq + .endif + movq rINSTq, MIRROR_WIDE_ARRAY_DATA_OFFSET(%rax,%rcx,8) + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_aput_object: /* 0x4d */ +/* File: x86_64/op_aput_object.S */ +/* + * Store an object into an array. vBB[vCC] <- vAA. + */ + /* op vAA, vBB, vCC */ + EXPORT_PC + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG0 + movq rPC, OUT_ARG1 + REFRESH_INST 77 + movq rINSTq, OUT_ARG2 + call SYMBOL(MterpAputObject) # (array, index) + testb %al, %al + jz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_aput_boolean: /* 0x4e */ +/* File: x86_64/op_aput_boolean.S */ +/* File: x86_64/op_aput.S */ +/* + * Array put, 32 bits or less. vBB[vCC] <- vAA. + * + * for: aput, aput-boolean, aput-byte, aput-char, aput-short, aput-wide + * + */ + /* op vAA, vBB, vCC */ + movzbq 2(rPC), %rax # rax <- BB + movzbq 3(rPC), %rcx # rcx <- CC + GET_VREG %eax, %rax # eax <- vBB (array object) + GET_VREG %ecx, %rcx # ecx <- vCC (requested index) + testl %eax, %eax # null array object? + je common_errNullObject # bail if so + cmpl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ecx + jae common_errArrayIndex # index >= length, bail. + .if 0 + GET_WIDE_VREG rINSTq, rINSTq + .else + GET_VREG rINST, rINSTq + .endif + movb rINSTbl, MIRROR_BOOLEAN_ARRAY_DATA_OFFSET(%rax,%rcx,1) + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_aput_byte: /* 0x4f */ +/* File: x86_64/op_aput_byte.S */ +/* File: x86_64/op_aput.S */ +/* + * Array put, 32 bits or less. vBB[vCC] <- vAA. + * + * for: aput, aput-boolean, aput-byte, aput-char, aput-short, aput-wide + * + */ + /* op vAA, vBB, vCC */ + movzbq 2(rPC), %rax # rax <- BB + movzbq 3(rPC), %rcx # rcx <- CC + GET_VREG %eax, %rax # eax <- vBB (array object) + GET_VREG %ecx, %rcx # ecx <- vCC (requested index) + testl %eax, %eax # null array object? + je common_errNullObject # bail if so + cmpl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ecx + jae common_errArrayIndex # index >= length, bail. + .if 0 + GET_WIDE_VREG rINSTq, rINSTq + .else + GET_VREG rINST, rINSTq + .endif + movb rINSTbl, MIRROR_BYTE_ARRAY_DATA_OFFSET(%rax,%rcx,1) + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_aput_char: /* 0x50 */ +/* File: x86_64/op_aput_char.S */ +/* File: x86_64/op_aput.S */ +/* + * Array put, 32 bits or less. vBB[vCC] <- vAA. + * + * for: aput, aput-boolean, aput-byte, aput-char, aput-short, aput-wide + * + */ + /* op vAA, vBB, vCC */ + movzbq 2(rPC), %rax # rax <- BB + movzbq 3(rPC), %rcx # rcx <- CC + GET_VREG %eax, %rax # eax <- vBB (array object) + GET_VREG %ecx, %rcx # ecx <- vCC (requested index) + testl %eax, %eax # null array object? + je common_errNullObject # bail if so + cmpl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ecx + jae common_errArrayIndex # index >= length, bail. + .if 0 + GET_WIDE_VREG rINSTq, rINSTq + .else + GET_VREG rINST, rINSTq + .endif + movw rINSTw, MIRROR_CHAR_ARRAY_DATA_OFFSET(%rax,%rcx,2) + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_aput_short: /* 0x51 */ +/* File: x86_64/op_aput_short.S */ +/* File: x86_64/op_aput.S */ +/* + * Array put, 32 bits or less. vBB[vCC] <- vAA. + * + * for: aput, aput-boolean, aput-byte, aput-char, aput-short, aput-wide + * + */ + /* op vAA, vBB, vCC */ + movzbq 2(rPC), %rax # rax <- BB + movzbq 3(rPC), %rcx # rcx <- CC + GET_VREG %eax, %rax # eax <- vBB (array object) + GET_VREG %ecx, %rcx # ecx <- vCC (requested index) + testl %eax, %eax # null array object? + je common_errNullObject # bail if so + cmpl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ecx + jae common_errArrayIndex # index >= length, bail. + .if 0 + GET_WIDE_VREG rINSTq, rINSTq + .else + GET_VREG rINST, rINSTq + .endif + movw rINSTw, MIRROR_SHORT_ARRAY_DATA_OFFSET(%rax,%rcx,2) + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iget: /* 0x52 */ +/* File: x86_64/op_iget.S */ +/* + * General instance field get. + * + * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short, iget-wide + */ + EXPORT_PC + movzbq rINSTbl, %rcx # rcx <- BA + movzwl 2(rPC), OUT_32_ARG0 # eax <- field ref CCCC + sarl $4, %ecx # ecx <- B + GET_VREG OUT_32_ARG1, %rcx # the object pointer + movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer + movq rSELF, OUT_ARG3 + call SYMBOL(artGet32InstanceFromCode) + cmpq $0, THREAD_EXCEPTION_OFFSET(rSELF) + jnz MterpException # bail out + andb $0xf, rINSTbl # rINST <- A + .if 0 + SET_VREG_OBJECT %eax, rINSTq # fp[A] <-value + .else + .if 0 + SET_WIDE_VREG %rax, rINSTq # fp[A] <-value + .else + SET_VREG %eax, rINSTq # fp[A] <-value + .endif + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_iget_wide: /* 0x53 */ +/* File: x86_64/op_iget_wide.S */ +/* File: x86_64/op_iget.S */ +/* + * General instance field get. + * + * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short, iget-wide + */ + EXPORT_PC + movzbq rINSTbl, %rcx # rcx <- BA + movzwl 2(rPC), OUT_32_ARG0 # eax <- field ref CCCC + sarl $4, %ecx # ecx <- B + GET_VREG OUT_32_ARG1, %rcx # the object pointer + movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer + movq rSELF, OUT_ARG3 + call SYMBOL(artGet64InstanceFromCode) + cmpq $0, THREAD_EXCEPTION_OFFSET(rSELF) + jnz MterpException # bail out + andb $0xf, rINSTbl # rINST <- A + .if 0 + SET_VREG_OBJECT %eax, rINSTq # fp[A] <-value + .else + .if 1 + SET_WIDE_VREG %rax, rINSTq # fp[A] <-value + .else + SET_VREG %eax, rINSTq # fp[A] <-value + .endif + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_object: /* 0x54 */ +/* File: x86_64/op_iget_object.S */ +/* File: x86_64/op_iget.S */ +/* + * General instance field get. + * + * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short, iget-wide + */ + EXPORT_PC + movzbq rINSTbl, %rcx # rcx <- BA + movzwl 2(rPC), OUT_32_ARG0 # eax <- field ref CCCC + sarl $4, %ecx # ecx <- B + GET_VREG OUT_32_ARG1, %rcx # the object pointer + movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer + movq rSELF, OUT_ARG3 + call SYMBOL(artGetObjInstanceFromCode) + cmpq $0, THREAD_EXCEPTION_OFFSET(rSELF) + jnz MterpException # bail out + andb $0xf, rINSTbl # rINST <- A + .if 1 + SET_VREG_OBJECT %eax, rINSTq # fp[A] <-value + .else + .if 0 + SET_WIDE_VREG %rax, rINSTq # fp[A] <-value + .else + SET_VREG %eax, rINSTq # fp[A] <-value + .endif + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_boolean: /* 0x55 */ +/* File: x86_64/op_iget_boolean.S */ +/* File: x86_64/op_iget.S */ +/* + * General instance field get. + * + * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short, iget-wide + */ + EXPORT_PC + movzbq rINSTbl, %rcx # rcx <- BA + movzwl 2(rPC), OUT_32_ARG0 # eax <- field ref CCCC + sarl $4, %ecx # ecx <- B + GET_VREG OUT_32_ARG1, %rcx # the object pointer + movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer + movq rSELF, OUT_ARG3 + call SYMBOL(artGetBooleanInstanceFromCode) + cmpq $0, THREAD_EXCEPTION_OFFSET(rSELF) + jnz MterpException # bail out + andb $0xf, rINSTbl # rINST <- A + .if 0 + SET_VREG_OBJECT %eax, rINSTq # fp[A] <-value + .else + .if 0 + SET_WIDE_VREG %rax, rINSTq # fp[A] <-value + .else + SET_VREG %eax, rINSTq # fp[A] <-value + .endif + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_byte: /* 0x56 */ +/* File: x86_64/op_iget_byte.S */ +/* File: x86_64/op_iget.S */ +/* + * General instance field get. + * + * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short, iget-wide + */ + EXPORT_PC + movzbq rINSTbl, %rcx # rcx <- BA + movzwl 2(rPC), OUT_32_ARG0 # eax <- field ref CCCC + sarl $4, %ecx # ecx <- B + GET_VREG OUT_32_ARG1, %rcx # the object pointer + movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer + movq rSELF, OUT_ARG3 + call SYMBOL(artGetByteInstanceFromCode) + cmpq $0, THREAD_EXCEPTION_OFFSET(rSELF) + jnz MterpException # bail out + andb $0xf, rINSTbl # rINST <- A + .if 0 + SET_VREG_OBJECT %eax, rINSTq # fp[A] <-value + .else + .if 0 + SET_WIDE_VREG %rax, rINSTq # fp[A] <-value + .else + SET_VREG %eax, rINSTq # fp[A] <-value + .endif + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_char: /* 0x57 */ +/* File: x86_64/op_iget_char.S */ +/* File: x86_64/op_iget.S */ +/* + * General instance field get. + * + * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short, iget-wide + */ + EXPORT_PC + movzbq rINSTbl, %rcx # rcx <- BA + movzwl 2(rPC), OUT_32_ARG0 # eax <- field ref CCCC + sarl $4, %ecx # ecx <- B + GET_VREG OUT_32_ARG1, %rcx # the object pointer + movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer + movq rSELF, OUT_ARG3 + call SYMBOL(artGetCharInstanceFromCode) + cmpq $0, THREAD_EXCEPTION_OFFSET(rSELF) + jnz MterpException # bail out + andb $0xf, rINSTbl # rINST <- A + .if 0 + SET_VREG_OBJECT %eax, rINSTq # fp[A] <-value + .else + .if 0 + SET_WIDE_VREG %rax, rINSTq # fp[A] <-value + .else + SET_VREG %eax, rINSTq # fp[A] <-value + .endif + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_short: /* 0x58 */ +/* File: x86_64/op_iget_short.S */ +/* File: x86_64/op_iget.S */ +/* + * General instance field get. + * + * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short, iget-wide + */ + EXPORT_PC + movzbq rINSTbl, %rcx # rcx <- BA + movzwl 2(rPC), OUT_32_ARG0 # eax <- field ref CCCC + sarl $4, %ecx # ecx <- B + GET_VREG OUT_32_ARG1, %rcx # the object pointer + movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer + movq rSELF, OUT_ARG3 + call SYMBOL(artGetShortInstanceFromCode) + cmpq $0, THREAD_EXCEPTION_OFFSET(rSELF) + jnz MterpException # bail out + andb $0xf, rINSTbl # rINST <- A + .if 0 + SET_VREG_OBJECT %eax, rINSTq # fp[A] <-value + .else + .if 0 + SET_WIDE_VREG %rax, rINSTq # fp[A] <-value + .else + SET_VREG %eax, rINSTq # fp[A] <-value + .endif + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iput: /* 0x59 */ +/* File: x86_64/op_iput.S */ +/* + * General 32-bit instance field put. + * + * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short + */ + /* op vA, vB, field@CCCC */ + .extern artSet32InstanceFromMterp + EXPORT_PC + movzwl 2(rPC), OUT_32_ARG0 # field ref <- 0000CCCC + movzbq rINSTbl, %rcx # rcx<- BA + sarl $4, %ecx # ecx<- B + GET_VREG OUT_32_ARG1, %rcx # the object pointer + andb $0xf, rINSTbl # rINST<- A + GET_VREG OUT_32_ARG2, rINSTq # fp[A] + movq OFF_FP_METHOD(rFP), OUT_ARG3 # referrer + call SYMBOL(artSet32InstanceFromMterp) + testb %al, %al + jnz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_iput_wide: /* 0x5a */ +/* File: x86_64/op_iput_wide.S */ + /* iput-wide vA, vB, field@CCCC */ + .extern artSet64InstanceFromMterp + EXPORT_PC + movzwq 2(rPC), OUT_ARG0 # field ref CCCC + movzbq rINSTbl, %rcx # rcx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG OUT_32_ARG1, %rcx # the object pointer + andb $0xf, rINSTbl # rINST <- A + leaq VREG_ADDRESS(rINSTq), OUT_ARG2 # &fp[A] + movq OFF_FP_METHOD(rFP), OUT_ARG3 # referrer + call SYMBOL(artSet64InstanceFromMterp) + testb %al, %al + jnz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_iput_object: /* 0x5b */ +/* File: x86_64/op_iput_object.S */ + EXPORT_PC + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG0 + movq rPC, OUT_ARG1 + REFRESH_INST 91 + movl rINST, OUT_32_ARG2 + movq rSELF, OUT_ARG3 + call SYMBOL(MterpIputObject) + testb %al, %al + jz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_iput_boolean: /* 0x5c */ +/* File: x86_64/op_iput_boolean.S */ +/* File: x86_64/op_iput.S */ +/* + * General 32-bit instance field put. + * + * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short + */ + /* op vA, vB, field@CCCC */ + .extern artSet8InstanceFromMterp + EXPORT_PC + movzwl 2(rPC), OUT_32_ARG0 # field ref <- 0000CCCC + movzbq rINSTbl, %rcx # rcx<- BA + sarl $4, %ecx # ecx<- B + GET_VREG OUT_32_ARG1, %rcx # the object pointer + andb $0xf, rINSTbl # rINST<- A + GET_VREG OUT_32_ARG2, rINSTq # fp[A] + movq OFF_FP_METHOD(rFP), OUT_ARG3 # referrer + call SYMBOL(artSet8InstanceFromMterp) + testb %al, %al + jnz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iput_byte: /* 0x5d */ +/* File: x86_64/op_iput_byte.S */ +/* File: x86_64/op_iput.S */ +/* + * General 32-bit instance field put. + * + * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short + */ + /* op vA, vB, field@CCCC */ + .extern artSet8InstanceFromMterp + EXPORT_PC + movzwl 2(rPC), OUT_32_ARG0 # field ref <- 0000CCCC + movzbq rINSTbl, %rcx # rcx<- BA + sarl $4, %ecx # ecx<- B + GET_VREG OUT_32_ARG1, %rcx # the object pointer + andb $0xf, rINSTbl # rINST<- A + GET_VREG OUT_32_ARG2, rINSTq # fp[A] + movq OFF_FP_METHOD(rFP), OUT_ARG3 # referrer + call SYMBOL(artSet8InstanceFromMterp) + testb %al, %al + jnz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iput_char: /* 0x5e */ +/* File: x86_64/op_iput_char.S */ +/* File: x86_64/op_iput.S */ +/* + * General 32-bit instance field put. + * + * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short + */ + /* op vA, vB, field@CCCC */ + .extern artSet16InstanceFromMterp + EXPORT_PC + movzwl 2(rPC), OUT_32_ARG0 # field ref <- 0000CCCC + movzbq rINSTbl, %rcx # rcx<- BA + sarl $4, %ecx # ecx<- B + GET_VREG OUT_32_ARG1, %rcx # the object pointer + andb $0xf, rINSTbl # rINST<- A + GET_VREG OUT_32_ARG2, rINSTq # fp[A] + movq OFF_FP_METHOD(rFP), OUT_ARG3 # referrer + call SYMBOL(artSet16InstanceFromMterp) + testb %al, %al + jnz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iput_short: /* 0x5f */ +/* File: x86_64/op_iput_short.S */ +/* File: x86_64/op_iput.S */ +/* + * General 32-bit instance field put. + * + * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short + */ + /* op vA, vB, field@CCCC */ + .extern artSet16InstanceFromMterp + EXPORT_PC + movzwl 2(rPC), OUT_32_ARG0 # field ref <- 0000CCCC + movzbq rINSTbl, %rcx # rcx<- BA + sarl $4, %ecx # ecx<- B + GET_VREG OUT_32_ARG1, %rcx # the object pointer + andb $0xf, rINSTbl # rINST<- A + GET_VREG OUT_32_ARG2, rINSTq # fp[A] + movq OFF_FP_METHOD(rFP), OUT_ARG3 # referrer + call SYMBOL(artSet16InstanceFromMterp) + testb %al, %al + jnz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_sget: /* 0x60 */ +/* File: x86_64/op_sget.S */ +/* + * General SGET handler wrapper. + * + * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short, sget-wide + */ + /* op vAA, field@BBBB */ + .extern artGet32StaticFromCode + EXPORT_PC + movzwq 2(rPC), OUT_ARG0 # field ref CCCC + movq OFF_FP_METHOD(rFP), OUT_ARG1 # referrer + movq rSELF, OUT_ARG2 # self + call SYMBOL(artGet32StaticFromCode) + cmpl $0, THREAD_EXCEPTION_OFFSET(rSELF) + jnz MterpException + .if 0 + SET_VREG_OBJECT %eax, rINSTq # fp[A] <- value + .else + .if 0 + SET_WIDE_VREG %rax, rINSTq # fp[A] <- value + .else + SET_VREG %eax, rINSTq # fp[A] <- value + .endif + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_sget_wide: /* 0x61 */ +/* File: x86_64/op_sget_wide.S */ +/* File: x86_64/op_sget.S */ +/* + * General SGET handler wrapper. + * + * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short, sget-wide + */ + /* op vAA, field@BBBB */ + .extern artGet64StaticFromCode + EXPORT_PC + movzwq 2(rPC), OUT_ARG0 # field ref CCCC + movq OFF_FP_METHOD(rFP), OUT_ARG1 # referrer + movq rSELF, OUT_ARG2 # self + call SYMBOL(artGet64StaticFromCode) + cmpl $0, THREAD_EXCEPTION_OFFSET(rSELF) + jnz MterpException + .if 0 + SET_VREG_OBJECT %eax, rINSTq # fp[A] <- value + .else + .if 1 + SET_WIDE_VREG %rax, rINSTq # fp[A] <- value + .else + SET_VREG %eax, rINSTq # fp[A] <- value + .endif + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_sget_object: /* 0x62 */ +/* File: x86_64/op_sget_object.S */ +/* File: x86_64/op_sget.S */ +/* + * General SGET handler wrapper. + * + * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short, sget-wide + */ + /* op vAA, field@BBBB */ + .extern artGetObjStaticFromCode + EXPORT_PC + movzwq 2(rPC), OUT_ARG0 # field ref CCCC + movq OFF_FP_METHOD(rFP), OUT_ARG1 # referrer + movq rSELF, OUT_ARG2 # self + call SYMBOL(artGetObjStaticFromCode) + cmpl $0, THREAD_EXCEPTION_OFFSET(rSELF) + jnz MterpException + .if 1 + SET_VREG_OBJECT %eax, rINSTq # fp[A] <- value + .else + .if 0 + SET_WIDE_VREG %rax, rINSTq # fp[A] <- value + .else + SET_VREG %eax, rINSTq # fp[A] <- value + .endif + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_sget_boolean: /* 0x63 */ +/* File: x86_64/op_sget_boolean.S */ +/* File: x86_64/op_sget.S */ +/* + * General SGET handler wrapper. + * + * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short, sget-wide + */ + /* op vAA, field@BBBB */ + .extern artGetBooleanStaticFromCode + EXPORT_PC + movzwq 2(rPC), OUT_ARG0 # field ref CCCC + movq OFF_FP_METHOD(rFP), OUT_ARG1 # referrer + movq rSELF, OUT_ARG2 # self + call SYMBOL(artGetBooleanStaticFromCode) + cmpl $0, THREAD_EXCEPTION_OFFSET(rSELF) + jnz MterpException + .if 0 + SET_VREG_OBJECT %eax, rINSTq # fp[A] <- value + .else + .if 0 + SET_WIDE_VREG %rax, rINSTq # fp[A] <- value + .else + SET_VREG %eax, rINSTq # fp[A] <- value + .endif + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_sget_byte: /* 0x64 */ +/* File: x86_64/op_sget_byte.S */ +/* File: x86_64/op_sget.S */ +/* + * General SGET handler wrapper. + * + * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short, sget-wide + */ + /* op vAA, field@BBBB */ + .extern artGetByteStaticFromCode + EXPORT_PC + movzwq 2(rPC), OUT_ARG0 # field ref CCCC + movq OFF_FP_METHOD(rFP), OUT_ARG1 # referrer + movq rSELF, OUT_ARG2 # self + call SYMBOL(artGetByteStaticFromCode) + cmpl $0, THREAD_EXCEPTION_OFFSET(rSELF) + jnz MterpException + .if 0 + SET_VREG_OBJECT %eax, rINSTq # fp[A] <- value + .else + .if 0 + SET_WIDE_VREG %rax, rINSTq # fp[A] <- value + .else + SET_VREG %eax, rINSTq # fp[A] <- value + .endif + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_sget_char: /* 0x65 */ +/* File: x86_64/op_sget_char.S */ +/* File: x86_64/op_sget.S */ +/* + * General SGET handler wrapper. + * + * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short, sget-wide + */ + /* op vAA, field@BBBB */ + .extern artGetCharStaticFromCode + EXPORT_PC + movzwq 2(rPC), OUT_ARG0 # field ref CCCC + movq OFF_FP_METHOD(rFP), OUT_ARG1 # referrer + movq rSELF, OUT_ARG2 # self + call SYMBOL(artGetCharStaticFromCode) + cmpl $0, THREAD_EXCEPTION_OFFSET(rSELF) + jnz MterpException + .if 0 + SET_VREG_OBJECT %eax, rINSTq # fp[A] <- value + .else + .if 0 + SET_WIDE_VREG %rax, rINSTq # fp[A] <- value + .else + SET_VREG %eax, rINSTq # fp[A] <- value + .endif + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_sget_short: /* 0x66 */ +/* File: x86_64/op_sget_short.S */ +/* File: x86_64/op_sget.S */ +/* + * General SGET handler wrapper. + * + * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short, sget-wide + */ + /* op vAA, field@BBBB */ + .extern artGetShortStaticFromCode + EXPORT_PC + movzwq 2(rPC), OUT_ARG0 # field ref CCCC + movq OFF_FP_METHOD(rFP), OUT_ARG1 # referrer + movq rSELF, OUT_ARG2 # self + call SYMBOL(artGetShortStaticFromCode) + cmpl $0, THREAD_EXCEPTION_OFFSET(rSELF) + jnz MterpException + .if 0 + SET_VREG_OBJECT %eax, rINSTq # fp[A] <- value + .else + .if 0 + SET_WIDE_VREG %rax, rINSTq # fp[A] <- value + .else + SET_VREG %eax, rINSTq # fp[A] <- value + .endif + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_sput: /* 0x67 */ +/* File: x86_64/op_sput.S */ +/* + * General SPUT handler wrapper. + * + * for: sput, sput-boolean, sput-byte, sput-char, sput-short + */ + /* op vAA, field@BBBB */ + .extern artSet32StaticFromCode + EXPORT_PC + movzwq 2(rPC), OUT_ARG0 # field ref BBBB + GET_VREG OUT_32_ARG1, rINSTq # fp[AA] + movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer + movq rSELF, OUT_ARG3 # self + call SYMBOL(artSet32StaticFromCode) + testb %al, %al + jnz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_sput_wide: /* 0x68 */ +/* File: x86_64/op_sput_wide.S */ +/* + * SPUT_WIDE handler wrapper. + * + */ + /* sput-wide vAA, field@BBBB */ + .extern artSet64IndirectStaticFromMterp + EXPORT_PC + movzwq 2(rPC), OUT_ARG0 # field ref BBBB + movq OFF_FP_METHOD(rFP), OUT_ARG1 # referrer + leaq VREG_ADDRESS(rINSTq), OUT_ARG2 # &fp[AA] + movq rSELF, OUT_ARG3 # self + call SYMBOL(artSet64IndirectStaticFromMterp) + testb %al, %al + jnz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_sput_object: /* 0x69 */ +/* File: x86_64/op_sput_object.S */ + EXPORT_PC + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG0 + movq rPC, OUT_ARG1 + REFRESH_INST 105 + movq rINSTq, OUT_ARG2 + movq rSELF, OUT_ARG3 + call SYMBOL(MterpSputObject) + testb %al, %al + jz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_sput_boolean: /* 0x6a */ +/* File: x86_64/op_sput_boolean.S */ +/* File: x86_64/op_sput.S */ +/* + * General SPUT handler wrapper. + * + * for: sput, sput-boolean, sput-byte, sput-char, sput-short + */ + /* op vAA, field@BBBB */ + .extern artSet8StaticFromCode + EXPORT_PC + movzwq 2(rPC), OUT_ARG0 # field ref BBBB + GET_VREG OUT_32_ARG1, rINSTq # fp[AA] + movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer + movq rSELF, OUT_ARG3 # self + call SYMBOL(artSet8StaticFromCode) + testb %al, %al + jnz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_sput_byte: /* 0x6b */ +/* File: x86_64/op_sput_byte.S */ +/* File: x86_64/op_sput.S */ +/* + * General SPUT handler wrapper. + * + * for: sput, sput-boolean, sput-byte, sput-char, sput-short + */ + /* op vAA, field@BBBB */ + .extern artSet8StaticFromCode + EXPORT_PC + movzwq 2(rPC), OUT_ARG0 # field ref BBBB + GET_VREG OUT_32_ARG1, rINSTq # fp[AA] + movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer + movq rSELF, OUT_ARG3 # self + call SYMBOL(artSet8StaticFromCode) + testb %al, %al + jnz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_sput_char: /* 0x6c */ +/* File: x86_64/op_sput_char.S */ +/* File: x86_64/op_sput.S */ +/* + * General SPUT handler wrapper. + * + * for: sput, sput-boolean, sput-byte, sput-char, sput-short + */ + /* op vAA, field@BBBB */ + .extern artSet16StaticFromCode + EXPORT_PC + movzwq 2(rPC), OUT_ARG0 # field ref BBBB + GET_VREG OUT_32_ARG1, rINSTq # fp[AA] + movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer + movq rSELF, OUT_ARG3 # self + call SYMBOL(artSet16StaticFromCode) + testb %al, %al + jnz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_sput_short: /* 0x6d */ +/* File: x86_64/op_sput_short.S */ +/* File: x86_64/op_sput.S */ +/* + * General SPUT handler wrapper. + * + * for: sput, sput-boolean, sput-byte, sput-char, sput-short + */ + /* op vAA, field@BBBB */ + .extern artSet16StaticFromCode + EXPORT_PC + movzwq 2(rPC), OUT_ARG0 # field ref BBBB + GET_VREG OUT_32_ARG1, rINSTq # fp[AA] + movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer + movq rSELF, OUT_ARG3 # self + call SYMBOL(artSet16StaticFromCode) + testb %al, %al + jnz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_virtual: /* 0x6e */ +/* File: x86_64/op_invoke_virtual.S */ +/* File: x86_64/invoke.S */ +/* + * Generic invoke handler wrapper. + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + .extern MterpInvokeVirtual + EXPORT_PC + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + movq rPC, OUT_ARG2 + REFRESH_INST 110 + movl rINST, OUT_32_ARG3 + call SYMBOL(MterpInvokeVirtual) + testb %al, %al + jz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + +/* + * Handle a virtual method call. + * + * for: invoke-virtual, invoke-virtual/range + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_super: /* 0x6f */ +/* File: x86_64/op_invoke_super.S */ +/* File: x86_64/invoke.S */ +/* + * Generic invoke handler wrapper. + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + .extern MterpInvokeSuper + EXPORT_PC + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + movq rPC, OUT_ARG2 + REFRESH_INST 111 + movl rINST, OUT_32_ARG3 + call SYMBOL(MterpInvokeSuper) + testb %al, %al + jz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + +/* + * Handle a "super" method call. + * + * for: invoke-super, invoke-super/range + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_direct: /* 0x70 */ +/* File: x86_64/op_invoke_direct.S */ +/* File: x86_64/invoke.S */ +/* + * Generic invoke handler wrapper. + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + .extern MterpInvokeDirect + EXPORT_PC + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + movq rPC, OUT_ARG2 + REFRESH_INST 112 + movl rINST, OUT_32_ARG3 + call SYMBOL(MterpInvokeDirect) + testb %al, %al + jz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_static: /* 0x71 */ +/* File: x86_64/op_invoke_static.S */ +/* File: x86_64/invoke.S */ +/* + * Generic invoke handler wrapper. + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + .extern MterpInvokeStatic + EXPORT_PC + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + movq rPC, OUT_ARG2 + REFRESH_INST 113 + movl rINST, OUT_32_ARG3 + call SYMBOL(MterpInvokeStatic) + testb %al, %al + jz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_interface: /* 0x72 */ +/* File: x86_64/op_invoke_interface.S */ +/* File: x86_64/invoke.S */ +/* + * Generic invoke handler wrapper. + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + .extern MterpInvokeInterface + EXPORT_PC + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + movq rPC, OUT_ARG2 + REFRESH_INST 114 + movl rINST, OUT_32_ARG3 + call SYMBOL(MterpInvokeInterface) + testb %al, %al + jz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + +/* + * Handle an interface method call. + * + * for: invoke-interface, invoke-interface/range + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + +/* ------------------------------ */ + .balign 128 +.L_op_return_void_no_barrier: /* 0x73 */ +/* File: x86_64/op_return_void_no_barrier.S */ + testl $(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(rSELF) + jz 1f + movq rSELF, OUT_ARG0 + call SYMBOL(MterpSuspendCheck) +1: + xorq %rax, %rax + jmp MterpReturn + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_virtual_range: /* 0x74 */ +/* File: x86_64/op_invoke_virtual_range.S */ +/* File: x86_64/invoke.S */ +/* + * Generic invoke handler wrapper. + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + .extern MterpInvokeVirtualRange + EXPORT_PC + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + movq rPC, OUT_ARG2 + REFRESH_INST 116 + movl rINST, OUT_32_ARG3 + call SYMBOL(MterpInvokeVirtualRange) + testb %al, %al + jz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_super_range: /* 0x75 */ +/* File: x86_64/op_invoke_super_range.S */ +/* File: x86_64/invoke.S */ +/* + * Generic invoke handler wrapper. + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + .extern MterpInvokeSuperRange + EXPORT_PC + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + movq rPC, OUT_ARG2 + REFRESH_INST 117 + movl rINST, OUT_32_ARG3 + call SYMBOL(MterpInvokeSuperRange) + testb %al, %al + jz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_direct_range: /* 0x76 */ +/* File: x86_64/op_invoke_direct_range.S */ +/* File: x86_64/invoke.S */ +/* + * Generic invoke handler wrapper. + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + .extern MterpInvokeDirectRange + EXPORT_PC + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + movq rPC, OUT_ARG2 + REFRESH_INST 118 + movl rINST, OUT_32_ARG3 + call SYMBOL(MterpInvokeDirectRange) + testb %al, %al + jz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_static_range: /* 0x77 */ +/* File: x86_64/op_invoke_static_range.S */ +/* File: x86_64/invoke.S */ +/* + * Generic invoke handler wrapper. + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + .extern MterpInvokeStaticRange + EXPORT_PC + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + movq rPC, OUT_ARG2 + REFRESH_INST 119 + movl rINST, OUT_32_ARG3 + call SYMBOL(MterpInvokeStaticRange) + testb %al, %al + jz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_interface_range: /* 0x78 */ +/* File: x86_64/op_invoke_interface_range.S */ +/* File: x86_64/invoke.S */ +/* + * Generic invoke handler wrapper. + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + .extern MterpInvokeInterfaceRange + EXPORT_PC + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + movq rPC, OUT_ARG2 + REFRESH_INST 120 + movl rINST, OUT_32_ARG3 + call SYMBOL(MterpInvokeInterfaceRange) + testb %al, %al + jz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_79: /* 0x79 */ +/* File: x86_64/op_unused_79.S */ +/* File: x86_64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + jmp MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_7a: /* 0x7a */ +/* File: x86_64/op_unused_7a.S */ +/* File: x86_64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + jmp MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_neg_int: /* 0x7b */ +/* File: x86_64/op_neg_int.S */ +/* File: x86_64/unop.S */ +/* + * Generic 32/64-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "result = op eax". + */ + /* unop vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4,rINST # rINST <- B + .if 0 + GET_WIDE_VREG %rax, rINSTq # rax <- vB + .else + GET_VREG %eax, rINSTq # eax <- vB + .endif + andb $0xf,%cl # ecx <- A + + negl %eax + .if 0 + SET_WIDE_VREG %rax, %rcx + .else + SET_VREG %eax, %rcx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_not_int: /* 0x7c */ +/* File: x86_64/op_not_int.S */ +/* File: x86_64/unop.S */ +/* + * Generic 32/64-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "result = op eax". + */ + /* unop vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4,rINST # rINST <- B + .if 0 + GET_WIDE_VREG %rax, rINSTq # rax <- vB + .else + GET_VREG %eax, rINSTq # eax <- vB + .endif + andb $0xf,%cl # ecx <- A + + notl %eax + .if 0 + SET_WIDE_VREG %rax, %rcx + .else + SET_VREG %eax, %rcx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_neg_long: /* 0x7d */ +/* File: x86_64/op_neg_long.S */ +/* File: x86_64/unop.S */ +/* + * Generic 32/64-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "result = op eax". + */ + /* unop vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4,rINST # rINST <- B + .if 1 + GET_WIDE_VREG %rax, rINSTq # rax <- vB + .else + GET_VREG %eax, rINSTq # eax <- vB + .endif + andb $0xf,%cl # ecx <- A + + negq %rax + .if 1 + SET_WIDE_VREG %rax, %rcx + .else + SET_VREG %eax, %rcx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_not_long: /* 0x7e */ +/* File: x86_64/op_not_long.S */ +/* File: x86_64/unop.S */ +/* + * Generic 32/64-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "result = op eax". + */ + /* unop vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4,rINST # rINST <- B + .if 1 + GET_WIDE_VREG %rax, rINSTq # rax <- vB + .else + GET_VREG %eax, rINSTq # eax <- vB + .endif + andb $0xf,%cl # ecx <- A + + notq %rax + .if 1 + SET_WIDE_VREG %rax, %rcx + .else + SET_VREG %eax, %rcx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_neg_float: /* 0x7f */ +/* File: x86_64/op_neg_float.S */ +/* File: x86_64/unop.S */ +/* + * Generic 32/64-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "result = op eax". + */ + /* unop vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4,rINST # rINST <- B + .if 0 + GET_WIDE_VREG %rax, rINSTq # rax <- vB + .else + GET_VREG %eax, rINSTq # eax <- vB + .endif + andb $0xf,%cl # ecx <- A + + xorl $0x80000000, %eax + .if 0 + SET_WIDE_VREG %rax, %rcx + .else + SET_VREG %eax, %rcx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_neg_double: /* 0x80 */ +/* File: x86_64/op_neg_double.S */ +/* File: x86_64/unop.S */ +/* + * Generic 32/64-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "result = op eax". + */ + /* unop vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4,rINST # rINST <- B + .if 1 + GET_WIDE_VREG %rax, rINSTq # rax <- vB + .else + GET_VREG %eax, rINSTq # eax <- vB + .endif + andb $0xf,%cl # ecx <- A + movq $0x8000000000000000, %rsi + xorq %rsi, %rax + .if 1 + SET_WIDE_VREG %rax, %rcx + .else + SET_VREG %eax, %rcx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_int_to_long: /* 0x81 */ +/* File: x86_64/op_int_to_long.S */ + /* int to long vA, vB */ + movzbq rINSTbl, %rax # rax <- +A + sarl $4, %eax # eax <- B + andb $0xf, rINSTbl # rINST <- A + movslq VREG_ADDRESS(%rax), %rax + SET_WIDE_VREG %rax, rINSTq # v[A] <- %rax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_int_to_float: /* 0x82 */ +/* File: x86_64/op_int_to_float.S */ +/* File: x86_64/fpcvt.S */ +/* + * Generic 32-bit FP conversion operation. + */ + /* unop vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # ecx <- A + cvtsi2ssl VREG_ADDRESS(rINSTq), %xmm0 + .if 0 + movsd %xmm0, VREG_ADDRESS(%rcx) + CLEAR_WIDE_REF %rcx + .else + movss %xmm0, VREG_ADDRESS(%rcx) + CLEAR_REF %rcx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_int_to_double: /* 0x83 */ +/* File: x86_64/op_int_to_double.S */ +/* File: x86_64/fpcvt.S */ +/* + * Generic 32-bit FP conversion operation. + */ + /* unop vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # ecx <- A + cvtsi2sdl VREG_ADDRESS(rINSTq), %xmm0 + .if 1 + movsd %xmm0, VREG_ADDRESS(%rcx) + CLEAR_WIDE_REF %rcx + .else + movss %xmm0, VREG_ADDRESS(%rcx) + CLEAR_REF %rcx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_long_to_int: /* 0x84 */ +/* File: x86_64/op_long_to_int.S */ +/* we ignore the high word, making this equivalent to a 32-bit reg move */ +/* File: x86_64/op_move.S */ + /* for move, move-object, long-to-int */ + /* op vA, vB */ + movl rINST, %eax # eax <- BA + andb $0xf, %al # eax <- A + shrl $4, rINST # rINST <- B + GET_VREG %edx, rINSTq + .if 0 + SET_VREG_OBJECT %edx, %rax # fp[A] <- fp[B] + .else + SET_VREG %edx, %rax # fp[A] <- fp[B] + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_long_to_float: /* 0x85 */ +/* File: x86_64/op_long_to_float.S */ +/* File: x86_64/fpcvt.S */ +/* + * Generic 32-bit FP conversion operation. + */ + /* unop vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # ecx <- A + cvtsi2ssq VREG_ADDRESS(rINSTq), %xmm0 + .if 0 + movsd %xmm0, VREG_ADDRESS(%rcx) + CLEAR_WIDE_REF %rcx + .else + movss %xmm0, VREG_ADDRESS(%rcx) + CLEAR_REF %rcx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_long_to_double: /* 0x86 */ +/* File: x86_64/op_long_to_double.S */ +/* File: x86_64/fpcvt.S */ +/* + * Generic 32-bit FP conversion operation. + */ + /* unop vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # ecx <- A + cvtsi2sdq VREG_ADDRESS(rINSTq), %xmm0 + .if 1 + movsd %xmm0, VREG_ADDRESS(%rcx) + CLEAR_WIDE_REF %rcx + .else + movss %xmm0, VREG_ADDRESS(%rcx) + CLEAR_REF %rcx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_float_to_int: /* 0x87 */ +/* File: x86_64/op_float_to_int.S */ +/* File: x86_64/cvtfp_int.S */ +/* On fp to int conversions, Java requires that + * if the result > maxint, it should be clamped to maxint. If it is less + * than minint, it should be clamped to minint. If it is a nan, the result + * should be zero. Further, the rounding mode is to truncate. + */ + /* float/double to int/long vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # ecx <- A + movss VREG_ADDRESS(rINSTq), %xmm0 + movl $0x7fffffff, %eax + cvtsi2ssl %eax, %xmm1 + comiss %xmm1, %xmm0 + jae 1f + jp 2f + cvttss2sil %xmm0, %eax + jmp 1f +2: + xorl %eax, %eax +1: + .if 0 + SET_WIDE_VREG %eax, %rcx + .else + SET_VREG %eax, %rcx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_float_to_long: /* 0x88 */ +/* File: x86_64/op_float_to_long.S */ +/* File: x86_64/cvtfp_int.S */ +/* On fp to int conversions, Java requires that + * if the result > maxint, it should be clamped to maxint. If it is less + * than minint, it should be clamped to minint. If it is a nan, the result + * should be zero. Further, the rounding mode is to truncate. + */ + /* float/double to int/long vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # ecx <- A + movss VREG_ADDRESS(rINSTq), %xmm0 + movq $0x7fffffffffffffff, %rax + cvtsi2ssq %rax, %xmm1 + comiss %xmm1, %xmm0 + jae 1f + jp 2f + cvttss2siq %xmm0, %rax + jmp 1f +2: + xorq %rax, %rax +1: + .if 1 + SET_WIDE_VREG %rax, %rcx + .else + SET_VREG %rax, %rcx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_float_to_double: /* 0x89 */ +/* File: x86_64/op_float_to_double.S */ +/* File: x86_64/fpcvt.S */ +/* + * Generic 32-bit FP conversion operation. + */ + /* unop vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # ecx <- A + cvtss2sd VREG_ADDRESS(rINSTq), %xmm0 + .if 1 + movsd %xmm0, VREG_ADDRESS(%rcx) + CLEAR_WIDE_REF %rcx + .else + movss %xmm0, VREG_ADDRESS(%rcx) + CLEAR_REF %rcx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_double_to_int: /* 0x8a */ +/* File: x86_64/op_double_to_int.S */ +/* File: x86_64/cvtfp_int.S */ +/* On fp to int conversions, Java requires that + * if the result > maxint, it should be clamped to maxint. If it is less + * than minint, it should be clamped to minint. If it is a nan, the result + * should be zero. Further, the rounding mode is to truncate. + */ + /* float/double to int/long vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # ecx <- A + movsd VREG_ADDRESS(rINSTq), %xmm0 + movl $0x7fffffff, %eax + cvtsi2sdl %eax, %xmm1 + comisd %xmm1, %xmm0 + jae 1f + jp 2f + cvttsd2sil %xmm0, %eax + jmp 1f +2: + xorl %eax, %eax +1: + .if 0 + SET_WIDE_VREG %eax, %rcx + .else + SET_VREG %eax, %rcx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_double_to_long: /* 0x8b */ +/* File: x86_64/op_double_to_long.S */ +/* File: x86_64/cvtfp_int.S */ +/* On fp to int conversions, Java requires that + * if the result > maxint, it should be clamped to maxint. If it is less + * than minint, it should be clamped to minint. If it is a nan, the result + * should be zero. Further, the rounding mode is to truncate. + */ + /* float/double to int/long vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # ecx <- A + movsd VREG_ADDRESS(rINSTq), %xmm0 + movq $0x7fffffffffffffff, %rax + cvtsi2sdq %rax, %xmm1 + comisd %xmm1, %xmm0 + jae 1f + jp 2f + cvttsd2siq %xmm0, %rax + jmp 1f +2: + xorq %rax, %rax +1: + .if 1 + SET_WIDE_VREG %rax, %rcx + .else + SET_VREG %rax, %rcx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_double_to_float: /* 0x8c */ +/* File: x86_64/op_double_to_float.S */ +/* File: x86_64/fpcvt.S */ +/* + * Generic 32-bit FP conversion operation. + */ + /* unop vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # ecx <- A + cvtsd2ss VREG_ADDRESS(rINSTq), %xmm0 + .if 0 + movsd %xmm0, VREG_ADDRESS(%rcx) + CLEAR_WIDE_REF %rcx + .else + movss %xmm0, VREG_ADDRESS(%rcx) + CLEAR_REF %rcx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_int_to_byte: /* 0x8d */ +/* File: x86_64/op_int_to_byte.S */ +/* File: x86_64/unop.S */ +/* + * Generic 32/64-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "result = op eax". + */ + /* unop vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4,rINST # rINST <- B + .if 0 + GET_WIDE_VREG %rax, rINSTq # rax <- vB + .else + GET_VREG %eax, rINSTq # eax <- vB + .endif + andb $0xf,%cl # ecx <- A + +movsbl %al, %eax + .if 0 + SET_WIDE_VREG %rax, %rcx + .else + SET_VREG %eax, %rcx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_int_to_char: /* 0x8e */ +/* File: x86_64/op_int_to_char.S */ +/* File: x86_64/unop.S */ +/* + * Generic 32/64-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "result = op eax". + */ + /* unop vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4,rINST # rINST <- B + .if 0 + GET_WIDE_VREG %rax, rINSTq # rax <- vB + .else + GET_VREG %eax, rINSTq # eax <- vB + .endif + andb $0xf,%cl # ecx <- A + +movzwl %ax,%eax + .if 0 + SET_WIDE_VREG %rax, %rcx + .else + SET_VREG %eax, %rcx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_int_to_short: /* 0x8f */ +/* File: x86_64/op_int_to_short.S */ +/* File: x86_64/unop.S */ +/* + * Generic 32/64-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "result = op eax". + */ + /* unop vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4,rINST # rINST <- B + .if 0 + GET_WIDE_VREG %rax, rINSTq # rax <- vB + .else + GET_VREG %eax, rINSTq # eax <- vB + .endif + andb $0xf,%cl # ecx <- A + +movswl %ax, %eax + .if 0 + SET_WIDE_VREG %rax, %rcx + .else + SET_VREG %eax, %rcx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_add_int: /* 0x90 */ +/* File: x86_64/op_add_int.S */ +/* File: x86_64/binop.S */ +/* + * Generic 32-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = eax op (rFP,%ecx,4)". + * This could be an x86 instruction or a function call. (If the result + * comes back in a register other than eax, you can override "result".) + * + * For: add-int, sub-int, and-int, or-int, + * xor-int, shl-int, shr-int, ushr-int + */ + /* binop vAA, vBB, vCC */ + movzbq 2(rPC), %rax # rax <- BB + movzbq 3(rPC), %rcx # rcx <- CC + GET_VREG %eax, %rax # eax <- vBB + addl (rFP,%rcx,4), %eax # ex: addl (rFP,%rcx,4),%eax + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_sub_int: /* 0x91 */ +/* File: x86_64/op_sub_int.S */ +/* File: x86_64/binop.S */ +/* + * Generic 32-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = eax op (rFP,%ecx,4)". + * This could be an x86 instruction or a function call. (If the result + * comes back in a register other than eax, you can override "result".) + * + * For: add-int, sub-int, and-int, or-int, + * xor-int, shl-int, shr-int, ushr-int + */ + /* binop vAA, vBB, vCC */ + movzbq 2(rPC), %rax # rax <- BB + movzbq 3(rPC), %rcx # rcx <- CC + GET_VREG %eax, %rax # eax <- vBB + subl (rFP,%rcx,4), %eax # ex: addl (rFP,%rcx,4),%eax + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_int: /* 0x92 */ +/* File: x86_64/op_mul_int.S */ +/* File: x86_64/binop.S */ +/* + * Generic 32-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = eax op (rFP,%ecx,4)". + * This could be an x86 instruction or a function call. (If the result + * comes back in a register other than eax, you can override "result".) + * + * For: add-int, sub-int, and-int, or-int, + * xor-int, shl-int, shr-int, ushr-int + */ + /* binop vAA, vBB, vCC */ + movzbq 2(rPC), %rax # rax <- BB + movzbq 3(rPC), %rcx # rcx <- CC + GET_VREG %eax, %rax # eax <- vBB + imull (rFP,%rcx,4), %eax # ex: addl (rFP,%rcx,4),%eax + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_div_int: /* 0x93 */ +/* File: x86_64/op_div_int.S */ +/* File: x86_64/bindiv.S */ +/* + * 32-bit binary div/rem operation. Handles special case of op1=-1. + */ + /* div/rem vAA, vBB, vCC */ + movzbq 2(rPC), %rax # rax <- BB + movzbq 3(rPC), %rcx # rcx <- CC + .if 0 + GET_WIDE_VREG %rax, %rax # eax <- vBB + GET_WIDE_VREG %ecx, %rcx # ecx <- vCC + .else + GET_VREG %eax, %rax # eax <- vBB + GET_VREG %ecx, %rcx # ecx <- vCC + .endif + testl %ecx, %ecx + jz common_errDivideByZero + cmpl $-1, %ecx + je 2f + cdq # rdx:rax <- sign-extended of rax + idivl %ecx +1: + .if 0 + SET_WIDE_VREG %eax, rINSTq # eax <- vBB + .else + SET_VREG %eax, rINSTq # eax <- vBB + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 +2: + .if 0 + xorl %eax, %eax + .else + negl %eax + .endif + jmp 1b + + +/* ------------------------------ */ + .balign 128 +.L_op_rem_int: /* 0x94 */ +/* File: x86_64/op_rem_int.S */ +/* File: x86_64/bindiv.S */ +/* + * 32-bit binary div/rem operation. Handles special case of op1=-1. + */ + /* div/rem vAA, vBB, vCC */ + movzbq 2(rPC), %rax # rax <- BB + movzbq 3(rPC), %rcx # rcx <- CC + .if 0 + GET_WIDE_VREG %rax, %rax # eax <- vBB + GET_WIDE_VREG %ecx, %rcx # ecx <- vCC + .else + GET_VREG %eax, %rax # eax <- vBB + GET_VREG %ecx, %rcx # ecx <- vCC + .endif + testl %ecx, %ecx + jz common_errDivideByZero + cmpl $-1, %ecx + je 2f + cdq # rdx:rax <- sign-extended of rax + idivl %ecx +1: + .if 0 + SET_WIDE_VREG %edx, rINSTq # eax <- vBB + .else + SET_VREG %edx, rINSTq # eax <- vBB + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 +2: + .if 1 + xorl %edx, %edx + .else + negl %edx + .endif + jmp 1b + + +/* ------------------------------ */ + .balign 128 +.L_op_and_int: /* 0x95 */ +/* File: x86_64/op_and_int.S */ +/* File: x86_64/binop.S */ +/* + * Generic 32-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = eax op (rFP,%ecx,4)". + * This could be an x86 instruction or a function call. (If the result + * comes back in a register other than eax, you can override "result".) + * + * For: add-int, sub-int, and-int, or-int, + * xor-int, shl-int, shr-int, ushr-int + */ + /* binop vAA, vBB, vCC */ + movzbq 2(rPC), %rax # rax <- BB + movzbq 3(rPC), %rcx # rcx <- CC + GET_VREG %eax, %rax # eax <- vBB + andl (rFP,%rcx,4), %eax # ex: addl (rFP,%rcx,4),%eax + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_or_int: /* 0x96 */ +/* File: x86_64/op_or_int.S */ +/* File: x86_64/binop.S */ +/* + * Generic 32-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = eax op (rFP,%ecx,4)". + * This could be an x86 instruction or a function call. (If the result + * comes back in a register other than eax, you can override "result".) + * + * For: add-int, sub-int, and-int, or-int, + * xor-int, shl-int, shr-int, ushr-int + */ + /* binop vAA, vBB, vCC */ + movzbq 2(rPC), %rax # rax <- BB + movzbq 3(rPC), %rcx # rcx <- CC + GET_VREG %eax, %rax # eax <- vBB + orl (rFP,%rcx,4), %eax # ex: addl (rFP,%rcx,4),%eax + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_xor_int: /* 0x97 */ +/* File: x86_64/op_xor_int.S */ +/* File: x86_64/binop.S */ +/* + * Generic 32-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = eax op (rFP,%ecx,4)". + * This could be an x86 instruction or a function call. (If the result + * comes back in a register other than eax, you can override "result".) + * + * For: add-int, sub-int, and-int, or-int, + * xor-int, shl-int, shr-int, ushr-int + */ + /* binop vAA, vBB, vCC */ + movzbq 2(rPC), %rax # rax <- BB + movzbq 3(rPC), %rcx # rcx <- CC + GET_VREG %eax, %rax # eax <- vBB + xorl (rFP,%rcx,4), %eax # ex: addl (rFP,%rcx,4),%eax + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_shl_int: /* 0x98 */ +/* File: x86_64/op_shl_int.S */ +/* File: x86_64/binop1.S */ +/* + * Generic 32-bit binary operation in which both operands loaded to + * registers (op0 in eax, op1 in ecx). + */ + /* binop vAA, vBB, vCC */ + movzbq 2(rPC), %rax # eax <- BB + movzbq 3(rPC), %rcx # ecx <- CC + GET_VREG %ecx, %rcx # eax <- vCC + .if 0 + GET_WIDE_VREG %rax, %rax # rax <- vBB + sall %cl, %eax # ex: addl %ecx,%eax + SET_WIDE_VREG %rax, rINSTq + .else + GET_VREG %eax, %rax # eax <- vBB + sall %cl, %eax # ex: addl %ecx,%eax + SET_VREG %eax, rINSTq + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_shr_int: /* 0x99 */ +/* File: x86_64/op_shr_int.S */ +/* File: x86_64/binop1.S */ +/* + * Generic 32-bit binary operation in which both operands loaded to + * registers (op0 in eax, op1 in ecx). + */ + /* binop vAA, vBB, vCC */ + movzbq 2(rPC), %rax # eax <- BB + movzbq 3(rPC), %rcx # ecx <- CC + GET_VREG %ecx, %rcx # eax <- vCC + .if 0 + GET_WIDE_VREG %rax, %rax # rax <- vBB + sarl %cl, %eax # ex: addl %ecx,%eax + SET_WIDE_VREG %rax, rINSTq + .else + GET_VREG %eax, %rax # eax <- vBB + sarl %cl, %eax # ex: addl %ecx,%eax + SET_VREG %eax, rINSTq + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_ushr_int: /* 0x9a */ +/* File: x86_64/op_ushr_int.S */ +/* File: x86_64/binop1.S */ +/* + * Generic 32-bit binary operation in which both operands loaded to + * registers (op0 in eax, op1 in ecx). + */ + /* binop vAA, vBB, vCC */ + movzbq 2(rPC), %rax # eax <- BB + movzbq 3(rPC), %rcx # ecx <- CC + GET_VREG %ecx, %rcx # eax <- vCC + .if 0 + GET_WIDE_VREG %rax, %rax # rax <- vBB + shrl %cl, %eax # ex: addl %ecx,%eax + SET_WIDE_VREG %rax, rINSTq + .else + GET_VREG %eax, %rax # eax <- vBB + shrl %cl, %eax # ex: addl %ecx,%eax + SET_VREG %eax, rINSTq + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_add_long: /* 0x9b */ +/* File: x86_64/op_add_long.S */ +/* File: x86_64/binopWide.S */ +/* + * Generic 64-bit binary operation. + */ + /* binop vAA, vBB, vCC */ + movzbq 2(rPC), %rax # eax <- BB + movzbq 3(rPC), %rcx # ecx <- CC + GET_WIDE_VREG %rax, %rax # rax <- v[BB] + addq (rFP,%rcx,4), %rax # ex: addq (rFP,%rcx,4),%rax + SET_WIDE_VREG %rax, rINSTq # v[AA] <- rax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_sub_long: /* 0x9c */ +/* File: x86_64/op_sub_long.S */ +/* File: x86_64/binopWide.S */ +/* + * Generic 64-bit binary operation. + */ + /* binop vAA, vBB, vCC */ + movzbq 2(rPC), %rax # eax <- BB + movzbq 3(rPC), %rcx # ecx <- CC + GET_WIDE_VREG %rax, %rax # rax <- v[BB] + subq (rFP,%rcx,4), %rax # ex: addq (rFP,%rcx,4),%rax + SET_WIDE_VREG %rax, rINSTq # v[AA] <- rax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_long: /* 0x9d */ +/* File: x86_64/op_mul_long.S */ +/* File: x86_64/binopWide.S */ +/* + * Generic 64-bit binary operation. + */ + /* binop vAA, vBB, vCC */ + movzbq 2(rPC), %rax # eax <- BB + movzbq 3(rPC), %rcx # ecx <- CC + GET_WIDE_VREG %rax, %rax # rax <- v[BB] + imulq (rFP,%rcx,4), %rax # ex: addq (rFP,%rcx,4),%rax + SET_WIDE_VREG %rax, rINSTq # v[AA] <- rax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_div_long: /* 0x9e */ +/* File: x86_64/op_div_long.S */ +/* File: x86_64/bindiv.S */ +/* + * 32-bit binary div/rem operation. Handles special case of op1=-1. + */ + /* div/rem vAA, vBB, vCC */ + movzbq 2(rPC), %rax # rax <- BB + movzbq 3(rPC), %rcx # rcx <- CC + .if 1 + GET_WIDE_VREG %rax, %rax # eax <- vBB + GET_WIDE_VREG %rcx, %rcx # ecx <- vCC + .else + GET_VREG %eax, %rax # eax <- vBB + GET_VREG %rcx, %rcx # ecx <- vCC + .endif + testq %rcx, %rcx + jz common_errDivideByZero + cmpq $-1, %rcx + je 2f + cqo # rdx:rax <- sign-extended of rax + idivq %rcx +1: + .if 1 + SET_WIDE_VREG %rax, rINSTq # eax <- vBB + .else + SET_VREG %rax, rINSTq # eax <- vBB + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 +2: + .if 0 + xorq %rax, %rax + .else + negq %rax + .endif + jmp 1b + + +/* ------------------------------ */ + .balign 128 +.L_op_rem_long: /* 0x9f */ +/* File: x86_64/op_rem_long.S */ +/* File: x86_64/bindiv.S */ +/* + * 32-bit binary div/rem operation. Handles special case of op1=-1. + */ + /* div/rem vAA, vBB, vCC */ + movzbq 2(rPC), %rax # rax <- BB + movzbq 3(rPC), %rcx # rcx <- CC + .if 1 + GET_WIDE_VREG %rax, %rax # eax <- vBB + GET_WIDE_VREG %rcx, %rcx # ecx <- vCC + .else + GET_VREG %eax, %rax # eax <- vBB + GET_VREG %rcx, %rcx # ecx <- vCC + .endif + testq %rcx, %rcx + jz common_errDivideByZero + cmpq $-1, %rcx + je 2f + cqo # rdx:rax <- sign-extended of rax + idivq %rcx +1: + .if 1 + SET_WIDE_VREG %rdx, rINSTq # eax <- vBB + .else + SET_VREG %rdx, rINSTq # eax <- vBB + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 +2: + .if 1 + xorq %rdx, %rdx + .else + negq %rdx + .endif + jmp 1b + + +/* ------------------------------ */ + .balign 128 +.L_op_and_long: /* 0xa0 */ +/* File: x86_64/op_and_long.S */ +/* File: x86_64/binopWide.S */ +/* + * Generic 64-bit binary operation. + */ + /* binop vAA, vBB, vCC */ + movzbq 2(rPC), %rax # eax <- BB + movzbq 3(rPC), %rcx # ecx <- CC + GET_WIDE_VREG %rax, %rax # rax <- v[BB] + andq (rFP,%rcx,4), %rax # ex: addq (rFP,%rcx,4),%rax + SET_WIDE_VREG %rax, rINSTq # v[AA] <- rax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_or_long: /* 0xa1 */ +/* File: x86_64/op_or_long.S */ +/* File: x86_64/binopWide.S */ +/* + * Generic 64-bit binary operation. + */ + /* binop vAA, vBB, vCC */ + movzbq 2(rPC), %rax # eax <- BB + movzbq 3(rPC), %rcx # ecx <- CC + GET_WIDE_VREG %rax, %rax # rax <- v[BB] + orq (rFP,%rcx,4), %rax # ex: addq (rFP,%rcx,4),%rax + SET_WIDE_VREG %rax, rINSTq # v[AA] <- rax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_xor_long: /* 0xa2 */ +/* File: x86_64/op_xor_long.S */ +/* File: x86_64/binopWide.S */ +/* + * Generic 64-bit binary operation. + */ + /* binop vAA, vBB, vCC */ + movzbq 2(rPC), %rax # eax <- BB + movzbq 3(rPC), %rcx # ecx <- CC + GET_WIDE_VREG %rax, %rax # rax <- v[BB] + xorq (rFP,%rcx,4), %rax # ex: addq (rFP,%rcx,4),%rax + SET_WIDE_VREG %rax, rINSTq # v[AA] <- rax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_shl_long: /* 0xa3 */ +/* File: x86_64/op_shl_long.S */ +/* File: x86_64/binop1.S */ +/* + * Generic 32-bit binary operation in which both operands loaded to + * registers (op0 in eax, op1 in ecx). + */ + /* binop vAA, vBB, vCC */ + movzbq 2(rPC), %rax # eax <- BB + movzbq 3(rPC), %rcx # ecx <- CC + GET_VREG %ecx, %rcx # eax <- vCC + .if 1 + GET_WIDE_VREG %rax, %rax # rax <- vBB + salq %cl, %rax # ex: addl %ecx,%eax + SET_WIDE_VREG %rax, rINSTq + .else + GET_VREG %eax, %rax # eax <- vBB + salq %cl, %rax # ex: addl %ecx,%eax + SET_VREG %eax, rINSTq + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_shr_long: /* 0xa4 */ +/* File: x86_64/op_shr_long.S */ +/* File: x86_64/binop1.S */ +/* + * Generic 32-bit binary operation in which both operands loaded to + * registers (op0 in eax, op1 in ecx). + */ + /* binop vAA, vBB, vCC */ + movzbq 2(rPC), %rax # eax <- BB + movzbq 3(rPC), %rcx # ecx <- CC + GET_VREG %ecx, %rcx # eax <- vCC + .if 1 + GET_WIDE_VREG %rax, %rax # rax <- vBB + sarq %cl, %rax # ex: addl %ecx,%eax + SET_WIDE_VREG %rax, rINSTq + .else + GET_VREG %eax, %rax # eax <- vBB + sarq %cl, %rax # ex: addl %ecx,%eax + SET_VREG %eax, rINSTq + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_ushr_long: /* 0xa5 */ +/* File: x86_64/op_ushr_long.S */ +/* File: x86_64/binop1.S */ +/* + * Generic 32-bit binary operation in which both operands loaded to + * registers (op0 in eax, op1 in ecx). + */ + /* binop vAA, vBB, vCC */ + movzbq 2(rPC), %rax # eax <- BB + movzbq 3(rPC), %rcx # ecx <- CC + GET_VREG %ecx, %rcx # eax <- vCC + .if 1 + GET_WIDE_VREG %rax, %rax # rax <- vBB + shrq %cl, %rax # ex: addl %ecx,%eax + SET_WIDE_VREG %rax, rINSTq + .else + GET_VREG %eax, %rax # eax <- vBB + shrq %cl, %rax # ex: addl %ecx,%eax + SET_VREG %eax, rINSTq + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_add_float: /* 0xa6 */ +/* File: x86_64/op_add_float.S */ +/* File: x86_64/sseBinop.S */ + movzbq 2(rPC), %rcx # ecx <- BB + movzbq 3(rPC), %rax # eax <- CC + movss VREG_ADDRESS(%rcx), %xmm0 # %xmm0 <- 1st src + addss VREG_ADDRESS(%rax), %xmm0 + movss %xmm0, VREG_ADDRESS(rINSTq) # vAA <- %xmm0 + pxor %xmm0, %xmm0 + movss %xmm0, VREG_REF_ADDRESS(rINSTq) # clear ref + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_sub_float: /* 0xa7 */ +/* File: x86_64/op_sub_float.S */ +/* File: x86_64/sseBinop.S */ + movzbq 2(rPC), %rcx # ecx <- BB + movzbq 3(rPC), %rax # eax <- CC + movss VREG_ADDRESS(%rcx), %xmm0 # %xmm0 <- 1st src + subss VREG_ADDRESS(%rax), %xmm0 + movss %xmm0, VREG_ADDRESS(rINSTq) # vAA <- %xmm0 + pxor %xmm0, %xmm0 + movss %xmm0, VREG_REF_ADDRESS(rINSTq) # clear ref + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_float: /* 0xa8 */ +/* File: x86_64/op_mul_float.S */ +/* File: x86_64/sseBinop.S */ + movzbq 2(rPC), %rcx # ecx <- BB + movzbq 3(rPC), %rax # eax <- CC + movss VREG_ADDRESS(%rcx), %xmm0 # %xmm0 <- 1st src + mulss VREG_ADDRESS(%rax), %xmm0 + movss %xmm0, VREG_ADDRESS(rINSTq) # vAA <- %xmm0 + pxor %xmm0, %xmm0 + movss %xmm0, VREG_REF_ADDRESS(rINSTq) # clear ref + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_div_float: /* 0xa9 */ +/* File: x86_64/op_div_float.S */ +/* File: x86_64/sseBinop.S */ + movzbq 2(rPC), %rcx # ecx <- BB + movzbq 3(rPC), %rax # eax <- CC + movss VREG_ADDRESS(%rcx), %xmm0 # %xmm0 <- 1st src + divss VREG_ADDRESS(%rax), %xmm0 + movss %xmm0, VREG_ADDRESS(rINSTq) # vAA <- %xmm0 + pxor %xmm0, %xmm0 + movss %xmm0, VREG_REF_ADDRESS(rINSTq) # clear ref + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_rem_float: /* 0xaa */ +/* File: x86_64/op_rem_float.S */ + /* rem_float vAA, vBB, vCC */ + movzbq 3(rPC), %rcx # ecx <- BB + movzbq 2(rPC), %rax # eax <- CC + flds VREG_ADDRESS(%rcx) # vBB to fp stack + flds VREG_ADDRESS(%rax) # vCC to fp stack +1: + fprem + fstsw %ax + sahf + jp 1b + fstp %st(1) + fstps VREG_ADDRESS(rINSTq) # %st to vAA + CLEAR_REF rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_add_double: /* 0xab */ +/* File: x86_64/op_add_double.S */ +/* File: x86_64/sseBinop.S */ + movzbq 2(rPC), %rcx # ecx <- BB + movzbq 3(rPC), %rax # eax <- CC + movsd VREG_ADDRESS(%rcx), %xmm0 # %xmm0 <- 1st src + addsd VREG_ADDRESS(%rax), %xmm0 + movsd %xmm0, VREG_ADDRESS(rINSTq) # vAA <- %xmm0 + pxor %xmm0, %xmm0 + movsd %xmm0, VREG_REF_ADDRESS(rINSTq) # clear ref + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_sub_double: /* 0xac */ +/* File: x86_64/op_sub_double.S */ +/* File: x86_64/sseBinop.S */ + movzbq 2(rPC), %rcx # ecx <- BB + movzbq 3(rPC), %rax # eax <- CC + movsd VREG_ADDRESS(%rcx), %xmm0 # %xmm0 <- 1st src + subsd VREG_ADDRESS(%rax), %xmm0 + movsd %xmm0, VREG_ADDRESS(rINSTq) # vAA <- %xmm0 + pxor %xmm0, %xmm0 + movsd %xmm0, VREG_REF_ADDRESS(rINSTq) # clear ref + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_double: /* 0xad */ +/* File: x86_64/op_mul_double.S */ +/* File: x86_64/sseBinop.S */ + movzbq 2(rPC), %rcx # ecx <- BB + movzbq 3(rPC), %rax # eax <- CC + movsd VREG_ADDRESS(%rcx), %xmm0 # %xmm0 <- 1st src + mulsd VREG_ADDRESS(%rax), %xmm0 + movsd %xmm0, VREG_ADDRESS(rINSTq) # vAA <- %xmm0 + pxor %xmm0, %xmm0 + movsd %xmm0, VREG_REF_ADDRESS(rINSTq) # clear ref + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_div_double: /* 0xae */ +/* File: x86_64/op_div_double.S */ +/* File: x86_64/sseBinop.S */ + movzbq 2(rPC), %rcx # ecx <- BB + movzbq 3(rPC), %rax # eax <- CC + movsd VREG_ADDRESS(%rcx), %xmm0 # %xmm0 <- 1st src + divsd VREG_ADDRESS(%rax), %xmm0 + movsd %xmm0, VREG_ADDRESS(rINSTq) # vAA <- %xmm0 + pxor %xmm0, %xmm0 + movsd %xmm0, VREG_REF_ADDRESS(rINSTq) # clear ref + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_rem_double: /* 0xaf */ +/* File: x86_64/op_rem_double.S */ + /* rem_double vAA, vBB, vCC */ + movzbq 3(rPC), %rcx # ecx <- BB + movzbq 2(rPC), %rax # eax <- CC + fldl VREG_ADDRESS(%rcx) # %st1 <- fp[vBB] + fldl VREG_ADDRESS(%rax) # %st0 <- fp[vCC] +1: + fprem + fstsw %ax + sahf + jp 1b + fstp %st(1) + fstpl VREG_ADDRESS(rINSTq) # fp[vAA] <- %st + CLEAR_WIDE_REF rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_add_int_2addr: /* 0xb0 */ +/* File: x86_64/op_add_int_2addr.S */ +/* File: x86_64/binop2addr.S */ +/* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = r0 op r1". + * This could be an instruction or a function call. + * + * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, + * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, + * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr, + * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr + */ + /* binop/2addr vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # ecx <- A + GET_VREG %eax, rINSTq # eax <- vB + addl %eax, (rFP,%rcx,4) # for ex: addl %eax,(rFP,%ecx,4) + CLEAR_REF %rcx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_sub_int_2addr: /* 0xb1 */ +/* File: x86_64/op_sub_int_2addr.S */ +/* File: x86_64/binop2addr.S */ +/* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = r0 op r1". + * This could be an instruction or a function call. + * + * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, + * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, + * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr, + * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr + */ + /* binop/2addr vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # ecx <- A + GET_VREG %eax, rINSTq # eax <- vB + subl %eax, (rFP,%rcx,4) # for ex: addl %eax,(rFP,%ecx,4) + CLEAR_REF %rcx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_int_2addr: /* 0xb2 */ +/* File: x86_64/op_mul_int_2addr.S */ + /* mul vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # ecx <- A + GET_VREG %eax, %rcx # eax <- vA + imull (rFP,rINSTq,4), %eax + SET_VREG %eax, %rcx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + +/* ------------------------------ */ + .balign 128 +.L_op_div_int_2addr: /* 0xb3 */ +/* File: x86_64/op_div_int_2addr.S */ +/* File: x86_64/bindiv2addr.S */ +/* + * 32-bit binary div/rem operation. Handles special case of op1=-1. + */ + /* div/rem/2addr vA, vB */ + movl rINST, %ecx # rcx <- BA + sarl $4, %ecx # rcx <- B + andb $0xf, rINSTbl # rINST <- A + .if 0 + GET_WIDE_VREG %rax, rINSTq # eax <- vA + GET_WIDE_VREG %ecx, %rcx # ecx <- vB + .else + GET_VREG %eax, rINSTq # eax <- vA + GET_VREG %ecx, %rcx # ecx <- vB + .endif + testl %ecx, %ecx + jz common_errDivideByZero + cmpl $-1, %ecx + je 2f + cdq # rdx:rax <- sign-extended of rax + idivl %ecx +1: + .if 0 + SET_WIDE_VREG %eax, rINSTq # vA <- result + .else + SET_VREG %eax, rINSTq # vA <- result + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 +2: + .if 0 + xorl %eax, %eax + .else + negl %eax + .endif + jmp 1b + + +/* ------------------------------ */ + .balign 128 +.L_op_rem_int_2addr: /* 0xb4 */ +/* File: x86_64/op_rem_int_2addr.S */ +/* File: x86_64/bindiv2addr.S */ +/* + * 32-bit binary div/rem operation. Handles special case of op1=-1. + */ + /* div/rem/2addr vA, vB */ + movl rINST, %ecx # rcx <- BA + sarl $4, %ecx # rcx <- B + andb $0xf, rINSTbl # rINST <- A + .if 0 + GET_WIDE_VREG %rax, rINSTq # eax <- vA + GET_WIDE_VREG %ecx, %rcx # ecx <- vB + .else + GET_VREG %eax, rINSTq # eax <- vA + GET_VREG %ecx, %rcx # ecx <- vB + .endif + testl %ecx, %ecx + jz common_errDivideByZero + cmpl $-1, %ecx + je 2f + cdq # rdx:rax <- sign-extended of rax + idivl %ecx +1: + .if 0 + SET_WIDE_VREG %edx, rINSTq # vA <- result + .else + SET_VREG %edx, rINSTq # vA <- result + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 +2: + .if 1 + xorl %edx, %edx + .else + negl %edx + .endif + jmp 1b + + +/* ------------------------------ */ + .balign 128 +.L_op_and_int_2addr: /* 0xb5 */ +/* File: x86_64/op_and_int_2addr.S */ +/* File: x86_64/binop2addr.S */ +/* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = r0 op r1". + * This could be an instruction or a function call. + * + * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, + * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, + * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr, + * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr + */ + /* binop/2addr vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # ecx <- A + GET_VREG %eax, rINSTq # eax <- vB + andl %eax, (rFP,%rcx,4) # for ex: addl %eax,(rFP,%ecx,4) + CLEAR_REF %rcx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_or_int_2addr: /* 0xb6 */ +/* File: x86_64/op_or_int_2addr.S */ +/* File: x86_64/binop2addr.S */ +/* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = r0 op r1". + * This could be an instruction or a function call. + * + * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, + * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, + * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr, + * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr + */ + /* binop/2addr vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # ecx <- A + GET_VREG %eax, rINSTq # eax <- vB + orl %eax, (rFP,%rcx,4) # for ex: addl %eax,(rFP,%ecx,4) + CLEAR_REF %rcx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_xor_int_2addr: /* 0xb7 */ +/* File: x86_64/op_xor_int_2addr.S */ +/* File: x86_64/binop2addr.S */ +/* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = r0 op r1". + * This could be an instruction or a function call. + * + * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, + * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, + * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr, + * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr + */ + /* binop/2addr vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # ecx <- A + GET_VREG %eax, rINSTq # eax <- vB + xorl %eax, (rFP,%rcx,4) # for ex: addl %eax,(rFP,%ecx,4) + CLEAR_REF %rcx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_shl_int_2addr: /* 0xb8 */ +/* File: x86_64/op_shl_int_2addr.S */ +/* File: x86_64/shop2addr.S */ +/* + * Generic 32-bit "shift/2addr" operation. + */ + /* shift/2addr vA, vB */ + movl rINST, %ecx # ecx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG %ecx, %rcx # ecx <- vBB + andb $0xf, rINSTbl # rINST <- A + .if 0 + GET_WIDE_VREG %rax, rINSTq # rax <- vAA + sall %cl, %eax # ex: sarl %cl, %eax + SET_WIDE_VREG %rax, rINSTq + .else + GET_VREG %eax, rINSTq # eax <- vAA + sall %cl, %eax # ex: sarl %cl, %eax + SET_VREG %eax, rINSTq + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_shr_int_2addr: /* 0xb9 */ +/* File: x86_64/op_shr_int_2addr.S */ +/* File: x86_64/shop2addr.S */ +/* + * Generic 32-bit "shift/2addr" operation. + */ + /* shift/2addr vA, vB */ + movl rINST, %ecx # ecx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG %ecx, %rcx # ecx <- vBB + andb $0xf, rINSTbl # rINST <- A + .if 0 + GET_WIDE_VREG %rax, rINSTq # rax <- vAA + sarl %cl, %eax # ex: sarl %cl, %eax + SET_WIDE_VREG %rax, rINSTq + .else + GET_VREG %eax, rINSTq # eax <- vAA + sarl %cl, %eax # ex: sarl %cl, %eax + SET_VREG %eax, rINSTq + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_ushr_int_2addr: /* 0xba */ +/* File: x86_64/op_ushr_int_2addr.S */ +/* File: x86_64/shop2addr.S */ +/* + * Generic 32-bit "shift/2addr" operation. + */ + /* shift/2addr vA, vB */ + movl rINST, %ecx # ecx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG %ecx, %rcx # ecx <- vBB + andb $0xf, rINSTbl # rINST <- A + .if 0 + GET_WIDE_VREG %rax, rINSTq # rax <- vAA + shrl %cl, %eax # ex: sarl %cl, %eax + SET_WIDE_VREG %rax, rINSTq + .else + GET_VREG %eax, rINSTq # eax <- vAA + shrl %cl, %eax # ex: sarl %cl, %eax + SET_VREG %eax, rINSTq + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_add_long_2addr: /* 0xbb */ +/* File: x86_64/op_add_long_2addr.S */ +/* File: x86_64/binopWide2addr.S */ +/* + * Generic 64-bit binary operation. + */ + /* binop/2addr vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # ecx <- A + GET_WIDE_VREG %rax, rINSTq # rax <- vB + addq %rax, (rFP,%rcx,4) # for ex: addq %rax,(rFP,%rcx,4) + CLEAR_WIDE_REF %rcx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_sub_long_2addr: /* 0xbc */ +/* File: x86_64/op_sub_long_2addr.S */ +/* File: x86_64/binopWide2addr.S */ +/* + * Generic 64-bit binary operation. + */ + /* binop/2addr vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # ecx <- A + GET_WIDE_VREG %rax, rINSTq # rax <- vB + subq %rax, (rFP,%rcx,4) # for ex: addq %rax,(rFP,%rcx,4) + CLEAR_WIDE_REF %rcx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_long_2addr: /* 0xbd */ +/* File: x86_64/op_mul_long_2addr.S */ + /* mul vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # ecx <- A + GET_WIDE_VREG %rax, %rcx # rax <- vA + imulq (rFP,rINSTq,4), %rax + SET_WIDE_VREG %rax, %rcx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + +/* ------------------------------ */ + .balign 128 +.L_op_div_long_2addr: /* 0xbe */ +/* File: x86_64/op_div_long_2addr.S */ +/* File: x86_64/bindiv2addr.S */ +/* + * 32-bit binary div/rem operation. Handles special case of op1=-1. + */ + /* div/rem/2addr vA, vB */ + movl rINST, %ecx # rcx <- BA + sarl $4, %ecx # rcx <- B + andb $0xf, rINSTbl # rINST <- A + .if 1 + GET_WIDE_VREG %rax, rINSTq # eax <- vA + GET_WIDE_VREG %rcx, %rcx # ecx <- vB + .else + GET_VREG %eax, rINSTq # eax <- vA + GET_VREG %rcx, %rcx # ecx <- vB + .endif + testq %rcx, %rcx + jz common_errDivideByZero + cmpq $-1, %rcx + je 2f + cqo # rdx:rax <- sign-extended of rax + idivq %rcx +1: + .if 1 + SET_WIDE_VREG %rax, rINSTq # vA <- result + .else + SET_VREG %rax, rINSTq # vA <- result + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 +2: + .if 0 + xorq %rax, %rax + .else + negq %rax + .endif + jmp 1b + + +/* ------------------------------ */ + .balign 128 +.L_op_rem_long_2addr: /* 0xbf */ +/* File: x86_64/op_rem_long_2addr.S */ +/* File: x86_64/bindiv2addr.S */ +/* + * 32-bit binary div/rem operation. Handles special case of op1=-1. + */ + /* div/rem/2addr vA, vB */ + movl rINST, %ecx # rcx <- BA + sarl $4, %ecx # rcx <- B + andb $0xf, rINSTbl # rINST <- A + .if 1 + GET_WIDE_VREG %rax, rINSTq # eax <- vA + GET_WIDE_VREG %rcx, %rcx # ecx <- vB + .else + GET_VREG %eax, rINSTq # eax <- vA + GET_VREG %rcx, %rcx # ecx <- vB + .endif + testq %rcx, %rcx + jz common_errDivideByZero + cmpq $-1, %rcx + je 2f + cqo # rdx:rax <- sign-extended of rax + idivq %rcx +1: + .if 1 + SET_WIDE_VREG %rdx, rINSTq # vA <- result + .else + SET_VREG %rdx, rINSTq # vA <- result + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 +2: + .if 1 + xorq %rdx, %rdx + .else + negq %rdx + .endif + jmp 1b + + +/* ------------------------------ */ + .balign 128 +.L_op_and_long_2addr: /* 0xc0 */ +/* File: x86_64/op_and_long_2addr.S */ +/* File: x86_64/binopWide2addr.S */ +/* + * Generic 64-bit binary operation. + */ + /* binop/2addr vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # ecx <- A + GET_WIDE_VREG %rax, rINSTq # rax <- vB + andq %rax, (rFP,%rcx,4) # for ex: addq %rax,(rFP,%rcx,4) + CLEAR_WIDE_REF %rcx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_or_long_2addr: /* 0xc1 */ +/* File: x86_64/op_or_long_2addr.S */ +/* File: x86_64/binopWide2addr.S */ +/* + * Generic 64-bit binary operation. + */ + /* binop/2addr vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # ecx <- A + GET_WIDE_VREG %rax, rINSTq # rax <- vB + orq %rax, (rFP,%rcx,4) # for ex: addq %rax,(rFP,%rcx,4) + CLEAR_WIDE_REF %rcx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_xor_long_2addr: /* 0xc2 */ +/* File: x86_64/op_xor_long_2addr.S */ +/* File: x86_64/binopWide2addr.S */ +/* + * Generic 64-bit binary operation. + */ + /* binop/2addr vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # ecx <- A + GET_WIDE_VREG %rax, rINSTq # rax <- vB + xorq %rax, (rFP,%rcx,4) # for ex: addq %rax,(rFP,%rcx,4) + CLEAR_WIDE_REF %rcx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_shl_long_2addr: /* 0xc3 */ +/* File: x86_64/op_shl_long_2addr.S */ +/* File: x86_64/shop2addr.S */ +/* + * Generic 32-bit "shift/2addr" operation. + */ + /* shift/2addr vA, vB */ + movl rINST, %ecx # ecx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG %ecx, %rcx # ecx <- vBB + andb $0xf, rINSTbl # rINST <- A + .if 1 + GET_WIDE_VREG %rax, rINSTq # rax <- vAA + salq %cl, %rax # ex: sarl %cl, %eax + SET_WIDE_VREG %rax, rINSTq + .else + GET_VREG %eax, rINSTq # eax <- vAA + salq %cl, %rax # ex: sarl %cl, %eax + SET_VREG %eax, rINSTq + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_shr_long_2addr: /* 0xc4 */ +/* File: x86_64/op_shr_long_2addr.S */ +/* File: x86_64/shop2addr.S */ +/* + * Generic 32-bit "shift/2addr" operation. + */ + /* shift/2addr vA, vB */ + movl rINST, %ecx # ecx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG %ecx, %rcx # ecx <- vBB + andb $0xf, rINSTbl # rINST <- A + .if 1 + GET_WIDE_VREG %rax, rINSTq # rax <- vAA + sarq %cl, %rax # ex: sarl %cl, %eax + SET_WIDE_VREG %rax, rINSTq + .else + GET_VREG %eax, rINSTq # eax <- vAA + sarq %cl, %rax # ex: sarl %cl, %eax + SET_VREG %eax, rINSTq + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_ushr_long_2addr: /* 0xc5 */ +/* File: x86_64/op_ushr_long_2addr.S */ +/* File: x86_64/shop2addr.S */ +/* + * Generic 32-bit "shift/2addr" operation. + */ + /* shift/2addr vA, vB */ + movl rINST, %ecx # ecx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG %ecx, %rcx # ecx <- vBB + andb $0xf, rINSTbl # rINST <- A + .if 1 + GET_WIDE_VREG %rax, rINSTq # rax <- vAA + shrq %cl, %rax # ex: sarl %cl, %eax + SET_WIDE_VREG %rax, rINSTq + .else + GET_VREG %eax, rINSTq # eax <- vAA + shrq %cl, %rax # ex: sarl %cl, %eax + SET_VREG %eax, rINSTq + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_add_float_2addr: /* 0xc6 */ +/* File: x86_64/op_add_float_2addr.S */ +/* File: x86_64/sseBinop2Addr.S */ + movl rINST, %ecx # ecx <- A+ + andl $0xf, %ecx # ecx <- A + movss VREG_ADDRESS(%rcx), %xmm0 # %xmm0 <- 1st src + sarl $4, rINST # rINST<- B + addss VREG_ADDRESS(rINSTq), %xmm0 + movss %xmm0, VREG_ADDRESS(%rcx) # vAA<- %xmm0 + pxor %xmm0, %xmm0 + movss %xmm0, VREG_REF_ADDRESS(rINSTq) # clear ref + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_sub_float_2addr: /* 0xc7 */ +/* File: x86_64/op_sub_float_2addr.S */ +/* File: x86_64/sseBinop2Addr.S */ + movl rINST, %ecx # ecx <- A+ + andl $0xf, %ecx # ecx <- A + movss VREG_ADDRESS(%rcx), %xmm0 # %xmm0 <- 1st src + sarl $4, rINST # rINST<- B + subss VREG_ADDRESS(rINSTq), %xmm0 + movss %xmm0, VREG_ADDRESS(%rcx) # vAA<- %xmm0 + pxor %xmm0, %xmm0 + movss %xmm0, VREG_REF_ADDRESS(rINSTq) # clear ref + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_float_2addr: /* 0xc8 */ +/* File: x86_64/op_mul_float_2addr.S */ +/* File: x86_64/sseBinop2Addr.S */ + movl rINST, %ecx # ecx <- A+ + andl $0xf, %ecx # ecx <- A + movss VREG_ADDRESS(%rcx), %xmm0 # %xmm0 <- 1st src + sarl $4, rINST # rINST<- B + mulss VREG_ADDRESS(rINSTq), %xmm0 + movss %xmm0, VREG_ADDRESS(%rcx) # vAA<- %xmm0 + pxor %xmm0, %xmm0 + movss %xmm0, VREG_REF_ADDRESS(rINSTq) # clear ref + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_div_float_2addr: /* 0xc9 */ +/* File: x86_64/op_div_float_2addr.S */ +/* File: x86_64/sseBinop2Addr.S */ + movl rINST, %ecx # ecx <- A+ + andl $0xf, %ecx # ecx <- A + movss VREG_ADDRESS(%rcx), %xmm0 # %xmm0 <- 1st src + sarl $4, rINST # rINST<- B + divss VREG_ADDRESS(rINSTq), %xmm0 + movss %xmm0, VREG_ADDRESS(%rcx) # vAA<- %xmm0 + pxor %xmm0, %xmm0 + movss %xmm0, VREG_REF_ADDRESS(rINSTq) # clear ref + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_rem_float_2addr: /* 0xca */ +/* File: x86_64/op_rem_float_2addr.S */ + /* rem_float/2addr vA, vB */ + movzbq rINSTbl, %rcx # ecx <- A+ + sarl $4, rINST # rINST <- B + flds VREG_ADDRESS(rINSTq) # vB to fp stack + andb $0xf, %cl # ecx <- A + flds VREG_ADDRESS(%rcx) # vA to fp stack +1: + fprem + fstsw %ax + sahf + jp 1b + fstp %st(1) + fstps VREG_ADDRESS(%rcx) # %st to vA + CLEAR_REF %rcx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + +/* ------------------------------ */ + .balign 128 +.L_op_add_double_2addr: /* 0xcb */ +/* File: x86_64/op_add_double_2addr.S */ +/* File: x86_64/sseBinop2Addr.S */ + movl rINST, %ecx # ecx <- A+ + andl $0xf, %ecx # ecx <- A + movsd VREG_ADDRESS(%rcx), %xmm0 # %xmm0 <- 1st src + sarl $4, rINST # rINST<- B + addsd VREG_ADDRESS(rINSTq), %xmm0 + movsd %xmm0, VREG_ADDRESS(%rcx) # vAA<- %xmm0 + pxor %xmm0, %xmm0 + movsd %xmm0, VREG_REF_ADDRESS(rINSTq) # clear ref + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_sub_double_2addr: /* 0xcc */ +/* File: x86_64/op_sub_double_2addr.S */ +/* File: x86_64/sseBinop2Addr.S */ + movl rINST, %ecx # ecx <- A+ + andl $0xf, %ecx # ecx <- A + movsd VREG_ADDRESS(%rcx), %xmm0 # %xmm0 <- 1st src + sarl $4, rINST # rINST<- B + subsd VREG_ADDRESS(rINSTq), %xmm0 + movsd %xmm0, VREG_ADDRESS(%rcx) # vAA<- %xmm0 + pxor %xmm0, %xmm0 + movsd %xmm0, VREG_REF_ADDRESS(rINSTq) # clear ref + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_double_2addr: /* 0xcd */ +/* File: x86_64/op_mul_double_2addr.S */ +/* File: x86_64/sseBinop2Addr.S */ + movl rINST, %ecx # ecx <- A+ + andl $0xf, %ecx # ecx <- A + movsd VREG_ADDRESS(%rcx), %xmm0 # %xmm0 <- 1st src + sarl $4, rINST # rINST<- B + mulsd VREG_ADDRESS(rINSTq), %xmm0 + movsd %xmm0, VREG_ADDRESS(%rcx) # vAA<- %xmm0 + pxor %xmm0, %xmm0 + movsd %xmm0, VREG_REF_ADDRESS(rINSTq) # clear ref + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_div_double_2addr: /* 0xce */ +/* File: x86_64/op_div_double_2addr.S */ +/* File: x86_64/sseBinop2Addr.S */ + movl rINST, %ecx # ecx <- A+ + andl $0xf, %ecx # ecx <- A + movsd VREG_ADDRESS(%rcx), %xmm0 # %xmm0 <- 1st src + sarl $4, rINST # rINST<- B + divsd VREG_ADDRESS(rINSTq), %xmm0 + movsd %xmm0, VREG_ADDRESS(%rcx) # vAA<- %xmm0 + pxor %xmm0, %xmm0 + movsd %xmm0, VREG_REF_ADDRESS(rINSTq) # clear ref + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_rem_double_2addr: /* 0xcf */ +/* File: x86_64/op_rem_double_2addr.S */ + /* rem_double/2addr vA, vB */ + movzbq rINSTbl, %rcx # ecx <- A+ + sarl $4, rINST # rINST <- B + fldl VREG_ADDRESS(rINSTq) # vB to fp stack + andb $0xf, %cl # ecx <- A + fldl VREG_ADDRESS(%rcx) # vA to fp stack +1: + fprem + fstsw %ax + sahf + jp 1b + fstp %st(1) + fstpl VREG_ADDRESS(%rcx) # %st to vA + CLEAR_WIDE_REF %rcx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + +/* ------------------------------ */ + .balign 128 +.L_op_add_int_lit16: /* 0xd0 */ +/* File: x86_64/op_add_int_lit16.S */ +/* File: x86_64/binopLit16.S */ +/* + * Generic 32-bit "lit16" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = eax op ecx". + * This could be an x86 instruction or a function call. (If the result + * comes back in a register other than eax, you can override "result".) + * + * For: add-int/lit16, rsub-int, + * and-int/lit16, or-int/lit16, xor-int/lit16 + */ + /* binop/lit16 vA, vB, #+CCCC */ + movl rINST, %eax # rax <- 000000BA + sarl $4, %eax # eax <- B + GET_VREG %eax, %rax # eax <- vB + andb $0xf, rINSTbl # rINST <- A + movswl 2(rPC), %ecx # ecx <- ssssCCCC + addl %ecx, %eax # for example: addl %ecx, %eax + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_rsub_int: /* 0xd1 */ +/* File: x86_64/op_rsub_int.S */ +/* this op is "rsub-int", but can be thought of as "rsub-int/lit16" */ +/* File: x86_64/binopLit16.S */ +/* + * Generic 32-bit "lit16" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = eax op ecx". + * This could be an x86 instruction or a function call. (If the result + * comes back in a register other than eax, you can override "result".) + * + * For: add-int/lit16, rsub-int, + * and-int/lit16, or-int/lit16, xor-int/lit16 + */ + /* binop/lit16 vA, vB, #+CCCC */ + movl rINST, %eax # rax <- 000000BA + sarl $4, %eax # eax <- B + GET_VREG %eax, %rax # eax <- vB + andb $0xf, rINSTbl # rINST <- A + movswl 2(rPC), %ecx # ecx <- ssssCCCC + subl %eax, %ecx # for example: addl %ecx, %eax + SET_VREG %ecx, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_int_lit16: /* 0xd2 */ +/* File: x86_64/op_mul_int_lit16.S */ +/* File: x86_64/binopLit16.S */ +/* + * Generic 32-bit "lit16" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = eax op ecx". + * This could be an x86 instruction or a function call. (If the result + * comes back in a register other than eax, you can override "result".) + * + * For: add-int/lit16, rsub-int, + * and-int/lit16, or-int/lit16, xor-int/lit16 + */ + /* binop/lit16 vA, vB, #+CCCC */ + movl rINST, %eax # rax <- 000000BA + sarl $4, %eax # eax <- B + GET_VREG %eax, %rax # eax <- vB + andb $0xf, rINSTbl # rINST <- A + movswl 2(rPC), %ecx # ecx <- ssssCCCC + imull %ecx, %eax # for example: addl %ecx, %eax + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_div_int_lit16: /* 0xd3 */ +/* File: x86_64/op_div_int_lit16.S */ +/* File: x86_64/bindivLit16.S */ +/* + * 32-bit binary div/rem operation. Handles special case of op1=-1. + */ + /* div/rem/lit16 vA, vB, #+CCCC */ + /* Need A in rINST, ssssCCCC in ecx, vB in eax */ + movl rINST, %eax # rax <- 000000BA + sarl $4, %eax # eax <- B + GET_VREG %eax, %rax # eax <- vB + movswl 2(rPC), %ecx # ecx <- ssssCCCC + andb $0xf, rINSTbl # rINST <- A + testl %ecx, %ecx + jz common_errDivideByZero + cmpl $-1, %ecx + je 2f + cdq # rax <- sign-extended of eax + idivl %ecx +1: + SET_VREG %eax, rINSTq # vA <- result + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 +2: + .if 0 + xorl %eax, %eax + .else + negl %eax + .endif + jmp 1b + + +/* ------------------------------ */ + .balign 128 +.L_op_rem_int_lit16: /* 0xd4 */ +/* File: x86_64/op_rem_int_lit16.S */ +/* File: x86_64/bindivLit16.S */ +/* + * 32-bit binary div/rem operation. Handles special case of op1=-1. + */ + /* div/rem/lit16 vA, vB, #+CCCC */ + /* Need A in rINST, ssssCCCC in ecx, vB in eax */ + movl rINST, %eax # rax <- 000000BA + sarl $4, %eax # eax <- B + GET_VREG %eax, %rax # eax <- vB + movswl 2(rPC), %ecx # ecx <- ssssCCCC + andb $0xf, rINSTbl # rINST <- A + testl %ecx, %ecx + jz common_errDivideByZero + cmpl $-1, %ecx + je 2f + cdq # rax <- sign-extended of eax + idivl %ecx +1: + SET_VREG %edx, rINSTq # vA <- result + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 +2: + .if 1 + xorl %edx, %edx + .else + negl %edx + .endif + jmp 1b + + +/* ------------------------------ */ + .balign 128 +.L_op_and_int_lit16: /* 0xd5 */ +/* File: x86_64/op_and_int_lit16.S */ +/* File: x86_64/binopLit16.S */ +/* + * Generic 32-bit "lit16" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = eax op ecx". + * This could be an x86 instruction or a function call. (If the result + * comes back in a register other than eax, you can override "result".) + * + * For: add-int/lit16, rsub-int, + * and-int/lit16, or-int/lit16, xor-int/lit16 + */ + /* binop/lit16 vA, vB, #+CCCC */ + movl rINST, %eax # rax <- 000000BA + sarl $4, %eax # eax <- B + GET_VREG %eax, %rax # eax <- vB + andb $0xf, rINSTbl # rINST <- A + movswl 2(rPC), %ecx # ecx <- ssssCCCC + andl %ecx, %eax # for example: addl %ecx, %eax + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_or_int_lit16: /* 0xd6 */ +/* File: x86_64/op_or_int_lit16.S */ +/* File: x86_64/binopLit16.S */ +/* + * Generic 32-bit "lit16" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = eax op ecx". + * This could be an x86 instruction or a function call. (If the result + * comes back in a register other than eax, you can override "result".) + * + * For: add-int/lit16, rsub-int, + * and-int/lit16, or-int/lit16, xor-int/lit16 + */ + /* binop/lit16 vA, vB, #+CCCC */ + movl rINST, %eax # rax <- 000000BA + sarl $4, %eax # eax <- B + GET_VREG %eax, %rax # eax <- vB + andb $0xf, rINSTbl # rINST <- A + movswl 2(rPC), %ecx # ecx <- ssssCCCC + orl %ecx, %eax # for example: addl %ecx, %eax + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_xor_int_lit16: /* 0xd7 */ +/* File: x86_64/op_xor_int_lit16.S */ +/* File: x86_64/binopLit16.S */ +/* + * Generic 32-bit "lit16" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = eax op ecx". + * This could be an x86 instruction or a function call. (If the result + * comes back in a register other than eax, you can override "result".) + * + * For: add-int/lit16, rsub-int, + * and-int/lit16, or-int/lit16, xor-int/lit16 + */ + /* binop/lit16 vA, vB, #+CCCC */ + movl rINST, %eax # rax <- 000000BA + sarl $4, %eax # eax <- B + GET_VREG %eax, %rax # eax <- vB + andb $0xf, rINSTbl # rINST <- A + movswl 2(rPC), %ecx # ecx <- ssssCCCC + xorl %ecx, %eax # for example: addl %ecx, %eax + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_add_int_lit8: /* 0xd8 */ +/* File: x86_64/op_add_int_lit8.S */ +/* File: x86_64/binopLit8.S */ +/* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = eax op ecx". + * This could be an x86 instruction or a function call. (If the result + * comes back in a register other than r0, you can override "result".) + * + * For: add-int/lit8, rsub-int/lit8 + * and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + /* binop/lit8 vAA, vBB, #+CC */ + movzbq 2(rPC), %rax # rax <- BB + movsbl 3(rPC), %ecx # rcx <- ssssssCC + GET_VREG %eax, %rax # eax <- rBB + addl %ecx, %eax # ex: addl %ecx,%eax + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_rsub_int_lit8: /* 0xd9 */ +/* File: x86_64/op_rsub_int_lit8.S */ +/* File: x86_64/binopLit8.S */ +/* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = eax op ecx". + * This could be an x86 instruction or a function call. (If the result + * comes back in a register other than r0, you can override "result".) + * + * For: add-int/lit8, rsub-int/lit8 + * and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + /* binop/lit8 vAA, vBB, #+CC */ + movzbq 2(rPC), %rax # rax <- BB + movsbl 3(rPC), %ecx # rcx <- ssssssCC + GET_VREG %eax, %rax # eax <- rBB + subl %eax, %ecx # ex: addl %ecx,%eax + SET_VREG %ecx, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_int_lit8: /* 0xda */ +/* File: x86_64/op_mul_int_lit8.S */ +/* File: x86_64/binopLit8.S */ +/* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = eax op ecx". + * This could be an x86 instruction or a function call. (If the result + * comes back in a register other than r0, you can override "result".) + * + * For: add-int/lit8, rsub-int/lit8 + * and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + /* binop/lit8 vAA, vBB, #+CC */ + movzbq 2(rPC), %rax # rax <- BB + movsbl 3(rPC), %ecx # rcx <- ssssssCC + GET_VREG %eax, %rax # eax <- rBB + imull %ecx, %eax # ex: addl %ecx,%eax + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_div_int_lit8: /* 0xdb */ +/* File: x86_64/op_div_int_lit8.S */ +/* File: x86_64/bindivLit8.S */ +/* + * 32-bit div/rem "lit8" binary operation. Handles special case of + * op0=minint & op1=-1 + */ + /* div/rem/lit8 vAA, vBB, #+CC */ + movzbq 2(rPC), %rax # eax <- BB + movsbl 3(rPC), %ecx # ecx <- ssssssCC + GET_VREG %eax, %rax # eax <- rBB + testl %ecx, %ecx + je common_errDivideByZero + cmpl $-1, %ecx + je 2f + cdq # rax <- sign-extended of eax + idivl %ecx +1: + SET_VREG %eax, rINSTq # vA <- result + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 +2: + .if 0 + xorl %eax, %eax + .else + negl %eax + .endif + jmp 1b + + +/* ------------------------------ */ + .balign 128 +.L_op_rem_int_lit8: /* 0xdc */ +/* File: x86_64/op_rem_int_lit8.S */ +/* File: x86_64/bindivLit8.S */ +/* + * 32-bit div/rem "lit8" binary operation. Handles special case of + * op0=minint & op1=-1 + */ + /* div/rem/lit8 vAA, vBB, #+CC */ + movzbq 2(rPC), %rax # eax <- BB + movsbl 3(rPC), %ecx # ecx <- ssssssCC + GET_VREG %eax, %rax # eax <- rBB + testl %ecx, %ecx + je common_errDivideByZero + cmpl $-1, %ecx + je 2f + cdq # rax <- sign-extended of eax + idivl %ecx +1: + SET_VREG %edx, rINSTq # vA <- result + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 +2: + .if 1 + xorl %edx, %edx + .else + negl %edx + .endif + jmp 1b + + +/* ------------------------------ */ + .balign 128 +.L_op_and_int_lit8: /* 0xdd */ +/* File: x86_64/op_and_int_lit8.S */ +/* File: x86_64/binopLit8.S */ +/* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = eax op ecx". + * This could be an x86 instruction or a function call. (If the result + * comes back in a register other than r0, you can override "result".) + * + * For: add-int/lit8, rsub-int/lit8 + * and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + /* binop/lit8 vAA, vBB, #+CC */ + movzbq 2(rPC), %rax # rax <- BB + movsbl 3(rPC), %ecx # rcx <- ssssssCC + GET_VREG %eax, %rax # eax <- rBB + andl %ecx, %eax # ex: addl %ecx,%eax + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_or_int_lit8: /* 0xde */ +/* File: x86_64/op_or_int_lit8.S */ +/* File: x86_64/binopLit8.S */ +/* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = eax op ecx". + * This could be an x86 instruction or a function call. (If the result + * comes back in a register other than r0, you can override "result".) + * + * For: add-int/lit8, rsub-int/lit8 + * and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + /* binop/lit8 vAA, vBB, #+CC */ + movzbq 2(rPC), %rax # rax <- BB + movsbl 3(rPC), %ecx # rcx <- ssssssCC + GET_VREG %eax, %rax # eax <- rBB + orl %ecx, %eax # ex: addl %ecx,%eax + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_xor_int_lit8: /* 0xdf */ +/* File: x86_64/op_xor_int_lit8.S */ +/* File: x86_64/binopLit8.S */ +/* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = eax op ecx". + * This could be an x86 instruction or a function call. (If the result + * comes back in a register other than r0, you can override "result".) + * + * For: add-int/lit8, rsub-int/lit8 + * and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + /* binop/lit8 vAA, vBB, #+CC */ + movzbq 2(rPC), %rax # rax <- BB + movsbl 3(rPC), %ecx # rcx <- ssssssCC + GET_VREG %eax, %rax # eax <- rBB + xorl %ecx, %eax # ex: addl %ecx,%eax + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_shl_int_lit8: /* 0xe0 */ +/* File: x86_64/op_shl_int_lit8.S */ +/* File: x86_64/binopLit8.S */ +/* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = eax op ecx". + * This could be an x86 instruction or a function call. (If the result + * comes back in a register other than r0, you can override "result".) + * + * For: add-int/lit8, rsub-int/lit8 + * and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + /* binop/lit8 vAA, vBB, #+CC */ + movzbq 2(rPC), %rax # rax <- BB + movsbl 3(rPC), %ecx # rcx <- ssssssCC + GET_VREG %eax, %rax # eax <- rBB + sall %cl, %eax # ex: addl %ecx,%eax + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_shr_int_lit8: /* 0xe1 */ +/* File: x86_64/op_shr_int_lit8.S */ +/* File: x86_64/binopLit8.S */ +/* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = eax op ecx". + * This could be an x86 instruction or a function call. (If the result + * comes back in a register other than r0, you can override "result".) + * + * For: add-int/lit8, rsub-int/lit8 + * and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + /* binop/lit8 vAA, vBB, #+CC */ + movzbq 2(rPC), %rax # rax <- BB + movsbl 3(rPC), %ecx # rcx <- ssssssCC + GET_VREG %eax, %rax # eax <- rBB + sarl %cl, %eax # ex: addl %ecx,%eax + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_ushr_int_lit8: /* 0xe2 */ +/* File: x86_64/op_ushr_int_lit8.S */ +/* File: x86_64/binopLit8.S */ +/* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = eax op ecx". + * This could be an x86 instruction or a function call. (If the result + * comes back in a register other than r0, you can override "result".) + * + * For: add-int/lit8, rsub-int/lit8 + * and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + /* binop/lit8 vAA, vBB, #+CC */ + movzbq 2(rPC), %rax # rax <- BB + movsbl 3(rPC), %ecx # rcx <- ssssssCC + GET_VREG %eax, %rax # eax <- rBB + shrl %cl, %eax # ex: addl %ecx,%eax + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_quick: /* 0xe3 */ +/* File: x86_64/op_iget_quick.S */ + /* For: iget-quick, iget-boolean-quick, iget-byte-quick, iget-char-quick, iget-short-quick, iget-wide-quick */ + /* op vA, vB, offset@CCCC */ + movl rINST, %ecx # rcx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG %ecx, %rcx # vB (object we're operating on) + movzwq 2(rPC), %rax # eax <- field byte offset + testl %ecx, %ecx # is object null? + je common_errNullObject + andb $0xf,rINSTbl # rINST <- A + .if 0 + movq (%rcx,%rax,1), %rax + SET_WIDE_VREG %rax, rINSTq # fp[A] <- value + .else + movl (%rcx,%rax,1), %eax + SET_VREG %eax, rINSTq # fp[A] <- value + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_iget_wide_quick: /* 0xe4 */ +/* File: x86_64/op_iget_wide_quick.S */ +/* File: x86_64/op_iget_quick.S */ + /* For: iget-quick, iget-boolean-quick, iget-byte-quick, iget-char-quick, iget-short-quick, iget-wide-quick */ + /* op vA, vB, offset@CCCC */ + movl rINST, %ecx # rcx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG %ecx, %rcx # vB (object we're operating on) + movzwq 2(rPC), %rax # eax <- field byte offset + testl %ecx, %ecx # is object null? + je common_errNullObject + andb $0xf,rINSTbl # rINST <- A + .if 1 + movq (%rcx,%rax,1), %rax + SET_WIDE_VREG %rax, rINSTq # fp[A] <- value + .else + movswl (%rcx,%rax,1), %eax + SET_VREG %eax, rINSTq # fp[A] <- value + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_object_quick: /* 0xe5 */ +/* File: x86_64/op_iget_object_quick.S */ + /* For: iget-object-quick */ + /* op vA, vB, offset@CCCC */ + .extern artIGetObjectFromMterp + movzbq rINSTbl, %rcx # rcx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG OUT_32_ARG0, %rcx # vB (object we're operating on) + movzwl 2(rPC), OUT_32_ARG1 # eax <- field byte offset + EXPORT_PC + callq SYMBOL(artIGetObjectFromMterp) # (obj, offset) + cmpq $0, THREAD_EXCEPTION_OFFSET(rSELF) + jnz MterpException # bail out + andb $0xf, rINSTbl # rINST <- A + SET_VREG_OBJECT %eax, rINSTq # fp[A] <- value + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_iput_quick: /* 0xe6 */ +/* File: x86_64/op_iput_quick.S */ + /* For: iput-quick, iput-object-quick */ + /* op vA, vB, offset@CCCC */ + movzbq rINSTbl, %rcx # rcx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG %ecx, %rcx # vB (object we're operating on) + testl %ecx, %ecx # is object null? + je common_errNullObject + andb $0xf, rINSTbl # rINST <- A + GET_VREG rINST, rINSTq # rINST <- v[A] + movzwq 2(rPC), %rax # rax <- field byte offset + movl rINST, (%rcx,%rax,1) + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_iput_wide_quick: /* 0xe7 */ +/* File: x86_64/op_iput_wide_quick.S */ + /* iput-wide-quick vA, vB, offset@CCCC */ + movzbq rINSTbl, %rcx # rcx<- BA + sarl $4, %ecx # ecx<- B + GET_VREG %ecx, %rcx # vB (object we're operating on) + testl %ecx, %ecx # is object null? + je common_errNullObject + movzwq 2(rPC), %rax # rax<- field byte offset + leaq (%rcx,%rax,1), %rcx # ecx<- Address of 64-bit target + andb $0xf, rINSTbl # rINST<- A + GET_WIDE_VREG %rax, rINSTq # rax<- fp[A]/fp[A+1] + movq %rax, (%rcx) # obj.field<- r0/r1 + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_iput_object_quick: /* 0xe8 */ +/* File: x86_64/op_iput_object_quick.S */ + EXPORT_PC + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG0 + movq rPC, OUT_ARG1 + REFRESH_INST 232 + movl rINST, OUT_32_ARG2 + call SYMBOL(MterpIputObjectQuick) + testb %al, %al + jz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_virtual_quick: /* 0xe9 */ +/* File: x86_64/op_invoke_virtual_quick.S */ +/* File: x86_64/invoke.S */ +/* + * Generic invoke handler wrapper. + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + .extern MterpInvokeVirtualQuick + EXPORT_PC + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + movq rPC, OUT_ARG2 + REFRESH_INST 233 + movl rINST, OUT_32_ARG3 + call SYMBOL(MterpInvokeVirtualQuick) + testb %al, %al + jz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_virtual_range_quick: /* 0xea */ +/* File: x86_64/op_invoke_virtual_range_quick.S */ +/* File: x86_64/invoke.S */ +/* + * Generic invoke handler wrapper. + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + .extern MterpInvokeVirtualQuickRange + EXPORT_PC + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + movq rPC, OUT_ARG2 + REFRESH_INST 234 + movl rINST, OUT_32_ARG3 + call SYMBOL(MterpInvokeVirtualQuickRange) + testb %al, %al + jz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + + +/* ------------------------------ */ + .balign 128 +.L_op_iput_boolean_quick: /* 0xeb */ +/* File: x86_64/op_iput_boolean_quick.S */ +/* File: x86_64/op_iput_quick.S */ + /* For: iput-quick, iput-object-quick */ + /* op vA, vB, offset@CCCC */ + movzbq rINSTbl, %rcx # rcx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG %ecx, %rcx # vB (object we're operating on) + testl %ecx, %ecx # is object null? + je common_errNullObject + andb $0xf, rINSTbl # rINST <- A + GET_VREG rINST, rINSTq # rINST <- v[A] + movzwq 2(rPC), %rax # rax <- field byte offset + movb rINSTbl, (%rcx,%rax,1) + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iput_byte_quick: /* 0xec */ +/* File: x86_64/op_iput_byte_quick.S */ +/* File: x86_64/op_iput_quick.S */ + /* For: iput-quick, iput-object-quick */ + /* op vA, vB, offset@CCCC */ + movzbq rINSTbl, %rcx # rcx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG %ecx, %rcx # vB (object we're operating on) + testl %ecx, %ecx # is object null? + je common_errNullObject + andb $0xf, rINSTbl # rINST <- A + GET_VREG rINST, rINSTq # rINST <- v[A] + movzwq 2(rPC), %rax # rax <- field byte offset + movb rINSTbl, (%rcx,%rax,1) + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iput_char_quick: /* 0xed */ +/* File: x86_64/op_iput_char_quick.S */ +/* File: x86_64/op_iput_quick.S */ + /* For: iput-quick, iput-object-quick */ + /* op vA, vB, offset@CCCC */ + movzbq rINSTbl, %rcx # rcx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG %ecx, %rcx # vB (object we're operating on) + testl %ecx, %ecx # is object null? + je common_errNullObject + andb $0xf, rINSTbl # rINST <- A + GET_VREG rINST, rINSTq # rINST <- v[A] + movzwq 2(rPC), %rax # rax <- field byte offset + movw rINSTw, (%rcx,%rax,1) + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iput_short_quick: /* 0xee */ +/* File: x86_64/op_iput_short_quick.S */ +/* File: x86_64/op_iput_quick.S */ + /* For: iput-quick, iput-object-quick */ + /* op vA, vB, offset@CCCC */ + movzbq rINSTbl, %rcx # rcx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG %ecx, %rcx # vB (object we're operating on) + testl %ecx, %ecx # is object null? + je common_errNullObject + andb $0xf, rINSTbl # rINST <- A + GET_VREG rINST, rINSTq # rINST <- v[A] + movzwq 2(rPC), %rax # rax <- field byte offset + movw rINSTw, (%rcx,%rax,1) + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_boolean_quick: /* 0xef */ +/* File: x86_64/op_iget_boolean_quick.S */ +/* File: x86_64/op_iget_quick.S */ + /* For: iget-quick, iget-boolean-quick, iget-byte-quick, iget-char-quick, iget-short-quick, iget-wide-quick */ + /* op vA, vB, offset@CCCC */ + movl rINST, %ecx # rcx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG %ecx, %rcx # vB (object we're operating on) + movzwq 2(rPC), %rax # eax <- field byte offset + testl %ecx, %ecx # is object null? + je common_errNullObject + andb $0xf,rINSTbl # rINST <- A + .if 0 + movq (%rcx,%rax,1), %rax + SET_WIDE_VREG %rax, rINSTq # fp[A] <- value + .else + movsbl (%rcx,%rax,1), %eax + SET_VREG %eax, rINSTq # fp[A] <- value + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_byte_quick: /* 0xf0 */ +/* File: x86_64/op_iget_byte_quick.S */ +/* File: x86_64/op_iget_quick.S */ + /* For: iget-quick, iget-boolean-quick, iget-byte-quick, iget-char-quick, iget-short-quick, iget-wide-quick */ + /* op vA, vB, offset@CCCC */ + movl rINST, %ecx # rcx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG %ecx, %rcx # vB (object we're operating on) + movzwq 2(rPC), %rax # eax <- field byte offset + testl %ecx, %ecx # is object null? + je common_errNullObject + andb $0xf,rINSTbl # rINST <- A + .if 0 + movq (%rcx,%rax,1), %rax + SET_WIDE_VREG %rax, rINSTq # fp[A] <- value + .else + movsbl (%rcx,%rax,1), %eax + SET_VREG %eax, rINSTq # fp[A] <- value + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_char_quick: /* 0xf1 */ +/* File: x86_64/op_iget_char_quick.S */ +/* File: x86_64/op_iget_quick.S */ + /* For: iget-quick, iget-boolean-quick, iget-byte-quick, iget-char-quick, iget-short-quick, iget-wide-quick */ + /* op vA, vB, offset@CCCC */ + movl rINST, %ecx # rcx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG %ecx, %rcx # vB (object we're operating on) + movzwq 2(rPC), %rax # eax <- field byte offset + testl %ecx, %ecx # is object null? + je common_errNullObject + andb $0xf,rINSTbl # rINST <- A + .if 0 + movq (%rcx,%rax,1), %rax + SET_WIDE_VREG %rax, rINSTq # fp[A] <- value + .else + movzwl (%rcx,%rax,1), %eax + SET_VREG %eax, rINSTq # fp[A] <- value + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_short_quick: /* 0xf2 */ +/* File: x86_64/op_iget_short_quick.S */ +/* File: x86_64/op_iget_quick.S */ + /* For: iget-quick, iget-boolean-quick, iget-byte-quick, iget-char-quick, iget-short-quick, iget-wide-quick */ + /* op vA, vB, offset@CCCC */ + movl rINST, %ecx # rcx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG %ecx, %rcx # vB (object we're operating on) + movzwq 2(rPC), %rax # eax <- field byte offset + testl %ecx, %ecx # is object null? + je common_errNullObject + andb $0xf,rINSTbl # rINST <- A + .if 0 + movq (%rcx,%rax,1), %rax + SET_WIDE_VREG %rax, rINSTq # fp[A] <- value + .else + movswl (%rcx,%rax,1), %eax + SET_VREG %eax, rINSTq # fp[A] <- value + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_lambda: /* 0xf3 */ +/* Transfer stub to alternate interpreter */ + jmp MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_f4: /* 0xf4 */ +/* File: x86_64/op_unused_f4.S */ +/* File: x86_64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + jmp MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_capture_variable: /* 0xf5 */ +/* Transfer stub to alternate interpreter */ + jmp MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_create_lambda: /* 0xf6 */ +/* Transfer stub to alternate interpreter */ + jmp MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_liberate_variable: /* 0xf7 */ +/* Transfer stub to alternate interpreter */ + jmp MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_box_lambda: /* 0xf8 */ +/* Transfer stub to alternate interpreter */ + jmp MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unbox_lambda: /* 0xf9 */ +/* Transfer stub to alternate interpreter */ + jmp MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_fa: /* 0xfa */ +/* File: x86_64/op_unused_fa.S */ +/* File: x86_64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + jmp MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_fb: /* 0xfb */ +/* File: x86_64/op_unused_fb.S */ +/* File: x86_64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + jmp MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_fc: /* 0xfc */ +/* File: x86_64/op_unused_fc.S */ +/* File: x86_64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + jmp MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_fd: /* 0xfd */ +/* File: x86_64/op_unused_fd.S */ +/* File: x86_64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + jmp MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_fe: /* 0xfe */ +/* File: x86_64/op_unused_fe.S */ +/* File: x86_64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + jmp MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_ff: /* 0xff */ +/* File: x86_64/op_unused_ff.S */ +/* File: x86_64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + jmp MterpFallback + + + .balign 128 + SIZE(SYMBOL(artMterpAsmInstructionStart),SYMBOL(artMterpAsmInstructionStart)) + .global SYMBOL(artMterpAsmInstructionEnd) +SYMBOL(artMterpAsmInstructionEnd): + +/* + * =========================================================================== + * Sister implementations + * =========================================================================== + */ + .global SYMBOL(artMterpAsmSisterStart) + FUNCTION_TYPE(SYMBOL(artMterpAsmSisterStart)) + .text + .balign 4 +SYMBOL(artMterpAsmSisterStart): + + SIZE(SYMBOL(artMterpAsmSisterStart),SYMBOL(artMterpAsmSisterStart)) + .global SYMBOL(artMterpAsmSisterEnd) +SYMBOL(artMterpAsmSisterEnd): + + + .global SYMBOL(artMterpAsmAltInstructionStart) + FUNCTION_TYPE(SYMBOL(artMterpAsmAltInstructionStart)) + .text + +SYMBOL(artMterpAsmAltInstructionStart) = .L_ALT_op_nop +/* ------------------------------ */ + .balign 128 +.L_ALT_op_nop: /* 0x00 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(0*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move: /* 0x01 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(1*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_from16: /* 0x02 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(2*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_16: /* 0x03 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(3*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_wide: /* 0x04 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(4*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_wide_from16: /* 0x05 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(5*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_wide_16: /* 0x06 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(6*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_object: /* 0x07 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(7*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_object_from16: /* 0x08 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(8*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_object_16: /* 0x09 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(9*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_result: /* 0x0a */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(10*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_result_wide: /* 0x0b */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(11*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_result_object: /* 0x0c */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(12*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_exception: /* 0x0d */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(13*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_return_void: /* 0x0e */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(14*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_return: /* 0x0f */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(15*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_return_wide: /* 0x10 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(16*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_return_object: /* 0x11 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(17*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_4: /* 0x12 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(18*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_16: /* 0x13 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(19*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const: /* 0x14 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(20*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_high16: /* 0x15 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(21*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_wide_16: /* 0x16 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(22*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_wide_32: /* 0x17 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(23*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_wide: /* 0x18 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(24*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_wide_high16: /* 0x19 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(25*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_string: /* 0x1a */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(26*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_string_jumbo: /* 0x1b */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(27*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_class: /* 0x1c */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(28*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_monitor_enter: /* 0x1d */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(29*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_monitor_exit: /* 0x1e */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(30*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_check_cast: /* 0x1f */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(31*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_instance_of: /* 0x20 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(32*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_array_length: /* 0x21 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(33*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_new_instance: /* 0x22 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(34*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_new_array: /* 0x23 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(35*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_filled_new_array: /* 0x24 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(36*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_filled_new_array_range: /* 0x25 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(37*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_fill_array_data: /* 0x26 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(38*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_throw: /* 0x27 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(39*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_goto: /* 0x28 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(40*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_goto_16: /* 0x29 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(41*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_goto_32: /* 0x2a */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(42*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_packed_switch: /* 0x2b */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(43*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sparse_switch: /* 0x2c */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(44*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_cmpl_float: /* 0x2d */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(45*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_cmpg_float: /* 0x2e */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(46*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_cmpl_double: /* 0x2f */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(47*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_cmpg_double: /* 0x30 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(48*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_cmp_long: /* 0x31 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(49*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_eq: /* 0x32 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(50*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_ne: /* 0x33 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(51*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_lt: /* 0x34 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(52*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_ge: /* 0x35 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(53*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_gt: /* 0x36 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(54*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_le: /* 0x37 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(55*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_eqz: /* 0x38 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(56*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_nez: /* 0x39 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(57*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_ltz: /* 0x3a */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(58*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_gez: /* 0x3b */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(59*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_gtz: /* 0x3c */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(60*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_lez: /* 0x3d */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(61*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_3e: /* 0x3e */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(62*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_3f: /* 0x3f */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(63*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_40: /* 0x40 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(64*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_41: /* 0x41 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(65*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_42: /* 0x42 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(66*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_43: /* 0x43 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(67*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aget: /* 0x44 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(68*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aget_wide: /* 0x45 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(69*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aget_object: /* 0x46 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(70*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aget_boolean: /* 0x47 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(71*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aget_byte: /* 0x48 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(72*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aget_char: /* 0x49 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(73*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aget_short: /* 0x4a */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(74*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aput: /* 0x4b */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(75*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aput_wide: /* 0x4c */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(76*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aput_object: /* 0x4d */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(77*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aput_boolean: /* 0x4e */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(78*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aput_byte: /* 0x4f */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(79*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aput_char: /* 0x50 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(80*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aput_short: /* 0x51 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(81*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget: /* 0x52 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(82*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_wide: /* 0x53 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(83*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_object: /* 0x54 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(84*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_boolean: /* 0x55 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(85*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_byte: /* 0x56 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(86*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_char: /* 0x57 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(87*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_short: /* 0x58 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(88*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput: /* 0x59 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(89*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_wide: /* 0x5a */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(90*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_object: /* 0x5b */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(91*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_boolean: /* 0x5c */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(92*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_byte: /* 0x5d */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(93*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_char: /* 0x5e */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(94*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_short: /* 0x5f */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(95*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sget: /* 0x60 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(96*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sget_wide: /* 0x61 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(97*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sget_object: /* 0x62 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(98*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sget_boolean: /* 0x63 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(99*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sget_byte: /* 0x64 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(100*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sget_char: /* 0x65 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(101*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sget_short: /* 0x66 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(102*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sput: /* 0x67 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(103*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sput_wide: /* 0x68 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(104*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sput_object: /* 0x69 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(105*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sput_boolean: /* 0x6a */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(106*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sput_byte: /* 0x6b */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(107*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sput_char: /* 0x6c */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(108*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sput_short: /* 0x6d */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(109*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_virtual: /* 0x6e */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(110*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_super: /* 0x6f */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(111*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_direct: /* 0x70 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(112*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_static: /* 0x71 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(113*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_interface: /* 0x72 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(114*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_return_void_no_barrier: /* 0x73 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(115*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_virtual_range: /* 0x74 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(116*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_super_range: /* 0x75 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(117*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_direct_range: /* 0x76 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(118*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_static_range: /* 0x77 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(119*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_interface_range: /* 0x78 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(120*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_79: /* 0x79 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(121*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_7a: /* 0x7a */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(122*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_neg_int: /* 0x7b */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(123*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_not_int: /* 0x7c */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(124*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_neg_long: /* 0x7d */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(125*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_not_long: /* 0x7e */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(126*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_neg_float: /* 0x7f */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(127*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_neg_double: /* 0x80 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(128*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_int_to_long: /* 0x81 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(129*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_int_to_float: /* 0x82 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(130*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_int_to_double: /* 0x83 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(131*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_long_to_int: /* 0x84 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(132*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_long_to_float: /* 0x85 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(133*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_long_to_double: /* 0x86 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(134*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_float_to_int: /* 0x87 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(135*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_float_to_long: /* 0x88 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(136*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_float_to_double: /* 0x89 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(137*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_double_to_int: /* 0x8a */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(138*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_double_to_long: /* 0x8b */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(139*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_double_to_float: /* 0x8c */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(140*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_int_to_byte: /* 0x8d */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(141*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_int_to_char: /* 0x8e */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(142*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_int_to_short: /* 0x8f */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(143*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_int: /* 0x90 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(144*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sub_int: /* 0x91 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(145*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_int: /* 0x92 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(146*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_int: /* 0x93 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(147*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_int: /* 0x94 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(148*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_and_int: /* 0x95 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(149*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_or_int: /* 0x96 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(150*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_xor_int: /* 0x97 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(151*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shl_int: /* 0x98 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(152*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shr_int: /* 0x99 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(153*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_ushr_int: /* 0x9a */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(154*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_long: /* 0x9b */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(155*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sub_long: /* 0x9c */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(156*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_long: /* 0x9d */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(157*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_long: /* 0x9e */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(158*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_long: /* 0x9f */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(159*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_and_long: /* 0xa0 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(160*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_or_long: /* 0xa1 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(161*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_xor_long: /* 0xa2 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(162*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shl_long: /* 0xa3 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(163*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shr_long: /* 0xa4 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(164*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_ushr_long: /* 0xa5 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(165*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_float: /* 0xa6 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(166*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sub_float: /* 0xa7 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(167*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_float: /* 0xa8 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(168*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_float: /* 0xa9 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(169*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_float: /* 0xaa */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(170*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_double: /* 0xab */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(171*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sub_double: /* 0xac */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(172*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_double: /* 0xad */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(173*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_double: /* 0xae */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(174*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_double: /* 0xaf */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(175*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_int_2addr: /* 0xb0 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(176*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sub_int_2addr: /* 0xb1 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(177*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_int_2addr: /* 0xb2 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(178*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_int_2addr: /* 0xb3 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(179*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_int_2addr: /* 0xb4 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(180*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_and_int_2addr: /* 0xb5 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(181*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_or_int_2addr: /* 0xb6 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(182*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_xor_int_2addr: /* 0xb7 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(183*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shl_int_2addr: /* 0xb8 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(184*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shr_int_2addr: /* 0xb9 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(185*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_ushr_int_2addr: /* 0xba */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(186*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_long_2addr: /* 0xbb */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(187*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sub_long_2addr: /* 0xbc */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(188*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_long_2addr: /* 0xbd */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(189*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_long_2addr: /* 0xbe */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(190*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_long_2addr: /* 0xbf */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(191*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_and_long_2addr: /* 0xc0 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(192*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_or_long_2addr: /* 0xc1 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(193*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_xor_long_2addr: /* 0xc2 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(194*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shl_long_2addr: /* 0xc3 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(195*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shr_long_2addr: /* 0xc4 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(196*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_ushr_long_2addr: /* 0xc5 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(197*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_float_2addr: /* 0xc6 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(198*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sub_float_2addr: /* 0xc7 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(199*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_float_2addr: /* 0xc8 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(200*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_float_2addr: /* 0xc9 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(201*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_float_2addr: /* 0xca */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(202*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_double_2addr: /* 0xcb */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(203*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sub_double_2addr: /* 0xcc */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(204*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_double_2addr: /* 0xcd */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(205*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_double_2addr: /* 0xce */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(206*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_double_2addr: /* 0xcf */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(207*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_int_lit16: /* 0xd0 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(208*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rsub_int: /* 0xd1 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(209*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_int_lit16: /* 0xd2 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(210*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_int_lit16: /* 0xd3 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(211*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_int_lit16: /* 0xd4 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(212*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_and_int_lit16: /* 0xd5 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(213*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_or_int_lit16: /* 0xd6 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(214*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_xor_int_lit16: /* 0xd7 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(215*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_int_lit8: /* 0xd8 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(216*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rsub_int_lit8: /* 0xd9 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(217*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_int_lit8: /* 0xda */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(218*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_int_lit8: /* 0xdb */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(219*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_int_lit8: /* 0xdc */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(220*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_and_int_lit8: /* 0xdd */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(221*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_or_int_lit8: /* 0xde */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(222*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_xor_int_lit8: /* 0xdf */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(223*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shl_int_lit8: /* 0xe0 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(224*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shr_int_lit8: /* 0xe1 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(225*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_ushr_int_lit8: /* 0xe2 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(226*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_quick: /* 0xe3 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(227*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_wide_quick: /* 0xe4 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(228*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_object_quick: /* 0xe5 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(229*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_quick: /* 0xe6 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(230*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_wide_quick: /* 0xe7 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(231*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_object_quick: /* 0xe8 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(232*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_virtual_quick: /* 0xe9 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(233*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_virtual_range_quick: /* 0xea */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(234*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_boolean_quick: /* 0xeb */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(235*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_byte_quick: /* 0xec */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(236*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_char_quick: /* 0xed */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(237*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_short_quick: /* 0xee */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(238*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_boolean_quick: /* 0xef */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(239*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_byte_quick: /* 0xf0 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(240*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_char_quick: /* 0xf1 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(241*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_short_quick: /* 0xf2 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(242*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_lambda: /* 0xf3 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(243*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_f4: /* 0xf4 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(244*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_capture_variable: /* 0xf5 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(245*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_create_lambda: /* 0xf6 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(246*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_liberate_variable: /* 0xf7 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(247*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_box_lambda: /* 0xf8 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(248*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unbox_lambda: /* 0xf9 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(249*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_fa: /* 0xfa */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(250*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_fb: /* 0xfb */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(251*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_fc: /* 0xfc */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(252*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_fd: /* 0xfd */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(253*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_fe: /* 0xfe */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(254*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_ff: /* 0xff */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(255*128) + + .balign 128 + SIZE(SYMBOL(artMterpAsmAltInstructionStart),SYMBOL(artMterpAsmAltInstructionStart)) + .global SYMBOL(artMterpAsmAltInstructionEnd) +SYMBOL(artMterpAsmAltInstructionEnd): +/* File: x86_64/footer.S */ +/* + * =========================================================================== + * Common subroutines and data + * =========================================================================== + */ + + .text + .align 2 + +/* + * We've detected a condition that will result in an exception, but the exception + * has not yet been thrown. Just bail out to the reference interpreter to deal with it. + * TUNING: for consistency, we may want to just go ahead and handle these here. + */ +#define MTERP_LOGGING 0 +common_errDivideByZero: + EXPORT_PC +#if MTERP_LOGGING + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpLogDivideByZeroException) +#endif + jmp MterpCommonFallback + +common_errArrayIndex: + EXPORT_PC +#if MTERP_LOGGING + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpLogArrayIndexException) +#endif + jmp MterpCommonFallback + +common_errNegativeArraySize: + EXPORT_PC +#if MTERP_LOGGING + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpLogNegativeArraySizeException) +#endif + jmp MterpCommonFallback + +common_errNoSuchMethod: + EXPORT_PC +#if MTERP_LOGGING + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpLogNoSuchMethodException) +#endif + jmp MterpCommonFallback + +common_errNullObject: + EXPORT_PC +#if MTERP_LOGGING + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpLogNullObjectException) +#endif + jmp MterpCommonFallback + +common_exceptionThrown: + EXPORT_PC +#if MTERP_LOGGING + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpLogExceptionThrownException) +#endif + jmp MterpCommonFallback + +MterpSuspendFallback: + EXPORT_PC +#if MTERP_LOGGING + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + movl THREAD_FLAGS_OFFSET(rSELF), OUT_32_ARG2 + call SYMBOL(MterpLogSuspendFallback) +#endif + jmp MterpCommonFallback + +/* + * If we're here, something is out of the ordinary. If there is a pending + * exception, handle it. Otherwise, roll back and retry with the reference + * interpreter. + */ +MterpPossibleException: + cmpq $0, THREAD_EXCEPTION_OFFSET(rSELF) + jz MterpFallback + /* intentional fallthrough - handle pending exception. */ + +/* + * On return from a runtime helper routine, we've found a pending exception. + * Can we handle it here - or need to bail out to caller? + * + */ +MterpException: + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpHandleException) + testb %al, %al + jz MterpExceptionReturn + REFRESH_IBASE + movq OFF_FP_CODE_ITEM(rFP), %rax + mov OFF_FP_DEX_PC(rFP), %ecx + leaq CODEITEM_INSNS_OFFSET(%rax), rPC + leaq (rPC, %rcx, 2), rPC + movq rPC, OFF_FP_DEX_PC_PTR(rFP) + /* resume execution at catch block */ + FETCH_INST + GOTO_NEXT + /* NOTE: no fallthrough */ + +/* + * Check for suspend check request. Assumes rINST already loaded, rPC advanced and + * still needs to get the opcode and branch to it, and flags are in lr. + */ +MterpCheckSuspendAndContinue: + REFRESH_IBASE + testl $(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(rSELF) + jz 1f + EXPORT_PC + movq rSELF, OUT_ARG0 + call SYMBOL(MterpSuspendCheck) +1: + GOTO_NEXT + +/* + * Bail out to reference interpreter. + */ +MterpFallback: + EXPORT_PC +#if MTERP_LOGGING + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpLogFallback) +#endif +MterpCommonFallback: + xorl %eax, %eax + jmp MterpDone + +/* + * On entry: + * uint32_t* rFP (should still be live, pointer to base of vregs) + */ +MterpExceptionReturn: + movl $1, %eax + jmp MterpDone +MterpReturn: + movq OFF_FP_RESULT_REGISTER(rFP), %rdx + movq %rax, (%rdx) + movl $1, %eax +MterpDone: + /* Restore callee save register */ + movq RBX_SPILL(%rsp), %rbx + movq RBP_SPILL(%rsp), %rbp + movq R12_SPILL(%rsp), %r12 + movq R13_SPILL(%rsp), %r13 + movq R14_SPILL(%rsp), %r14 + movq R15_SPILL(%rsp), %r15 + + /* pop up frame */ + addq $FRAME_SIZE, %rsp + .cfi_adjust_cfa_offset -FRAME_SIZE + ret + + .cfi_endproc + SIZE(ExecuteMterpImpl,ExecuteMterpImpl) + diff --git a/runtime/interpreter/mterp/rebuild.sh b/runtime/interpreter/mterp/rebuild.sh index ac8794581c..0c2d5479a0 100755 --- a/runtime/interpreter/mterp/rebuild.sh +++ b/runtime/interpreter/mterp/rebuild.sh @@ -21,4 +21,4 @@ set -e # for arch in arm x86 mips arm64 x86_64 mips64; do TARGET_ARCH_EXT=$arch make -f Makefile_mterp; done -for arch in arm x86 arm64 ; do TARGET_ARCH_EXT=$arch make -f Makefile_mterp; done +for arch in arm x86 arm64 x86_64 ; do TARGET_ARCH_EXT=$arch make -f Makefile_mterp; done diff --git a/runtime/interpreter/mterp/x86_64/alt_stub.S b/runtime/interpreter/mterp/x86_64/alt_stub.S new file mode 100644 index 0000000000..6fcebbba3c --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/alt_stub.S @@ -0,0 +1,17 @@ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(${opnum}*${handler_size_bytes}) diff --git a/runtime/interpreter/mterp/x86_64/bincmp.S b/runtime/interpreter/mterp/x86_64/bincmp.S new file mode 100644 index 0000000000..5e4225fd86 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/bincmp.S @@ -0,0 +1,28 @@ +/* + * Generic two-operand compare-and-branch operation. Provide a "revcmp" + * fragment that specifies the *reverse* comparison to perform, e.g. + * for "if-le" you would use "gt". + * + * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le + */ + /* if-cmp vA, vB, +CCCC */ + movl rINST, %ecx # rcx <- A+ + sarl $$4, rINST # rINST <- B + andb $$0xf, %cl # rcx <- A + GET_VREG %eax, %rcx # eax <- vA + cmpl VREG_ADDRESS(rINSTq), %eax # compare (vA, vB) + movl $$2, %eax # assume not taken + j${revcmp} 1f + movswq 2(rPC),%rax # Get signed branch offset +1: + addq %rax, %rax # rax <- AA * 2 + leaq (rPC, %rax), rPC + FETCH_INST + jg 2f # AA * 2 > 0 => no suspend check +#if MTERP_SUSPEND + REFRESH_IBASE +#else + jmp MterpCheckSuspendAndContinue +#endif +2: + GOTO_NEXT diff --git a/runtime/interpreter/mterp/x86_64/bindiv.S b/runtime/interpreter/mterp/x86_64/bindiv.S new file mode 100644 index 0000000000..e10d1dc4b1 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/bindiv.S @@ -0,0 +1,34 @@ +%default {"result":"","second":"","wide":"","suffix":"","rem":"0","ext":"cdq"} +/* + * 32-bit binary div/rem operation. Handles special case of op1=-1. + */ + /* div/rem vAA, vBB, vCC */ + movzbq 2(rPC), %rax # rax <- BB + movzbq 3(rPC), %rcx # rcx <- CC + .if $wide + GET_WIDE_VREG %rax, %rax # eax <- vBB + GET_WIDE_VREG $second, %rcx # ecx <- vCC + .else + GET_VREG %eax, %rax # eax <- vBB + GET_VREG $second, %rcx # ecx <- vCC + .endif + test${suffix} $second, $second + jz common_errDivideByZero + cmp${suffix} $$-1, $second + je 2f + $ext # rdx:rax <- sign-extended of rax + idiv${suffix} $second +1: + .if $wide + SET_WIDE_VREG $result, rINSTq # eax <- vBB + .else + SET_VREG $result, rINSTq # eax <- vBB + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 +2: + .if $rem + xor${suffix} $result, $result + .else + neg${suffix} $result + .endif + jmp 1b diff --git a/runtime/interpreter/mterp/x86_64/bindiv2addr.S b/runtime/interpreter/mterp/x86_64/bindiv2addr.S new file mode 100644 index 0000000000..8b9bc953d2 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/bindiv2addr.S @@ -0,0 +1,35 @@ +%default {"result":"","second":"","wide":"","suffix":"","rem":"0","ext":"cdq"} +/* + * 32-bit binary div/rem operation. Handles special case of op1=-1. + */ + /* div/rem/2addr vA, vB */ + movl rINST, %ecx # rcx <- BA + sarl $$4, %ecx # rcx <- B + andb $$0xf, rINSTbl # rINST <- A + .if $wide + GET_WIDE_VREG %rax, rINSTq # eax <- vA + GET_WIDE_VREG $second, %rcx # ecx <- vB + .else + GET_VREG %eax, rINSTq # eax <- vA + GET_VREG $second, %rcx # ecx <- vB + .endif + test${suffix} $second, $second + jz common_errDivideByZero + cmp${suffix} $$-1, $second + je 2f + $ext # rdx:rax <- sign-extended of rax + idiv${suffix} $second +1: + .if $wide + SET_WIDE_VREG $result, rINSTq # vA <- result + .else + SET_VREG $result, rINSTq # vA <- result + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 +2: + .if $rem + xor${suffix} $result, $result + .else + neg${suffix} $result + .endif + jmp 1b diff --git a/runtime/interpreter/mterp/x86_64/bindivLit16.S b/runtime/interpreter/mterp/x86_64/bindivLit16.S new file mode 100644 index 0000000000..80dbce2975 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/bindivLit16.S @@ -0,0 +1,27 @@ +%default {"result":"","rem":"0"} +/* + * 32-bit binary div/rem operation. Handles special case of op1=-1. + */ + /* div/rem/lit16 vA, vB, #+CCCC */ + /* Need A in rINST, ssssCCCC in ecx, vB in eax */ + movl rINST, %eax # rax <- 000000BA + sarl $$4, %eax # eax <- B + GET_VREG %eax, %rax # eax <- vB + movswl 2(rPC), %ecx # ecx <- ssssCCCC + andb $$0xf, rINSTbl # rINST <- A + testl %ecx, %ecx + jz common_errDivideByZero + cmpl $$-1, %ecx + je 2f + cdq # rax <- sign-extended of eax + idivl %ecx +1: + SET_VREG $result, rINSTq # vA <- result + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 +2: + .if $rem + xorl $result, $result + .else + negl $result + .endif + jmp 1b diff --git a/runtime/interpreter/mterp/x86_64/bindivLit8.S b/runtime/interpreter/mterp/x86_64/bindivLit8.S new file mode 100644 index 0000000000..ab535f3fb0 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/bindivLit8.S @@ -0,0 +1,25 @@ +%default {"result":"","rem":"0"} +/* + * 32-bit div/rem "lit8" binary operation. Handles special case of + * op0=minint & op1=-1 + */ + /* div/rem/lit8 vAA, vBB, #+CC */ + movzbq 2(rPC), %rax # eax <- BB + movsbl 3(rPC), %ecx # ecx <- ssssssCC + GET_VREG %eax, %rax # eax <- rBB + testl %ecx, %ecx + je common_errDivideByZero + cmpl $$-1, %ecx + je 2f + cdq # rax <- sign-extended of eax + idivl %ecx +1: + SET_VREG $result, rINSTq # vA <- result + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 +2: + .if $rem + xorl $result, $result + .else + negl $result + .endif + jmp 1b diff --git a/runtime/interpreter/mterp/x86_64/binop.S b/runtime/interpreter/mterp/x86_64/binop.S new file mode 100644 index 0000000000..962dd61eea --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/binop.S @@ -0,0 +1,17 @@ +%default {"result":"%eax"} +/* + * Generic 32-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = eax op (rFP,%ecx,4)". + * This could be an x86 instruction or a function call. (If the result + * comes back in a register other than eax, you can override "result".) + * + * For: add-int, sub-int, and-int, or-int, + * xor-int, shl-int, shr-int, ushr-int + */ + /* binop vAA, vBB, vCC */ + movzbq 2(rPC), %rax # rax <- BB + movzbq 3(rPC), %rcx # rcx <- CC + GET_VREG %eax, %rax # eax <- vBB + $instr # ex: addl (rFP,%rcx,4),%eax + SET_VREG $result, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/binop1.S b/runtime/interpreter/mterp/x86_64/binop1.S new file mode 100644 index 0000000000..bdd57325aa --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/binop1.S @@ -0,0 +1,19 @@ +%default {"wide":"0"} +/* + * Generic 32-bit binary operation in which both operands loaded to + * registers (op0 in eax, op1 in ecx). + */ + /* binop vAA, vBB, vCC */ + movzbq 2(rPC), %rax # eax <- BB + movzbq 3(rPC), %rcx # ecx <- CC + GET_VREG %ecx, %rcx # eax <- vCC + .if $wide + GET_WIDE_VREG %rax, %rax # rax <- vBB + $instr # ex: addl %ecx,%eax + SET_WIDE_VREG %rax, rINSTq + .else + GET_VREG %eax, %rax # eax <- vBB + $instr # ex: addl %ecx,%eax + SET_VREG %eax, rINSTq + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/binop2addr.S b/runtime/interpreter/mterp/x86_64/binop2addr.S new file mode 100644 index 0000000000..4448a815e9 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/binop2addr.S @@ -0,0 +1,19 @@ +%default {"result":"%eax"} +/* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = r0 op r1". + * This could be an instruction or a function call. + * + * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, + * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, + * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr, + * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr + */ + /* binop/2addr vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $$4, rINST # rINST <- B + andb $$0xf, %cl # ecx <- A + GET_VREG %eax, rINSTq # eax <- vB + $instr # for ex: addl %eax,(rFP,%ecx,4) + CLEAR_REF %rcx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/binopLit16.S b/runtime/interpreter/mterp/x86_64/binopLit16.S new file mode 100644 index 0000000000..de43b53d5c --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/binopLit16.S @@ -0,0 +1,19 @@ +%default {"result":"%eax"} +/* + * Generic 32-bit "lit16" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = eax op ecx". + * This could be an x86 instruction or a function call. (If the result + * comes back in a register other than eax, you can override "result".) + * + * For: add-int/lit16, rsub-int, + * and-int/lit16, or-int/lit16, xor-int/lit16 + */ + /* binop/lit16 vA, vB, #+CCCC */ + movl rINST, %eax # rax <- 000000BA + sarl $$4, %eax # eax <- B + GET_VREG %eax, %rax # eax <- vB + andb $$0xf, rINSTbl # rINST <- A + movswl 2(rPC), %ecx # ecx <- ssssCCCC + $instr # for example: addl %ecx, %eax + SET_VREG $result, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/binopLit8.S b/runtime/interpreter/mterp/x86_64/binopLit8.S new file mode 100644 index 0000000000..995002b7e4 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/binopLit8.S @@ -0,0 +1,18 @@ +%default {"result":"%eax"} +/* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = eax op ecx". + * This could be an x86 instruction or a function call. (If the result + * comes back in a register other than r0, you can override "result".) + * + * For: add-int/lit8, rsub-int/lit8 + * and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + /* binop/lit8 vAA, vBB, #+CC */ + movzbq 2(rPC), %rax # rax <- BB + movsbl 3(rPC), %ecx # rcx <- ssssssCC + GET_VREG %eax, %rax # eax <- rBB + $instr # ex: addl %ecx,%eax + SET_VREG $result, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/binopWide.S b/runtime/interpreter/mterp/x86_64/binopWide.S new file mode 100644 index 0000000000..f92f18e013 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/binopWide.S @@ -0,0 +1,10 @@ +/* + * Generic 64-bit binary operation. + */ + /* binop vAA, vBB, vCC */ + movzbq 2(rPC), %rax # eax <- BB + movzbq 3(rPC), %rcx # ecx <- CC + GET_WIDE_VREG %rax, %rax # rax <- v[BB] + $instr # ex: addq (rFP,%rcx,4),%rax + SET_WIDE_VREG %rax, rINSTq # v[AA] <- rax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/binopWide2addr.S b/runtime/interpreter/mterp/x86_64/binopWide2addr.S new file mode 100644 index 0000000000..d9e6cfbc9d --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/binopWide2addr.S @@ -0,0 +1,11 @@ +/* + * Generic 64-bit binary operation. + */ + /* binop/2addr vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $$4, rINST # rINST <- B + andb $$0xf, %cl # ecx <- A + GET_WIDE_VREG %rax, rINSTq # rax <- vB + $instr # for ex: addq %rax,(rFP,%rcx,4) + CLEAR_WIDE_REF %rcx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/cvtfp_int.S b/runtime/interpreter/mterp/x86_64/cvtfp_int.S new file mode 100644 index 0000000000..1472bd26bd --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/cvtfp_int.S @@ -0,0 +1,27 @@ +%default {"fp_suffix":"","i_suffix":"","max_const":"","result_reg":"","wide":""} +/* On fp to int conversions, Java requires that + * if the result > maxint, it should be clamped to maxint. If it is less + * than minint, it should be clamped to minint. If it is a nan, the result + * should be zero. Further, the rounding mode is to truncate. + */ + /* float/double to int/long vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $$4, rINST # rINST <- B + andb $$0xf, %cl # ecx <- A + movs${fp_suffix} VREG_ADDRESS(rINSTq), %xmm0 + mov${i_suffix} ${max_const}, ${result_reg} + cvtsi2s${fp_suffix}${i_suffix} ${result_reg}, %xmm1 + comis${fp_suffix} %xmm1, %xmm0 + jae 1f + jp 2f + cvtts${fp_suffix}2si${i_suffix} %xmm0, ${result_reg} + jmp 1f +2: + xor${i_suffix} ${result_reg}, ${result_reg} +1: + .if $wide + SET_WIDE_VREG ${result_reg}, %rcx + .else + SET_VREG ${result_reg}, %rcx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/entry.S b/runtime/interpreter/mterp/x86_64/entry.S new file mode 100644 index 0000000000..4b868f2ae1 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/entry.S @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * Interpreter entry point. + */ + + .text + .global SYMBOL(ExecuteMterpImpl) + FUNCTION_TYPE(ExecuteMterpImpl) + +/* + * On entry: + * 0 Thread* self + * 1 code_item + * 2 ShadowFrame + * 3 JValue* result_register + * + */ + +SYMBOL(ExecuteMterpImpl): + .cfi_startproc + + /* Allocate frame */ + subq $$FRAME_SIZE, %rsp + .cfi_adjust_cfa_offset FRAME_SIZE + + /* Spill callee save regs */ + movq %rbx, RBX_SPILL(%rsp) + movq %rbp, RBP_SPILL(%rsp) + movq %r12, R12_SPILL(%rsp) + movq %r13, R13_SPILL(%rsp) + movq %r14, R14_SPILL(%rsp) + movq %r15, R15_SPILL(%rsp) + + /* Remember the return register */ + movq IN_ARG3, SHADOWFRAME_RESULT_REGISTER_OFFSET(IN_ARG2) + + /* Remember the code_item */ + movq IN_ARG1, SHADOWFRAME_CODE_ITEM_OFFSET(IN_ARG2) + + /* set up "named" registers */ + movl SHADOWFRAME_NUMBER_OF_VREGS_OFFSET(IN_ARG2), %eax + leaq SHADOWFRAME_VREGS_OFFSET(IN_ARG2), rFP + leaq (rFP, %rax, 4), rREFS + movl SHADOWFRAME_DEX_PC_OFFSET(IN_ARG2), %eax + leaq CODEITEM_INSNS_OFFSET(IN_ARG1), rPC + leaq (rPC, %rax, 2), rPC + EXPORT_PC + + /* Starting ibase */ + movq IN_ARG0, rSELF + REFRESH_IBASE + + /* start executing the instruction at rPC */ + FETCH_INST + GOTO_NEXT + /* NOTE: no fallthrough */ diff --git a/runtime/interpreter/mterp/x86_64/fallback.S b/runtime/interpreter/mterp/x86_64/fallback.S new file mode 100644 index 0000000000..8d61166f63 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/fallback.S @@ -0,0 +1,3 @@ +/* Transfer stub to alternate interpreter */ + jmp MterpFallback + diff --git a/runtime/interpreter/mterp/x86_64/footer.S b/runtime/interpreter/mterp/x86_64/footer.S new file mode 100644 index 0000000000..165f380ba4 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/footer.S @@ -0,0 +1,166 @@ +/* + * =========================================================================== + * Common subroutines and data + * =========================================================================== + */ + + .text + .align 2 + +/* + * We've detected a condition that will result in an exception, but the exception + * has not yet been thrown. Just bail out to the reference interpreter to deal with it. + * TUNING: for consistency, we may want to just go ahead and handle these here. + */ +#define MTERP_LOGGING 0 +common_errDivideByZero: + EXPORT_PC +#if MTERP_LOGGING + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpLogDivideByZeroException) +#endif + jmp MterpCommonFallback + +common_errArrayIndex: + EXPORT_PC +#if MTERP_LOGGING + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpLogArrayIndexException) +#endif + jmp MterpCommonFallback + +common_errNegativeArraySize: + EXPORT_PC +#if MTERP_LOGGING + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpLogNegativeArraySizeException) +#endif + jmp MterpCommonFallback + +common_errNoSuchMethod: + EXPORT_PC +#if MTERP_LOGGING + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpLogNoSuchMethodException) +#endif + jmp MterpCommonFallback + +common_errNullObject: + EXPORT_PC +#if MTERP_LOGGING + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpLogNullObjectException) +#endif + jmp MterpCommonFallback + +common_exceptionThrown: + EXPORT_PC +#if MTERP_LOGGING + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpLogExceptionThrownException) +#endif + jmp MterpCommonFallback + +MterpSuspendFallback: + EXPORT_PC +#if MTERP_LOGGING + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + movl THREAD_FLAGS_OFFSET(rSELF), OUT_32_ARG2 + call SYMBOL(MterpLogSuspendFallback) +#endif + jmp MterpCommonFallback + +/* + * If we're here, something is out of the ordinary. If there is a pending + * exception, handle it. Otherwise, roll back and retry with the reference + * interpreter. + */ +MterpPossibleException: + cmpq $$0, THREAD_EXCEPTION_OFFSET(rSELF) + jz MterpFallback + /* intentional fallthrough - handle pending exception. */ + +/* + * On return from a runtime helper routine, we've found a pending exception. + * Can we handle it here - or need to bail out to caller? + * + */ +MterpException: + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpHandleException) + testb %al, %al + jz MterpExceptionReturn + REFRESH_IBASE + movq OFF_FP_CODE_ITEM(rFP), %rax + mov OFF_FP_DEX_PC(rFP), %ecx + leaq CODEITEM_INSNS_OFFSET(%rax), rPC + leaq (rPC, %rcx, 2), rPC + movq rPC, OFF_FP_DEX_PC_PTR(rFP) + /* resume execution at catch block */ + FETCH_INST + GOTO_NEXT + /* NOTE: no fallthrough */ + +/* + * Check for suspend check request. Assumes rINST already loaded, rPC advanced and + * still needs to get the opcode and branch to it, and flags are in lr. + */ +MterpCheckSuspendAndContinue: + REFRESH_IBASE + testl $$(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(rSELF) + jz 1f + EXPORT_PC + movq rSELF, OUT_ARG0 + call SYMBOL(MterpSuspendCheck) +1: + GOTO_NEXT + +/* + * Bail out to reference interpreter. + */ +MterpFallback: + EXPORT_PC +#if MTERP_LOGGING + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpLogFallback) +#endif +MterpCommonFallback: + xorl %eax, %eax + jmp MterpDone + +/* + * On entry: + * uint32_t* rFP (should still be live, pointer to base of vregs) + */ +MterpExceptionReturn: + movl $$1, %eax + jmp MterpDone +MterpReturn: + movq OFF_FP_RESULT_REGISTER(rFP), %rdx + movq %rax, (%rdx) + movl $$1, %eax +MterpDone: + /* Restore callee save register */ + movq RBX_SPILL(%rsp), %rbx + movq RBP_SPILL(%rsp), %rbp + movq R12_SPILL(%rsp), %r12 + movq R13_SPILL(%rsp), %r13 + movq R14_SPILL(%rsp), %r14 + movq R15_SPILL(%rsp), %r15 + + /* pop up frame */ + addq $$FRAME_SIZE, %rsp + .cfi_adjust_cfa_offset -FRAME_SIZE + ret + + .cfi_endproc + SIZE(ExecuteMterpImpl,ExecuteMterpImpl) diff --git a/runtime/interpreter/mterp/x86_64/fpcmp.S b/runtime/interpreter/mterp/x86_64/fpcmp.S new file mode 100644 index 0000000000..806bc2b1ef --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/fpcmp.S @@ -0,0 +1,35 @@ +%default {"suff":"d","nanval":"pos"} +/* + * Compare two floating-point values. Puts 0, 1, or -1 into the + * destination register based on the results of the comparison. + * + * int compare(x, y) { + * if (x == y) { + * return 0; + * } else if (x < y) { + * return -1; + * } else if (x > y) { + * return 1; + * } else { + * return nanval ? 1 : -1; + * } + * } + */ + /* op vAA, vBB, vCC */ + movzbq 3(rPC), %rcx # ecx<- CC + movzbq 2(rPC), %rax # eax<- BB + movs${suff} VREG_ADDRESS(%rax), %xmm0 + xor %eax, %eax + ucomis${suff} VREG_ADDRESS(%rcx), %xmm0 + jp .L${opcode}_nan_is_${nanval} + je .L${opcode}_finish + jb .L${opcode}_less +.L${opcode}_nan_is_pos: + addb $$1, %al + jmp .L${opcode}_finish +.L${opcode}_nan_is_neg: +.L${opcode}_less: + movl $$-1, %eax +.L${opcode}_finish: + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/fpcvt.S b/runtime/interpreter/mterp/x86_64/fpcvt.S new file mode 100644 index 0000000000..657869e0bd --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/fpcvt.S @@ -0,0 +1,17 @@ +%default {"source_suffix":"","dest_suffix":"","wide":""} +/* + * Generic 32-bit FP conversion operation. + */ + /* unop vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $$4, rINST # rINST <- B + andb $$0xf, %cl # ecx <- A + cvts${source_suffix}2s${dest_suffix} VREG_ADDRESS(rINSTq), %xmm0 + .if $wide + movsd %xmm0, VREG_ADDRESS(%rcx) + CLEAR_WIDE_REF %rcx + .else + movss %xmm0, VREG_ADDRESS(%rcx) + CLEAR_REF %rcx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/header.S b/runtime/interpreter/mterp/x86_64/header.S new file mode 100644 index 0000000000..fadfc19690 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/header.S @@ -0,0 +1,289 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + Art assembly interpreter notes: + + First validate assembly code by implementing ExecuteXXXImpl() style body (doesn't + handle invoke, allows higher-level code to create frame & shadow frame. + + Once that's working, support direct entry code & eliminate shadow frame (and + excess locals allocation. + + Some (hopefully) temporary ugliness. We'll treat rFP as pointing to the + base of the vreg array within the shadow frame. Access the other fields, + dex_pc_, method_ and number_of_vregs_ via negative offsets. For now, we'll continue + the shadow frame mechanism of double-storing object references - via rFP & + number_of_vregs_. + + */ + +/* +x86_64 ABI general notes: + +Caller save set: + rax, rdx, rcx, rsi, rdi, r8-r11, st(0)-st(7) +Callee save set: + rbx, rbp, r12-r15 +Return regs: + 32-bit in eax + 64-bit in rax + fp on xmm0 + +First 8 fp parameters came in xmm0-xmm7. +First 6 non-fp parameters came in rdi, rsi, rdx, rcx, r8, r9. +Other parameters passed on stack, pushed right-to-left. On entry to target, first +param is at 8(%esp). Traditional entry code is: + +Stack must be 16-byte aligned to support SSE in native code. + +If we're not doing variable stack allocation (alloca), the frame pointer can be +eliminated and all arg references adjusted to be esp relative. +*/ + +/* +Mterp and x86_64 notes: + +Some key interpreter variables will be assigned to registers. + + nick reg purpose + rSELF rbp pointer to ThreadSelf. + rPC r12 interpreted program counter, used for fetching instructions + rFP r13 interpreted frame pointer, used for accessing locals and args + rINSTw bx first 16-bit code of current instruction + rINSTbl bl opcode portion of instruction word + rINSTbh bh high byte of inst word, usually contains src/tgt reg names + rIBASE r14 base of instruction handler table + rREFS r15 base of object references in shadow frame. + +Notes: + o High order 16 bits of ebx must be zero on entry to handler + o rPC, rFP, rINSTw/rINSTbl valid on handler entry and exit + o eax and ecx are scratch, rINSTw/ebx sometimes scratch + +Macros are provided for common operations. Each macro MUST emit only +one instruction to make instruction-counting easier. They MUST NOT alter +unspecified registers or condition codes. +*/ + +/* + * This is a #include, not a %include, because we want the C pre-processor + * to expand the macros into assembler assignment statements. + */ +#include "asm_support.h" + +/* + * Handle mac compiler specific + */ +#if defined(__APPLE__) + #define MACRO_LITERAL(value) $$(value) + #define FUNCTION_TYPE(name) + #define SIZE(start,end) + // Mac OS' symbols have an _ prefix. + #define SYMBOL(name) _ ## name +#else + #define MACRO_LITERAL(value) $$value + #define FUNCTION_TYPE(name) .type name, @function + #define SIZE(start,end) .size start, .-end + #define SYMBOL(name) name +#endif + +/* Frame size must be 16-byte aligned. + * Remember about 8 bytes for return address + */ +#define FRAME_SIZE 56 + +/* Frame diagram while executing ExecuteMterpImpl, high to low addresses */ +#define IN_ARG3 %rcx +#define IN_ARG2 %rdx +#define IN_ARG1 %rsi +#define IN_ARG0 %rdi +#define CALLER_RP (FRAME_SIZE + 0) +/* Spill offsets relative to %esp */ +#define RBX_SPILL (FRAME_SIZE - 8) +#define RBP_SPILL (FRAME_SIZE - 16) +#define R12_SPILL (FRAME_SIZE - 24) +#define R13_SPILL (FRAME_SIZE - 32) +#define R14_SPILL (FRAME_SIZE - 40) +#define R15_SPILL (FRAME_SIZE - 48) +/* Out Arg offsets, relative to %esp */ +#define OUT_ARG3 %rcx +#define OUT_ARG2 %rdx +#define OUT_ARG1 %rsi +#define OUT_ARG0 %rdi +#define OUT_32_ARG3 %ecx +#define OUT_32_ARG2 %edx +#define OUT_32_ARG1 %esi +#define OUT_32_ARG0 %edi +#define OUT_FP_ARG1 %xmm1 +#define OUT_FP_ARG0 %xmm0 + +/* During bringup, we'll use the shadow frame model instead of rFP */ +/* single-purpose registers, given names for clarity */ +#define rSELF %rbp +#define rPC %r12 +#define rFP %r13 +#define rINST %ebx +#define rINSTq %rbx +#define rINSTw %bx +#define rINSTbh %bh +#define rINSTbl %bl +#define rIBASE %r14 +#define rREFS %r15 + +/* + * Instead of holding a pointer to the shadow frame, we keep rFP at the base of the vregs. So, + * to access other shadow frame fields, we need to use a backwards offset. Define those here. + */ +#define OFF_FP(a) (a - SHADOWFRAME_VREGS_OFFSET) +#define OFF_FP_NUMBER_OF_VREGS OFF_FP(SHADOWFRAME_NUMBER_OF_VREGS_OFFSET) +#define OFF_FP_DEX_PC OFF_FP(SHADOWFRAME_DEX_PC_OFFSET) +#define OFF_FP_LINK OFF_FP(SHADOWFRAME_LINK_OFFSET) +#define OFF_FP_METHOD OFF_FP(SHADOWFRAME_METHOD_OFFSET) +#define OFF_FP_RESULT_REGISTER OFF_FP(SHADOWFRAME_RESULT_REGISTER_OFFSET) +#define OFF_FP_DEX_PC_PTR OFF_FP(SHADOWFRAME_DEX_PC_PTR_OFFSET) +#define OFF_FP_CODE_ITEM OFF_FP(SHADOWFRAME_CODE_ITEM_OFFSET) +#define OFF_FP_SHADOWFRAME (-SHADOWFRAME_VREGS_OFFSET) + +/* + * + * The reference interpreter performs explicit suspect checks, which is somewhat wasteful. + * Dalvik's interpreter folded suspend checks into the jump table mechanism, and eventually + * mterp should do so as well. + */ +#define MTERP_SUSPEND 0 + +/* + * "export" the PC to dex_pc field in the shadow frame, f/b/o future exception objects. Must + * be done *before* something throws. + * + * It's okay to do this more than once. + * + * NOTE: the fast interpreter keeps track of dex pc as a direct pointer to the mapped + * dex byte codes. However, the rest of the runtime expects dex pc to be an instruction + * offset into the code_items_[] array. For effiency, we will "export" the + * current dex pc as a direct pointer using the EXPORT_PC macro, and rely on GetDexPC + * to convert to a dex pc when needed. + */ +.macro EXPORT_PC + movq rPC, OFF_FP_DEX_PC_PTR(rFP) +.endm + +/* + * Refresh handler table. + * IBase handles uses the caller save register so we must restore it after each call. + * Also it is used as a result of some 64-bit operations (like imul) and we should + * restore it in such cases also. + * + */ +.macro REFRESH_IBASE + movq THREAD_CURRENT_IBASE_OFFSET(rSELF), rIBASE +.endm + +/* + * Refresh rINST. + * At enter to handler rINST does not contain the opcode number. + * However some utilities require the full value, so this macro + * restores the opcode number. + */ +.macro REFRESH_INST _opnum + movb rINSTbl, rINSTbh + movb $$\_opnum, rINSTbl +.endm + +/* + * Fetch the next instruction from rPC into rINSTw. Does not advance rPC. + */ +.macro FETCH_INST + movzwq (rPC), rINSTq +.endm + +/* + * Remove opcode from rINST, compute the address of handler and jump to it. + */ +.macro GOTO_NEXT + movzx rINSTbl,%eax + movzbl rINSTbh,rINST + shll MACRO_LITERAL(${handler_size_bits}), %eax + addq rIBASE, %rax + jmp *%rax +.endm + +/* + * Advance rPC by instruction count. + */ +.macro ADVANCE_PC _count + leaq 2*\_count(rPC), rPC +.endm + +/* + * Advance rPC by instruction count, fetch instruction and jump to handler. + */ +.macro ADVANCE_PC_FETCH_AND_GOTO_NEXT _count + ADVANCE_PC \_count + FETCH_INST + GOTO_NEXT +.endm + +/* + * Get/set the 32-bit value from a Dalvik register. + */ +#define VREG_ADDRESS(_vreg) (rFP,_vreg,4) +#define VREG_REF_ADDRESS(_vreg) (rREFS,_vreg,4) + +.macro GET_VREG _reg _vreg + movl (rFP,\_vreg,4), \_reg +.endm + +/* Read wide value. */ +.macro GET_WIDE_VREG _reg _vreg + movq (rFP,\_vreg,4), \_reg +.endm + +.macro SET_VREG _reg _vreg + movl \_reg, (rFP,\_vreg,4) + movl MACRO_LITERAL(0), (rREFS,\_vreg,4) +.endm + +/* Write wide value. reg is clobbered. */ +.macro SET_WIDE_VREG _reg _vreg + movq \_reg, (rFP,\_vreg,4) + xorq \_reg, \_reg + movq \_reg, (rREFS,\_vreg,4) +.endm + +.macro SET_VREG_OBJECT _reg _vreg + movl \_reg, (rFP,\_vreg,4) + movl \_reg, (rREFS,\_vreg,4) +.endm + +.macro GET_VREG_HIGH _reg _vreg + movl 4(rFP,\_vreg,4), \_reg +.endm + +.macro SET_VREG_HIGH _reg _vreg + movl \_reg, 4(rFP,\_vreg,4) + movl MACRO_LITERAL(0), 4(rREFS,\_vreg,4) +.endm + +.macro CLEAR_REF _vreg + movl MACRO_LITERAL(0), (rREFS,\_vreg,4) +.endm + +.macro CLEAR_WIDE_REF _vreg + movl MACRO_LITERAL(0), (rREFS,\_vreg,4) + movl MACRO_LITERAL(0), 4(rREFS,\_vreg,4) +.endm diff --git a/runtime/interpreter/mterp/x86_64/invoke.S b/runtime/interpreter/mterp/x86_64/invoke.S new file mode 100644 index 0000000000..86eccdbf91 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/invoke.S @@ -0,0 +1,17 @@ +%default { "helper":"UndefinedInvokeHandler" } +/* + * Generic invoke handler wrapper. + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + .extern $helper + EXPORT_PC + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + movq rPC, OUT_ARG2 + REFRESH_INST ${opnum} + movl rINST, OUT_32_ARG3 + call SYMBOL($helper) + testb %al, %al + jz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 diff --git a/runtime/interpreter/mterp/x86_64/op_add_double.S b/runtime/interpreter/mterp/x86_64/op_add_double.S new file mode 100644 index 0000000000..cb462cb816 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_add_double.S @@ -0,0 +1 @@ +%include "x86_64/sseBinop.S" {"instr":"adds","suff":"d"} diff --git a/runtime/interpreter/mterp/x86_64/op_add_double_2addr.S b/runtime/interpreter/mterp/x86_64/op_add_double_2addr.S new file mode 100644 index 0000000000..063bde3fb3 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_add_double_2addr.S @@ -0,0 +1 @@ +%include "x86_64/sseBinop2Addr.S" {"instr":"adds","suff":"d"} diff --git a/runtime/interpreter/mterp/x86_64/op_add_float.S b/runtime/interpreter/mterp/x86_64/op_add_float.S new file mode 100644 index 0000000000..7753bf8870 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_add_float.S @@ -0,0 +1 @@ +%include "x86_64/sseBinop.S" {"instr":"adds","suff":"s"} diff --git a/runtime/interpreter/mterp/x86_64/op_add_float_2addr.S b/runtime/interpreter/mterp/x86_64/op_add_float_2addr.S new file mode 100644 index 0000000000..6c8005b182 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_add_float_2addr.S @@ -0,0 +1 @@ +%include "x86_64/sseBinop2Addr.S" {"instr":"adds","suff":"s"} diff --git a/runtime/interpreter/mterp/x86_64/op_add_int.S b/runtime/interpreter/mterp/x86_64/op_add_int.S new file mode 100644 index 0000000000..e316be7b9d --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_add_int.S @@ -0,0 +1 @@ +%include "x86_64/binop.S" {"instr":"addl (rFP,%rcx,4), %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_add_int_2addr.S b/runtime/interpreter/mterp/x86_64/op_add_int_2addr.S new file mode 100644 index 0000000000..2ff82935ae --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_add_int_2addr.S @@ -0,0 +1 @@ +%include "x86_64/binop2addr.S" {"instr":"addl %eax, (rFP,%rcx,4)"} diff --git a/runtime/interpreter/mterp/x86_64/op_add_int_lit16.S b/runtime/interpreter/mterp/x86_64/op_add_int_lit16.S new file mode 100644 index 0000000000..bfeb7caabc --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_add_int_lit16.S @@ -0,0 +1 @@ +%include "x86_64/binopLit16.S" {"instr":"addl %ecx, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_add_int_lit8.S b/runtime/interpreter/mterp/x86_64/op_add_int_lit8.S new file mode 100644 index 0000000000..8954844eae --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_add_int_lit8.S @@ -0,0 +1 @@ +%include "x86_64/binopLit8.S" {"instr":"addl %ecx, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_add_long.S b/runtime/interpreter/mterp/x86_64/op_add_long.S new file mode 100644 index 0000000000..89131ffe0e --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_add_long.S @@ -0,0 +1 @@ +%include "x86_64/binopWide.S" {"instr":"addq (rFP,%rcx,4), %rax"} diff --git a/runtime/interpreter/mterp/x86_64/op_add_long_2addr.S b/runtime/interpreter/mterp/x86_64/op_add_long_2addr.S new file mode 100644 index 0000000000..fed98bc3e6 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_add_long_2addr.S @@ -0,0 +1 @@ +%include "x86_64/binopWide2addr.S" {"instr":"addq %rax, (rFP,%rcx,4)"} diff --git a/runtime/interpreter/mterp/x86_64/op_aget.S b/runtime/interpreter/mterp/x86_64/op_aget.S new file mode 100644 index 0000000000..58d49481cf --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_aget.S @@ -0,0 +1,24 @@ +%default { "load":"movl", "shift":"4", "data_offset":"MIRROR_INT_ARRAY_DATA_OFFSET", "wide":"0" } +/* + * Array get, 32 bits or less. vAA <- vBB[vCC]. + * + * for: aget, aget-boolean, aget-byte, aget-char, aget-short, aget-wide + * + */ + /* op vAA, vBB, vCC */ + movzbq 2(rPC), %rax # eax <- BB + movzbq 3(rPC), %rcx # ecx <- CC + GET_VREG %eax, %rax # eax <- vBB (array object) + GET_VREG %ecx, %rcx # ecx <- vCC (requested index) + testl %eax, %eax # null array object? + je common_errNullObject # bail if so + cmpl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ecx + jae common_errArrayIndex # index >= length, bail. + .if $wide + movq $data_offset(%rax,%rcx,8), %rax + SET_WIDE_VREG %rax, rINSTq + .else + $load $data_offset(%rax,%rcx,$shift), %eax + SET_VREG %eax, rINSTq + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_aget_boolean.S b/runtime/interpreter/mterp/x86_64/op_aget_boolean.S new file mode 100644 index 0000000000..cf7bdb582b --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_aget_boolean.S @@ -0,0 +1 @@ +%include "x86_64/op_aget.S" { "load":"movzbl", "shift":"1", "data_offset":"MIRROR_BOOLEAN_ARRAY_DATA_OFFSET" } diff --git a/runtime/interpreter/mterp/x86_64/op_aget_byte.S b/runtime/interpreter/mterp/x86_64/op_aget_byte.S new file mode 100644 index 0000000000..1cbb569024 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_aget_byte.S @@ -0,0 +1 @@ +%include "x86_64/op_aget.S" { "load":"movsbl", "shift":"1", "data_offset":"MIRROR_BYTE_ARRAY_DATA_OFFSET" } diff --git a/runtime/interpreter/mterp/x86_64/op_aget_char.S b/runtime/interpreter/mterp/x86_64/op_aget_char.S new file mode 100644 index 0000000000..45c90851fd --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_aget_char.S @@ -0,0 +1 @@ +%include "x86_64/op_aget.S" { "load":"movzwl", "shift":"2", "data_offset":"MIRROR_CHAR_ARRAY_DATA_OFFSET" } diff --git a/runtime/interpreter/mterp/x86_64/op_aget_object.S b/runtime/interpreter/mterp/x86_64/op_aget_object.S new file mode 100644 index 0000000000..8baedeab5e --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_aget_object.S @@ -0,0 +1,16 @@ +/* + * Array object get. vAA <- vBB[vCC]. + * + * for: aget-object + */ + /* op vAA, vBB, vCC */ + movzbq 2(rPC), %rax # rax <- BB + movzbq 3(rPC), %rcx # rcx <- CC + GET_VREG OUT_32_ARG0, %rax # eax <- vBB (array object) + GET_VREG OUT_32_ARG1, %rcx # ecx <- vCC (requested index) + EXPORT_PC + call SYMBOL(artAGetObjectFromMterp) # (array, index) + cmpq $$0, THREAD_EXCEPTION_OFFSET(rSELF) + jnz MterpException + SET_VREG_OBJECT %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_aget_short.S b/runtime/interpreter/mterp/x86_64/op_aget_short.S new file mode 100644 index 0000000000..82c4a1ddf3 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_aget_short.S @@ -0,0 +1 @@ +%include "x86_64/op_aget.S" { "load":"movswl", "shift":"2", "data_offset":"MIRROR_SHORT_ARRAY_DATA_OFFSET" } diff --git a/runtime/interpreter/mterp/x86_64/op_aget_wide.S b/runtime/interpreter/mterp/x86_64/op_aget_wide.S new file mode 100644 index 0000000000..4f2771b9c4 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_aget_wide.S @@ -0,0 +1 @@ +%include "x86_64/op_aget.S" { "load":"movq", "shift":"8", "data_offset":"MIRROR_WIDE_ARRAY_DATA_OFFSET", "wide":"1" } diff --git a/runtime/interpreter/mterp/x86_64/op_and_int.S b/runtime/interpreter/mterp/x86_64/op_and_int.S new file mode 100644 index 0000000000..446988993d --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_and_int.S @@ -0,0 +1 @@ +%include "x86_64/binop.S" {"instr":"andl (rFP,%rcx,4), %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_and_int_2addr.S b/runtime/interpreter/mterp/x86_64/op_and_int_2addr.S new file mode 100644 index 0000000000..16315bba03 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_and_int_2addr.S @@ -0,0 +1 @@ +%include "x86_64/binop2addr.S" {"instr":"andl %eax, (rFP,%rcx,4)"} diff --git a/runtime/interpreter/mterp/x86_64/op_and_int_lit16.S b/runtime/interpreter/mterp/x86_64/op_and_int_lit16.S new file mode 100644 index 0000000000..63e851b449 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_and_int_lit16.S @@ -0,0 +1 @@ +%include "x86_64/binopLit16.S" {"instr":"andl %ecx, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_and_int_lit8.S b/runtime/interpreter/mterp/x86_64/op_and_int_lit8.S new file mode 100644 index 0000000000..da7a20fdff --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_and_int_lit8.S @@ -0,0 +1 @@ +%include "x86_64/binopLit8.S" {"instr":"andl %ecx, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_and_long.S b/runtime/interpreter/mterp/x86_64/op_and_long.S new file mode 100644 index 0000000000..ce1dd264dd --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_and_long.S @@ -0,0 +1 @@ +%include "x86_64/binopWide.S" {"instr":"andq (rFP,%rcx,4), %rax"} diff --git a/runtime/interpreter/mterp/x86_64/op_and_long_2addr.S b/runtime/interpreter/mterp/x86_64/op_and_long_2addr.S new file mode 100644 index 0000000000..d17ab8d58b --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_and_long_2addr.S @@ -0,0 +1 @@ +%include "x86_64/binopWide2addr.S" {"instr":"andq %rax, (rFP,%rcx,4)"} diff --git a/runtime/interpreter/mterp/x86_64/op_aput.S b/runtime/interpreter/mterp/x86_64/op_aput.S new file mode 100644 index 0000000000..11500ad201 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_aput.S @@ -0,0 +1,23 @@ +%default { "reg":"rINST", "store":"movl", "shift":"4", "data_offset":"MIRROR_INT_ARRAY_DATA_OFFSET", "wide":"0" } +/* + * Array put, 32 bits or less. vBB[vCC] <- vAA. + * + * for: aput, aput-boolean, aput-byte, aput-char, aput-short, aput-wide + * + */ + /* op vAA, vBB, vCC */ + movzbq 2(rPC), %rax # rax <- BB + movzbq 3(rPC), %rcx # rcx <- CC + GET_VREG %eax, %rax # eax <- vBB (array object) + GET_VREG %ecx, %rcx # ecx <- vCC (requested index) + testl %eax, %eax # null array object? + je common_errNullObject # bail if so + cmpl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ecx + jae common_errArrayIndex # index >= length, bail. + .if $wide + GET_WIDE_VREG rINSTq, rINSTq + .else + GET_VREG rINST, rINSTq + .endif + $store $reg, $data_offset(%rax,%rcx,$shift) + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_aput_boolean.S b/runtime/interpreter/mterp/x86_64/op_aput_boolean.S new file mode 100644 index 0000000000..7d77a86528 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_aput_boolean.S @@ -0,0 +1 @@ +%include "x86_64/op_aput.S" { "reg":"rINSTbl", "store":"movb", "shift":"1", "data_offset":"MIRROR_BOOLEAN_ARRAY_DATA_OFFSET" } diff --git a/runtime/interpreter/mterp/x86_64/op_aput_byte.S b/runtime/interpreter/mterp/x86_64/op_aput_byte.S new file mode 100644 index 0000000000..7a1723e0fe --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_aput_byte.S @@ -0,0 +1 @@ +%include "x86_64/op_aput.S" { "reg":"rINSTbl", "store":"movb", "shift":"1", "data_offset":"MIRROR_BYTE_ARRAY_DATA_OFFSET" } diff --git a/runtime/interpreter/mterp/x86_64/op_aput_char.S b/runtime/interpreter/mterp/x86_64/op_aput_char.S new file mode 100644 index 0000000000..f8f50a3b2e --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_aput_char.S @@ -0,0 +1 @@ +%include "x86_64/op_aput.S" { "reg":"rINSTw", "store":"movw", "shift":"2", "data_offset":"MIRROR_CHAR_ARRAY_DATA_OFFSET" } diff --git a/runtime/interpreter/mterp/x86_64/op_aput_object.S b/runtime/interpreter/mterp/x86_64/op_aput_object.S new file mode 100644 index 0000000000..b1bae0f457 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_aput_object.S @@ -0,0 +1,13 @@ +/* + * Store an object into an array. vBB[vCC] <- vAA. + */ + /* op vAA, vBB, vCC */ + EXPORT_PC + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG0 + movq rPC, OUT_ARG1 + REFRESH_INST ${opnum} + movq rINSTq, OUT_ARG2 + call SYMBOL(MterpAputObject) # (array, index) + testb %al, %al + jz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_aput_short.S b/runtime/interpreter/mterp/x86_64/op_aput_short.S new file mode 100644 index 0000000000..481fd6847b --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_aput_short.S @@ -0,0 +1 @@ +%include "x86_64/op_aput.S" { "reg":"rINSTw", "store":"movw", "shift":"2", "data_offset":"MIRROR_SHORT_ARRAY_DATA_OFFSET" } diff --git a/runtime/interpreter/mterp/x86_64/op_aput_wide.S b/runtime/interpreter/mterp/x86_64/op_aput_wide.S new file mode 100644 index 0000000000..5bbd39b0b6 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_aput_wide.S @@ -0,0 +1 @@ +%include "x86_64/op_aput.S" { "reg":"rINSTq", "store":"movq", "shift":"8", "data_offset":"MIRROR_WIDE_ARRAY_DATA_OFFSET", "wide":"1" } diff --git a/runtime/interpreter/mterp/x86_64/op_array_length.S b/runtime/interpreter/mterp/x86_64/op_array_length.S new file mode 100644 index 0000000000..e80d665160 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_array_length.S @@ -0,0 +1,12 @@ +/* + * Return the length of an array. + */ + movl rINST, %eax # eax <- BA + sarl $$4, rINST # rINST <- B + GET_VREG %ecx, rINSTq # ecx <- vB (object ref) + testl %ecx, %ecx # is null? + je common_errNullObject + andb $$0xf, %al # eax <- A + movl MIRROR_ARRAY_LENGTH_OFFSET(%rcx), rINST + SET_VREG rINST, %rax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/op_check_cast.S b/runtime/interpreter/mterp/x86_64/op_check_cast.S new file mode 100644 index 0000000000..f8fa7b2036 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_check_cast.S @@ -0,0 +1,13 @@ +/* + * Check to see if a cast from one class to another is allowed. + */ + /* check-cast vAA, class@BBBB */ + EXPORT_PC + movzwq 2(rPC), OUT_ARG0 # OUT_ARG0 <- BBBB + leaq VREG_ADDRESS(rINSTq), OUT_ARG1 + movq OFF_FP_METHOD(rFP), OUT_ARG2 + movq rSELF, OUT_ARG3 + call SYMBOL(MterpCheckCast) # (index, &obj, method, self) + testb %al, %al + jnz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_cmp_long.S b/runtime/interpreter/mterp/x86_64/op_cmp_long.S new file mode 100644 index 0000000000..23ca3e5e6d --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_cmp_long.S @@ -0,0 +1,17 @@ +/* + * Compare two 64-bit values. Puts 0, 1, or -1 into the destination + * register based on the results of the comparison. + */ + /* cmp-long vAA, vBB, vCC */ + movzbq 2(rPC), %rdx # edx <- BB + movzbq 3(rPC), %rcx # ecx <- CC + GET_WIDE_VREG %rdx, %rdx # rdx <- v[BB] + xorl %eax, %eax + xorl %edi, %edi + addb $$1, %al + movl $$-1, %esi + cmpq VREG_ADDRESS(%rcx), %rdx + cmovl %esi, %edi + cmovg %eax, %edi + SET_VREG %edi, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_cmpg_double.S b/runtime/interpreter/mterp/x86_64/op_cmpg_double.S new file mode 100644 index 0000000000..7c0aa1bdba --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_cmpg_double.S @@ -0,0 +1 @@ +%include "x86_64/fpcmp.S" {"suff":"d","nanval":"pos"} diff --git a/runtime/interpreter/mterp/x86_64/op_cmpg_float.S b/runtime/interpreter/mterp/x86_64/op_cmpg_float.S new file mode 100644 index 0000000000..14e8472672 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_cmpg_float.S @@ -0,0 +1 @@ +%include "x86_64/fpcmp.S" {"suff":"s","nanval":"pos"} diff --git a/runtime/interpreter/mterp/x86_64/op_cmpl_double.S b/runtime/interpreter/mterp/x86_64/op_cmpl_double.S new file mode 100644 index 0000000000..1d4c4243ae --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_cmpl_double.S @@ -0,0 +1 @@ +%include "x86_64/fpcmp.S" {"suff":"d","nanval":"neg"} diff --git a/runtime/interpreter/mterp/x86_64/op_cmpl_float.S b/runtime/interpreter/mterp/x86_64/op_cmpl_float.S new file mode 100644 index 0000000000..97a12a6a7d --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_cmpl_float.S @@ -0,0 +1 @@ +%include "x86_64/fpcmp.S" {"suff":"s","nanval":"neg"} diff --git a/runtime/interpreter/mterp/x86_64/op_const.S b/runtime/interpreter/mterp/x86_64/op_const.S new file mode 100644 index 0000000000..3cfafdb13b --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_const.S @@ -0,0 +1,4 @@ + /* const vAA, #+BBBBbbbb */ + movl 2(rPC), %eax # grab all 32 bits at once + SET_VREG %eax, rINSTq # vAA<- eax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 diff --git a/runtime/interpreter/mterp/x86_64/op_const_16.S b/runtime/interpreter/mterp/x86_64/op_const_16.S new file mode 100644 index 0000000000..1a139c683e --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_const_16.S @@ -0,0 +1,4 @@ + /* const/16 vAA, #+BBBB */ + movswl 2(rPC), %ecx # ecx <- ssssBBBB + SET_VREG %ecx, rINSTq # vAA <- ssssBBBB + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_const_4.S b/runtime/interpreter/mterp/x86_64/op_const_4.S new file mode 100644 index 0000000000..23c4816f82 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_const_4.S @@ -0,0 +1,7 @@ + /* const/4 vA, #+B */ + movsbl rINSTbl, %eax # eax <-ssssssBx + movl $$0xf, rINST + andl %eax, rINST # rINST <- A + sarl $$4, %eax + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/op_const_class.S b/runtime/interpreter/mterp/x86_64/op_const_class.S new file mode 100644 index 0000000000..494920a4a8 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_const_class.S @@ -0,0 +1,10 @@ + /* const/class vAA, Class@BBBB */ + EXPORT_PC + movzwq 2(rPC), OUT_ARG0 # eax <- OUT_ARG0 + movq rINSTq, OUT_ARG1 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG2 + movq rSELF, OUT_ARG3 + call SYMBOL(MterpConstClass) # (index, tgt_reg, shadow_frame, self) + testb %al, %al + jnz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_const_high16.S b/runtime/interpreter/mterp/x86_64/op_const_high16.S new file mode 100644 index 0000000000..64e633c7a0 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_const_high16.S @@ -0,0 +1,5 @@ + /* const/high16 vAA, #+BBBB0000 */ + movzwl 2(rPC), %eax # eax <- 0000BBBB + sall $$16, %eax # eax <- BBBB0000 + SET_VREG %eax, rINSTq # vAA <- eax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_const_string.S b/runtime/interpreter/mterp/x86_64/op_const_string.S new file mode 100644 index 0000000000..7c199ecad9 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_const_string.S @@ -0,0 +1,10 @@ + /* const/string vAA, String@BBBB */ + EXPORT_PC + movzwq 2(rPC), OUT_ARG0 # OUT_ARG0 <- BBBB + movq rINSTq, OUT_ARG1 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG2 + movq rSELF, OUT_ARG3 + call SYMBOL(MterpConstString) # (index, tgt_reg, shadow_frame, self) + testb %al, %al + jnz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_const_string_jumbo.S b/runtime/interpreter/mterp/x86_64/op_const_string_jumbo.S new file mode 100644 index 0000000000..ae03d20f4f --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_const_string_jumbo.S @@ -0,0 +1,10 @@ + /* const/string vAA, String@BBBBBBBB */ + EXPORT_PC + movl 2(rPC), OUT_32_ARG0 # OUT_32_ARG0 <- BBBB + movq rINSTq, OUT_ARG1 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG2 + movq rSELF, OUT_ARG3 + call SYMBOL(MterpConstString) # (index, tgt_reg, shadow_frame, self) + testb %al, %al + jnz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 diff --git a/runtime/interpreter/mterp/x86_64/op_const_wide.S b/runtime/interpreter/mterp/x86_64/op_const_wide.S new file mode 100644 index 0000000000..5615177175 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_const_wide.S @@ -0,0 +1,4 @@ + /* const-wide vAA, #+HHHHhhhhBBBBbbbb */ + movq 2(rPC), %rax # rax <- HHHHhhhhBBBBbbbb + SET_WIDE_VREG %rax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 5 diff --git a/runtime/interpreter/mterp/x86_64/op_const_wide_16.S b/runtime/interpreter/mterp/x86_64/op_const_wide_16.S new file mode 100644 index 0000000000..593b62466f --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_const_wide_16.S @@ -0,0 +1,4 @@ + /* const-wide/16 vAA, #+BBBB */ + movswq 2(rPC), %rax # rax <- ssssBBBB + SET_WIDE_VREG %rax, rINSTq # store + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_const_wide_32.S b/runtime/interpreter/mterp/x86_64/op_const_wide_32.S new file mode 100644 index 0000000000..5ef3636129 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_const_wide_32.S @@ -0,0 +1,4 @@ + /* const-wide/32 vAA, #+BBBBbbbb */ + movslq 2(rPC), %rax # eax <- ssssssssBBBBbbbb + SET_WIDE_VREG %rax, rINSTq # store + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 diff --git a/runtime/interpreter/mterp/x86_64/op_const_wide_high16.S b/runtime/interpreter/mterp/x86_64/op_const_wide_high16.S new file mode 100644 index 0000000000..b86b4e582b --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_const_wide_high16.S @@ -0,0 +1,5 @@ + /* const-wide/high16 vAA, #+BBBB000000000000 */ + movzwq 2(rPC), %rax # eax <- 0000BBBB + salq $$48, %rax # eax <- BBBB0000 + SET_WIDE_VREG %rax, rINSTq # v[AA+0] <- eax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_div_double.S b/runtime/interpreter/mterp/x86_64/op_div_double.S new file mode 100644 index 0000000000..45c700c066 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_div_double.S @@ -0,0 +1 @@ +%include "x86_64/sseBinop.S" {"instr":"divs","suff":"d"} diff --git a/runtime/interpreter/mterp/x86_64/op_div_double_2addr.S b/runtime/interpreter/mterp/x86_64/op_div_double_2addr.S new file mode 100644 index 0000000000..83f270e245 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_div_double_2addr.S @@ -0,0 +1 @@ +%include "x86_64/sseBinop2Addr.S" {"instr":"divs","suff":"d"} diff --git a/runtime/interpreter/mterp/x86_64/op_div_float.S b/runtime/interpreter/mterp/x86_64/op_div_float.S new file mode 100644 index 0000000000..aa90b24698 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_div_float.S @@ -0,0 +1 @@ +%include "x86_64/sseBinop.S" {"instr":"divs","suff":"s"} diff --git a/runtime/interpreter/mterp/x86_64/op_div_float_2addr.S b/runtime/interpreter/mterp/x86_64/op_div_float_2addr.S new file mode 100644 index 0000000000..f0f8f1a6c8 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_div_float_2addr.S @@ -0,0 +1 @@ +%include "x86_64/sseBinop2Addr.S" {"instr":"divs","suff":"s"} diff --git a/runtime/interpreter/mterp/x86_64/op_div_int.S b/runtime/interpreter/mterp/x86_64/op_div_int.S new file mode 100644 index 0000000000..bba5a176a0 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_div_int.S @@ -0,0 +1 @@ +%include "x86_64/bindiv.S" {"result":"%eax","second":"%ecx","wide":"0","suffix":"l"} diff --git a/runtime/interpreter/mterp/x86_64/op_div_int_2addr.S b/runtime/interpreter/mterp/x86_64/op_div_int_2addr.S new file mode 100644 index 0000000000..fa4255ddfa --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_div_int_2addr.S @@ -0,0 +1 @@ +%include "x86_64/bindiv2addr.S" {"result":"%eax","second":"%ecx","wide":"0","suffix":"l"} diff --git a/runtime/interpreter/mterp/x86_64/op_div_int_lit16.S b/runtime/interpreter/mterp/x86_64/op_div_int_lit16.S new file mode 100644 index 0000000000..3fa1e09fd6 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_div_int_lit16.S @@ -0,0 +1 @@ +%include "x86_64/bindivLit16.S" {"result":"%eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_div_int_lit8.S b/runtime/interpreter/mterp/x86_64/op_div_int_lit8.S new file mode 100644 index 0000000000..859883e5c7 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_div_int_lit8.S @@ -0,0 +1 @@ +%include "x86_64/bindivLit8.S" {"result":"%eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_div_long.S b/runtime/interpreter/mterp/x86_64/op_div_long.S new file mode 100644 index 0000000000..a061a88b13 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_div_long.S @@ -0,0 +1 @@ +%include "x86_64/bindiv.S" {"result":"%rax","second":"%rcx","wide":"1","suffix":"q","ext":"cqo"} diff --git a/runtime/interpreter/mterp/x86_64/op_div_long_2addr.S b/runtime/interpreter/mterp/x86_64/op_div_long_2addr.S new file mode 100644 index 0000000000..8886e68248 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_div_long_2addr.S @@ -0,0 +1 @@ +%include "x86_64/bindiv2addr.S" {"result":"%rax","second":"%rcx","wide":"1","suffix":"q","ext":"cqo"} diff --git a/runtime/interpreter/mterp/x86_64/op_double_to_float.S b/runtime/interpreter/mterp/x86_64/op_double_to_float.S new file mode 100644 index 0000000000..cea1482038 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_double_to_float.S @@ -0,0 +1 @@ +%include "x86_64/fpcvt.S" {"source_suffix":"d","dest_suffix":"s","wide":"0"} diff --git a/runtime/interpreter/mterp/x86_64/op_double_to_int.S b/runtime/interpreter/mterp/x86_64/op_double_to_int.S new file mode 100644 index 0000000000..a9965edcc3 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_double_to_int.S @@ -0,0 +1 @@ +%include "x86_64/cvtfp_int.S" {"fp_suffix":"d","i_suffix":"l","max_const":"$0x7fffffff","result_reg":"%eax","wide":"0"} diff --git a/runtime/interpreter/mterp/x86_64/op_double_to_long.S b/runtime/interpreter/mterp/x86_64/op_double_to_long.S new file mode 100644 index 0000000000..179e6a1605 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_double_to_long.S @@ -0,0 +1 @@ +%include "x86_64/cvtfp_int.S" {"fp_suffix":"d","i_suffix":"q","max_const":"$0x7fffffffffffffff","result_reg":"%rax","wide":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_fill_array_data.S b/runtime/interpreter/mterp/x86_64/op_fill_array_data.S new file mode 100644 index 0000000000..626bad47c9 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_fill_array_data.S @@ -0,0 +1,9 @@ + /* fill-array-data vAA, +BBBBBBBB */ + EXPORT_PC + movl 2(rPC), %ecx # ecx <- BBBBbbbb + leaq (rPC,%rcx,2), OUT_ARG1 # OUT_ARG1 <- PC + BBBBbbbb*2 + GET_VREG OUT_32_ARG0, rINSTq # OUT_ARG0 <- vAA (array object) + call SYMBOL(MterpFillArrayData) # (obj, payload) + testb %al, %al # 0 means an exception is thrown + jz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 diff --git a/runtime/interpreter/mterp/x86_64/op_filled_new_array.S b/runtime/interpreter/mterp/x86_64/op_filled_new_array.S new file mode 100644 index 0000000000..a7f7ddc2a0 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_filled_new_array.S @@ -0,0 +1,17 @@ +%default { "helper":"MterpFilledNewArray" } +/* + * Create a new array with elements filled from registers. + * + * for: filled-new-array, filled-new-array/range + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, type@BBBB */ + .extern $helper + EXPORT_PC + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG0 + movq rPC, OUT_ARG1 + movq rSELF, OUT_ARG2 + call SYMBOL($helper) + testb %al, %al # 0 means an exception is thrown + jz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 diff --git a/runtime/interpreter/mterp/x86_64/op_filled_new_array_range.S b/runtime/interpreter/mterp/x86_64/op_filled_new_array_range.S new file mode 100644 index 0000000000..4ca79a3fe1 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_filled_new_array_range.S @@ -0,0 +1 @@ +%include "x86_64/op_filled_new_array.S" { "helper":"MterpFilledNewArrayRange" } diff --git a/runtime/interpreter/mterp/x86_64/op_float_to_double.S b/runtime/interpreter/mterp/x86_64/op_float_to_double.S new file mode 100644 index 0000000000..7855205575 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_float_to_double.S @@ -0,0 +1 @@ +%include "x86_64/fpcvt.S" {"source_suffix":"s","dest_suffix":"d","wide":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_float_to_int.S b/runtime/interpreter/mterp/x86_64/op_float_to_int.S new file mode 100644 index 0000000000..cb90555405 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_float_to_int.S @@ -0,0 +1 @@ +%include "x86_64/cvtfp_int.S" {"fp_suffix":"s","i_suffix":"l","max_const":"$0x7fffffff","result_reg":"%eax","wide":"0"} diff --git a/runtime/interpreter/mterp/x86_64/op_float_to_long.S b/runtime/interpreter/mterp/x86_64/op_float_to_long.S new file mode 100644 index 0000000000..96bb4eee6f --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_float_to_long.S @@ -0,0 +1 @@ +%include "x86_64/cvtfp_int.S" {"fp_suffix":"s","i_suffix":"q","max_const":"$0x7fffffffffffffff","result_reg":"%rax","wide":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_goto.S b/runtime/interpreter/mterp/x86_64/op_goto.S new file mode 100644 index 0000000000..05a2dda1c0 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_goto.S @@ -0,0 +1,19 @@ +/* + * Unconditional branch, 8-bit offset. + * + * The branch distance is a signed code-unit offset, which we need to + * double to get a byte offset. + */ + /* goto +AA */ + movsbq rINSTbl, %rax # rax <- ssssssAA + addq %rax, %rax # rax <- AA * 2 + leaq (rPC, %rax), rPC + FETCH_INST + jg 1f # AA * 2 > 0 => no suspend check +#if MTERP_SUSPEND + REFRESH_IBASE +#else + jmp MterpCheckSuspendAndContinue +#endif +1: + GOTO_NEXT diff --git a/runtime/interpreter/mterp/x86_64/op_goto_16.S b/runtime/interpreter/mterp/x86_64/op_goto_16.S new file mode 100644 index 0000000000..029749c50a --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_goto_16.S @@ -0,0 +1,19 @@ +/* + * Unconditional branch, 16-bit offset. + * + * The branch distance is a signed code-unit offset, which we need to + * double to get a byte offset. + */ + /* goto/16 +AAAA */ + movswq 2(rPC), %rax # rax <- ssssAAAA + addq %rax, %rax # rax <- AA * 2 + leaq (rPC, %rax), rPC + FETCH_INST + jg 1f # AA * 2 > 0 => no suspend check +#if MTERP_SUSPEND + REFRESH_IBASE +#else + jmp MterpCheckSuspendAndContinue +#endif +1: + GOTO_NEXT diff --git a/runtime/interpreter/mterp/x86_64/op_goto_32.S b/runtime/interpreter/mterp/x86_64/op_goto_32.S new file mode 100644 index 0000000000..28233108e5 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_goto_32.S @@ -0,0 +1,22 @@ +/* + * Unconditional branch, 32-bit offset. + * + * The branch distance is a signed code-unit offset, which we need to + * double to get a byte offset. + * + * Because we need the SF bit set, we'll use an adds + * to convert from Dalvik offset to byte offset. + */ + /* goto/32 +AAAAAAAA */ + movslq 2(rPC), %rax # rax <- AAAAAAAA + addq %rax, %rax # rax <- AA * 2 + leaq (rPC, %rax), rPC + FETCH_INST + jg 1f # AA * 2 > 0 => no suspend check +#if MTERP_SUSPEND + REFRESH_IBASE +#else + jmp MterpCheckSuspendAndContinue +#endif +1: + GOTO_NEXT diff --git a/runtime/interpreter/mterp/x86_64/op_if_eq.S b/runtime/interpreter/mterp/x86_64/op_if_eq.S new file mode 100644 index 0000000000..d56ce72461 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_if_eq.S @@ -0,0 +1 @@ +%include "x86_64/bincmp.S" { "revcmp":"ne" } diff --git a/runtime/interpreter/mterp/x86_64/op_if_eqz.S b/runtime/interpreter/mterp/x86_64/op_if_eqz.S new file mode 100644 index 0000000000..a0fc4448a3 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_if_eqz.S @@ -0,0 +1 @@ +%include "x86_64/zcmp.S" { "revcmp":"ne" } diff --git a/runtime/interpreter/mterp/x86_64/op_if_ge.S b/runtime/interpreter/mterp/x86_64/op_if_ge.S new file mode 100644 index 0000000000..a7832efb68 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_if_ge.S @@ -0,0 +1 @@ +%include "x86_64/bincmp.S" { "revcmp":"l" } diff --git a/runtime/interpreter/mterp/x86_64/op_if_gez.S b/runtime/interpreter/mterp/x86_64/op_if_gez.S new file mode 100644 index 0000000000..f9af5db933 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_if_gez.S @@ -0,0 +1 @@ +%include "x86_64/zcmp.S" { "revcmp":"l" } diff --git a/runtime/interpreter/mterp/x86_64/op_if_gt.S b/runtime/interpreter/mterp/x86_64/op_if_gt.S new file mode 100644 index 0000000000..70f2b9e12f --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_if_gt.S @@ -0,0 +1 @@ +%include "x86_64/bincmp.S" { "revcmp":"le" } diff --git a/runtime/interpreter/mterp/x86_64/op_if_gtz.S b/runtime/interpreter/mterp/x86_64/op_if_gtz.S new file mode 100644 index 0000000000..2fb0d50937 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_if_gtz.S @@ -0,0 +1 @@ +%include "x86_64/zcmp.S" { "revcmp":"le" } diff --git a/runtime/interpreter/mterp/x86_64/op_if_le.S b/runtime/interpreter/mterp/x86_64/op_if_le.S new file mode 100644 index 0000000000..321962a040 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_if_le.S @@ -0,0 +1 @@ +%include "x86_64/bincmp.S" { "revcmp":"g" } diff --git a/runtime/interpreter/mterp/x86_64/op_if_lez.S b/runtime/interpreter/mterp/x86_64/op_if_lez.S new file mode 100644 index 0000000000..d3dc334f7b --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_if_lez.S @@ -0,0 +1 @@ +%include "x86_64/zcmp.S" { "revcmp":"g" } diff --git a/runtime/interpreter/mterp/x86_64/op_if_lt.S b/runtime/interpreter/mterp/x86_64/op_if_lt.S new file mode 100644 index 0000000000..f028005844 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_if_lt.S @@ -0,0 +1 @@ +%include "x86_64/bincmp.S" { "revcmp":"ge" } diff --git a/runtime/interpreter/mterp/x86_64/op_if_ltz.S b/runtime/interpreter/mterp/x86_64/op_if_ltz.S new file mode 100644 index 0000000000..383d73aa7a --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_if_ltz.S @@ -0,0 +1 @@ +%include "x86_64/zcmp.S" { "revcmp":"ge" } diff --git a/runtime/interpreter/mterp/x86_64/op_if_ne.S b/runtime/interpreter/mterp/x86_64/op_if_ne.S new file mode 100644 index 0000000000..ac6e063cd1 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_if_ne.S @@ -0,0 +1 @@ +%include "x86_64/bincmp.S" { "revcmp":"e" } diff --git a/runtime/interpreter/mterp/x86_64/op_if_nez.S b/runtime/interpreter/mterp/x86_64/op_if_nez.S new file mode 100644 index 0000000000..c96e4f3d16 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_if_nez.S @@ -0,0 +1 @@ +%include "x86_64/zcmp.S" { "revcmp":"e" } diff --git a/runtime/interpreter/mterp/x86_64/op_iget.S b/runtime/interpreter/mterp/x86_64/op_iget.S new file mode 100644 index 0000000000..a0d0fafba1 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iget.S @@ -0,0 +1,27 @@ +%default { "is_object":"0", "helper":"artGet32InstanceFromCode", "wide":"0"} +/* + * General instance field get. + * + * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short, iget-wide + */ + EXPORT_PC + movzbq rINSTbl, %rcx # rcx <- BA + movzwl 2(rPC), OUT_32_ARG0 # eax <- field ref CCCC + sarl $$4, %ecx # ecx <- B + GET_VREG OUT_32_ARG1, %rcx # the object pointer + movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer + movq rSELF, OUT_ARG3 + call SYMBOL($helper) + cmpq $$0, THREAD_EXCEPTION_OFFSET(rSELF) + jnz MterpException # bail out + andb $$0xf, rINSTbl # rINST <- A + .if $is_object + SET_VREG_OBJECT %eax, rINSTq # fp[A] <-value + .else + .if $wide + SET_WIDE_VREG %rax, rINSTq # fp[A] <-value + .else + SET_VREG %eax, rINSTq # fp[A] <-value + .endif + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_iget_boolean.S b/runtime/interpreter/mterp/x86_64/op_iget_boolean.S new file mode 100644 index 0000000000..6ac55231b8 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iget_boolean.S @@ -0,0 +1 @@ +%include "x86_64/op_iget.S" { "helper":"artGetBooleanInstanceFromCode" } diff --git a/runtime/interpreter/mterp/x86_64/op_iget_boolean_quick.S b/runtime/interpreter/mterp/x86_64/op_iget_boolean_quick.S new file mode 100644 index 0000000000..07139c75ee --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iget_boolean_quick.S @@ -0,0 +1 @@ +%include "x86_64/op_iget_quick.S" { "load":"movsbl" } diff --git a/runtime/interpreter/mterp/x86_64/op_iget_byte.S b/runtime/interpreter/mterp/x86_64/op_iget_byte.S new file mode 100644 index 0000000000..6a861b1b7a --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iget_byte.S @@ -0,0 +1 @@ +%include "x86_64/op_iget.S" { "helper":"artGetByteInstanceFromCode" } diff --git a/runtime/interpreter/mterp/x86_64/op_iget_byte_quick.S b/runtime/interpreter/mterp/x86_64/op_iget_byte_quick.S new file mode 100644 index 0000000000..07139c75ee --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iget_byte_quick.S @@ -0,0 +1 @@ +%include "x86_64/op_iget_quick.S" { "load":"movsbl" } diff --git a/runtime/interpreter/mterp/x86_64/op_iget_char.S b/runtime/interpreter/mterp/x86_64/op_iget_char.S new file mode 100644 index 0000000000..021a0f1b24 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iget_char.S @@ -0,0 +1 @@ +%include "x86_64/op_iget.S" { "helper":"artGetCharInstanceFromCode" } diff --git a/runtime/interpreter/mterp/x86_64/op_iget_char_quick.S b/runtime/interpreter/mterp/x86_64/op_iget_char_quick.S new file mode 100644 index 0000000000..8cb3be3b65 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iget_char_quick.S @@ -0,0 +1 @@ +%include "x86_64/op_iget_quick.S" { "load":"movzwl" } diff --git a/runtime/interpreter/mterp/x86_64/op_iget_object.S b/runtime/interpreter/mterp/x86_64/op_iget_object.S new file mode 100644 index 0000000000..d92bc9c345 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iget_object.S @@ -0,0 +1 @@ +%include "x86_64/op_iget.S" { "is_object":"1", "helper":"artGetObjInstanceFromCode" } diff --git a/runtime/interpreter/mterp/x86_64/op_iget_object_quick.S b/runtime/interpreter/mterp/x86_64/op_iget_object_quick.S new file mode 100644 index 0000000000..964d20ad74 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iget_object_quick.S @@ -0,0 +1,14 @@ + /* For: iget-object-quick */ + /* op vA, vB, offset@CCCC */ + .extern artIGetObjectFromMterp + movzbq rINSTbl, %rcx # rcx <- BA + sarl $$4, %ecx # ecx <- B + GET_VREG OUT_32_ARG0, %rcx # vB (object we're operating on) + movzwl 2(rPC), OUT_32_ARG1 # eax <- field byte offset + EXPORT_PC + callq SYMBOL(artIGetObjectFromMterp) # (obj, offset) + cmpq $$0, THREAD_EXCEPTION_OFFSET(rSELF) + jnz MterpException # bail out + andb $$0xf, rINSTbl # rINST <- A + SET_VREG_OBJECT %eax, rINSTq # fp[A] <- value + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_iget_quick.S b/runtime/interpreter/mterp/x86_64/op_iget_quick.S new file mode 100644 index 0000000000..bfb7530167 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iget_quick.S @@ -0,0 +1,18 @@ +%default { "load":"movl", "wide":"0"} + /* For: iget-quick, iget-boolean-quick, iget-byte-quick, iget-char-quick, iget-short-quick, iget-wide-quick */ + /* op vA, vB, offset@CCCC */ + movl rINST, %ecx # rcx <- BA + sarl $$4, %ecx # ecx <- B + GET_VREG %ecx, %rcx # vB (object we're operating on) + movzwq 2(rPC), %rax # eax <- field byte offset + testl %ecx, %ecx # is object null? + je common_errNullObject + andb $$0xf,rINSTbl # rINST <- A + .if $wide + movq (%rcx,%rax,1), %rax + SET_WIDE_VREG %rax, rINSTq # fp[A] <- value + .else + ${load} (%rcx,%rax,1), %eax + SET_VREG %eax, rINSTq # fp[A] <- value + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_iget_short.S b/runtime/interpreter/mterp/x86_64/op_iget_short.S new file mode 100644 index 0000000000..f158bea573 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iget_short.S @@ -0,0 +1 @@ +%include "x86_64/op_iget.S" { "helper":"artGetShortInstanceFromCode" } diff --git a/runtime/interpreter/mterp/x86_64/op_iget_short_quick.S b/runtime/interpreter/mterp/x86_64/op_iget_short_quick.S new file mode 100644 index 0000000000..56ca858e74 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iget_short_quick.S @@ -0,0 +1 @@ +%include "x86_64/op_iget_quick.S" { "load":"movswl" } diff --git a/runtime/interpreter/mterp/x86_64/op_iget_wide.S b/runtime/interpreter/mterp/x86_64/op_iget_wide.S new file mode 100644 index 0000000000..74bb9ffe1c --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iget_wide.S @@ -0,0 +1 @@ +%include "x86_64/op_iget.S" { "helper":"artGet64InstanceFromCode", "wide":"1" } diff --git a/runtime/interpreter/mterp/x86_64/op_iget_wide_quick.S b/runtime/interpreter/mterp/x86_64/op_iget_wide_quick.S new file mode 100644 index 0000000000..169d625529 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iget_wide_quick.S @@ -0,0 +1 @@ +%include "x86_64/op_iget_quick.S" { "load":"movswl", "wide":"1" } diff --git a/runtime/interpreter/mterp/x86_64/op_instance_of.S b/runtime/interpreter/mterp/x86_64/op_instance_of.S new file mode 100644 index 0000000000..6be37f9166 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_instance_of.S @@ -0,0 +1,21 @@ +/* + * Check to see if an object reference is an instance of a class. + * + * Most common situation is a non-null object, being compared against + * an already-resolved class. + */ + /* instance-of vA, vB, class@CCCC */ + EXPORT_PC + movzwl 2(rPC), OUT_32_ARG0 # OUT_32_ARG0 <- CCCC + movl rINST, %eax # eax <- BA + sarl $$4, %eax # eax <- B + leaq VREG_ADDRESS(%rax), OUT_ARG1 # Get object address + movq OFF_FP_METHOD(rFP), OUT_ARG2 + movq rSELF, OUT_ARG3 + call SYMBOL(MterpInstanceOf) # (index, &obj, method, self) + movsbl %al, %eax + cmpq $$0, THREAD_EXCEPTION_OFFSET(rSELF) + jnz MterpException + andb $$0xf, rINSTbl # rINSTbl <- A + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_int_to_byte.S b/runtime/interpreter/mterp/x86_64/op_int_to_byte.S new file mode 100644 index 0000000000..f4e578f868 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_int_to_byte.S @@ -0,0 +1 @@ +%include "x86_64/unop.S" {"instr":"movsbl %al, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_int_to_char.S b/runtime/interpreter/mterp/x86_64/op_int_to_char.S new file mode 100644 index 0000000000..c1bf17f271 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_int_to_char.S @@ -0,0 +1 @@ +%include "x86_64/unop.S" {"instr":"movzwl %ax,%eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_int_to_double.S b/runtime/interpreter/mterp/x86_64/op_int_to_double.S new file mode 100644 index 0000000000..27ebf42dbb --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_int_to_double.S @@ -0,0 +1 @@ +%include "x86_64/fpcvt.S" {"source_suffix":"i","dest_suffix":"dl","wide":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_int_to_float.S b/runtime/interpreter/mterp/x86_64/op_int_to_float.S new file mode 100644 index 0000000000..5a98d44337 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_int_to_float.S @@ -0,0 +1 @@ +%include "x86_64/fpcvt.S" {"source_suffix":"i","dest_suffix":"sl","wide":"0"} diff --git a/runtime/interpreter/mterp/x86_64/op_int_to_long.S b/runtime/interpreter/mterp/x86_64/op_int_to_long.S new file mode 100644 index 0000000000..9281137a54 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_int_to_long.S @@ -0,0 +1,8 @@ + /* int to long vA, vB */ + movzbq rINSTbl, %rax # rax <- +A + sarl $$4, %eax # eax <- B + andb $$0xf, rINSTbl # rINST <- A + movslq VREG_ADDRESS(%rax), %rax + SET_WIDE_VREG %rax, rINSTq # v[A] <- %rax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + diff --git a/runtime/interpreter/mterp/x86_64/op_int_to_short.S b/runtime/interpreter/mterp/x86_64/op_int_to_short.S new file mode 100644 index 0000000000..6ae6b50f34 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_int_to_short.S @@ -0,0 +1 @@ +%include "x86_64/unop.S" {"instr":"movswl %ax, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_invoke_direct.S b/runtime/interpreter/mterp/x86_64/op_invoke_direct.S new file mode 100644 index 0000000000..9628589b03 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_invoke_direct.S @@ -0,0 +1 @@ +%include "x86_64/invoke.S" { "helper":"MterpInvokeDirect" } diff --git a/runtime/interpreter/mterp/x86_64/op_invoke_direct_range.S b/runtime/interpreter/mterp/x86_64/op_invoke_direct_range.S new file mode 100644 index 0000000000..09ac8812fc --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_invoke_direct_range.S @@ -0,0 +1 @@ +%include "x86_64/invoke.S" { "helper":"MterpInvokeDirectRange" } diff --git a/runtime/interpreter/mterp/x86_64/op_invoke_interface.S b/runtime/interpreter/mterp/x86_64/op_invoke_interface.S new file mode 100644 index 0000000000..76d9cd426f --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_invoke_interface.S @@ -0,0 +1,8 @@ +%include "x86_64/invoke.S" { "helper":"MterpInvokeInterface" } +/* + * Handle an interface method call. + * + * for: invoke-interface, invoke-interface/range + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ diff --git a/runtime/interpreter/mterp/x86_64/op_invoke_interface_range.S b/runtime/interpreter/mterp/x86_64/op_invoke_interface_range.S new file mode 100644 index 0000000000..785b43c1a8 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_invoke_interface_range.S @@ -0,0 +1 @@ +%include "x86_64/invoke.S" { "helper":"MterpInvokeInterfaceRange" } diff --git a/runtime/interpreter/mterp/x86_64/op_invoke_static.S b/runtime/interpreter/mterp/x86_64/op_invoke_static.S new file mode 100644 index 0000000000..dd8027d58c --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_invoke_static.S @@ -0,0 +1,2 @@ +%include "x86_64/invoke.S" { "helper":"MterpInvokeStatic" } + diff --git a/runtime/interpreter/mterp/x86_64/op_invoke_static_range.S b/runtime/interpreter/mterp/x86_64/op_invoke_static_range.S new file mode 100644 index 0000000000..ee26074f92 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_invoke_static_range.S @@ -0,0 +1 @@ +%include "x86_64/invoke.S" { "helper":"MterpInvokeStaticRange" } diff --git a/runtime/interpreter/mterp/x86_64/op_invoke_super.S b/runtime/interpreter/mterp/x86_64/op_invoke_super.S new file mode 100644 index 0000000000..d07f8d555b --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_invoke_super.S @@ -0,0 +1,8 @@ +%include "x86_64/invoke.S" { "helper":"MterpInvokeSuper" } +/* + * Handle a "super" method call. + * + * for: invoke-super, invoke-super/range + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ diff --git a/runtime/interpreter/mterp/x86_64/op_invoke_super_range.S b/runtime/interpreter/mterp/x86_64/op_invoke_super_range.S new file mode 100644 index 0000000000..7245cfd405 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_invoke_super_range.S @@ -0,0 +1 @@ +%include "x86_64/invoke.S" { "helper":"MterpInvokeSuperRange" } diff --git a/runtime/interpreter/mterp/x86_64/op_invoke_virtual.S b/runtime/interpreter/mterp/x86_64/op_invoke_virtual.S new file mode 100644 index 0000000000..19c708bd2a --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_invoke_virtual.S @@ -0,0 +1,8 @@ +%include "x86_64/invoke.S" { "helper":"MterpInvokeVirtual" } +/* + * Handle a virtual method call. + * + * for: invoke-virtual, invoke-virtual/range + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ diff --git a/runtime/interpreter/mterp/x86_64/op_invoke_virtual_quick.S b/runtime/interpreter/mterp/x86_64/op_invoke_virtual_quick.S new file mode 100644 index 0000000000..313bd058b1 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_invoke_virtual_quick.S @@ -0,0 +1 @@ +%include "x86_64/invoke.S" { "helper":"MterpInvokeVirtualQuick" } diff --git a/runtime/interpreter/mterp/x86_64/op_invoke_virtual_range.S b/runtime/interpreter/mterp/x86_64/op_invoke_virtual_range.S new file mode 100644 index 0000000000..424ad321a3 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_invoke_virtual_range.S @@ -0,0 +1 @@ +%include "x86_64/invoke.S" { "helper":"MterpInvokeVirtualRange" } diff --git a/runtime/interpreter/mterp/x86_64/op_invoke_virtual_range_quick.S b/runtime/interpreter/mterp/x86_64/op_invoke_virtual_range_quick.S new file mode 100644 index 0000000000..556f718ffb --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_invoke_virtual_range_quick.S @@ -0,0 +1 @@ +%include "x86_64/invoke.S" { "helper":"MterpInvokeVirtualQuickRange" } diff --git a/runtime/interpreter/mterp/x86_64/op_iput.S b/runtime/interpreter/mterp/x86_64/op_iput.S new file mode 100644 index 0000000000..6b7cb1cc84 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iput.S @@ -0,0 +1,20 @@ +%default { "handler":"artSet32InstanceFromMterp"} +/* + * General 32-bit instance field put. + * + * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short + */ + /* op vA, vB, field@CCCC */ + .extern $handler + EXPORT_PC + movzwl 2(rPC), OUT_32_ARG0 # field ref <- 0000CCCC + movzbq rINSTbl, %rcx # rcx<- BA + sarl $$4, %ecx # ecx<- B + GET_VREG OUT_32_ARG1, %rcx # the object pointer + andb $$0xf, rINSTbl # rINST<- A + GET_VREG OUT_32_ARG2, rINSTq # fp[A] + movq OFF_FP_METHOD(rFP), OUT_ARG3 # referrer + call SYMBOL($handler) + testb %al, %al + jnz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_iput_boolean.S b/runtime/interpreter/mterp/x86_64/op_iput_boolean.S new file mode 100644 index 0000000000..cb4b1cde45 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iput_boolean.S @@ -0,0 +1 @@ +%include "x86_64/op_iput.S" { "handler":"artSet8InstanceFromMterp" } diff --git a/runtime/interpreter/mterp/x86_64/op_iput_boolean_quick.S b/runtime/interpreter/mterp/x86_64/op_iput_boolean_quick.S new file mode 100644 index 0000000000..6bd060e4f3 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iput_boolean_quick.S @@ -0,0 +1 @@ +%include "x86_64/op_iput_quick.S" { "reg":"rINSTbl", "store":"movb" } diff --git a/runtime/interpreter/mterp/x86_64/op_iput_byte.S b/runtime/interpreter/mterp/x86_64/op_iput_byte.S new file mode 100644 index 0000000000..cb4b1cde45 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iput_byte.S @@ -0,0 +1 @@ +%include "x86_64/op_iput.S" { "handler":"artSet8InstanceFromMterp" } diff --git a/runtime/interpreter/mterp/x86_64/op_iput_byte_quick.S b/runtime/interpreter/mterp/x86_64/op_iput_byte_quick.S new file mode 100644 index 0000000000..6bd060e4f3 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iput_byte_quick.S @@ -0,0 +1 @@ +%include "x86_64/op_iput_quick.S" { "reg":"rINSTbl", "store":"movb" } diff --git a/runtime/interpreter/mterp/x86_64/op_iput_char.S b/runtime/interpreter/mterp/x86_64/op_iput_char.S new file mode 100644 index 0000000000..b4e147cf5e --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iput_char.S @@ -0,0 +1 @@ +%include "x86_64/op_iput.S" { "handler":"artSet16InstanceFromMterp" } diff --git a/runtime/interpreter/mterp/x86_64/op_iput_char_quick.S b/runtime/interpreter/mterp/x86_64/op_iput_char_quick.S new file mode 100644 index 0000000000..3da96d53af --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iput_char_quick.S @@ -0,0 +1 @@ +%include "x86_64/op_iput_quick.S" { "reg":"rINSTw", "store":"movw" } diff --git a/runtime/interpreter/mterp/x86_64/op_iput_object.S b/runtime/interpreter/mterp/x86_64/op_iput_object.S new file mode 100644 index 0000000000..828712d8ba --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iput_object.S @@ -0,0 +1,10 @@ + EXPORT_PC + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG0 + movq rPC, OUT_ARG1 + REFRESH_INST ${opnum} + movl rINST, OUT_32_ARG2 + movq rSELF, OUT_ARG3 + call SYMBOL(MterpIputObject) + testb %al, %al + jz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_iput_object_quick.S b/runtime/interpreter/mterp/x86_64/op_iput_object_quick.S new file mode 100644 index 0000000000..b5b128ab7f --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iput_object_quick.S @@ -0,0 +1,9 @@ + EXPORT_PC + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG0 + movq rPC, OUT_ARG1 + REFRESH_INST ${opnum} + movl rINST, OUT_32_ARG2 + call SYMBOL(MterpIputObjectQuick) + testb %al, %al + jz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_iput_quick.S b/runtime/interpreter/mterp/x86_64/op_iput_quick.S new file mode 100644 index 0000000000..ecaf98e415 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iput_quick.S @@ -0,0 +1,13 @@ +%default { "reg":"rINST", "store":"movl" } + /* For: iput-quick, iput-object-quick */ + /* op vA, vB, offset@CCCC */ + movzbq rINSTbl, %rcx # rcx <- BA + sarl $$4, %ecx # ecx <- B + GET_VREG %ecx, %rcx # vB (object we're operating on) + testl %ecx, %ecx # is object null? + je common_errNullObject + andb $$0xf, rINSTbl # rINST <- A + GET_VREG rINST, rINSTq # rINST <- v[A] + movzwq 2(rPC), %rax # rax <- field byte offset + ${store} ${reg}, (%rcx,%rax,1) + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_iput_short.S b/runtime/interpreter/mterp/x86_64/op_iput_short.S new file mode 100644 index 0000000000..b4e147cf5e --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iput_short.S @@ -0,0 +1 @@ +%include "x86_64/op_iput.S" { "handler":"artSet16InstanceFromMterp" } diff --git a/runtime/interpreter/mterp/x86_64/op_iput_short_quick.S b/runtime/interpreter/mterp/x86_64/op_iput_short_quick.S new file mode 100644 index 0000000000..3da96d53af --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iput_short_quick.S @@ -0,0 +1 @@ +%include "x86_64/op_iput_quick.S" { "reg":"rINSTw", "store":"movw" } diff --git a/runtime/interpreter/mterp/x86_64/op_iput_wide.S b/runtime/interpreter/mterp/x86_64/op_iput_wide.S new file mode 100644 index 0000000000..e59717b846 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iput_wide.S @@ -0,0 +1,14 @@ + /* iput-wide vA, vB, field@CCCC */ + .extern artSet64InstanceFromMterp + EXPORT_PC + movzwq 2(rPC), OUT_ARG0 # field ref CCCC + movzbq rINSTbl, %rcx # rcx <- BA + sarl $$4, %ecx # ecx <- B + GET_VREG OUT_32_ARG1, %rcx # the object pointer + andb $$0xf, rINSTbl # rINST <- A + leaq VREG_ADDRESS(rINSTq), OUT_ARG2 # &fp[A] + movq OFF_FP_METHOD(rFP), OUT_ARG3 # referrer + call SYMBOL(artSet64InstanceFromMterp) + testb %al, %al + jnz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_iput_wide_quick.S b/runtime/interpreter/mterp/x86_64/op_iput_wide_quick.S new file mode 100644 index 0000000000..473189d007 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iput_wide_quick.S @@ -0,0 +1,12 @@ + /* iput-wide-quick vA, vB, offset@CCCC */ + movzbq rINSTbl, %rcx # rcx<- BA + sarl $$4, %ecx # ecx<- B + GET_VREG %ecx, %rcx # vB (object we're operating on) + testl %ecx, %ecx # is object null? + je common_errNullObject + movzwq 2(rPC), %rax # rax<- field byte offset + leaq (%rcx,%rax,1), %rcx # ecx<- Address of 64-bit target + andb $$0xf, rINSTbl # rINST<- A + GET_WIDE_VREG %rax, rINSTq # rax<- fp[A]/fp[A+1] + movq %rax, (%rcx) # obj.field<- r0/r1 + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_long_to_double.S b/runtime/interpreter/mterp/x86_64/op_long_to_double.S new file mode 100644 index 0000000000..7cdae32373 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_long_to_double.S @@ -0,0 +1 @@ +%include "x86_64/fpcvt.S" {"source_suffix":"i","dest_suffix":"dq","wide":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_long_to_float.S b/runtime/interpreter/mterp/x86_64/op_long_to_float.S new file mode 100644 index 0000000000..7553348633 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_long_to_float.S @@ -0,0 +1 @@ +%include "x86_64/fpcvt.S" {"source_suffix":"i","dest_suffix":"sq","wide":"0"} diff --git a/runtime/interpreter/mterp/x86_64/op_long_to_int.S b/runtime/interpreter/mterp/x86_64/op_long_to_int.S new file mode 100644 index 0000000000..7b50c8e0b3 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_long_to_int.S @@ -0,0 +1,2 @@ +/* we ignore the high word, making this equivalent to a 32-bit reg move */ +%include "x86_64/op_move.S" diff --git a/runtime/interpreter/mterp/x86_64/op_monitor_enter.S b/runtime/interpreter/mterp/x86_64/op_monitor_enter.S new file mode 100644 index 0000000000..411091f23e --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_monitor_enter.S @@ -0,0 +1,11 @@ +/* + * Synchronize on an object. + */ + /* monitor-enter vAA */ + EXPORT_PC + GET_VREG OUT_32_ARG0, rINSTq + movq rSELF, OUT_ARG1 + call SYMBOL(artLockObjectFromCode) # (object, self) + testq %rax, %rax + jnz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/op_monitor_exit.S b/runtime/interpreter/mterp/x86_64/op_monitor_exit.S new file mode 100644 index 0000000000..72d9a23a87 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_monitor_exit.S @@ -0,0 +1,15 @@ +/* + * Unlock an object. + * + * Exceptions that occur when unlocking a monitor need to appear as + * if they happened at the following instruction. See the Dalvik + * instruction spec. + */ + /* monitor-exit vAA */ + EXPORT_PC + GET_VREG OUT_32_ARG0, rINSTq + movq rSELF, OUT_ARG1 + call SYMBOL(artUnlockObjectFromCode) # (object, self) + testq %rax, %rax + jnz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/op_move.S b/runtime/interpreter/mterp/x86_64/op_move.S new file mode 100644 index 0000000000..ccaac2caa8 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_move.S @@ -0,0 +1,13 @@ +%default { "is_object":"0" } + /* for move, move-object, long-to-int */ + /* op vA, vB */ + movl rINST, %eax # eax <- BA + andb $$0xf, %al # eax <- A + shrl $$4, rINST # rINST <- B + GET_VREG %edx, rINSTq + .if $is_object + SET_VREG_OBJECT %edx, %rax # fp[A] <- fp[B] + .else + SET_VREG %edx, %rax # fp[A] <- fp[B] + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/op_move_16.S b/runtime/interpreter/mterp/x86_64/op_move_16.S new file mode 100644 index 0000000000..6a813eb5ce --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_move_16.S @@ -0,0 +1,12 @@ +%default { "is_object":"0" } + /* for: move/16, move-object/16 */ + /* op vAAAA, vBBBB */ + movzwq 4(rPC), %rcx # ecx <- BBBB + movzwq 2(rPC), %rax # eax <- AAAA + GET_VREG %edx, %rcx + .if $is_object + SET_VREG_OBJECT %edx, %rax # fp[A] <- fp[B] + .else + SET_VREG %edx, %rax # fp[A] <- fp[B] + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 diff --git a/runtime/interpreter/mterp/x86_64/op_move_exception.S b/runtime/interpreter/mterp/x86_64/op_move_exception.S new file mode 100644 index 0000000000..d0a14fdc8d --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_move_exception.S @@ -0,0 +1,5 @@ + /* move-exception vAA */ + movl THREAD_EXCEPTION_OFFSET(rSELF), %eax + SET_VREG_OBJECT %eax, rINSTq # fp[AA] <- exception object + movl $$0, THREAD_EXCEPTION_OFFSET(rSELF) + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/op_move_from16.S b/runtime/interpreter/mterp/x86_64/op_move_from16.S new file mode 100644 index 0000000000..150e9c2f2c --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_move_from16.S @@ -0,0 +1,11 @@ +%default { "is_object":"0" } + /* for: move/from16, move-object/from16 */ + /* op vAA, vBBBB */ + movzwq 2(rPC), %rax # eax <- BBBB + GET_VREG %edx, %rax # edx <- fp[BBBB] + .if $is_object + SET_VREG_OBJECT %edx, rINSTq # fp[A] <- fp[B] + .else + SET_VREG %edx, rINSTq # fp[A] <- fp[B] + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_move_object.S b/runtime/interpreter/mterp/x86_64/op_move_object.S new file mode 100644 index 0000000000..0d866496e8 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_move_object.S @@ -0,0 +1 @@ +%include "x86_64/op_move.S" {"is_object":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_move_object_16.S b/runtime/interpreter/mterp/x86_64/op_move_object_16.S new file mode 100644 index 0000000000..32541ff2bf --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_move_object_16.S @@ -0,0 +1 @@ +%include "x86_64/op_move_16.S" {"is_object":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_move_object_from16.S b/runtime/interpreter/mterp/x86_64/op_move_object_from16.S new file mode 100644 index 0000000000..983e4abae5 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_move_object_from16.S @@ -0,0 +1 @@ +%include "x86_64/op_move_from16.S" {"is_object":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_move_result.S b/runtime/interpreter/mterp/x86_64/op_move_result.S new file mode 100644 index 0000000000..8268344bce --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_move_result.S @@ -0,0 +1,11 @@ +%default { "is_object":"0" } + /* for: move-result, move-result-object */ + /* op vAA */ + movq OFF_FP_RESULT_REGISTER(rFP), %rax # get pointer to result JType. + movl (%rax), %eax # r0 <- result.i. + .if $is_object + SET_VREG_OBJECT %eax, rINSTq # fp[A] <- fp[B] + .else + SET_VREG %eax, rINSTq # fp[A] <- fp[B] + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/op_move_result_object.S b/runtime/interpreter/mterp/x86_64/op_move_result_object.S new file mode 100644 index 0000000000..c5aac17f41 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_move_result_object.S @@ -0,0 +1 @@ +%include "x86_64/op_move_result.S" {"is_object":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_move_result_wide.S b/runtime/interpreter/mterp/x86_64/op_move_result_wide.S new file mode 100644 index 0000000000..03de783627 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_move_result_wide.S @@ -0,0 +1,5 @@ + /* move-result-wide vAA */ + movq OFF_FP_RESULT_REGISTER(rFP), %rax # get pointer to result JType. + movq (%rax), %rdx # Get wide + SET_WIDE_VREG %rdx, rINSTq # v[AA] <- rdx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/op_move_wide.S b/runtime/interpreter/mterp/x86_64/op_move_wide.S new file mode 100644 index 0000000000..508f8cc152 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_move_wide.S @@ -0,0 +1,8 @@ + /* move-wide vA, vB */ + /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */ + movl rINST, %ecx # ecx <- BA + sarl $$4, rINST # rINST <- B + andb $$0xf, %cl # ecx <- A + GET_WIDE_VREG %rdx, rINSTq # rdx <- v[B] + SET_WIDE_VREG %rdx, %rcx # v[A] <- rdx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/op_move_wide_16.S b/runtime/interpreter/mterp/x86_64/op_move_wide_16.S new file mode 100644 index 0000000000..ce371a920e --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_move_wide_16.S @@ -0,0 +1,7 @@ + /* move-wide/16 vAAAA, vBBBB */ + /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */ + movzwq 4(rPC), %rcx # ecx<- BBBB + movzwq 2(rPC), %rax # eax<- AAAA + GET_WIDE_VREG %rdx, %rcx # rdx <- v[B] + SET_WIDE_VREG %rdx, %rax # v[A] <- rdx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 diff --git a/runtime/interpreter/mterp/x86_64/op_move_wide_from16.S b/runtime/interpreter/mterp/x86_64/op_move_wide_from16.S new file mode 100644 index 0000000000..0d6971a674 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_move_wide_from16.S @@ -0,0 +1,6 @@ + /* move-wide/from16 vAA, vBBBB */ + /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */ + movzwl 2(rPC), %ecx # ecx <- BBBB + GET_WIDE_VREG %rdx, %rcx # rdx <- v[B] + SET_WIDE_VREG %rdx, rINSTq # v[A] <- rdx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_mul_double.S b/runtime/interpreter/mterp/x86_64/op_mul_double.S new file mode 100644 index 0000000000..1f4bcb3d00 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_mul_double.S @@ -0,0 +1 @@ +%include "x86_64/sseBinop.S" {"instr":"muls","suff":"d"} diff --git a/runtime/interpreter/mterp/x86_64/op_mul_double_2addr.S b/runtime/interpreter/mterp/x86_64/op_mul_double_2addr.S new file mode 100644 index 0000000000..9850a28995 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_mul_double_2addr.S @@ -0,0 +1 @@ +%include "x86_64/sseBinop2Addr.S" {"instr":"muls","suff":"d"} diff --git a/runtime/interpreter/mterp/x86_64/op_mul_float.S b/runtime/interpreter/mterp/x86_64/op_mul_float.S new file mode 100644 index 0000000000..85960e9dec --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_mul_float.S @@ -0,0 +1 @@ +%include "x86_64/sseBinop.S" {"instr":"muls","suff":"s"} diff --git a/runtime/interpreter/mterp/x86_64/op_mul_float_2addr.S b/runtime/interpreter/mterp/x86_64/op_mul_float_2addr.S new file mode 100644 index 0000000000..6d36b6a178 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_mul_float_2addr.S @@ -0,0 +1 @@ +%include "x86_64/sseBinop2Addr.S" {"instr":"muls","suff":"s"} diff --git a/runtime/interpreter/mterp/x86_64/op_mul_int.S b/runtime/interpreter/mterp/x86_64/op_mul_int.S new file mode 100644 index 0000000000..5f3923a20e --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_mul_int.S @@ -0,0 +1 @@ +%include "x86_64/binop.S" {"instr":"imull (rFP,%rcx,4), %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_mul_int_2addr.S b/runtime/interpreter/mterp/x86_64/op_mul_int_2addr.S new file mode 100644 index 0000000000..0b5af8a927 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_mul_int_2addr.S @@ -0,0 +1,8 @@ + /* mul vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $$4, rINST # rINST <- B + andb $$0xf, %cl # ecx <- A + GET_VREG %eax, %rcx # eax <- vA + imull (rFP,rINSTq,4), %eax + SET_VREG %eax, %rcx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/op_mul_int_lit16.S b/runtime/interpreter/mterp/x86_64/op_mul_int_lit16.S new file mode 100644 index 0000000000..a4cfdbce3e --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_mul_int_lit16.S @@ -0,0 +1 @@ +%include "x86_64/binopLit16.S" {"instr":"imull %ecx, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_mul_int_lit8.S b/runtime/interpreter/mterp/x86_64/op_mul_int_lit8.S new file mode 100644 index 0000000000..89e9acb77d --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_mul_int_lit8.S @@ -0,0 +1 @@ +%include "x86_64/binopLit8.S" {"instr":"imull %ecx, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_mul_long.S b/runtime/interpreter/mterp/x86_64/op_mul_long.S new file mode 100644 index 0000000000..2b853705cf --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_mul_long.S @@ -0,0 +1 @@ +%include "x86_64/binopWide.S" {"instr":"imulq (rFP,%rcx,4), %rax"} diff --git a/runtime/interpreter/mterp/x86_64/op_mul_long_2addr.S b/runtime/interpreter/mterp/x86_64/op_mul_long_2addr.S new file mode 100644 index 0000000000..167128b4d1 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_mul_long_2addr.S @@ -0,0 +1,8 @@ + /* mul vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $$4, rINST # rINST <- B + andb $$0xf, %cl # ecx <- A + GET_WIDE_VREG %rax, %rcx # rax <- vA + imulq (rFP,rINSTq,4), %rax + SET_WIDE_VREG %rax, %rcx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/op_neg_double.S b/runtime/interpreter/mterp/x86_64/op_neg_double.S new file mode 100644 index 0000000000..2c14b091cb --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_neg_double.S @@ -0,0 +1 @@ +%include "x86_64/unop.S" {"preinstr":" movq $0x8000000000000000, %rsi", "instr":" xorq %rsi, %rax", "wide":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_neg_float.S b/runtime/interpreter/mterp/x86_64/op_neg_float.S new file mode 100644 index 0000000000..148b21ec9a --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_neg_float.S @@ -0,0 +1 @@ +%include "x86_64/unop.S" {"instr":" xorl $0x80000000, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_neg_int.S b/runtime/interpreter/mterp/x86_64/op_neg_int.S new file mode 100644 index 0000000000..f90a937a8b --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_neg_int.S @@ -0,0 +1 @@ +%include "x86_64/unop.S" {"instr":" negl %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_neg_long.S b/runtime/interpreter/mterp/x86_64/op_neg_long.S new file mode 100644 index 0000000000..18fc3ccece --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_neg_long.S @@ -0,0 +1 @@ +%include "x86_64/unop.S" {"instr":" negq %rax", "wide":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_new_array.S b/runtime/interpreter/mterp/x86_64/op_new_array.S new file mode 100644 index 0000000000..9831a0b888 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_new_array.S @@ -0,0 +1,18 @@ +/* + * Allocate an array of objects, specified with the array class + * and a count. + * + * The verifier guarantees that this is an array class, so we don't + * check for it here. + */ + /* new-array vA, vB, class@CCCC */ + EXPORT_PC + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG0 + movq rPC, OUT_ARG1 + REFRESH_INST ${opnum} + movq rINSTq, OUT_ARG2 + movq rSELF, OUT_ARG3 + call SYMBOL(MterpNewArray) + testb %al, %al # 0 means an exception is thrown + jz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_new_instance.S b/runtime/interpreter/mterp/x86_64/op_new_instance.S new file mode 100644 index 0000000000..fc8c8cd98c --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_new_instance.S @@ -0,0 +1,13 @@ +/* + * Create a new instance of a class. + */ + /* new-instance vAA, class@BBBB */ + EXPORT_PC + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG0 + movq rSELF, OUT_ARG1 + REFRESH_INST ${opnum} + movq rINSTq, OUT_ARG2 + call SYMBOL(MterpNewInstance) + testb %al, %al # 0 means an exception is thrown + jz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_nop.S b/runtime/interpreter/mterp/x86_64/op_nop.S new file mode 100644 index 0000000000..4cb68e392e --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_nop.S @@ -0,0 +1 @@ + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/op_not_int.S b/runtime/interpreter/mterp/x86_64/op_not_int.S new file mode 100644 index 0000000000..463d080de9 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_not_int.S @@ -0,0 +1 @@ +%include "x86_64/unop.S" {"instr":" notl %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_not_long.S b/runtime/interpreter/mterp/x86_64/op_not_long.S new file mode 100644 index 0000000000..c97bb9ea1a --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_not_long.S @@ -0,0 +1 @@ +%include "x86_64/unop.S" {"instr":" notq %rax", "wide":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_or_int.S b/runtime/interpreter/mterp/x86_64/op_or_int.S new file mode 100644 index 0000000000..730310f6af --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_or_int.S @@ -0,0 +1 @@ +%include "x86_64/binop.S" {"instr":"orl (rFP,%rcx,4), %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_or_int_2addr.S b/runtime/interpreter/mterp/x86_64/op_or_int_2addr.S new file mode 100644 index 0000000000..f722e4dd9c --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_or_int_2addr.S @@ -0,0 +1 @@ +%include "x86_64/binop2addr.S" {"instr":"orl %eax, (rFP,%rcx,4)"} diff --git a/runtime/interpreter/mterp/x86_64/op_or_int_lit16.S b/runtime/interpreter/mterp/x86_64/op_or_int_lit16.S new file mode 100644 index 0000000000..fee86c7c62 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_or_int_lit16.S @@ -0,0 +1 @@ +%include "x86_64/binopLit16.S" {"instr":"orl %ecx, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_or_int_lit8.S b/runtime/interpreter/mterp/x86_64/op_or_int_lit8.S new file mode 100644 index 0000000000..81104c7e56 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_or_int_lit8.S @@ -0,0 +1 @@ +%include "x86_64/binopLit8.S" {"instr":"orl %ecx, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_or_long.S b/runtime/interpreter/mterp/x86_64/op_or_long.S new file mode 100644 index 0000000000..6c70a2001e --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_or_long.S @@ -0,0 +1 @@ +%include "x86_64/binopWide.S" {"instr":"orq (rFP,%rcx,4), %rax"} diff --git a/runtime/interpreter/mterp/x86_64/op_or_long_2addr.S b/runtime/interpreter/mterp/x86_64/op_or_long_2addr.S new file mode 100644 index 0000000000..546da1de2d --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_or_long_2addr.S @@ -0,0 +1 @@ +%include "x86_64/binopWide2addr.S" {"instr":"orq %rax, (rFP,%rcx,4)"} diff --git a/runtime/interpreter/mterp/x86_64/op_packed_switch.S b/runtime/interpreter/mterp/x86_64/op_packed_switch.S new file mode 100644 index 0000000000..0400ca45cf --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_packed_switch.S @@ -0,0 +1,27 @@ +%default { "func":"MterpDoPackedSwitch" } +/* + * Handle a packed-switch or sparse-switch instruction. In both cases + * we decode it and hand it off to a helper function. + * + * We don't really expect backward branches in a switch statement, but + * they're perfectly legal, so we check for them here. + * + * for: packed-switch, sparse-switch + */ + /* op vAA, +BBBB */ + movslq 2(rPC), OUT_ARG0 # rcx <- BBBBbbbb + leaq (rPC,OUT_ARG0,2), OUT_ARG0 # rcx <- PC + BBBBbbbb*2 + GET_VREG OUT_32_ARG1, rINSTq # eax <- vAA + call SYMBOL($func) + addl %eax, %eax + movslq %eax, %rax + leaq (rPC, %rax), rPC + FETCH_INST + jg 1f +#if MTERP_SUSPEND + REFRESH_IBASE +#else + jmp MterpCheckSuspendAndContinue +#endif +1: + GOTO_NEXT diff --git a/runtime/interpreter/mterp/x86_64/op_rem_double.S b/runtime/interpreter/mterp/x86_64/op_rem_double.S new file mode 100644 index 0000000000..00aed787cb --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_rem_double.S @@ -0,0 +1,14 @@ + /* rem_double vAA, vBB, vCC */ + movzbq 3(rPC), %rcx # ecx <- BB + movzbq 2(rPC), %rax # eax <- CC + fldl VREG_ADDRESS(%rcx) # %st1 <- fp[vBB] + fldl VREG_ADDRESS(%rax) # %st0 <- fp[vCC] +1: + fprem + fstsw %ax + sahf + jp 1b + fstp %st(1) + fstpl VREG_ADDRESS(rINSTq) # fp[vAA] <- %st + CLEAR_WIDE_REF rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_rem_double_2addr.S b/runtime/interpreter/mterp/x86_64/op_rem_double_2addr.S new file mode 100644 index 0000000000..9768266e21 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_rem_double_2addr.S @@ -0,0 +1,15 @@ + /* rem_double/2addr vA, vB */ + movzbq rINSTbl, %rcx # ecx <- A+ + sarl $$4, rINST # rINST <- B + fldl VREG_ADDRESS(rINSTq) # vB to fp stack + andb $$0xf, %cl # ecx <- A + fldl VREG_ADDRESS(%rcx) # vA to fp stack +1: + fprem + fstsw %ax + sahf + jp 1b + fstp %st(1) + fstpl VREG_ADDRESS(%rcx) # %st to vA + CLEAR_WIDE_REF %rcx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/op_rem_float.S b/runtime/interpreter/mterp/x86_64/op_rem_float.S new file mode 100644 index 0000000000..5af28accec --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_rem_float.S @@ -0,0 +1,14 @@ + /* rem_float vAA, vBB, vCC */ + movzbq 3(rPC), %rcx # ecx <- BB + movzbq 2(rPC), %rax # eax <- CC + flds VREG_ADDRESS(%rcx) # vBB to fp stack + flds VREG_ADDRESS(%rax) # vCC to fp stack +1: + fprem + fstsw %ax + sahf + jp 1b + fstp %st(1) + fstps VREG_ADDRESS(rINSTq) # %st to vAA + CLEAR_REF rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_rem_float_2addr.S b/runtime/interpreter/mterp/x86_64/op_rem_float_2addr.S new file mode 100644 index 0000000000..e9282a8de9 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_rem_float_2addr.S @@ -0,0 +1,15 @@ + /* rem_float/2addr vA, vB */ + movzbq rINSTbl, %rcx # ecx <- A+ + sarl $$4, rINST # rINST <- B + flds VREG_ADDRESS(rINSTq) # vB to fp stack + andb $$0xf, %cl # ecx <- A + flds VREG_ADDRESS(%rcx) # vA to fp stack +1: + fprem + fstsw %ax + sahf + jp 1b + fstp %st(1) + fstps VREG_ADDRESS(%rcx) # %st to vA + CLEAR_REF %rcx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/op_rem_int.S b/runtime/interpreter/mterp/x86_64/op_rem_int.S new file mode 100644 index 0000000000..fd77d7cdfe --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_rem_int.S @@ -0,0 +1 @@ +%include "x86_64/bindiv.S" {"result":"%edx","second":"%ecx","wide":"0","suffix":"l","rem":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_rem_int_2addr.S b/runtime/interpreter/mterp/x86_64/op_rem_int_2addr.S new file mode 100644 index 0000000000..25ffbf713b --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_rem_int_2addr.S @@ -0,0 +1 @@ +%include "x86_64/bindiv2addr.S" {"result":"%edx","second":"%ecx","wide":"0","suffix":"l","rem":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_rem_int_lit16.S b/runtime/interpreter/mterp/x86_64/op_rem_int_lit16.S new file mode 100644 index 0000000000..21cc37087d --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_rem_int_lit16.S @@ -0,0 +1 @@ +%include "x86_64/bindivLit16.S" {"result":"%edx","rem":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_rem_int_lit8.S b/runtime/interpreter/mterp/x86_64/op_rem_int_lit8.S new file mode 100644 index 0000000000..2eb0150f63 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_rem_int_lit8.S @@ -0,0 +1 @@ +%include "x86_64/bindivLit8.S" {"result":"%edx","rem":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_rem_long.S b/runtime/interpreter/mterp/x86_64/op_rem_long.S new file mode 100644 index 0000000000..efa721520d --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_rem_long.S @@ -0,0 +1 @@ +%include "x86_64/bindiv.S" {"result":"%rdx","second":"%rcx","wide":"1","suffix":"q","ext":"cqo","rem":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_rem_long_2addr.S b/runtime/interpreter/mterp/x86_64/op_rem_long_2addr.S new file mode 100644 index 0000000000..ce0dd86539 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_rem_long_2addr.S @@ -0,0 +1 @@ +%include "x86_64/bindiv2addr.S" {"result":"%rdx","second":"%rcx","wide":"1","suffix":"q","rem":"1","ext":"cqo"} diff --git a/runtime/interpreter/mterp/x86_64/op_return.S b/runtime/interpreter/mterp/x86_64/op_return.S new file mode 100644 index 0000000000..14f4f8a446 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_return.S @@ -0,0 +1,15 @@ +/* + * Return a 32-bit value. + * + * for: return, return-object + */ + /* op vAA */ + .extern MterpThreadFenceForConstructor + call SYMBOL(MterpThreadFenceForConstructor) + testl $$(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(rSELF) + jz 1f + movq rSELF, OUT_ARG0 + call SYMBOL(MterpSuspendCheck) +1: + GET_VREG %eax, rINSTq # eax <- vAA + jmp MterpReturn diff --git a/runtime/interpreter/mterp/x86_64/op_return_object.S b/runtime/interpreter/mterp/x86_64/op_return_object.S new file mode 100644 index 0000000000..1ae69a501c --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_return_object.S @@ -0,0 +1 @@ +%include "x86_64/op_return.S" diff --git a/runtime/interpreter/mterp/x86_64/op_return_void.S b/runtime/interpreter/mterp/x86_64/op_return_void.S new file mode 100644 index 0000000000..46a5753c87 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_return_void.S @@ -0,0 +1,9 @@ + .extern MterpThreadFenceForConstructor + call SYMBOL(MterpThreadFenceForConstructor) + testl $$(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(rSELF) + jz 1f + movq rSELF, OUT_ARG0 + call SYMBOL(MterpSuspendCheck) +1: + xorq %rax, %rax + jmp MterpReturn diff --git a/runtime/interpreter/mterp/x86_64/op_return_void_no_barrier.S b/runtime/interpreter/mterp/x86_64/op_return_void_no_barrier.S new file mode 100644 index 0000000000..92e3506d1d --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_return_void_no_barrier.S @@ -0,0 +1,7 @@ + testl $$(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(rSELF) + jz 1f + movq rSELF, OUT_ARG0 + call SYMBOL(MterpSuspendCheck) +1: + xorq %rax, %rax + jmp MterpReturn diff --git a/runtime/interpreter/mterp/x86_64/op_return_wide.S b/runtime/interpreter/mterp/x86_64/op_return_wide.S new file mode 100644 index 0000000000..f2d6e04cab --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_return_wide.S @@ -0,0 +1,13 @@ +/* + * Return a 64-bit value. + */ + /* return-wide vAA */ + .extern MterpThreadFenceForConstructor + call SYMBOL(MterpThreadFenceForConstructor) + testl $$(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(rSELF) + jz 1f + movq rSELF, OUT_ARG0 + call SYMBOL(MterpSuspendCheck) +1: + GET_WIDE_VREG %rax, rINSTq # eax <- v[AA] + jmp MterpReturn diff --git a/runtime/interpreter/mterp/x86_64/op_rsub_int.S b/runtime/interpreter/mterp/x86_64/op_rsub_int.S new file mode 100644 index 0000000000..2dd20026df --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_rsub_int.S @@ -0,0 +1,2 @@ +/* this op is "rsub-int", but can be thought of as "rsub-int/lit16" */ +%include "x86_64/binopLit16.S" {"instr":"subl %eax, %ecx","result":"%ecx"} diff --git a/runtime/interpreter/mterp/x86_64/op_rsub_int_lit8.S b/runtime/interpreter/mterp/x86_64/op_rsub_int_lit8.S new file mode 100644 index 0000000000..64d0d8a704 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_rsub_int_lit8.S @@ -0,0 +1 @@ +%include "x86_64/binopLit8.S" {"instr":"subl %eax, %ecx" , "result":"%ecx"} diff --git a/runtime/interpreter/mterp/x86_64/op_sget.S b/runtime/interpreter/mterp/x86_64/op_sget.S new file mode 100644 index 0000000000..38d9a5e6c8 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_sget.S @@ -0,0 +1,25 @@ +%default { "is_object":"0", "helper":"artGet32StaticFromCode", "wide":"0" } +/* + * General SGET handler wrapper. + * + * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short, sget-wide + */ + /* op vAA, field@BBBB */ + .extern $helper + EXPORT_PC + movzwq 2(rPC), OUT_ARG0 # field ref CCCC + movq OFF_FP_METHOD(rFP), OUT_ARG1 # referrer + movq rSELF, OUT_ARG2 # self + call SYMBOL($helper) + cmpl $$0, THREAD_EXCEPTION_OFFSET(rSELF) + jnz MterpException + .if $is_object + SET_VREG_OBJECT %eax, rINSTq # fp[A] <- value + .else + .if $wide + SET_WIDE_VREG %rax, rINSTq # fp[A] <- value + .else + SET_VREG %eax, rINSTq # fp[A] <- value + .endif + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_sget_boolean.S b/runtime/interpreter/mterp/x86_64/op_sget_boolean.S new file mode 100644 index 0000000000..7d358daec2 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_sget_boolean.S @@ -0,0 +1 @@ +%include "x86_64/op_sget.S" {"helper":"artGetBooleanStaticFromCode"} diff --git a/runtime/interpreter/mterp/x86_64/op_sget_byte.S b/runtime/interpreter/mterp/x86_64/op_sget_byte.S new file mode 100644 index 0000000000..79d9ff448b --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_sget_byte.S @@ -0,0 +1 @@ +%include "x86_64/op_sget.S" {"helper":"artGetByteStaticFromCode"} diff --git a/runtime/interpreter/mterp/x86_64/op_sget_char.S b/runtime/interpreter/mterp/x86_64/op_sget_char.S new file mode 100644 index 0000000000..448861052f --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_sget_char.S @@ -0,0 +1 @@ +%include "x86_64/op_sget.S" {"helper":"artGetCharStaticFromCode"} diff --git a/runtime/interpreter/mterp/x86_64/op_sget_object.S b/runtime/interpreter/mterp/x86_64/op_sget_object.S new file mode 100644 index 0000000000..09b627e124 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_sget_object.S @@ -0,0 +1 @@ +%include "x86_64/op_sget.S" {"is_object":"1", "helper":"artGetObjStaticFromCode"} diff --git a/runtime/interpreter/mterp/x86_64/op_sget_short.S b/runtime/interpreter/mterp/x86_64/op_sget_short.S new file mode 100644 index 0000000000..47ac23803c --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_sget_short.S @@ -0,0 +1 @@ +%include "x86_64/op_sget.S" {"helper":"artGetShortStaticFromCode"} diff --git a/runtime/interpreter/mterp/x86_64/op_sget_wide.S b/runtime/interpreter/mterp/x86_64/op_sget_wide.S new file mode 100644 index 0000000000..aa223434cf --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_sget_wide.S @@ -0,0 +1 @@ +%include "x86_64/op_sget.S" {"helper":"artGet64StaticFromCode", "wide":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_shl_int.S b/runtime/interpreter/mterp/x86_64/op_shl_int.S new file mode 100644 index 0000000000..fa1edb7555 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_shl_int.S @@ -0,0 +1 @@ +%include "x86_64/binop1.S" {"instr":"sall %cl, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_shl_int_2addr.S b/runtime/interpreter/mterp/x86_64/op_shl_int_2addr.S new file mode 100644 index 0000000000..dd962792c9 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_shl_int_2addr.S @@ -0,0 +1 @@ +%include "x86_64/shop2addr.S" {"instr":"sall %cl, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_shl_int_lit8.S b/runtime/interpreter/mterp/x86_64/op_shl_int_lit8.S new file mode 100644 index 0000000000..39b23ae1fb --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_shl_int_lit8.S @@ -0,0 +1 @@ +%include "x86_64/binopLit8.S" {"instr":"sall %cl, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_shl_long.S b/runtime/interpreter/mterp/x86_64/op_shl_long.S new file mode 100644 index 0000000000..fdc7cb64de --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_shl_long.S @@ -0,0 +1 @@ +%include "x86_64/binop1.S" {"instr":"salq %cl, %rax","wide":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_shl_long_2addr.S b/runtime/interpreter/mterp/x86_64/op_shl_long_2addr.S new file mode 100644 index 0000000000..546633f7d5 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_shl_long_2addr.S @@ -0,0 +1 @@ +%include "x86_64/shop2addr.S" {"instr":"salq %cl, %rax","wide":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_shr_int.S b/runtime/interpreter/mterp/x86_64/op_shr_int.S new file mode 100644 index 0000000000..fc289f4638 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_shr_int.S @@ -0,0 +1 @@ +%include "x86_64/binop1.S" {"instr":"sarl %cl, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_shr_int_2addr.S b/runtime/interpreter/mterp/x86_64/op_shr_int_2addr.S new file mode 100644 index 0000000000..0e5bca7057 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_shr_int_2addr.S @@ -0,0 +1 @@ +%include "x86_64/shop2addr.S" {"instr":"sarl %cl, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_shr_int_lit8.S b/runtime/interpreter/mterp/x86_64/op_shr_int_lit8.S new file mode 100644 index 0000000000..3cc9307569 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_shr_int_lit8.S @@ -0,0 +1 @@ +%include "x86_64/binopLit8.S" {"instr":"sarl %cl, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_shr_long.S b/runtime/interpreter/mterp/x86_64/op_shr_long.S new file mode 100644 index 0000000000..25028d3560 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_shr_long.S @@ -0,0 +1 @@ +%include "x86_64/binop1.S" {"instr":"sarq %cl, %rax","wide":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_shr_long_2addr.S b/runtime/interpreter/mterp/x86_64/op_shr_long_2addr.S new file mode 100644 index 0000000000..373841322d --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_shr_long_2addr.S @@ -0,0 +1 @@ +%include "x86_64/shop2addr.S" {"instr":"sarq %cl, %rax","wide":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_sparse_switch.S b/runtime/interpreter/mterp/x86_64/op_sparse_switch.S new file mode 100644 index 0000000000..0eaa514813 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_sparse_switch.S @@ -0,0 +1 @@ +%include "x86_64/op_packed_switch.S" { "func":"MterpDoSparseSwitch" } diff --git a/runtime/interpreter/mterp/x86_64/op_sput.S b/runtime/interpreter/mterp/x86_64/op_sput.S new file mode 100644 index 0000000000..e92b03273b --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_sput.S @@ -0,0 +1,17 @@ +%default { "helper":"artSet32StaticFromCode"} +/* + * General SPUT handler wrapper. + * + * for: sput, sput-boolean, sput-byte, sput-char, sput-short + */ + /* op vAA, field@BBBB */ + .extern $helper + EXPORT_PC + movzwq 2(rPC), OUT_ARG0 # field ref BBBB + GET_VREG OUT_32_ARG1, rINSTq # fp[AA] + movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer + movq rSELF, OUT_ARG3 # self + call SYMBOL($helper) + testb %al, %al + jnz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_sput_boolean.S b/runtime/interpreter/mterp/x86_64/op_sput_boolean.S new file mode 100644 index 0000000000..8718915cb2 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_sput_boolean.S @@ -0,0 +1 @@ +%include "x86_64/op_sput.S" {"helper":"artSet8StaticFromCode"} diff --git a/runtime/interpreter/mterp/x86_64/op_sput_byte.S b/runtime/interpreter/mterp/x86_64/op_sput_byte.S new file mode 100644 index 0000000000..8718915cb2 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_sput_byte.S @@ -0,0 +1 @@ +%include "x86_64/op_sput.S" {"helper":"artSet8StaticFromCode"} diff --git a/runtime/interpreter/mterp/x86_64/op_sput_char.S b/runtime/interpreter/mterp/x86_64/op_sput_char.S new file mode 100644 index 0000000000..2fe9d14816 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_sput_char.S @@ -0,0 +1 @@ +%include "x86_64/op_sput.S" {"helper":"artSet16StaticFromCode"} diff --git a/runtime/interpreter/mterp/x86_64/op_sput_object.S b/runtime/interpreter/mterp/x86_64/op_sput_object.S new file mode 100644 index 0000000000..eb5a37673e --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_sput_object.S @@ -0,0 +1,10 @@ + EXPORT_PC + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG0 + movq rPC, OUT_ARG1 + REFRESH_INST ${opnum} + movq rINSTq, OUT_ARG2 + movq rSELF, OUT_ARG3 + call SYMBOL(MterpSputObject) + testb %al, %al + jz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_sput_short.S b/runtime/interpreter/mterp/x86_64/op_sput_short.S new file mode 100644 index 0000000000..2fe9d14816 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_sput_short.S @@ -0,0 +1 @@ +%include "x86_64/op_sput.S" {"helper":"artSet16StaticFromCode"} diff --git a/runtime/interpreter/mterp/x86_64/op_sput_wide.S b/runtime/interpreter/mterp/x86_64/op_sput_wide.S new file mode 100644 index 0000000000..c4bc269eb6 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_sput_wide.S @@ -0,0 +1,15 @@ +/* + * SPUT_WIDE handler wrapper. + * + */ + /* sput-wide vAA, field@BBBB */ + .extern artSet64IndirectStaticFromMterp + EXPORT_PC + movzwq 2(rPC), OUT_ARG0 # field ref BBBB + movq OFF_FP_METHOD(rFP), OUT_ARG1 # referrer + leaq VREG_ADDRESS(rINSTq), OUT_ARG2 # &fp[AA] + movq rSELF, OUT_ARG3 # self + call SYMBOL(artSet64IndirectStaticFromMterp) + testb %al, %al + jnz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_sub_double.S b/runtime/interpreter/mterp/x86_64/op_sub_double.S new file mode 100644 index 0000000000..952667e831 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_sub_double.S @@ -0,0 +1 @@ +%include "x86_64/sseBinop.S" {"instr":"subs","suff":"d"} diff --git a/runtime/interpreter/mterp/x86_64/op_sub_double_2addr.S b/runtime/interpreter/mterp/x86_64/op_sub_double_2addr.S new file mode 100644 index 0000000000..0bd5dbb8ff --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_sub_double_2addr.S @@ -0,0 +1 @@ +%include "x86_64/sseBinop2Addr.S" {"instr":"subs","suff":"d"} diff --git a/runtime/interpreter/mterp/x86_64/op_sub_float.S b/runtime/interpreter/mterp/x86_64/op_sub_float.S new file mode 100644 index 0000000000..ea0ae14f5b --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_sub_float.S @@ -0,0 +1 @@ +%include "x86_64/sseBinop.S" {"instr":"subs","suff":"s"} diff --git a/runtime/interpreter/mterp/x86_64/op_sub_float_2addr.S b/runtime/interpreter/mterp/x86_64/op_sub_float_2addr.S new file mode 100644 index 0000000000..9dd17805c8 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_sub_float_2addr.S @@ -0,0 +1 @@ +%include "x86_64/sseBinop2Addr.S" {"instr":"subs","suff":"s"} diff --git a/runtime/interpreter/mterp/x86_64/op_sub_int.S b/runtime/interpreter/mterp/x86_64/op_sub_int.S new file mode 100644 index 0000000000..560394f43f --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_sub_int.S @@ -0,0 +1 @@ +%include "x86_64/binop.S" {"instr":"subl (rFP,%rcx,4), %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_sub_int_2addr.S b/runtime/interpreter/mterp/x86_64/op_sub_int_2addr.S new file mode 100644 index 0000000000..6f50f78f41 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_sub_int_2addr.S @@ -0,0 +1 @@ +%include "x86_64/binop2addr.S" {"instr":"subl %eax, (rFP,%rcx,4)"} diff --git a/runtime/interpreter/mterp/x86_64/op_sub_long.S b/runtime/interpreter/mterp/x86_64/op_sub_long.S new file mode 100644 index 0000000000..7fa54e7a11 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_sub_long.S @@ -0,0 +1 @@ +%include "x86_64/binopWide.S" {"instr":"subq (rFP,%rcx,4), %rax"} diff --git a/runtime/interpreter/mterp/x86_64/op_sub_long_2addr.S b/runtime/interpreter/mterp/x86_64/op_sub_long_2addr.S new file mode 100644 index 0000000000..c18be10919 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_sub_long_2addr.S @@ -0,0 +1 @@ +%include "x86_64/binopWide2addr.S" {"instr":"subq %rax, (rFP,%rcx,4)"} diff --git a/runtime/interpreter/mterp/x86_64/op_throw.S b/runtime/interpreter/mterp/x86_64/op_throw.S new file mode 100644 index 0000000000..22ed990645 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_throw.S @@ -0,0 +1,10 @@ +/* + * Throw an exception object in the current thread. + */ + /* throw vAA */ + EXPORT_PC + GET_VREG %eax, rINSTq # eax<- vAA (exception object) + testb %al, %al + jz common_errNullObject + movq %rax, THREAD_EXCEPTION_OFFSET(rSELF) + jmp MterpException diff --git a/runtime/interpreter/mterp/x86_64/op_unused_3e.S b/runtime/interpreter/mterp/x86_64/op_unused_3e.S new file mode 100644 index 0000000000..280615f08b --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_unused_3e.S @@ -0,0 +1 @@ +%include "x86_64/unused.S" diff --git a/runtime/interpreter/mterp/x86_64/op_unused_3f.S b/runtime/interpreter/mterp/x86_64/op_unused_3f.S new file mode 100644 index 0000000000..280615f08b --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_unused_3f.S @@ -0,0 +1 @@ +%include "x86_64/unused.S" diff --git a/runtime/interpreter/mterp/x86_64/op_unused_40.S b/runtime/interpreter/mterp/x86_64/op_unused_40.S new file mode 100644 index 0000000000..280615f08b --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_unused_40.S @@ -0,0 +1 @@ +%include "x86_64/unused.S" diff --git a/runtime/interpreter/mterp/x86_64/op_unused_41.S b/runtime/interpreter/mterp/x86_64/op_unused_41.S new file mode 100644 index 0000000000..280615f08b --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_unused_41.S @@ -0,0 +1 @@ +%include "x86_64/unused.S" diff --git a/runtime/interpreter/mterp/x86_64/op_unused_42.S b/runtime/interpreter/mterp/x86_64/op_unused_42.S new file mode 100644 index 0000000000..280615f08b --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_unused_42.S @@ -0,0 +1 @@ +%include "x86_64/unused.S" diff --git a/runtime/interpreter/mterp/x86_64/op_unused_43.S b/runtime/interpreter/mterp/x86_64/op_unused_43.S new file mode 100644 index 0000000000..280615f08b --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_unused_43.S @@ -0,0 +1 @@ +%include "x86_64/unused.S" diff --git a/runtime/interpreter/mterp/x86_64/op_unused_79.S b/runtime/interpreter/mterp/x86_64/op_unused_79.S new file mode 100644 index 0000000000..280615f08b --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_unused_79.S @@ -0,0 +1 @@ +%include "x86_64/unused.S" diff --git a/runtime/interpreter/mterp/x86_64/op_unused_7a.S b/runtime/interpreter/mterp/x86_64/op_unused_7a.S new file mode 100644 index 0000000000..280615f08b --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_unused_7a.S @@ -0,0 +1 @@ +%include "x86_64/unused.S" diff --git a/runtime/interpreter/mterp/x86_64/op_unused_f4.S b/runtime/interpreter/mterp/x86_64/op_unused_f4.S new file mode 100644 index 0000000000..280615f08b --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_unused_f4.S @@ -0,0 +1 @@ +%include "x86_64/unused.S" diff --git a/runtime/interpreter/mterp/x86_64/op_unused_fa.S b/runtime/interpreter/mterp/x86_64/op_unused_fa.S new file mode 100644 index 0000000000..280615f08b --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_unused_fa.S @@ -0,0 +1 @@ +%include "x86_64/unused.S" diff --git a/runtime/interpreter/mterp/x86_64/op_unused_fb.S b/runtime/interpreter/mterp/x86_64/op_unused_fb.S new file mode 100644 index 0000000000..280615f08b --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_unused_fb.S @@ -0,0 +1 @@ +%include "x86_64/unused.S" diff --git a/runtime/interpreter/mterp/x86_64/op_unused_fc.S b/runtime/interpreter/mterp/x86_64/op_unused_fc.S new file mode 100644 index 0000000000..280615f08b --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_unused_fc.S @@ -0,0 +1 @@ +%include "x86_64/unused.S" diff --git a/runtime/interpreter/mterp/x86_64/op_unused_fd.S b/runtime/interpreter/mterp/x86_64/op_unused_fd.S new file mode 100644 index 0000000000..280615f08b --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_unused_fd.S @@ -0,0 +1 @@ +%include "x86_64/unused.S" diff --git a/runtime/interpreter/mterp/x86_64/op_unused_fe.S b/runtime/interpreter/mterp/x86_64/op_unused_fe.S new file mode 100644 index 0000000000..280615f08b --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_unused_fe.S @@ -0,0 +1 @@ +%include "x86_64/unused.S" diff --git a/runtime/interpreter/mterp/x86_64/op_unused_ff.S b/runtime/interpreter/mterp/x86_64/op_unused_ff.S new file mode 100644 index 0000000000..280615f08b --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_unused_ff.S @@ -0,0 +1 @@ +%include "x86_64/unused.S" diff --git a/runtime/interpreter/mterp/x86_64/op_ushr_int.S b/runtime/interpreter/mterp/x86_64/op_ushr_int.S new file mode 100644 index 0000000000..dd91086371 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_ushr_int.S @@ -0,0 +1 @@ +%include "x86_64/binop1.S" {"instr":"shrl %cl, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_ushr_int_2addr.S b/runtime/interpreter/mterp/x86_64/op_ushr_int_2addr.S new file mode 100644 index 0000000000..d38aedd234 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_ushr_int_2addr.S @@ -0,0 +1 @@ +%include "x86_64/shop2addr.S" {"instr":"shrl %cl, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_ushr_int_lit8.S b/runtime/interpreter/mterp/x86_64/op_ushr_int_lit8.S new file mode 100644 index 0000000000..f7ff8abc86 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_ushr_int_lit8.S @@ -0,0 +1 @@ +%include "x86_64/binopLit8.S" {"instr":"shrl %cl, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_ushr_long.S b/runtime/interpreter/mterp/x86_64/op_ushr_long.S new file mode 100644 index 0000000000..7c6daca05d --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_ushr_long.S @@ -0,0 +1 @@ +%include "x86_64/binop1.S" {"instr":"shrq %cl, %rax","wide":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_ushr_long_2addr.S b/runtime/interpreter/mterp/x86_64/op_ushr_long_2addr.S new file mode 100644 index 0000000000..cd6a22c6fa --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_ushr_long_2addr.S @@ -0,0 +1 @@ +%include "x86_64/shop2addr.S" {"instr":"shrq %cl, %rax","wide":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_xor_int.S b/runtime/interpreter/mterp/x86_64/op_xor_int.S new file mode 100644 index 0000000000..b295d74de0 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_xor_int.S @@ -0,0 +1 @@ +%include "x86_64/binop.S" {"instr":"xorl (rFP,%rcx,4), %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_xor_int_2addr.S b/runtime/interpreter/mterp/x86_64/op_xor_int_2addr.S new file mode 100644 index 0000000000..879bfc05dc --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_xor_int_2addr.S @@ -0,0 +1 @@ +%include "x86_64/binop2addr.S" {"instr":"xorl %eax, (rFP,%rcx,4)"} diff --git a/runtime/interpreter/mterp/x86_64/op_xor_int_lit16.S b/runtime/interpreter/mterp/x86_64/op_xor_int_lit16.S new file mode 100644 index 0000000000..5d375a1cf6 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_xor_int_lit16.S @@ -0,0 +1 @@ +%include "x86_64/binopLit16.S" {"instr":"xorl %ecx, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_xor_int_lit8.S b/runtime/interpreter/mterp/x86_64/op_xor_int_lit8.S new file mode 100644 index 0000000000..54cce9c18e --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_xor_int_lit8.S @@ -0,0 +1 @@ +%include "x86_64/binopLit8.S" {"instr":"xorl %ecx, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_xor_long.S b/runtime/interpreter/mterp/x86_64/op_xor_long.S new file mode 100644 index 0000000000..52b44e29c1 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_xor_long.S @@ -0,0 +1 @@ +%include "x86_64/binopWide.S" {"instr":"xorq (rFP,%rcx,4), %rax"} diff --git a/runtime/interpreter/mterp/x86_64/op_xor_long_2addr.S b/runtime/interpreter/mterp/x86_64/op_xor_long_2addr.S new file mode 100644 index 0000000000..d75c4ba6ce --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_xor_long_2addr.S @@ -0,0 +1 @@ +%include "x86_64/binopWide2addr.S" {"instr":"xorq %rax, (rFP,%rcx,4)"} diff --git a/runtime/interpreter/mterp/x86_64/shop2addr.S b/runtime/interpreter/mterp/x86_64/shop2addr.S new file mode 100644 index 0000000000..6b06d002fc --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/shop2addr.S @@ -0,0 +1,19 @@ +%default {"wide":"0"} +/* + * Generic 32-bit "shift/2addr" operation. + */ + /* shift/2addr vA, vB */ + movl rINST, %ecx # ecx <- BA + sarl $$4, %ecx # ecx <- B + GET_VREG %ecx, %rcx # ecx <- vBB + andb $$0xf, rINSTbl # rINST <- A + .if $wide + GET_WIDE_VREG %rax, rINSTq # rax <- vAA + $instr # ex: sarl %cl, %eax + SET_WIDE_VREG %rax, rINSTq + .else + GET_VREG %eax, rINSTq # eax <- vAA + $instr # ex: sarl %cl, %eax + SET_VREG %eax, rINSTq + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/sseBinop.S b/runtime/interpreter/mterp/x86_64/sseBinop.S new file mode 100644 index 0000000000..09d3364de7 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/sseBinop.S @@ -0,0 +1,9 @@ +%default {"instr":"","suff":""} + movzbq 2(rPC), %rcx # ecx <- BB + movzbq 3(rPC), %rax # eax <- CC + movs${suff} VREG_ADDRESS(%rcx), %xmm0 # %xmm0 <- 1st src + ${instr}${suff} VREG_ADDRESS(%rax), %xmm0 + movs${suff} %xmm0, VREG_ADDRESS(rINSTq) # vAA <- %xmm0 + pxor %xmm0, %xmm0 + movs${suff} %xmm0, VREG_REF_ADDRESS(rINSTq) # clear ref + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/sseBinop2Addr.S b/runtime/interpreter/mterp/x86_64/sseBinop2Addr.S new file mode 100644 index 0000000000..084166b95d --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/sseBinop2Addr.S @@ -0,0 +1,10 @@ +%default {"instr":"","suff":""} + movl rINST, %ecx # ecx <- A+ + andl $$0xf, %ecx # ecx <- A + movs${suff} VREG_ADDRESS(%rcx), %xmm0 # %xmm0 <- 1st src + sarl $$4, rINST # rINST<- B + ${instr}${suff} VREG_ADDRESS(rINSTq), %xmm0 + movs${suff} %xmm0, VREG_ADDRESS(%rcx) # vAA<- %xmm0 + pxor %xmm0, %xmm0 + movs${suff} %xmm0, VREG_REF_ADDRESS(rINSTq) # clear ref + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/unop.S b/runtime/interpreter/mterp/x86_64/unop.S new file mode 100644 index 0000000000..1777123f36 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/unop.S @@ -0,0 +1,22 @@ +%default {"preinstr":"", "instr":"", "wide":"0"} +/* + * Generic 32/64-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "result = op eax". + */ + /* unop vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $$4,rINST # rINST <- B + .if ${wide} + GET_WIDE_VREG %rax, rINSTq # rax <- vB + .else + GET_VREG %eax, rINSTq # eax <- vB + .endif + andb $$0xf,%cl # ecx <- A +$preinstr +$instr + .if ${wide} + SET_WIDE_VREG %rax, %rcx + .else + SET_VREG %eax, %rcx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/unused.S b/runtime/interpreter/mterp/x86_64/unused.S new file mode 100644 index 0000000000..c95ef947d3 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/unused.S @@ -0,0 +1,4 @@ +/* + * Bail to reference interpreter to throw. + */ + jmp MterpFallback diff --git a/runtime/interpreter/mterp/x86_64/zcmp.S b/runtime/interpreter/mterp/x86_64/zcmp.S new file mode 100644 index 0000000000..e503ec1f38 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/zcmp.S @@ -0,0 +1,24 @@ +/* + * Generic one-operand compare-and-branch operation. Provide a "revcmp" + * fragment that specifies the *reverse* comparison to perform, e.g. + * for "if-le" you would use "gt". + * + * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez + */ + /* if-cmp vAA, +BBBB */ + cmpl $$0, VREG_ADDRESS(rINSTq) # compare (vA, 0) + movl $$2, %eax # assume branch not taken + j${revcmp} 1f + movswq 2(rPC),%rax # fetch signed displacement +1: + addq %rax, %rax # eax <- AA * 2 + leaq (rPC, %rax), rPC + FETCH_INST + jg 2f # AA * 2 > 0 => no suspend check +#if MTERP_SUSPEND + REFRESH_IBASE +#else + jmp MterpCheckSuspendAndContinue +#endif +2: + GOTO_NEXT -- GitLab From 0ae0e3c8a501befa88e479dd5ee59819b350be3d Mon Sep 17 00:00:00 2001 From: Jean-Philippe Halimi Date: Thu, 18 Feb 2016 16:46:37 +0100 Subject: [PATCH 002/204] Remove useless map in SwapAllocator This patch removes a map in SwapAllocator because there is no need for it. Change-Id: Ifbc641d0d919eaaae7994836dfe423883046c61f Signed-off-by: Jean-Philippe Halimi --- compiler/utils/swap_space.cc | 1 - compiler/utils/swap_space.h | 1 - 2 files changed, 2 deletions(-) diff --git a/compiler/utils/swap_space.cc b/compiler/utils/swap_space.cc index 244a5fedbe..1a8f567aa1 100644 --- a/compiler/utils/swap_space.cc +++ b/compiler/utils/swap_space.cc @@ -152,7 +152,6 @@ SwapSpace::SpaceChunk SwapSpace::NewFileChunk(size_t min_size) { } size_ += next_part; SpaceChunk new_chunk = {ptr, next_part}; - maps_.push_back(new_chunk); return new_chunk; #else UNUSED(min_size, kMininumMapSize); diff --git a/compiler/utils/swap_space.h b/compiler/utils/swap_space.h index b659f1d3c7..bf06675d72 100644 --- a/compiler/utils/swap_space.h +++ b/compiler/utils/swap_space.h @@ -85,7 +85,6 @@ class SwapSpace { int fd_; size_t size_; - std::list maps_; // NOTE: Boost.Bimap would be useful for the two following members. -- GitLab From b74353a6765447b1551b337fd76803eb6aa86b8b Mon Sep 17 00:00:00 2001 From: Chris Larsen Date: Fri, 20 Nov 2015 09:07:09 -0800 Subject: [PATCH 003/204] MIPS32: Implement intrinsics from java.lang.Math: - abs(double) - abs(float) - abs(int) - abs(long) - max(double, double) - max(float, float) - max(int, int) - max(long, long) - min(double, double) - min(float, float) - min(int, int) - min(long, long) - sqrt(double) The math intrinsics: - ceil(double) - floor(double) - rint(double) - round(double) - round(float) aren't implemented because they require instructions which only exist for MIPS64, or for MIPS32r6. Change-Id: I943be3592b52a423fcb7ac40f46f38a5e2a58c50 --- compiler/optimizing/code_generator_mips.cc | 3 - compiler/optimizing/code_generator_mips64.cc | 3 - compiler/optimizing/intrinsics_mips.cc | 591 ++++++++++++++++++- compiler/optimizing/intrinsics_mips64.cc | 152 +++-- compiler/utils/mips/assembler_mips.cc | 160 +++++ compiler/utils/mips/assembler_mips.h | 54 +- runtime/arch/mips/registers_mips.h | 1 + runtime/arch/mips64/registers_mips64.h | 1 + test/082-inline-execute/src/Main.java | 4 + 9 files changed, 888 insertions(+), 81 deletions(-) diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc index c500ea4408..c29f290ae5 100644 --- a/compiler/optimizing/code_generator_mips.cc +++ b/compiler/optimizing/code_generator_mips.cc @@ -39,9 +39,6 @@ namespace mips { static constexpr int kCurrentMethodStackOffset = 0; static constexpr Register kMethodRegisterArgument = A0; -// We need extra temporary/scratch registers (in addition to AT) in some cases. -static constexpr FRegister FTMP = F8; - Location MipsReturnLocation(Primitive::Type return_type) { switch (return_type) { case Primitive::kPrimBoolean: diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc index e3a44f1c96..3929cde98a 100644 --- a/compiler/optimizing/code_generator_mips64.cc +++ b/compiler/optimizing/code_generator_mips64.cc @@ -37,9 +37,6 @@ namespace mips64 { static constexpr int kCurrentMethodStackOffset = 0; static constexpr GpuRegister kMethodRegisterArgument = A0; -// We need extra temporary/scratch registers (in addition to AT) in some cases. -static constexpr FpuRegister FTMP = F8; - Location Mips64ReturnLocation(Primitive::Type return_type) { switch (return_type) { case Primitive::kPrimBoolean: diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc index c8629644b6..c75af64e51 100644 --- a/compiler/optimizing/intrinsics_mips.cc +++ b/compiler/optimizing/intrinsics_mips.cc @@ -407,7 +407,7 @@ void IntrinsicCodeGeneratorMIPS::VisitIntegerReverseBytes(HInvoke* invoke) { Primitive::kPrimInt, IsR2OrNewer(), IsR6(), - false, + /* reverseBits */ false, GetAssembler()); } @@ -421,7 +421,7 @@ void IntrinsicCodeGeneratorMIPS::VisitLongReverseBytes(HInvoke* invoke) { Primitive::kPrimLong, IsR2OrNewer(), IsR6(), - false, + /* reverseBits */ false, GetAssembler()); } @@ -435,7 +435,7 @@ void IntrinsicCodeGeneratorMIPS::VisitShortReverseBytes(HInvoke* invoke) { Primitive::kPrimShort, IsR2OrNewer(), IsR6(), - false, + /* reverseBits */ false, GetAssembler()); } @@ -475,7 +475,7 @@ void IntrinsicLocationsBuilderMIPS::VisitIntegerNumberOfLeadingZeros(HInvoke* in } void IntrinsicCodeGeneratorMIPS::VisitIntegerNumberOfLeadingZeros(HInvoke* invoke) { - GenNumberOfLeadingZeroes(invoke->GetLocations(), false, IsR6(), GetAssembler()); + GenNumberOfLeadingZeroes(invoke->GetLocations(), /* is64bit */ false, IsR6(), GetAssembler()); } // int java.lang.Long.numberOfLeadingZeros(long i) @@ -484,7 +484,7 @@ void IntrinsicLocationsBuilderMIPS::VisitLongNumberOfLeadingZeros(HInvoke* invok } void IntrinsicCodeGeneratorMIPS::VisitLongNumberOfLeadingZeros(HInvoke* invoke) { - GenNumberOfLeadingZeroes(invoke->GetLocations(), true, IsR6(), GetAssembler()); + GenNumberOfLeadingZeroes(invoke->GetLocations(), /* is64bit */ true, IsR6(), GetAssembler()); } static void GenNumberOfTrailingZeroes(LocationSummary* locations, @@ -497,7 +497,6 @@ static void GenNumberOfTrailingZeroes(LocationSummary* locations, Register in; if (is64bit) { - MipsLabel done; Register in_hi = locations->InAt(0).AsRegisterPairHigh(); in_lo = locations->InAt(0).AsRegisterPairLow(); @@ -588,7 +587,11 @@ void IntrinsicLocationsBuilderMIPS::VisitIntegerNumberOfTrailingZeros(HInvoke* i } void IntrinsicCodeGeneratorMIPS::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) { - GenNumberOfTrailingZeroes(invoke->GetLocations(), false, IsR6(), IsR2OrNewer(), GetAssembler()); + GenNumberOfTrailingZeroes(invoke->GetLocations(), + /* is64bit */ false, + IsR6(), + IsR2OrNewer(), + GetAssembler()); } // int java.lang.Long.numberOfTrailingZeros(long i) @@ -597,7 +600,11 @@ void IntrinsicLocationsBuilderMIPS::VisitLongNumberOfTrailingZeros(HInvoke* invo } void IntrinsicCodeGeneratorMIPS::VisitLongNumberOfTrailingZeros(HInvoke* invoke) { - GenNumberOfTrailingZeroes(invoke->GetLocations(), true, IsR6(), IsR2OrNewer(), GetAssembler()); + GenNumberOfTrailingZeroes(invoke->GetLocations(), + /* is64bit */ true, + IsR6(), + IsR2OrNewer(), + GetAssembler()); } enum RotationDirection { @@ -806,7 +813,7 @@ void IntrinsicCodeGeneratorMIPS::VisitIntegerReverse(HInvoke* invoke) { Primitive::kPrimInt, IsR2OrNewer(), IsR6(), - true, + /* reverseBits */ true, GetAssembler()); } @@ -820,10 +827,561 @@ void IntrinsicCodeGeneratorMIPS::VisitLongReverse(HInvoke* invoke) { Primitive::kPrimLong, IsR2OrNewer(), IsR6(), - true, + /* reverseBits */ true, GetAssembler()); } +static void CreateFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) { + LocationSummary* locations = new (arena) LocationSummary(invoke, + LocationSummary::kNoCall, + kIntrinsified); + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); +} + +static void MathAbsFP(LocationSummary* locations, bool is64bit, MipsAssembler* assembler) { + FRegister in = locations->InAt(0).AsFpuRegister(); + FRegister out = locations->Out().AsFpuRegister(); + + if (is64bit) { + __ AbsD(out, in); + } else { + __ AbsS(out, in); + } +} + +// double java.lang.Math.abs(double) +void IntrinsicLocationsBuilderMIPS::VisitMathAbsDouble(HInvoke* invoke) { + CreateFPToFPLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitMathAbsDouble(HInvoke* invoke) { + MathAbsFP(invoke->GetLocations(), /* is64bit */ true, GetAssembler()); +} + +// float java.lang.Math.abs(float) +void IntrinsicLocationsBuilderMIPS::VisitMathAbsFloat(HInvoke* invoke) { + CreateFPToFPLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitMathAbsFloat(HInvoke* invoke) { + MathAbsFP(invoke->GetLocations(), /* is64bit */ false, GetAssembler()); +} + +static void GenAbsInteger(LocationSummary* locations, bool is64bit, MipsAssembler* assembler) { + if (is64bit) { + Register in_lo = locations->InAt(0).AsRegisterPairLow(); + Register in_hi = locations->InAt(0).AsRegisterPairHigh(); + Register out_lo = locations->Out().AsRegisterPairLow(); + Register out_hi = locations->Out().AsRegisterPairHigh(); + + // The comments in this section show the analogous operations which would + // be performed if we had 64-bit registers "in", and "out". + // __ Dsra32(AT, in, 31); + __ Sra(AT, in_hi, 31); + // __ Xor(out, in, AT); + __ Xor(TMP, in_lo, AT); + __ Xor(out_hi, in_hi, AT); + // __ Dsubu(out, out, AT); + __ Subu(out_lo, TMP, AT); + __ Sltu(TMP, out_lo, TMP); + __ Addu(out_hi, out_hi, TMP); + } else { + Register in = locations->InAt(0).AsRegister(); + Register out = locations->Out().AsRegister(); + + __ Sra(AT, in, 31); + __ Xor(out, in, AT); + __ Subu(out, out, AT); + } +} + +// int java.lang.Math.abs(int) +void IntrinsicLocationsBuilderMIPS::VisitMathAbsInt(HInvoke* invoke) { + CreateIntToIntLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitMathAbsInt(HInvoke* invoke) { + GenAbsInteger(invoke->GetLocations(), /* is64bit */ false, GetAssembler()); +} + +// long java.lang.Math.abs(long) +void IntrinsicLocationsBuilderMIPS::VisitMathAbsLong(HInvoke* invoke) { + CreateIntToIntLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitMathAbsLong(HInvoke* invoke) { + GenAbsInteger(invoke->GetLocations(), /* is64bit */ true, GetAssembler()); +} + +static void GenMinMaxFP(LocationSummary* locations, + bool is_min, + Primitive::Type type, + bool is_R6, + MipsAssembler* assembler) { + FRegister out = locations->Out().AsFpuRegister(); + FRegister a = locations->InAt(0).AsFpuRegister(); + FRegister b = locations->InAt(1).AsFpuRegister(); + + if (is_R6) { + MipsLabel noNaNs; + MipsLabel done; + FRegister ftmp = ((out != a) && (out != b)) ? out : FTMP; + + // When Java computes min/max it prefers a NaN to a number; the + // behavior of MIPSR6 is to prefer numbers to NaNs, i.e., if one of + // the inputs is a NaN and the other is a valid number, the MIPS + // instruction will return the number; Java wants the NaN value + // returned. This is why there is extra logic preceding the use of + // the MIPS min.fmt/max.fmt instructions. If either a, or b holds a + // NaN, return the NaN, otherwise return the min/max. + if (type == Primitive::kPrimDouble) { + __ CmpUnD(FTMP, a, b); + __ Bc1eqz(FTMP, &noNaNs); + + // One of the inputs is a NaN + __ CmpEqD(ftmp, a, a); + // If a == a then b is the NaN, otherwise a is the NaN. + __ SelD(ftmp, a, b); + + if (ftmp != out) { + __ MovD(out, ftmp); + } + + __ B(&done); + + __ Bind(&noNaNs); + + if (is_min) { + __ MinD(out, a, b); + } else { + __ MaxD(out, a, b); + } + } else { + DCHECK_EQ(type, Primitive::kPrimFloat); + __ CmpUnS(FTMP, a, b); + __ Bc1eqz(FTMP, &noNaNs); + + // One of the inputs is a NaN + __ CmpEqS(ftmp, a, a); + // If a == a then b is the NaN, otherwise a is the NaN. + __ SelS(ftmp, a, b); + + if (ftmp != out) { + __ MovS(out, ftmp); + } + + __ B(&done); + + __ Bind(&noNaNs); + + if (is_min) { + __ MinS(out, a, b); + } else { + __ MaxS(out, a, b); + } + } + + __ Bind(&done); + } else { + MipsLabel ordered; + MipsLabel compare; + MipsLabel select; + MipsLabel done; + + if (type == Primitive::kPrimDouble) { + __ CunD(a, b); + } else { + DCHECK_EQ(type, Primitive::kPrimFloat); + __ CunS(a, b); + } + __ Bc1f(&ordered); + + // a or b (or both) is a NaN. Return one, which is a NaN. + if (type == Primitive::kPrimDouble) { + __ CeqD(b, b); + } else { + __ CeqS(b, b); + } + __ B(&select); + + __ Bind(&ordered); + + // Neither is a NaN. + // a == b? (-0.0 compares equal with +0.0) + // If equal, handle zeroes, else compare further. + if (type == Primitive::kPrimDouble) { + __ CeqD(a, b); + } else { + __ CeqS(a, b); + } + __ Bc1f(&compare); + + // a == b either bit for bit or one is -0.0 and the other is +0.0. + if (type == Primitive::kPrimDouble) { + __ MoveFromFpuHigh(TMP, a); + __ MoveFromFpuHigh(AT, b); + } else { + __ Mfc1(TMP, a); + __ Mfc1(AT, b); + } + + if (is_min) { + // -0.0 prevails over +0.0. + __ Or(TMP, TMP, AT); + } else { + // +0.0 prevails over -0.0. + __ And(TMP, TMP, AT); + } + + if (type == Primitive::kPrimDouble) { + __ Mfc1(AT, a); + __ Mtc1(AT, out); + __ MoveToFpuHigh(TMP, out); + } else { + __ Mtc1(TMP, out); + } + __ B(&done); + + __ Bind(&compare); + + if (type == Primitive::kPrimDouble) { + if (is_min) { + // return (a <= b) ? a : b; + __ ColeD(a, b); + } else { + // return (a >= b) ? a : b; + __ ColeD(b, a); // b <= a + } + } else { + if (is_min) { + // return (a <= b) ? a : b; + __ ColeS(a, b); + } else { + // return (a >= b) ? a : b; + __ ColeS(b, a); // b <= a + } + } + + __ Bind(&select); + + if (type == Primitive::kPrimDouble) { + __ MovtD(out, a); + __ MovfD(out, b); + } else { + __ MovtS(out, a); + __ MovfS(out, b); + } + + __ Bind(&done); + } +} + +static void CreateFPFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) { + LocationSummary* locations = new (arena) LocationSummary(invoke, + LocationSummary::kNoCall, + kIntrinsified); + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetInAt(1, Location::RequiresFpuRegister()); + locations->SetOut(Location::RequiresFpuRegister(), Location::kOutputOverlap); +} + +// double java.lang.Math.min(double, double) +void IntrinsicLocationsBuilderMIPS::VisitMathMinDoubleDouble(HInvoke* invoke) { + CreateFPFPToFPLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitMathMinDoubleDouble(HInvoke* invoke) { + GenMinMaxFP(invoke->GetLocations(), + /* is_min */ true, + Primitive::kPrimDouble, + IsR6(), + GetAssembler()); +} + +// float java.lang.Math.min(float, float) +void IntrinsicLocationsBuilderMIPS::VisitMathMinFloatFloat(HInvoke* invoke) { + CreateFPFPToFPLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitMathMinFloatFloat(HInvoke* invoke) { + GenMinMaxFP(invoke->GetLocations(), + /* is_min */ true, + Primitive::kPrimFloat, + IsR6(), + GetAssembler()); +} + +// double java.lang.Math.max(double, double) +void IntrinsicLocationsBuilderMIPS::VisitMathMaxDoubleDouble(HInvoke* invoke) { + CreateFPFPToFPLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitMathMaxDoubleDouble(HInvoke* invoke) { + GenMinMaxFP(invoke->GetLocations(), + /* is_min */ false, + Primitive::kPrimDouble, + IsR6(), + GetAssembler()); +} + +// float java.lang.Math.max(float, float) +void IntrinsicLocationsBuilderMIPS::VisitMathMaxFloatFloat(HInvoke* invoke) { + CreateFPFPToFPLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitMathMaxFloatFloat(HInvoke* invoke) { + GenMinMaxFP(invoke->GetLocations(), + /* is_min */ false, + Primitive::kPrimFloat, + IsR6(), + GetAssembler()); +} + +static void CreateIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) { + LocationSummary* locations = new (arena) LocationSummary(invoke, + LocationSummary::kNoCall, + kIntrinsified); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); +} + +static void GenMinMax(LocationSummary* locations, + bool is_min, + Primitive::Type type, + bool is_R6, + MipsAssembler* assembler) { + if (is_R6) { + // Some architectures, such as ARM and MIPS (prior to r6), have a + // conditional move instruction which only changes the target + // (output) register if the condition is true (MIPS prior to r6 had + // MOVF, MOVT, MOVN, and MOVZ). The SELEQZ and SELNEZ instructions + // always change the target (output) register. If the condition is + // true the output register gets the contents of the "rs" register; + // otherwise, the output register is set to zero. One consequence + // of this is that to implement something like "rd = c==0 ? rs : rt" + // MIPS64r6 needs to use a pair of SELEQZ/SELNEZ instructions. + // After executing this pair of instructions one of the output + // registers from the pair will necessarily contain zero. Then the + // code ORs the output registers from the SELEQZ/SELNEZ instructions + // to get the final result. + // + // The initial test to see if the output register is same as the + // first input register is needed to make sure that value in the + // first input register isn't clobbered before we've finished + // computing the output value. The logic in the corresponding else + // clause performs the same task but makes sure the second input + // register isn't clobbered in the event that it's the same register + // as the output register; the else clause also handles the case + // where the output register is distinct from both the first, and the + // second input registers. + if (type == Primitive::kPrimLong) { + Register a_lo = locations->InAt(0).AsRegisterPairLow(); + Register a_hi = locations->InAt(0).AsRegisterPairHigh(); + Register b_lo = locations->InAt(1).AsRegisterPairLow(); + Register b_hi = locations->InAt(1).AsRegisterPairHigh(); + Register out_lo = locations->Out().AsRegisterPairLow(); + Register out_hi = locations->Out().AsRegisterPairHigh(); + + MipsLabel compare_done; + + if (a_lo == b_lo) { + if (out_lo != a_lo) { + __ Move(out_lo, a_lo); + __ Move(out_hi, a_hi); + } + } else { + __ Slt(TMP, b_hi, a_hi); + __ Bne(b_hi, a_hi, &compare_done); + + __ Sltu(TMP, b_lo, a_lo); + + __ Bind(&compare_done); + + if (is_min) { + __ Seleqz(AT, a_lo, TMP); + __ Selnez(out_lo, b_lo, TMP); // Safe even if out_lo == a_lo/b_lo + // because at this point we're + // done using a_lo/b_lo. + } else { + __ Selnez(AT, a_lo, TMP); + __ Seleqz(out_lo, b_lo, TMP); // ditto + } + __ Or(out_lo, out_lo, AT); + if (is_min) { + __ Seleqz(AT, a_hi, TMP); + __ Selnez(out_hi, b_hi, TMP); // ditto but for out_hi & a_hi/b_hi + } else { + __ Selnez(AT, a_hi, TMP); + __ Seleqz(out_hi, b_hi, TMP); // ditto but for out_hi & a_hi/b_hi + } + __ Or(out_hi, out_hi, AT); + } + } else { + DCHECK_EQ(type, Primitive::kPrimInt); + Register a = locations->InAt(0).AsRegister(); + Register b = locations->InAt(1).AsRegister(); + Register out = locations->Out().AsRegister(); + + if (a == b) { + if (out != a) { + __ Move(out, a); + } + } else { + __ Slt(AT, b, a); + if (is_min) { + __ Seleqz(TMP, a, AT); + __ Selnez(AT, b, AT); + } else { + __ Selnez(TMP, a, AT); + __ Seleqz(AT, b, AT); + } + __ Or(out, TMP, AT); + } + } + } else { + if (type == Primitive::kPrimLong) { + Register a_lo = locations->InAt(0).AsRegisterPairLow(); + Register a_hi = locations->InAt(0).AsRegisterPairHigh(); + Register b_lo = locations->InAt(1).AsRegisterPairLow(); + Register b_hi = locations->InAt(1).AsRegisterPairHigh(); + Register out_lo = locations->Out().AsRegisterPairLow(); + Register out_hi = locations->Out().AsRegisterPairHigh(); + + MipsLabel compare_done; + + if (a_lo == b_lo) { + if (out_lo != a_lo) { + __ Move(out_lo, a_lo); + __ Move(out_hi, a_hi); + } + } else { + __ Slt(TMP, a_hi, b_hi); + __ Bne(a_hi, b_hi, &compare_done); + + __ Sltu(TMP, a_lo, b_lo); + + __ Bind(&compare_done); + + if (is_min) { + if (out_lo != a_lo) { + __ Movn(out_hi, a_hi, TMP); + __ Movn(out_lo, a_lo, TMP); + } + if (out_lo != b_lo) { + __ Movz(out_hi, b_hi, TMP); + __ Movz(out_lo, b_lo, TMP); + } + } else { + if (out_lo != a_lo) { + __ Movz(out_hi, a_hi, TMP); + __ Movz(out_lo, a_lo, TMP); + } + if (out_lo != b_lo) { + __ Movn(out_hi, b_hi, TMP); + __ Movn(out_lo, b_lo, TMP); + } + } + } + } else { + DCHECK_EQ(type, Primitive::kPrimInt); + Register a = locations->InAt(0).AsRegister(); + Register b = locations->InAt(1).AsRegister(); + Register out = locations->Out().AsRegister(); + + if (a == b) { + if (out != a) { + __ Move(out, a); + } + } else { + __ Slt(AT, a, b); + if (is_min) { + if (out != a) { + __ Movn(out, a, AT); + } + if (out != b) { + __ Movz(out, b, AT); + } + } else { + if (out != a) { + __ Movz(out, a, AT); + } + if (out != b) { + __ Movn(out, b, AT); + } + } + } + } + } +} + +// int java.lang.Math.min(int, int) +void IntrinsicLocationsBuilderMIPS::VisitMathMinIntInt(HInvoke* invoke) { + CreateIntIntToIntLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitMathMinIntInt(HInvoke* invoke) { + GenMinMax(invoke->GetLocations(), + /* is_min */ true, + Primitive::kPrimInt, + IsR6(), + GetAssembler()); +} + +// long java.lang.Math.min(long, long) +void IntrinsicLocationsBuilderMIPS::VisitMathMinLongLong(HInvoke* invoke) { + CreateIntIntToIntLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitMathMinLongLong(HInvoke* invoke) { + GenMinMax(invoke->GetLocations(), + /* is_min */ true, + Primitive::kPrimLong, + IsR6(), + GetAssembler()); +} + +// int java.lang.Math.max(int, int) +void IntrinsicLocationsBuilderMIPS::VisitMathMaxIntInt(HInvoke* invoke) { + CreateIntIntToIntLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitMathMaxIntInt(HInvoke* invoke) { + GenMinMax(invoke->GetLocations(), + /* is_min */ false, + Primitive::kPrimInt, + IsR6(), + GetAssembler()); +} + +// long java.lang.Math.max(long, long) +void IntrinsicLocationsBuilderMIPS::VisitMathMaxLongLong(HInvoke* invoke) { + CreateIntIntToIntLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitMathMaxLongLong(HInvoke* invoke) { + GenMinMax(invoke->GetLocations(), + /* is_min */ false, + Primitive::kPrimLong, + IsR6(), + GetAssembler()); +} + +// double java.lang.Math.sqrt(double) +void IntrinsicLocationsBuilderMIPS::VisitMathSqrt(HInvoke* invoke) { + CreateFPToFPLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitMathSqrt(HInvoke* invoke) { + LocationSummary* locations = invoke->GetLocations(); + MipsAssembler* assembler = GetAssembler(); + FRegister in = locations->InAt(0).AsFpuRegister(); + FRegister out = locations->Out().AsFpuRegister(); + + __ SqrtD(out, in); +} + // byte libcore.io.Memory.peekByte(long address) void IntrinsicLocationsBuilderMIPS::VisitMemoryPeekByte(HInvoke* invoke) { CreateIntToIntLocations(arena_, invoke); @@ -1151,19 +1709,6 @@ void IntrinsicCodeGeneratorMIPS::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) UNIMPLEMENTED_INTRINSIC(IntegerBitCount) UNIMPLEMENTED_INTRINSIC(LongBitCount) -UNIMPLEMENTED_INTRINSIC(MathAbsDouble) -UNIMPLEMENTED_INTRINSIC(MathAbsFloat) -UNIMPLEMENTED_INTRINSIC(MathAbsInt) -UNIMPLEMENTED_INTRINSIC(MathAbsLong) -UNIMPLEMENTED_INTRINSIC(MathMinDoubleDouble) -UNIMPLEMENTED_INTRINSIC(MathMinFloatFloat) -UNIMPLEMENTED_INTRINSIC(MathMaxDoubleDouble) -UNIMPLEMENTED_INTRINSIC(MathMaxFloatFloat) -UNIMPLEMENTED_INTRINSIC(MathMinIntInt) -UNIMPLEMENTED_INTRINSIC(MathMinLongLong) -UNIMPLEMENTED_INTRINSIC(MathMaxIntInt) -UNIMPLEMENTED_INTRINSIC(MathMaxLongLong) -UNIMPLEMENTED_INTRINSIC(MathSqrt) UNIMPLEMENTED_INTRINSIC(MathCeil) UNIMPLEMENTED_INTRINSIC(MathFloor) UNIMPLEMENTED_INTRINSIC(MathRint) diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc index cf3a3657de..e29f9e599a 100644 --- a/compiler/optimizing/intrinsics_mips64.cc +++ b/compiler/optimizing/intrinsics_mips64.cc @@ -580,25 +580,71 @@ void IntrinsicCodeGeneratorMIPS64::VisitMathAbsLong(HInvoke* invoke) { static void GenMinMaxFP(LocationSummary* locations, bool is_min, - bool is_double, + Primitive::Type type, Mips64Assembler* assembler) { - FpuRegister lhs = locations->InAt(0).AsFpuRegister(); - FpuRegister rhs = locations->InAt(1).AsFpuRegister(); + FpuRegister a = locations->InAt(0).AsFpuRegister(); + FpuRegister b = locations->InAt(1).AsFpuRegister(); FpuRegister out = locations->Out().AsFpuRegister(); - if (is_double) { + Mips64Label noNaNs; + Mips64Label done; + FpuRegister ftmp = ((out != a) && (out != b)) ? out : FTMP; + + // When Java computes min/max it prefers a NaN to a number; the + // behavior of MIPSR6 is to prefer numbers to NaNs, i.e., if one of + // the inputs is a NaN and the other is a valid number, the MIPS + // instruction will return the number; Java wants the NaN value + // returned. This is why there is extra logic preceding the use of + // the MIPS min.fmt/max.fmt instructions. If either a, or b holds a + // NaN, return the NaN, otherwise return the min/max. + if (type == Primitive::kPrimDouble) { + __ CmpUnD(FTMP, a, b); + __ Bc1eqz(FTMP, &noNaNs); + + // One of the inputs is a NaN + __ CmpEqD(ftmp, a, a); + // If a == a then b is the NaN, otherwise a is the NaN. + __ SelD(ftmp, a, b); + + if (ftmp != out) { + __ MovD(out, ftmp); + } + + __ Bc(&done); + + __ Bind(&noNaNs); + if (is_min) { - __ MinD(out, lhs, rhs); + __ MinD(out, a, b); } else { - __ MaxD(out, lhs, rhs); + __ MaxD(out, a, b); } } else { + DCHECK_EQ(type, Primitive::kPrimFloat); + __ CmpUnS(FTMP, a, b); + __ Bc1eqz(FTMP, &noNaNs); + + // One of the inputs is a NaN + __ CmpEqS(ftmp, a, a); + // If a == a then b is the NaN, otherwise a is the NaN. + __ SelS(ftmp, a, b); + + if (ftmp != out) { + __ MovS(out, ftmp); + } + + __ Bc(&done); + + __ Bind(&noNaNs); + if (is_min) { - __ MinS(out, lhs, rhs); + __ MinS(out, a, b); } else { - __ MaxS(out, lhs, rhs); + __ MaxS(out, a, b); } } + + __ Bind(&done); } static void CreateFPFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) { @@ -616,7 +662,7 @@ void IntrinsicLocationsBuilderMIPS64::VisitMathMinDoubleDouble(HInvoke* invoke) } void IntrinsicCodeGeneratorMIPS64::VisitMathMinDoubleDouble(HInvoke* invoke) { - GenMinMaxFP(invoke->GetLocations(), /* is_min */ true, /* is_double */ true, GetAssembler()); + GenMinMaxFP(invoke->GetLocations(), /* is_min */ true, Primitive::kPrimDouble, GetAssembler()); } // float java.lang.Math.min(float, float) @@ -625,7 +671,7 @@ void IntrinsicLocationsBuilderMIPS64::VisitMathMinFloatFloat(HInvoke* invoke) { } void IntrinsicCodeGeneratorMIPS64::VisitMathMinFloatFloat(HInvoke* invoke) { - GenMinMaxFP(invoke->GetLocations(), /* is_min */ true, /* is_double */ false, GetAssembler()); + GenMinMaxFP(invoke->GetLocations(), /* is_min */ true, Primitive::kPrimFloat, GetAssembler()); } // double java.lang.Math.max(double, double) @@ -634,7 +680,7 @@ void IntrinsicLocationsBuilderMIPS64::VisitMathMaxDoubleDouble(HInvoke* invoke) } void IntrinsicCodeGeneratorMIPS64::VisitMathMaxDoubleDouble(HInvoke* invoke) { - GenMinMaxFP(invoke->GetLocations(), /* is_min */ false, /* is_double */ true, GetAssembler()); + GenMinMaxFP(invoke->GetLocations(), /* is_min */ false, Primitive::kPrimDouble, GetAssembler()); } // float java.lang.Math.max(float, float) @@ -643,7 +689,7 @@ void IntrinsicLocationsBuilderMIPS64::VisitMathMaxFloatFloat(HInvoke* invoke) { } void IntrinsicCodeGeneratorMIPS64::VisitMathMaxFloatFloat(HInvoke* invoke) { - GenMinMaxFP(invoke->GetLocations(), /* is_min */ false, /* is_double */ false, GetAssembler()); + GenMinMaxFP(invoke->GetLocations(), /* is_min */ false, Primitive::kPrimFloat, GetAssembler()); } static void GenMinMax(LocationSummary* locations, @@ -653,49 +699,55 @@ static void GenMinMax(LocationSummary* locations, GpuRegister rhs = locations->InAt(1).AsRegister(); GpuRegister out = locations->Out().AsRegister(); - // Some architectures, such as ARM and MIPS (prior to r6), have a - // conditional move instruction which only changes the target - // (output) register if the condition is true (MIPS prior to r6 had - // MOVF, MOVT, and MOVZ). The SELEQZ and SELNEZ instructions always - // change the target (output) register. If the condition is true the - // output register gets the contents of the "rs" register; otherwise, - // the output register is set to zero. One consequence of this is - // that to implement something like "rd = c==0 ? rs : rt" MIPS64r6 - // needs to use a pair of SELEQZ/SELNEZ instructions. After - // executing this pair of instructions one of the output registers - // from the pair will necessarily contain zero. Then the code ORs the - // output registers from the SELEQZ/SELNEZ instructions to get the - // final result. - // - // The initial test to see if the output register is same as the - // first input register is needed to make sure that value in the - // first input register isn't clobbered before we've finished - // computing the output value. The logic in the corresponding else - // clause performs the same task but makes sure the second input - // register isn't clobbered in the event that it's the same register - // as the output register; the else clause also handles the case - // where the output register is distinct from both the first, and the - // second input registers. - if (out == lhs) { - __ Slt(AT, rhs, lhs); - if (is_min) { - __ Seleqz(out, lhs, AT); - __ Selnez(AT, rhs, AT); - } else { - __ Selnez(out, lhs, AT); - __ Seleqz(AT, rhs, AT); + if (lhs == rhs) { + if (out != lhs) { + __ Move(out, lhs); } } else { - __ Slt(AT, lhs, rhs); - if (is_min) { - __ Seleqz(out, rhs, AT); - __ Selnez(AT, lhs, AT); + // Some architectures, such as ARM and MIPS (prior to r6), have a + // conditional move instruction which only changes the target + // (output) register if the condition is true (MIPS prior to r6 had + // MOVF, MOVT, and MOVZ). The SELEQZ and SELNEZ instructions always + // change the target (output) register. If the condition is true the + // output register gets the contents of the "rs" register; otherwise, + // the output register is set to zero. One consequence of this is + // that to implement something like "rd = c==0 ? rs : rt" MIPS64r6 + // needs to use a pair of SELEQZ/SELNEZ instructions. After + // executing this pair of instructions one of the output registers + // from the pair will necessarily contain zero. Then the code ORs the + // output registers from the SELEQZ/SELNEZ instructions to get the + // final result. + // + // The initial test to see if the output register is same as the + // first input register is needed to make sure that value in the + // first input register isn't clobbered before we've finished + // computing the output value. The logic in the corresponding else + // clause performs the same task but makes sure the second input + // register isn't clobbered in the event that it's the same register + // as the output register; the else clause also handles the case + // where the output register is distinct from both the first, and the + // second input registers. + if (out == lhs) { + __ Slt(AT, rhs, lhs); + if (is_min) { + __ Seleqz(out, lhs, AT); + __ Selnez(AT, rhs, AT); + } else { + __ Selnez(out, lhs, AT); + __ Seleqz(AT, rhs, AT); + } } else { - __ Selnez(out, rhs, AT); - __ Seleqz(AT, lhs, AT); + __ Slt(AT, lhs, rhs); + if (is_min) { + __ Seleqz(out, rhs, AT); + __ Selnez(AT, lhs, AT); + } else { + __ Selnez(out, rhs, AT); + __ Seleqz(AT, lhs, AT); + } } + __ Or(out, out, AT); } - __ Or(out, out, AT); } static void CreateIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) { diff --git a/compiler/utils/mips/assembler_mips.cc b/compiler/utils/mips/assembler_mips.cc index 6fd65ee9a4..7c41813457 100644 --- a/compiler/utils/mips/assembler_mips.cc +++ b/compiler/utils/mips/assembler_mips.cc @@ -537,12 +537,20 @@ void MipsAssembler::Bgtz(Register rt, uint16_t imm16) { EmitI(0x7, rt, static_cast(0), imm16); } +void MipsAssembler::Bc1f(uint16_t imm16) { + Bc1f(0, imm16); +} + void MipsAssembler::Bc1f(int cc, uint16_t imm16) { CHECK(!IsR6()); CHECK(IsUint<3>(cc)) << cc; EmitI(0x11, static_cast(0x8), static_cast(cc << 2), imm16); } +void MipsAssembler::Bc1t(uint16_t imm16) { + Bc1t(0, imm16); +} + void MipsAssembler::Bc1t(int cc, uint16_t imm16) { CHECK(!IsR6()); CHECK(IsUint<3>(cc)) << cc; @@ -843,6 +851,22 @@ void MipsAssembler::DivD(FRegister fd, FRegister fs, FRegister ft) { EmitFR(0x11, 0x11, ft, fs, fd, 0x3); } +void MipsAssembler::SqrtS(FRegister fd, FRegister fs) { + EmitFR(0x11, 0x10, static_cast(0), fs, fd, 0x4); +} + +void MipsAssembler::SqrtD(FRegister fd, FRegister fs) { + EmitFR(0x11, 0x11, static_cast(0), fs, fd, 0x4); +} + +void MipsAssembler::AbsS(FRegister fd, FRegister fs) { + EmitFR(0x11, 0x10, static_cast(0), fs, fd, 0x5); +} + +void MipsAssembler::AbsD(FRegister fd, FRegister fs) { + EmitFR(0x11, 0x11, static_cast(0), fs, fd, 0x5); +} + void MipsAssembler::MovS(FRegister fd, FRegister fs) { EmitFR(0x11, 0x10, static_cast(0), fs, fd, 0x6); } @@ -859,84 +883,140 @@ void MipsAssembler::NegD(FRegister fd, FRegister fs) { EmitFR(0x11, 0x11, static_cast(0), fs, fd, 0x7); } +void MipsAssembler::CunS(FRegister fs, FRegister ft) { + CunS(0, fs, ft); +} + void MipsAssembler::CunS(int cc, FRegister fs, FRegister ft) { CHECK(!IsR6()); CHECK(IsUint<3>(cc)) << cc; EmitFR(0x11, 0x10, ft, fs, static_cast(cc << 2), 0x31); } +void MipsAssembler::CeqS(FRegister fs, FRegister ft) { + CeqS(0, fs, ft); +} + void MipsAssembler::CeqS(int cc, FRegister fs, FRegister ft) { CHECK(!IsR6()); CHECK(IsUint<3>(cc)) << cc; EmitFR(0x11, 0x10, ft, fs, static_cast(cc << 2), 0x32); } +void MipsAssembler::CueqS(FRegister fs, FRegister ft) { + CueqS(0, fs, ft); +} + void MipsAssembler::CueqS(int cc, FRegister fs, FRegister ft) { CHECK(!IsR6()); CHECK(IsUint<3>(cc)) << cc; EmitFR(0x11, 0x10, ft, fs, static_cast(cc << 2), 0x33); } +void MipsAssembler::ColtS(FRegister fs, FRegister ft) { + ColtS(0, fs, ft); +} + void MipsAssembler::ColtS(int cc, FRegister fs, FRegister ft) { CHECK(!IsR6()); CHECK(IsUint<3>(cc)) << cc; EmitFR(0x11, 0x10, ft, fs, static_cast(cc << 2), 0x34); } +void MipsAssembler::CultS(FRegister fs, FRegister ft) { + CultS(0, fs, ft); +} + void MipsAssembler::CultS(int cc, FRegister fs, FRegister ft) { CHECK(!IsR6()); CHECK(IsUint<3>(cc)) << cc; EmitFR(0x11, 0x10, ft, fs, static_cast(cc << 2), 0x35); } +void MipsAssembler::ColeS(FRegister fs, FRegister ft) { + ColeS(0, fs, ft); +} + void MipsAssembler::ColeS(int cc, FRegister fs, FRegister ft) { CHECK(!IsR6()); CHECK(IsUint<3>(cc)) << cc; EmitFR(0x11, 0x10, ft, fs, static_cast(cc << 2), 0x36); } +void MipsAssembler::CuleS(FRegister fs, FRegister ft) { + CuleS(0, fs, ft); +} + void MipsAssembler::CuleS(int cc, FRegister fs, FRegister ft) { CHECK(!IsR6()); CHECK(IsUint<3>(cc)) << cc; EmitFR(0x11, 0x10, ft, fs, static_cast(cc << 2), 0x37); } +void MipsAssembler::CunD(FRegister fs, FRegister ft) { + CunD(0, fs, ft); +} + void MipsAssembler::CunD(int cc, FRegister fs, FRegister ft) { CHECK(!IsR6()); CHECK(IsUint<3>(cc)) << cc; EmitFR(0x11, 0x11, ft, fs, static_cast(cc << 2), 0x31); } +void MipsAssembler::CeqD(FRegister fs, FRegister ft) { + CeqD(0, fs, ft); +} + void MipsAssembler::CeqD(int cc, FRegister fs, FRegister ft) { CHECK(!IsR6()); CHECK(IsUint<3>(cc)) << cc; EmitFR(0x11, 0x11, ft, fs, static_cast(cc << 2), 0x32); } +void MipsAssembler::CueqD(FRegister fs, FRegister ft) { + CueqD(0, fs, ft); +} + void MipsAssembler::CueqD(int cc, FRegister fs, FRegister ft) { CHECK(!IsR6()); CHECK(IsUint<3>(cc)) << cc; EmitFR(0x11, 0x11, ft, fs, static_cast(cc << 2), 0x33); } +void MipsAssembler::ColtD(FRegister fs, FRegister ft) { + ColtD(0, fs, ft); +} + void MipsAssembler::ColtD(int cc, FRegister fs, FRegister ft) { CHECK(!IsR6()); CHECK(IsUint<3>(cc)) << cc; EmitFR(0x11, 0x11, ft, fs, static_cast(cc << 2), 0x34); } +void MipsAssembler::CultD(FRegister fs, FRegister ft) { + CultD(0, fs, ft); +} + void MipsAssembler::CultD(int cc, FRegister fs, FRegister ft) { CHECK(!IsR6()); CHECK(IsUint<3>(cc)) << cc; EmitFR(0x11, 0x11, ft, fs, static_cast(cc << 2), 0x35); } +void MipsAssembler::ColeD(FRegister fs, FRegister ft) { + ColeD(0, fs, ft); +} + void MipsAssembler::ColeD(int cc, FRegister fs, FRegister ft) { CHECK(!IsR6()); CHECK(IsUint<3>(cc)) << cc; EmitFR(0x11, 0x11, ft, fs, static_cast(cc << 2), 0x36); } +void MipsAssembler::CuleD(FRegister fs, FRegister ft) { + CuleD(0, fs, ft); +} + void MipsAssembler::CuleD(int cc, FRegister fs, FRegister ft) { CHECK(!IsR6()); CHECK(IsUint<3>(cc)) << cc; @@ -1055,6 +1135,70 @@ void MipsAssembler::Movt(Register rd, Register rs, int cc) { EmitR(0, rs, static_cast((cc << 2) | 1), rd, 0, 0x01); } +void MipsAssembler::MovfS(FRegister fd, FRegister fs, int cc) { + CHECK(!IsR6()); + CHECK(IsUint<3>(cc)) << cc; + EmitFR(0x11, 0x10, static_cast(cc << 2), fs, fd, 0x11); +} + +void MipsAssembler::MovfD(FRegister fd, FRegister fs, int cc) { + CHECK(!IsR6()); + CHECK(IsUint<3>(cc)) << cc; + EmitFR(0x11, 0x11, static_cast(cc << 2), fs, fd, 0x11); +} + +void MipsAssembler::MovtS(FRegister fd, FRegister fs, int cc) { + CHECK(!IsR6()); + CHECK(IsUint<3>(cc)) << cc; + EmitFR(0x11, 0x10, static_cast((cc << 2) | 1), fs, fd, 0x11); +} + +void MipsAssembler::MovtD(FRegister fd, FRegister fs, int cc) { + CHECK(!IsR6()); + CHECK(IsUint<3>(cc)) << cc; + EmitFR(0x11, 0x11, static_cast((cc << 2) | 1), fs, fd, 0x11); +} + +void MipsAssembler::SelS(FRegister fd, FRegister fs, FRegister ft) { + CHECK(IsR6()); + EmitFR(0x11, 0x10, ft, fs, fd, 0x10); +} + +void MipsAssembler::SelD(FRegister fd, FRegister fs, FRegister ft) { + CHECK(IsR6()); + EmitFR(0x11, 0x11, ft, fs, fd, 0x10); +} + +void MipsAssembler::ClassS(FRegister fd, FRegister fs) { + CHECK(IsR6()); + EmitFR(0x11, 0x10, static_cast(0), fs, fd, 0x1b); +} + +void MipsAssembler::ClassD(FRegister fd, FRegister fs) { + CHECK(IsR6()); + EmitFR(0x11, 0x11, static_cast(0), fs, fd, 0x1b); +} + +void MipsAssembler::MinS(FRegister fd, FRegister fs, FRegister ft) { + CHECK(IsR6()); + EmitFR(0x11, 0x10, ft, fs, fd, 0x1c); +} + +void MipsAssembler::MinD(FRegister fd, FRegister fs, FRegister ft) { + CHECK(IsR6()); + EmitFR(0x11, 0x11, ft, fs, fd, 0x1c); +} + +void MipsAssembler::MaxS(FRegister fd, FRegister fs, FRegister ft) { + CHECK(IsR6()); + EmitFR(0x11, 0x10, ft, fs, fd, 0x1e); +} + +void MipsAssembler::MaxD(FRegister fd, FRegister fs, FRegister ft) { + CHECK(IsR6()); + EmitFR(0x11, 0x11, ft, fs, fd, 0x1e); +} + void MipsAssembler::TruncLS(FRegister fd, FRegister fs) { EmitFR(0x11, 0x10, static_cast(0), fs, fd, 0x09); } @@ -1095,6 +1239,14 @@ void MipsAssembler::Cvtdl(FRegister fd, FRegister fs) { EmitFR(0x11, 0x15, static_cast(0), fs, fd, 0x21); } +void MipsAssembler::FloorWS(FRegister fd, FRegister fs) { + EmitFR(0x11, 0x10, static_cast(0), fs, fd, 0xf); +} + +void MipsAssembler::FloorWD(FRegister fd, FRegister fs) { + EmitFR(0x11, 0x11, static_cast(0), fs, fd, 0xf); +} + void MipsAssembler::Mfc1(Register rt, FRegister fs) { EmitFR(0x11, 0x00, static_cast(rt), fs, static_cast(0), 0x0); } @@ -2062,11 +2214,19 @@ void MipsAssembler::Bgeu(Register rs, Register rt, MipsLabel* label) { } } +void MipsAssembler::Bc1f(MipsLabel* label) { + Bc1f(0, label); +} + void MipsAssembler::Bc1f(int cc, MipsLabel* label) { CHECK(IsUint<3>(cc)) << cc; Bcond(label, kCondF, static_cast(cc), ZERO); } +void MipsAssembler::Bc1t(MipsLabel* label) { + Bc1t(0, label); +} + void MipsAssembler::Bc1t(int cc, MipsLabel* label) { CHECK(IsUint<3>(cc)) << cc; Bcond(label, kCondT, static_cast(cc), ZERO); diff --git a/compiler/utils/mips/assembler_mips.h b/compiler/utils/mips/assembler_mips.h index 2262af49b3..a7179fd1dc 100644 --- a/compiler/utils/mips/assembler_mips.h +++ b/compiler/utils/mips/assembler_mips.h @@ -51,6 +51,20 @@ enum StoreOperandType { kStoreDoubleword }; +// Used to test the values returned by ClassS/ClassD. +enum FPClassMaskType { + kSignalingNaN = 0x001, + kQuietNaN = 0x002, + kNegativeInfinity = 0x004, + kNegativeNormal = 0x008, + kNegativeSubnormal = 0x010, + kNegativeZero = 0x020, + kPositiveInfinity = 0x040, + kPositiveNormal = 0x080, + kPositiveSubnormal = 0x100, + kPositiveZero = 0x200, +}; + class MipsLabel : public Label { public: MipsLabel() : prev_branch_id_plus_one_(0) {} @@ -191,7 +205,9 @@ class MipsAssembler FINAL : public Assembler { void Bgez(Register rt, uint16_t imm16); void Blez(Register rt, uint16_t imm16); void Bgtz(Register rt, uint16_t imm16); + void Bc1f(uint16_t imm16); // R2 void Bc1f(int cc, uint16_t imm16); // R2 + void Bc1t(uint16_t imm16); // R2 void Bc1t(int cc, uint16_t imm16); // R2 void J(uint32_t addr26); void Jal(uint32_t addr26); @@ -227,24 +243,42 @@ class MipsAssembler FINAL : public Assembler { void SubD(FRegister fd, FRegister fs, FRegister ft); void MulD(FRegister fd, FRegister fs, FRegister ft); void DivD(FRegister fd, FRegister fs, FRegister ft); + void SqrtS(FRegister fd, FRegister fs); + void SqrtD(FRegister fd, FRegister fs); + void AbsS(FRegister fd, FRegister fs); + void AbsD(FRegister fd, FRegister fs); void MovS(FRegister fd, FRegister fs); void MovD(FRegister fd, FRegister fs); void NegS(FRegister fd, FRegister fs); void NegD(FRegister fd, FRegister fs); + void CunS(FRegister fs, FRegister ft); // R2 void CunS(int cc, FRegister fs, FRegister ft); // R2 + void CeqS(FRegister fs, FRegister ft); // R2 void CeqS(int cc, FRegister fs, FRegister ft); // R2 + void CueqS(FRegister fs, FRegister ft); // R2 void CueqS(int cc, FRegister fs, FRegister ft); // R2 + void ColtS(FRegister fs, FRegister ft); // R2 void ColtS(int cc, FRegister fs, FRegister ft); // R2 + void CultS(FRegister fs, FRegister ft); // R2 void CultS(int cc, FRegister fs, FRegister ft); // R2 + void ColeS(FRegister fs, FRegister ft); // R2 void ColeS(int cc, FRegister fs, FRegister ft); // R2 + void CuleS(FRegister fs, FRegister ft); // R2 void CuleS(int cc, FRegister fs, FRegister ft); // R2 + void CunD(FRegister fs, FRegister ft); // R2 void CunD(int cc, FRegister fs, FRegister ft); // R2 + void CeqD(FRegister fs, FRegister ft); // R2 void CeqD(int cc, FRegister fs, FRegister ft); // R2 + void CueqD(FRegister fs, FRegister ft); // R2 void CueqD(int cc, FRegister fs, FRegister ft); // R2 + void ColtD(FRegister fs, FRegister ft); // R2 void ColtD(int cc, FRegister fs, FRegister ft); // R2 + void CultD(FRegister fs, FRegister ft); // R2 void CultD(int cc, FRegister fs, FRegister ft); // R2 + void ColeD(FRegister fs, FRegister ft); // R2 void ColeD(int cc, FRegister fs, FRegister ft); // R2 + void CuleD(FRegister fs, FRegister ft); // R2 void CuleD(int cc, FRegister fs, FRegister ft); // R2 void CmpUnS(FRegister fd, FRegister fs, FRegister ft); // R6 void CmpEqS(FRegister fd, FRegister fs, FRegister ft); // R6 @@ -266,8 +300,20 @@ class MipsAssembler FINAL : public Assembler { void CmpOrD(FRegister fd, FRegister fs, FRegister ft); // R6 void CmpUneD(FRegister fd, FRegister fs, FRegister ft); // R6 void CmpNeD(FRegister fd, FRegister fs, FRegister ft); // R6 - void Movf(Register rd, Register rs, int cc); // R2 - void Movt(Register rd, Register rs, int cc); // R2 + void Movf(Register rd, Register rs, int cc = 0); // R2 + void Movt(Register rd, Register rs, int cc = 0); // R2 + void MovfS(FRegister fd, FRegister fs, int cc = 0); // R2 + void MovfD(FRegister fd, FRegister fs, int cc = 0); // R2 + void MovtS(FRegister fd, FRegister fs, int cc = 0); // R2 + void MovtD(FRegister fd, FRegister fs, int cc = 0); // R2 + void SelS(FRegister fd, FRegister fs, FRegister ft); // R6 + void SelD(FRegister fd, FRegister fs, FRegister ft); // R6 + void ClassS(FRegister fd, FRegister fs); // R6 + void ClassD(FRegister fd, FRegister fs); // R6 + void MinS(FRegister fd, FRegister fs, FRegister ft); // R6 + void MinD(FRegister fd, FRegister fs, FRegister ft); // R6 + void MaxS(FRegister fd, FRegister fs, FRegister ft); // R6 + void MaxD(FRegister fd, FRegister fs, FRegister ft); // R6 void TruncLS(FRegister fd, FRegister fs); // R2+, FR=1 void TruncLD(FRegister fd, FRegister fs); // R2+, FR=1 @@ -279,6 +325,8 @@ class MipsAssembler FINAL : public Assembler { void Cvtds(FRegister fd, FRegister fs); void Cvtsl(FRegister fd, FRegister fs); // R2+, FR=1 void Cvtdl(FRegister fd, FRegister fs); // R2+, FR=1 + void FloorWS(FRegister fd, FRegister fs); + void FloorWD(FRegister fd, FRegister fs); void Mfc1(Register rt, FRegister fs); void Mtc1(Register rt, FRegister fs); @@ -322,7 +370,9 @@ class MipsAssembler FINAL : public Assembler { void Bge(Register rs, Register rt, MipsLabel* label); void Bltu(Register rs, Register rt, MipsLabel* label); void Bgeu(Register rs, Register rt, MipsLabel* label); + void Bc1f(MipsLabel* label); // R2 void Bc1f(int cc, MipsLabel* label); // R2 + void Bc1t(MipsLabel* label); // R2 void Bc1t(int cc, MipsLabel* label); // R2 void Bc1eqz(FRegister ft, MipsLabel* label); // R6 void Bc1nez(FRegister ft, MipsLabel* label); // R6 diff --git a/runtime/arch/mips/registers_mips.h b/runtime/arch/mips/registers_mips.h index 1096af0131..ae01bd5d18 100644 --- a/runtime/arch/mips/registers_mips.h +++ b/runtime/arch/mips/registers_mips.h @@ -100,6 +100,7 @@ enum FRegister { F29 = 29, F30 = 30, F31 = 31, + FTMP = F8, // scratch register kNumberOfFRegisters = 32, kNoFRegister = -1, }; diff --git a/runtime/arch/mips64/registers_mips64.h b/runtime/arch/mips64/registers_mips64.h index b027c955bf..81fae72b44 100644 --- a/runtime/arch/mips64/registers_mips64.h +++ b/runtime/arch/mips64/registers_mips64.h @@ -101,6 +101,7 @@ enum FpuRegister { F29 = 29, F30 = 30, F31 = 31, + FTMP = F8, // scratch register kNumberOfFpuRegisters = 32, kNoFpuRegister = -1, }; diff --git a/test/082-inline-execute/src/Main.java b/test/082-inline-execute/src/Main.java index e5c9dba63f..5b3fa14076 100644 --- a/test/082-inline-execute/src/Main.java +++ b/test/082-inline-execute/src/Main.java @@ -826,6 +826,8 @@ public class Main { Assert.assertEquals(Math.round(-2.5f), -2); Assert.assertEquals(Math.round(-2.9f), -3); Assert.assertEquals(Math.round(-3.0f), -3); + // 0.4999999701976776123046875 + Assert.assertEquals(Math.round(Float.intBitsToFloat(0x3EFFFFFF)), (int)+0.0f); Assert.assertEquals(Math.round(16777215.0f), 16777215); // 2^24 - 1 Assert.assertEquals(Math.round(Float.NaN), (int)+0.0f); Assert.assertEquals(Math.round(Integer.MAX_VALUE + 1.0f), Integer.MAX_VALUE); @@ -1058,6 +1060,8 @@ public class Main { Assert.assertEquals(StrictMath.round(-2.5f), -2); Assert.assertEquals(StrictMath.round(-2.9f), -3); Assert.assertEquals(StrictMath.round(-3.0f), -3); + // 0.4999999701976776123046875 + Assert.assertEquals(StrictMath.round(Float.intBitsToFloat(0x3EFFFFFF)), (int)+0.0f); Assert.assertEquals(StrictMath.round(Float.NaN), (int)+0.0f); Assert.assertEquals(StrictMath.round(Integer.MAX_VALUE + 1.0f), Integer.MAX_VALUE); Assert.assertEquals(StrictMath.round(Integer.MIN_VALUE - 1.0f), Integer.MIN_VALUE); -- GitLab From 2e2db786b8fbaa4dceb37603a4296b0b2aea4e9e Mon Sep 17 00:00:00 2001 From: Calin Juravle Date: Tue, 23 Feb 2016 12:00:03 +0000 Subject: [PATCH 004/204] Revert "Revert "Add profman tool: responsible to process profiles"" This reverts commit 3da74687e42de7d33a8e75df9bd64374e650f75e. Change-Id: Id005096bd8063c6c602744d4476d5eb7e0d34e90 --- Android.mk | 1 + build/Android.gtest.mk | 8 +- compiler/Android.mk | 3 +- compiler/profile_assistant.h | 72 ------ dex2oat/dex2oat.cc | 112 ++++------ profman/Android.mk | 45 ++++ {compiler => profman}/profile_assistant.cc | 120 +++++----- profman/profile_assistant.h | 72 ++++++ .../profile_assistant_test.cc | 204 ++++++++--------- profman/profman.cc | 208 ++++++++++++++++++ runtime/base/scoped_flock.cc | 10 +- runtime/base/unix_file/fd_file.cc | 31 ++- runtime/base/unix_file/fd_file.h | 4 + runtime/jit/offline_profiling_info.cc | 10 +- runtime/jit/offline_profiling_info.h | 7 +- runtime/utils.cc | 27 ++- runtime/utils.h | 3 +- 17 files changed, 584 insertions(+), 353 deletions(-) delete mode 100644 compiler/profile_assistant.h create mode 100644 profman/Android.mk rename {compiler => profman}/profile_assistant.cc (51%) create mode 100644 profman/profile_assistant.h rename {compiler => profman}/profile_assistant_test.cc (53%) create mode 100644 profman/profman.cc diff --git a/Android.mk b/Android.mk index 2e05d33209..e762814385 100644 --- a/Android.mk +++ b/Android.mk @@ -86,6 +86,7 @@ include $(art_path)/disassembler/Android.mk include $(art_path)/oatdump/Android.mk include $(art_path)/imgdiag/Android.mk include $(art_path)/patchoat/Android.mk +include $(art_path)/profman/Android.mk include $(art_path)/dalvikvm/Android.mk include $(art_path)/tools/Android.mk include $(art_path)/tools/ahat/Android.mk diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index 704d69a37a..22c0e8d7d0 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -144,6 +144,12 @@ ART_GTEST_oatdump_test_TARGET_DEPS := \ $(TARGET_CORE_IMAGE_default_no-pic_32) \ oatdump +# Profile assistant tests requires profman utility. +ART_GTEST_profile_assistant_test_HOST_DEPS := \ + $(HOST_OUT_EXECUTABLES)/profmand +ART_GTEST_profile_assistant_test_TARGET_DEPS := \ + profman + # The path for which all the source files are relative, not actually the current directory. LOCAL_PATH := art @@ -153,6 +159,7 @@ RUNTIME_GTEST_COMMON_SRC_FILES := \ dexlist/dexlist_test.cc \ imgdiag/imgdiag_test.cc \ oatdump/oatdump_test.cc \ + profman/profile_assistant_test.cc \ runtime/arch/arch_test.cc \ runtime/arch/instruction_set_test.cc \ runtime/arch/instruction_set_features_test.cc \ @@ -270,7 +277,6 @@ COMPILER_GTEST_COMMON_SRC_FILES := \ compiler/optimizing/ssa_test.cc \ compiler/optimizing/stack_map_test.cc \ compiler/optimizing/suspend_check_test.cc \ - compiler/profile_assistant_test.cc \ compiler/utils/arena_allocator_test.cc \ compiler/utils/dedupe_set_test.cc \ compiler/utils/swap_space_test.cc \ diff --git a/compiler/Android.mk b/compiler/Android.mk index b16494248f..3dfb4b1fd4 100644 --- a/compiler/Android.mk +++ b/compiler/Android.mk @@ -108,8 +108,7 @@ LIBART_COMPILER_SRC_FILES := \ elf_writer.cc \ elf_writer_quick.cc \ image_writer.cc \ - oat_writer.cc \ - profile_assistant.cc + oat_writer.cc LIBART_COMPILER_SRC_FILES_arm := \ dex/quick/arm/assemble_arm.cc \ diff --git a/compiler/profile_assistant.h b/compiler/profile_assistant.h deleted file mode 100644 index ad5e2163cf..0000000000 --- a/compiler/profile_assistant.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ART_COMPILER_PROFILE_ASSISTANT_H_ -#define ART_COMPILER_PROFILE_ASSISTANT_H_ - -#include -#include - -#include "base/scoped_flock.h" -#include "jit/offline_profiling_info.cc" - -namespace art { - -class ProfileAssistant { - public: - // Process the profile information present in the given files. Returns true - // if the analysis ended up successfully (i.e. no errors during reading, - // merging or writing of profile files). - // - // If the returned value is true and there is a significant difference between - // profile_files and reference_profile_files: - // - profile_compilation_info is set to a not null object that - // can be used to drive compilation. It will be the merge of all the data - // found in profile_files and reference_profile_files. - // - the data from profile_files[i] is merged into - // reference_profile_files[i] and the corresponding backing file is - // updated. - // - // If the returned value is false or the difference is insignificant, - // profile_compilation_info will be set to null. - // - // Additional notes: - // - as mentioned above, this function may update the content of the files - // passed with the reference_profile_files. - // - if reference_profile_files is not empty it must be the same size as - // profile_files. - static bool ProcessProfiles( - const std::vector& profile_files, - const std::vector& reference_profile_files, - /*out*/ ProfileCompilationInfo** profile_compilation_info); - - static bool ProcessProfiles( - const std::vector& profile_files_fd_, - const std::vector& reference_profile_files_fd_, - /*out*/ ProfileCompilationInfo** profile_compilation_info); - - private: - static bool ProcessProfilesInternal( - const std::vector& profile_files, - const std::vector& reference_profile_files, - /*out*/ ProfileCompilationInfo** profile_compilation_info); - - DISALLOW_COPY_AND_ASSIGN(ProfileAssistant); -}; - -} // namespace art - -#endif // ART_COMPILER_PROFILE_ASSISTANT_H_ diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 541fb5a423..f7efdc5bac 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -40,6 +40,7 @@ #include "art_method-inl.h" #include "base/dumpable.h" #include "base/macros.h" +#include "base/scoped_flock.h" #include "base/stl_util.h" #include "base/stringpiece.h" #include "base/time_utils.h" @@ -71,7 +72,6 @@ #include "mirror/object_array-inl.h" #include "oat_writer.h" #include "os.h" -#include "profile_assistant.h" #include "runtime.h" #include "runtime_options.h" #include "ScopedLocalRef.h" @@ -339,23 +339,10 @@ NO_RETURN static void Usage(const char* fmt, ...) { UsageError(" Example: --runtime-arg -Xms256m"); UsageError(""); UsageError(" --profile-file=: specify profiler output file to use for compilation."); - UsageError(" Can be specified multiple time, in which case the data from the different"); - UsageError(" profiles will be aggregated."); - UsageError(""); - UsageError(" --reference-profile-file=: specify a reference profile file to use when"); - UsageError(" compiling. The data in this file will be compared with the data in the"); - UsageError(" associated --profile-file and the compilation will proceed only if there is"); - UsageError(" a significant difference (--reference-profile-file is paired with"); - UsageError(" --profile-file in the natural order). If the compilation was attempted then"); - UsageError(" --profile-file will be merged into --reference-profile-file. Valid only when"); - UsageError(" specified together with --profile-file."); UsageError(""); UsageError(" --profile-file-fd=: same as --profile-file but accepts a file descriptor."); UsageError(" Cannot be used together with --profile-file."); UsageError(""); - UsageError(" --reference-profile-file-fd=: same as --reference-profile-file but"); - UsageError(" accepts a file descriptor. Cannot be used together with"); - UsageError(" --reference-profile-file."); UsageError(" --print-pass-names: print a list of pass names"); UsageError(""); UsageError(" --disable-passes=: disable one or more passes separated by comma."); @@ -517,14 +504,6 @@ static bool UseSwap(bool is_image, std::vector& dex_files) { return dex_files_size >= kMinDexFileCumulativeSizeForSwap; } -static void CloseAllFds(const std::vector& fds, const char* descriptor) { - for (size_t i = 0; i < fds.size(); i++) { - if (close(fds[i]) < 0) { - PLOG(WARNING) << "Failed to close descriptor for " << descriptor << " at index " << i; - } - } -} - class Dex2Oat FINAL { public: explicit Dex2Oat(TimingLogger* timings) : @@ -567,10 +546,12 @@ class Dex2Oat FINAL { dump_passes_(false), dump_timing_(false), dump_slow_timing_(kIsDebugBuild), - swap_fd_(-1), + swap_fd_(kInvalidFd), app_image_fd_(kInvalidFd), + profile_file_fd_(kInvalidFd), timings_(timings), - force_determinism_(false) {} + force_determinism_(false) + {} ~Dex2Oat() { // Log completion time before deleting the runtime_, because this accesses @@ -824,25 +805,8 @@ class Dex2Oat FINAL { } } - if (!profile_files_.empty() && !profile_files_fd_.empty()) { - Usage("Profile files should not be specified with both --profile-file-fd and --profile-file"); - } - if (!profile_files_.empty()) { - if (!reference_profile_files_.empty() && - (reference_profile_files_.size() != profile_files_.size())) { - Usage("If specified, --reference-profile-file should match the number of --profile-file."); - } - } else if (!reference_profile_files_.empty()) { - Usage("--reference-profile-file should only be supplied with --profile-file"); - } - if (!profile_files_fd_.empty()) { - if (!reference_profile_files_fd_.empty() && - (reference_profile_files_fd_.size() != profile_files_fd_.size())) { - Usage("If specified, --reference-profile-file-fd should match the number", - " of --profile-file-fd."); - } - } else if (!reference_profile_files_fd_.empty()) { - Usage("--reference-profile-file-fd should only be supplied with --profile-file-fd"); + if (!profile_file_.empty() && (profile_file_fd_ != kInvalidFd)) { + Usage("Profile file should not be specified with both --profile-file-fd and --profile-file"); } if (!parser_options->oat_symbols.empty()) { @@ -1153,16 +1117,9 @@ class Dex2Oat FINAL { } else if (option.starts_with("--compiler-backend=")) { ParseCompilerBackend(option, parser_options.get()); } else if (option.starts_with("--profile-file=")) { - profile_files_.push_back(option.substr(strlen("--profile-file=")).ToString()); - } else if (option.starts_with("--reference-profile-file=")) { - reference_profile_files_.push_back( - option.substr(strlen("--reference-profile-file=")).ToString()); + profile_file_ = option.substr(strlen("--profile-file=")).ToString(); } else if (option.starts_with("--profile-file-fd=")) { - ParseFdForCollection(option, "--profile-file-fd", &profile_files_fd_); - } else if (option.starts_with("--reference-profile-file-fd=")) { - ParseFdForCollection(option, "--reference_profile-file-fd", &reference_profile_files_fd_); - } else if (option == "--no-profile-file") { - // No profile + ParseUintOption(option, "--profile-file-fd", &profile_file_fd_, Usage); } else if (option == "--host") { is_host_ = true; } else if (option == "--runtime-arg") { @@ -1865,33 +1822,44 @@ class Dex2Oat FINAL { } bool UseProfileGuidedCompilation() const { - return !profile_files_.empty() || !profile_files_fd_.empty(); + return !profile_file_.empty() || (profile_file_fd_ != kInvalidFd); } - bool ProcessProfiles() { + bool LoadProfile() { DCHECK(UseProfileGuidedCompilation()); - ProfileCompilationInfo* info = nullptr; - bool result = false; - if (profile_files_.empty()) { - DCHECK(!profile_files_fd_.empty()); - result = ProfileAssistant::ProcessProfiles( - profile_files_fd_, reference_profile_files_fd_, &info); - CloseAllFds(profile_files_fd_, "profile_files_fd_"); - CloseAllFds(reference_profile_files_fd_, "reference_profile_files_fd_"); + + profile_compilation_info_.reset(new ProfileCompilationInfo()); + ScopedFlock flock; + bool success = false; + std::string error; + if (profile_file_fd_ != -1) { + // The file doesn't need to be flushed so don't check the usage. + // Pass a bogus path so that we can easily attribute any reported error. + File file(profile_file_fd_, "profile", /*check_usage*/ false, /*read_only_mode*/ true); + if (flock.Init(&file, &error)) { + success = profile_compilation_info_->Load(profile_file_fd_); + } } else { - result = ProfileAssistant::ProcessProfiles( - profile_files_, reference_profile_files_, &info); + if (flock.Init(profile_file_.c_str(), O_RDONLY, /* block */ true, &error)) { + success = profile_compilation_info_->Load(flock.GetFile()->Fd()); + } + } + if (!error.empty()) { + LOG(WARNING) << "Cannot lock profiles: " << error; } - profile_compilation_info_.reset(info); + if (!success) { + profile_compilation_info_.reset(nullptr); + } - return result; + return success; } bool ShouldCompileBasedOnProfiles() const { DCHECK(UseProfileGuidedCompilation()); - // If we are given profiles, compile only if we have new information. - return profile_compilation_info_ != nullptr; + // If we are given a profile, compile only if we have some data in it. + return (profile_compilation_info_ != nullptr) && + (profile_compilation_info_->GetNumberOfMethods() != 0); } private: @@ -2460,10 +2428,8 @@ class Dex2Oat FINAL { int swap_fd_; std::string app_image_file_name_; int app_image_fd_; - std::vector profile_files_; - std::vector reference_profile_files_; - std::vector profile_files_fd_; - std::vector reference_profile_files_fd_; + std::string profile_file_; + int profile_file_fd_; std::unique_ptr profile_compilation_info_; TimingLogger* timings_; std::unique_ptr compiler_phases_timings_; @@ -2592,7 +2558,7 @@ static int dex2oat(int argc, char** argv) { // Process profile information and assess if we need to do a profile guided compilation. // This operation involves I/O. if (dex2oat.UseProfileGuidedCompilation()) { - if (dex2oat.ProcessProfiles()) { + if (dex2oat.LoadProfile()) { if (!dex2oat.ShouldCompileBasedOnProfiles()) { LOG(INFO) << "Skipped compilation because of insignificant profile delta"; return EXIT_SUCCESS; diff --git a/profman/Android.mk b/profman/Android.mk new file mode 100644 index 0000000000..d38d107d21 --- /dev/null +++ b/profman/Android.mk @@ -0,0 +1,45 @@ +# +# Copyright (C) 2016 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +LOCAL_PATH := $(call my-dir) + +include art/build/Android.executable.mk + +PROFMAN_SRC_FILES := \ + profman.cc \ + profile_assistant.cc + +# TODO: Remove this when the framework (installd) supports pushing the +# right instruction-set parameter for the primary architecture. +ifneq ($(filter ro.zygote=zygote64,$(PRODUCT_DEFAULT_PROPERTY_OVERRIDES)),) + profman_arch := 64 +else + profman_arch := 32 +endif + +ifeq ($(ART_BUILD_TARGET_NDEBUG),true) + $(eval $(call build-art-executable,profman,$(PROFMAN_SRC_FILES),libcutils,art/profman,target,ndebug,$(profman_arch))) +endif +ifeq ($(ART_BUILD_TARGET_DEBUG),true) + $(eval $(call build-art-executable,profman,$(PROFMAN_SRC_FILES),libcutils,art/profman,target,debug,$(profman_arch))) +endif + +ifeq ($(ART_BUILD_HOST_NDEBUG),true) + $(eval $(call build-art-executable,profman,$(PROFMAN_SRC_FILES),libcutils,art/profman,host,ndebug)) +endif +ifeq ($(ART_BUILD_HOST_DEBUG),true) + $(eval $(call build-art-executable,profman,$(PROFMAN_SRC_FILES),libcutils,art/profman,host,debug)) +endif diff --git a/compiler/profile_assistant.cc b/profman/profile_assistant.cc similarity index 51% rename from compiler/profile_assistant.cc rename to profman/profile_assistant.cc index 85335efcc4..58e8a3a7d1 100644 --- a/compiler/profile_assistant.cc +++ b/profman/profile_assistant.cc @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 The Android Open Source Project + * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,13 +24,10 @@ namespace art { // Minimum number of new methods that profiles must contain to enable recompilation. static constexpr const uint32_t kMinNewMethodsForCompilation = 10; -bool ProfileAssistant::ProcessProfilesInternal( +ProfileAssistant::ProcessingResult ProfileAssistant::ProcessProfilesInternal( const std::vector& profile_files, - const std::vector& reference_profile_files, - /*out*/ ProfileCompilationInfo** profile_compilation_info) { + const ScopedFlock& reference_profile_file) { DCHECK(!profile_files.empty()); - DCHECK(!reference_profile_files.empty() || - (profile_files.size() == reference_profile_files.size())); std::vector new_info(profile_files.size()); bool should_compile = false; @@ -38,51 +35,53 @@ bool ProfileAssistant::ProcessProfilesInternal( for (size_t i = 0; i < new_info.size(); i++) { if (!new_info[i].Load(profile_files[i].GetFile()->Fd())) { LOG(WARNING) << "Could not load profile file at index " << i; - return false; + return kErrorBadProfiles; } // Do we have enough new profiled methods that will make the compilation worthwhile? should_compile |= (new_info[i].GetNumberOfMethods() > kMinNewMethodsForCompilation); } if (!should_compile) { - return true; + return kSkipCompilation; } - std::unique_ptr result(new ProfileCompilationInfo()); // Merge information. + ProfileCompilationInfo info; + if (!info.Load(reference_profile_file.GetFile()->Fd())) { + LOG(WARNING) << "Could not load reference profile file"; + return kErrorBadProfiles; + } + for (size_t i = 0; i < new_info.size(); i++) { - if (!reference_profile_files.empty()) { - if (!new_info[i].Load(reference_profile_files[i].GetFile()->Fd())) { - LOG(WARNING) << "Could not load reference profile file at index " << i; - return false; - } - } // Merge all data into a single object. - if (!result->Load(new_info[i])) { + if (!info.Load(new_info[i])) { LOG(WARNING) << "Could not merge profile data at index " << i; - return false; + return kErrorBadProfiles; } } - // We were successful in merging all profile information. Update the files. - for (size_t i = 0; i < new_info.size(); i++) { - if (!reference_profile_files.empty()) { - if (!reference_profile_files[i].GetFile()->ClearContent()) { - PLOG(WARNING) << "Could not clear reference profile file at index " << i; - return false; - } - if (!new_info[i].Save(reference_profile_files[i].GetFile()->Fd())) { - LOG(WARNING) << "Could not save reference profile file at index " << i; - return false; - } - if (!profile_files[i].GetFile()->ClearContent()) { - PLOG(WARNING) << "Could not clear profile file at index " << i; - return false; - } - } + // We were successful in merging all profile information. Update the reference profile. + if (!reference_profile_file.GetFile()->ClearContent()) { + PLOG(WARNING) << "Could not clear reference profile file"; + return kErrorIO; } + if (!info.Save(reference_profile_file.GetFile()->Fd())) { + LOG(WARNING) << "Could not save reference profile file"; + return kErrorIO; + } + + return kCompile; +} - *profile_compilation_info = result.release(); - return true; +static bool InitFlock(const std::string& filename, ScopedFlock& flock, std::string* error) { + return flock.Init(filename.c_str(), O_RDWR, /* block */ true, error); +} + +static bool InitFlock(int fd, ScopedFlock& flock, std::string* error) { + DCHECK_GE(fd, 0); + // We do not own the descriptor, so disable auto-close and don't check usage. + File file(fd, false); + file.DisableAutoClose(); + return flock.Init(&file, error); } class ScopedCollectionFlock { @@ -92,7 +91,7 @@ class ScopedCollectionFlock { // Will block until all the locks are acquired. bool Init(const std::vector& filenames, /* out */ std::string* error) { for (size_t i = 0; i < filenames.size(); i++) { - if (!flocks_[i].Init(filenames[i].c_str(), O_RDWR, /* block */ true, error)) { + if (!InitFlock(filenames[i], flocks_[i], error)) { *error += " (index=" + std::to_string(i) + ")"; return false; } @@ -101,12 +100,10 @@ class ScopedCollectionFlock { } // Will block until all the locks are acquired. - bool Init(const std::vector& fds, /* out */ std::string* error) { + bool Init(const std::vector& fds, /* out */ std::string* error) { for (size_t i = 0; i < fds.size(); i++) { - // We do not own the descriptor, so disable auto-close and don't check usage. - File file(fds[i], false); - file.DisableAutoClose(); - if (!flocks_[i].Init(&file, error)) { + DCHECK_GE(fds[i], 0); + if (!InitFlock(fds[i], flocks_[i], error)) { *error += " (index=" + std::to_string(i) + ")"; return false; } @@ -120,50 +117,43 @@ class ScopedCollectionFlock { std::vector flocks_; }; -bool ProfileAssistant::ProcessProfiles( - const std::vector& profile_files_fd, - const std::vector& reference_profile_files_fd, - /*out*/ ProfileCompilationInfo** profile_compilation_info) { - *profile_compilation_info = nullptr; - +ProfileAssistant::ProcessingResult ProfileAssistant::ProcessProfiles( + const std::vector& profile_files_fd, + int reference_profile_file_fd) { + DCHECK_GE(reference_profile_file_fd, 0); std::string error; ScopedCollectionFlock profile_files_flocks(profile_files_fd.size()); if (!profile_files_flocks.Init(profile_files_fd, &error)) { LOG(WARNING) << "Could not lock profile files: " << error; - return false; + return kErrorCannotLock; } - ScopedCollectionFlock reference_profile_files_flocks(reference_profile_files_fd.size()); - if (!reference_profile_files_flocks.Init(reference_profile_files_fd, &error)) { - LOG(WARNING) << "Could not lock reference profile files: " << error; - return false; + ScopedFlock reference_profile_file_flock; + if (!InitFlock(reference_profile_file_fd, reference_profile_file_flock, &error)) { + LOG(WARNING) << "Could not lock reference profiled files: " << error; + return kErrorCannotLock; } return ProcessProfilesInternal(profile_files_flocks.Get(), - reference_profile_files_flocks.Get(), - profile_compilation_info); + reference_profile_file_flock); } -bool ProfileAssistant::ProcessProfiles( +ProfileAssistant::ProcessingResult ProfileAssistant::ProcessProfiles( const std::vector& profile_files, - const std::vector& reference_profile_files, - /*out*/ ProfileCompilationInfo** profile_compilation_info) { - *profile_compilation_info = nullptr; - + const std::string& reference_profile_file) { std::string error; ScopedCollectionFlock profile_files_flocks(profile_files.size()); if (!profile_files_flocks.Init(profile_files, &error)) { LOG(WARNING) << "Could not lock profile files: " << error; - return false; + return kErrorCannotLock; } - ScopedCollectionFlock reference_profile_files_flocks(reference_profile_files.size()); - if (!reference_profile_files_flocks.Init(reference_profile_files, &error)) { + ScopedFlock reference_profile_file_flock; + if (!InitFlock(reference_profile_file, reference_profile_file_flock, &error)) { LOG(WARNING) << "Could not lock reference profile files: " << error; - return false; + return kErrorCannotLock; } return ProcessProfilesInternal(profile_files_flocks.Get(), - reference_profile_files_flocks.Get(), - profile_compilation_info); + reference_profile_file_flock); } } // namespace art diff --git a/profman/profile_assistant.h b/profman/profile_assistant.h new file mode 100644 index 0000000000..d3c75b817a --- /dev/null +++ b/profman/profile_assistant.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_PROFMAN_PROFILE_ASSISTANT_H_ +#define ART_PROFMAN_PROFILE_ASSISTANT_H_ + +#include +#include + +#include "base/scoped_flock.h" +#include "jit/offline_profiling_info.h" + +namespace art { + +class ProfileAssistant { + public: + // These also serve as return codes of profman and are processed by installd + // (frameworks/native/cmds/installd/commands.cpp) + enum ProcessingResult { + kCompile = 0, + kSkipCompilation = 1, + kErrorBadProfiles = 2, + kErrorIO = 3, + kErrorCannotLock = 4 + }; + + // Process the profile information present in the given files. Returns one of + // ProcessingResult values depending on profile information and whether or not + // the analysis ended up successfully (i.e. no errors during reading, + // merging or writing of profile files). + // + // When the returned value is kCompile there is a significant difference + // between profile_files and reference_profile_files. In this case + // reference_profile will be updated with the profiling info obtain after + // merging all profiles. + // + // When the returned value is kSkipCompilation, the difference between the + // merge of the current profiles and the reference one is insignificant. In + // this case no file will be updated. + // + static ProcessingResult ProcessProfiles( + const std::vector& profile_files, + const std::string& reference_profile_file); + + static ProcessingResult ProcessProfiles( + const std::vector& profile_files_fd_, + int reference_profile_file_fd); + + private: + static ProcessingResult ProcessProfilesInternal( + const std::vector& profile_files, + const ScopedFlock& reference_profile_file); + + DISALLOW_COPY_AND_ASSIGN(ProfileAssistant); +}; + +} // namespace art + +#endif // ART_PROFMAN_PROFILE_ASSISTANT_H_ diff --git a/compiler/profile_assistant_test.cc b/profman/profile_assistant_test.cc similarity index 53% rename from compiler/profile_assistant_test.cc rename to profman/profile_assistant_test.cc index 58b7513377..543be5d181 100644 --- a/compiler/profile_assistant_test.cc +++ b/profman/profile_assistant_test.cc @@ -18,8 +18,9 @@ #include "base/unix_file/fd_file.h" #include "common_runtime_test.h" -#include "compiler/profile_assistant.h" +#include "profile_assistant.h" #include "jit/offline_profiling_info.h" +#include "utils.h" namespace art { @@ -44,23 +45,51 @@ class ProfileAssistantTest : public CommonRuntimeTest { ASSERT_TRUE(profile.GetFile()->ResetOffset()); } - uint32_t GetFd(const ScratchFile& file) const { - return static_cast(file.GetFd()); + int GetFd(const ScratchFile& file) const { + return static_cast(file.GetFd()); + } + + void CheckProfileInfo(ScratchFile& file, const ProfileCompilationInfo& info) { + ProfileCompilationInfo file_info; + ASSERT_TRUE(file.GetFile()->ResetOffset()); + ASSERT_TRUE(file_info.Load(GetFd(file))); + ASSERT_TRUE(file_info.Equals(info)); + } + + // Runs test with given arguments. + int ProcessProfiles(const std::vector& profiles_fd, int reference_profile_fd) { + std::string file_path = GetTestAndroidRoot(); + if (IsHost()) { + file_path += "/bin/profman"; + } else { + file_path += "/xbin/profman"; + } + if (kIsDebugBuild) { + file_path += "d"; + } + + EXPECT_TRUE(OS::FileExists(file_path.c_str())) << file_path << " should be a valid file path"; + std::vector argv_str; + argv_str.push_back(file_path); + for (size_t k = 0; k < profiles_fd.size(); k++) { + argv_str.push_back("--profile-file-fd=" + std::to_string(profiles_fd[k])); + } + argv_str.push_back("--reference-profile-file-fd=" + std::to_string(reference_profile_fd)); + + std::string error; + return ExecAndReturnCode(argv_str, &error); } }; TEST_F(ProfileAssistantTest, AdviseCompilationEmptyReferences) { ScratchFile profile1; ScratchFile profile2; - ScratchFile reference_profile1; - ScratchFile reference_profile2; + ScratchFile reference_profile; - std::vector profile_fds({ + std::vector profile_fds({ GetFd(profile1), GetFd(profile2)}); - std::vector reference_profile_fds({ - GetFd(reference_profile1), - GetFd(reference_profile2)}); + int reference_profile_fd = GetFd(reference_profile); const uint16_t kNumberOfMethodsToEnableCompilation = 100; ProfileCompilationInfo info1; @@ -69,44 +98,32 @@ TEST_F(ProfileAssistantTest, AdviseCompilationEmptyReferences) { SetupProfile("p2", 2, kNumberOfMethodsToEnableCompilation, profile2, &info2); // We should advise compilation. - ProfileCompilationInfo* result; - ASSERT_TRUE(ProfileAssistant::ProcessProfiles(profile_fds, reference_profile_fds, &result)); - ASSERT_TRUE(result != nullptr); - + ASSERT_EQ(ProfileAssistant::kCompile, + ProcessProfiles(profile_fds, reference_profile_fd)); // The resulting compilation info must be equal to the merge of the inputs. + ProfileCompilationInfo result; + ASSERT_TRUE(reference_profile.GetFile()->ResetOffset()); + ASSERT_TRUE(result.Load(reference_profile_fd)); + ProfileCompilationInfo expected; ASSERT_TRUE(expected.Load(info1)); ASSERT_TRUE(expected.Load(info2)); - ASSERT_TRUE(expected.Equals(*result)); + ASSERT_TRUE(expected.Equals(result)); - // The information from profiles must be transfered to the reference profiles. - ProfileCompilationInfo file_info1; - ASSERT_TRUE(reference_profile1.GetFile()->ResetOffset()); - ASSERT_TRUE(file_info1.Load(GetFd(reference_profile1))); - ASSERT_TRUE(file_info1.Equals(info1)); - - ProfileCompilationInfo file_info2; - ASSERT_TRUE(reference_profile2.GetFile()->ResetOffset()); - ASSERT_TRUE(file_info2.Load(GetFd(reference_profile2))); - ASSERT_TRUE(file_info2.Equals(info2)); - - // Initial profiles must be cleared. - ASSERT_EQ(0, profile1.GetFile()->GetLength()); - ASSERT_EQ(0, profile2.GetFile()->GetLength()); + // The information from profiles must remain the same. + CheckProfileInfo(profile1, info1); + CheckProfileInfo(profile2, info2); } TEST_F(ProfileAssistantTest, AdviseCompilationNonEmptyReferences) { ScratchFile profile1; ScratchFile profile2; - ScratchFile reference_profile1; - ScratchFile reference_profile2; + ScratchFile reference_profile; - std::vector profile_fds({ + std::vector profile_fds({ GetFd(profile1), GetFd(profile2)}); - std::vector reference_profile_fds({ - GetFd(reference_profile1), - GetFd(reference_profile2)}); + int reference_profile_fd = GetFd(reference_profile); // The new profile info will contain the methods with indices 0-100. const uint16_t kNumberOfMethodsToEnableCompilation = 100; @@ -118,60 +135,39 @@ TEST_F(ProfileAssistantTest, AdviseCompilationNonEmptyReferences) { // The reference profile info will contain the methods with indices 50-150. const uint16_t kNumberOfMethodsAlreadyCompiled = 100; - ProfileCompilationInfo reference_info1; - SetupProfile("p1", 1, kNumberOfMethodsAlreadyCompiled, reference_profile1, - &reference_info1, kNumberOfMethodsToEnableCompilation / 2); - ProfileCompilationInfo reference_info2; - SetupProfile("p2", 2, kNumberOfMethodsAlreadyCompiled, reference_profile2, - &reference_info2, kNumberOfMethodsToEnableCompilation / 2); + ProfileCompilationInfo reference_info; + SetupProfile("p1", 1, kNumberOfMethodsAlreadyCompiled, reference_profile, + &reference_info, kNumberOfMethodsToEnableCompilation / 2); // We should advise compilation. - ProfileCompilationInfo* result; - ASSERT_TRUE(ProfileAssistant::ProcessProfiles(profile_fds, reference_profile_fds, &result)); - ASSERT_TRUE(result != nullptr); + ASSERT_EQ(ProfileAssistant::kCompile, + ProcessProfiles(profile_fds, reference_profile_fd)); // The resulting compilation info must be equal to the merge of the inputs + ProfileCompilationInfo result; + ASSERT_TRUE(reference_profile.GetFile()->ResetOffset()); + ASSERT_TRUE(result.Load(reference_profile_fd)); + ProfileCompilationInfo expected; ASSERT_TRUE(expected.Load(info1)); ASSERT_TRUE(expected.Load(info2)); - ASSERT_TRUE(expected.Load(reference_info1)); - ASSERT_TRUE(expected.Load(reference_info2)); - ASSERT_TRUE(expected.Equals(*result)); - - // The information from profiles must be transfered to the reference profiles. - ProfileCompilationInfo file_info1; - ProfileCompilationInfo merge1; - ASSERT_TRUE(merge1.Load(info1)); - ASSERT_TRUE(merge1.Load(reference_info1)); - ASSERT_TRUE(reference_profile1.GetFile()->ResetOffset()); - ASSERT_TRUE(file_info1.Load(GetFd(reference_profile1))); - ASSERT_TRUE(file_info1.Equals(merge1)); + ASSERT_TRUE(expected.Load(reference_info)); + ASSERT_TRUE(expected.Equals(result)); - ProfileCompilationInfo file_info2; - ProfileCompilationInfo merge2; - ASSERT_TRUE(merge2.Load(info2)); - ASSERT_TRUE(merge2.Load(reference_info2)); - ASSERT_TRUE(reference_profile2.GetFile()->ResetOffset()); - ASSERT_TRUE(file_info2.Load(GetFd(reference_profile2))); - ASSERT_TRUE(file_info2.Equals(merge2)); - - // Initial profiles must be cleared. - ASSERT_EQ(0, profile1.GetFile()->GetLength()); - ASSERT_EQ(0, profile2.GetFile()->GetLength()); + // The information from profiles must remain the same. + CheckProfileInfo(profile1, info1); + CheckProfileInfo(profile2, info2); } TEST_F(ProfileAssistantTest, DoNotAdviseCompilation) { ScratchFile profile1; ScratchFile profile2; - ScratchFile reference_profile1; - ScratchFile reference_profile2; + ScratchFile reference_profile; - std::vector profile_fds({ + std::vector profile_fds({ GetFd(profile1), GetFd(profile2)}); - std::vector reference_profile_fds({ - GetFd(reference_profile1), - GetFd(reference_profile2)}); + int reference_profile_fd = GetFd(reference_profile); const uint16_t kNumberOfMethodsToSkipCompilation = 1; ProfileCompilationInfo info1; @@ -180,9 +176,8 @@ TEST_F(ProfileAssistantTest, DoNotAdviseCompilation) { SetupProfile("p2", 2, kNumberOfMethodsToSkipCompilation, profile2, &info2); // We should not advise compilation. - ProfileCompilationInfo* result = nullptr; - ASSERT_TRUE(ProfileAssistant::ProcessProfiles(profile_fds, reference_profile_fds, &result)); - ASSERT_TRUE(result == nullptr); + ASSERT_EQ(ProfileAssistant::kSkipCompilation, + ProcessProfiles(profile_fds, reference_profile_fd)); // The information from profiles must remain the same. ProfileCompilationInfo file_info1; @@ -196,22 +191,22 @@ TEST_F(ProfileAssistantTest, DoNotAdviseCompilation) { ASSERT_TRUE(file_info2.Equals(info2)); // Reference profile files must remain empty. - ASSERT_EQ(0, reference_profile1.GetFile()->GetLength()); - ASSERT_EQ(0, reference_profile2.GetFile()->GetLength()); + ASSERT_EQ(0, reference_profile.GetFile()->GetLength()); + + // The information from profiles must remain the same. + CheckProfileInfo(profile1, info1); + CheckProfileInfo(profile2, info2); } TEST_F(ProfileAssistantTest, FailProcessingBecauseOfProfiles) { ScratchFile profile1; ScratchFile profile2; - ScratchFile reference_profile1; - ScratchFile reference_profile2; + ScratchFile reference_profile; - std::vector profile_fds({ + std::vector profile_fds({ GetFd(profile1), GetFd(profile2)}); - std::vector reference_profile_fds({ - GetFd(reference_profile1), - GetFd(reference_profile2)}); + int reference_profile_fd = GetFd(reference_profile); const uint16_t kNumberOfMethodsToEnableCompilation = 100; // Assign different hashes for the same dex file. This will make merging of information to fail. @@ -221,34 +216,24 @@ TEST_F(ProfileAssistantTest, FailProcessingBecauseOfProfiles) { SetupProfile("p1", 2, kNumberOfMethodsToEnableCompilation, profile2, &info2); // We should fail processing. - ProfileCompilationInfo* result = nullptr; - ASSERT_FALSE(ProfileAssistant::ProcessProfiles(profile_fds, reference_profile_fds, &result)); - ASSERT_TRUE(result == nullptr); + ASSERT_EQ(ProfileAssistant::kErrorBadProfiles, + ProcessProfiles(profile_fds, reference_profile_fd)); - // The information from profiles must still remain the same. - ProfileCompilationInfo file_info1; - ASSERT_TRUE(profile1.GetFile()->ResetOffset()); - ASSERT_TRUE(file_info1.Load(GetFd(profile1))); - ASSERT_TRUE(file_info1.Equals(info1)); - - ProfileCompilationInfo file_info2; - ASSERT_TRUE(profile2.GetFile()->ResetOffset()); - ASSERT_TRUE(file_info2.Load(GetFd(profile2))); - ASSERT_TRUE(file_info2.Equals(info2)); + // The information from profiles must remain the same. + CheckProfileInfo(profile1, info1); + CheckProfileInfo(profile2, info2); // Reference profile files must still remain empty. - ASSERT_EQ(0, reference_profile1.GetFile()->GetLength()); - ASSERT_EQ(0, reference_profile2.GetFile()->GetLength()); + ASSERT_EQ(0, reference_profile.GetFile()->GetLength()); } TEST_F(ProfileAssistantTest, FailProcessingBecauseOfReferenceProfiles) { ScratchFile profile1; ScratchFile reference_profile; - std::vector profile_fds({ + std::vector profile_fds({ GetFd(profile1)}); - std::vector reference_profile_fds({ - GetFd(reference_profile)}); + int reference_profile_fd = GetFd(reference_profile); const uint16_t kNumberOfMethodsToEnableCompilation = 100; // Assign different hashes for the same dex file. This will make merging of information to fail. @@ -258,22 +243,13 @@ TEST_F(ProfileAssistantTest, FailProcessingBecauseOfReferenceProfiles) { SetupProfile("p1", 2, kNumberOfMethodsToEnableCompilation, reference_profile, &reference_info); // We should not advise compilation. - ProfileCompilationInfo* result = nullptr; ASSERT_TRUE(profile1.GetFile()->ResetOffset()); ASSERT_TRUE(reference_profile.GetFile()->ResetOffset()); - ASSERT_FALSE(ProfileAssistant::ProcessProfiles(profile_fds, reference_profile_fds, &result)); - ASSERT_TRUE(result == nullptr); - - // The information from profiles must still remain the same. - ProfileCompilationInfo file_info1; - ASSERT_TRUE(profile1.GetFile()->ResetOffset()); - ASSERT_TRUE(file_info1.Load(GetFd(profile1))); - ASSERT_TRUE(file_info1.Equals(info1)); + ASSERT_EQ(ProfileAssistant::kErrorBadProfiles, + ProcessProfiles(profile_fds, reference_profile_fd)); - ProfileCompilationInfo file_info2; - ASSERT_TRUE(reference_profile.GetFile()->ResetOffset()); - ASSERT_TRUE(file_info2.Load(GetFd(reference_profile))); - ASSERT_TRUE(file_info2.Equals(reference_info)); + // The information from profiles must remain the same. + CheckProfileInfo(profile1, info1); } } // namespace art diff --git a/profman/profman.cc b/profman/profman.cc new file mode 100644 index 0000000000..7c9e449ed5 --- /dev/null +++ b/profman/profman.cc @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include "base/dumpable.h" +#include "base/scoped_flock.h" +#include "base/stringpiece.h" +#include "base/stringprintf.h" +#include "base/time_utils.h" +#include "base/unix_file/fd_file.h" +#include "jit/offline_profiling_info.h" +#include "utils.h" +#include "profile_assistant.h" + +namespace art { + +static int original_argc; +static char** original_argv; + +static std::string CommandLine() { + std::vector command; + for (int i = 0; i < original_argc; ++i) { + command.push_back(original_argv[i]); + } + return Join(command, ' '); +} + +static void UsageErrorV(const char* fmt, va_list ap) { + std::string error; + StringAppendV(&error, fmt, ap); + LOG(ERROR) << error; +} + +static void UsageError(const char* fmt, ...) { + va_list ap; + va_start(ap, fmt); + UsageErrorV(fmt, ap); + va_end(ap); +} + +NO_RETURN static void Usage(const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + UsageErrorV(fmt, ap); + va_end(ap); + + UsageError("Command: %s", CommandLine().c_str()); + UsageError("Usage: profman [options]..."); + UsageError(""); + UsageError(" --profile-file=: specify profiler output file to use for compilation."); + UsageError(" Can be specified multiple time, in which case the data from the different"); + UsageError(" profiles will be aggregated."); + UsageError(""); + UsageError(" --profile-file-fd=: same as --profile-file but accepts a file descriptor."); + UsageError(" Cannot be used together with --profile-file."); + UsageError(""); + UsageError(" --reference-profile-file=: specify a reference profile."); + UsageError(" The data in this file will be compared with the data obtained by merging"); + UsageError(" all the files specified with --profile-file or --profile-file-fd."); + UsageError(" If the exit code is EXIT_COMPILE then all --profile-file will be merged into"); + UsageError(" --reference-profile-file. "); + UsageError(""); + UsageError(" --reference-profile-file-fd=: same as --reference-profile-file but"); + UsageError(" accepts a file descriptor. Cannot be used together with"); + UsageError(" --reference-profile-file."); + UsageError(""); + + exit(EXIT_FAILURE); +} + +class ProfMan FINAL { + public: + ProfMan() : + reference_profile_file_fd_(-1), + start_ns_(NanoTime()) {} + + ~ProfMan() { + LogCompletionTime(); + } + + void ParseArgs(int argc, char **argv) { + original_argc = argc; + original_argv = argv; + + InitLogging(argv); + + // Skip over the command name. + argv++; + argc--; + + if (argc == 0) { + Usage("No arguments specified"); + } + + for (int i = 0; i < argc; ++i) { + const StringPiece option(argv[i]); + const bool log_options = false; + if (log_options) { + LOG(INFO) << "patchoat: option[" << i << "]=" << argv[i]; + } + if (option.starts_with("--profile-file=")) { + profile_files_.push_back(option.substr(strlen("--profile-file=")).ToString()); + } else if (option.starts_with("--profile-file-fd=")) { + ParseFdForCollection(option, "--profile-file-fd", &profile_files_fd_); + } else if (option.starts_with("--reference-profile-file=")) { + reference_profile_file_ = option.substr(strlen("--reference-profile-file=")).ToString(); + } else if (option.starts_with("--reference-profile-file-fd=")) { + ParseUintOption(option, "--reference-profile-file-fd", &reference_profile_file_fd_, Usage); + } else { + Usage("Unknown argument %s", option.data()); + } + } + + if (profile_files_.empty() && profile_files_fd_.empty()) { + Usage("No profile files specified."); + } + if (!profile_files_.empty() && !profile_files_fd_.empty()) { + Usage("Profile files should not be specified with both --profile-file-fd and --profile-file"); + } + if (!reference_profile_file_.empty() && (reference_profile_file_fd_ != -1)) { + Usage("--reference-profile-file-fd should only be supplied with --profile-file-fd"); + } + if (reference_profile_file_.empty() && (reference_profile_file_fd_ == -1)) { + Usage("Reference profile file not specified"); + } + } + + ProfileAssistant::ProcessingResult ProcessProfiles() { + ProfileAssistant::ProcessingResult result; + if (profile_files_.empty()) { + // The file doesn't need to be flushed here (ProcessProfiles will do it) + // so don't check the usage. + File file(reference_profile_file_fd_, false); + result = ProfileAssistant::ProcessProfiles(profile_files_fd_, reference_profile_file_fd_); + CloseAllFds(profile_files_fd_, "profile_files_fd_"); + } else { + result = ProfileAssistant::ProcessProfiles(profile_files_, reference_profile_file_); + } + return result; + } + + private: + static void ParseFdForCollection(const StringPiece& option, + const char* arg_name, + std::vector* fds) { + int fd; + ParseUintOption(option, arg_name, &fd, Usage); + fds->push_back(fd); + } + + static void CloseAllFds(const std::vector& fds, const char* descriptor) { + for (size_t i = 0; i < fds.size(); i++) { + if (close(fds[i]) < 0) { + PLOG(WARNING) << "Failed to close descriptor for " << descriptor << " at index " << i; + } + } + } + + void LogCompletionTime() { + LOG(INFO) << "profman took " << PrettyDuration(NanoTime() - start_ns_); + } + + std::vector profile_files_; + std::vector profile_files_fd_; + std::string reference_profile_file_; + int reference_profile_file_fd_; + uint64_t start_ns_; +}; + +// See ProfileAssistant::ProcessingResult for return codes. +static int profman(int argc, char** argv) { + ProfMan profman; + + // Parse arguments. Argument mistakes will lead to exit(EXIT_FAILURE) in UsageError. + profman.ParseArgs(argc, argv); + + // Process profile information and assess if we need to do a profile guided compilation. + // This operation involves I/O. + return profman.ProcessProfiles(); +} + +} // namespace art + +int main(int argc, char **argv) { + return art::profman(argc, argv); +} + diff --git a/runtime/base/scoped_flock.cc b/runtime/base/scoped_flock.cc index 814cbd093a..0e8031f4f2 100644 --- a/runtime/base/scoped_flock.cc +++ b/runtime/base/scoped_flock.cc @@ -83,7 +83,7 @@ bool ScopedFlock::Init(const char* filename, int flags, bool block, std::string* } bool ScopedFlock::Init(File* file, std::string* error_msg) { - file_.reset(new File(dup(file->Fd()), true)); + file_.reset(new File(dup(file->Fd()), file->GetPath(), file->CheckUsage(), file->ReadOnlyMode())); if (file_->Fd() == -1) { file_.reset(); *error_msg = StringPrintf("Failed to duplicate open file '%s': %s", @@ -114,7 +114,13 @@ ScopedFlock::~ScopedFlock() { if (file_.get() != nullptr) { int flock_result = TEMP_FAILURE_RETRY(flock(file_->Fd(), LOCK_UN)); CHECK_EQ(0, flock_result); - if (file_->FlushCloseOrErase() != 0) { + int close_result = -1; + if (file_->ReadOnlyMode()) { + close_result = file_->Close(); + } else { + close_result = file_->FlushCloseOrErase(); + } + if (close_result != 0) { PLOG(WARNING) << "Could not close scoped file lock file."; } } diff --git a/runtime/base/unix_file/fd_file.cc b/runtime/base/unix_file/fd_file.cc index e17bebb4fb..4672948f31 100644 --- a/runtime/base/unix_file/fd_file.cc +++ b/runtime/base/unix_file/fd_file.cc @@ -35,18 +35,22 @@ namespace unix_file { -FdFile::FdFile() : guard_state_(GuardState::kClosed), fd_(-1), auto_close_(true) { +FdFile::FdFile() + : guard_state_(GuardState::kClosed), fd_(-1), auto_close_(true), read_only_mode_(false) { } FdFile::FdFile(int fd, bool check_usage) : guard_state_(check_usage ? GuardState::kBase : GuardState::kNoCheck), - fd_(fd), auto_close_(true) { + fd_(fd), auto_close_(true), read_only_mode_(false) { } FdFile::FdFile(int fd, const std::string& path, bool check_usage) + : FdFile(fd, path, check_usage, false) { +} + +FdFile::FdFile(int fd, const std::string& path, bool check_usage, bool read_only_mode) : guard_state_(check_usage ? GuardState::kBase : GuardState::kNoCheck), - fd_(fd), file_path_(path), auto_close_(true) { - CHECK_NE(0U, path.size()); + fd_(fd), file_path_(path), auto_close_(true), read_only_mode_(read_only_mode) { } FdFile::~FdFile() { @@ -99,6 +103,7 @@ bool FdFile::Open(const std::string& path, int flags) { bool FdFile::Open(const std::string& path, int flags, mode_t mode) { CHECK_EQ(fd_, -1) << path; + read_only_mode_ = (flags & O_RDONLY) != 0; fd_ = TEMP_FAILURE_RETRY(open(path.c_str(), flags, mode)); if (fd_ == -1) { return false; @@ -136,6 +141,7 @@ int FdFile::Close() { } int FdFile::Flush() { + DCHECK(!read_only_mode_); #ifdef __linux__ int rc = TEMP_FAILURE_RETRY(fdatasync(fd_)); #else @@ -155,6 +161,7 @@ int64_t FdFile::Read(char* buf, int64_t byte_count, int64_t offset) const { } int FdFile::SetLength(int64_t new_length) { + DCHECK(!read_only_mode_); #ifdef __linux__ int rc = TEMP_FAILURE_RETRY(ftruncate64(fd_, new_length)); #else @@ -171,6 +178,7 @@ int64_t FdFile::GetLength() const { } int64_t FdFile::Write(const char* buf, int64_t byte_count, int64_t offset) { + DCHECK(!read_only_mode_); #ifdef __linux__ int rc = TEMP_FAILURE_RETRY(pwrite64(fd_, buf, byte_count, offset)); #else @@ -184,6 +192,14 @@ int FdFile::Fd() const { return fd_; } +bool FdFile::ReadOnlyMode() const { + return read_only_mode_; +} + +bool FdFile::CheckUsage() const { + return guard_state_ != GuardState::kNoCheck; +} + bool FdFile::IsOpened() const { return fd_ >= 0; } @@ -219,6 +235,7 @@ bool FdFile::PreadFully(void* buffer, size_t byte_count, size_t offset) { } bool FdFile::WriteFully(const void* buffer, size_t byte_count) { + DCHECK(!read_only_mode_); const char* ptr = static_cast(buffer); moveTo(GuardState::kBase, GuardState::kClosed, "Writing into closed file."); while (byte_count > 0) { @@ -233,6 +250,7 @@ bool FdFile::WriteFully(const void* buffer, size_t byte_count) { } bool FdFile::Copy(FdFile* input_file, int64_t offset, int64_t size) { + DCHECK(!read_only_mode_); off_t off = static_cast(offset); off_t sz = static_cast(size); if (offset < 0 || static_cast(off) != offset || @@ -279,12 +297,14 @@ bool FdFile::Copy(FdFile* input_file, int64_t offset, int64_t size) { } void FdFile::Erase() { + DCHECK(!read_only_mode_); TEMP_FAILURE_RETRY(SetLength(0)); TEMP_FAILURE_RETRY(Flush()); TEMP_FAILURE_RETRY(Close()); } int FdFile::FlushCloseOrErase() { + DCHECK(!read_only_mode_); int flush_result = TEMP_FAILURE_RETRY(Flush()); if (flush_result != 0) { LOG(::art::ERROR) << "CloseOrErase failed while flushing a file."; @@ -301,6 +321,7 @@ int FdFile::FlushCloseOrErase() { } int FdFile::FlushClose() { + DCHECK(!read_only_mode_); int flush_result = TEMP_FAILURE_RETRY(Flush()); if (flush_result != 0) { LOG(::art::ERROR) << "FlushClose failed while flushing a file."; @@ -317,6 +338,7 @@ void FdFile::MarkUnchecked() { } bool FdFile::ClearContent() { + DCHECK(!read_only_mode_); if (SetLength(0) < 0) { PLOG(art::ERROR) << "Failed to reset the length"; return false; @@ -325,6 +347,7 @@ bool FdFile::ClearContent() { } bool FdFile::ResetOffset() { + DCHECK(!read_only_mode_); off_t rc = TEMP_FAILURE_RETRY(lseek(fd_, 0, SEEK_SET)); if (rc == static_cast(-1)) { PLOG(art::ERROR) << "Failed to reset the offset"; diff --git a/runtime/base/unix_file/fd_file.h b/runtime/base/unix_file/fd_file.h index 1e2d8af151..8040afe9b7 100644 --- a/runtime/base/unix_file/fd_file.h +++ b/runtime/base/unix_file/fd_file.h @@ -37,6 +37,7 @@ class FdFile : public RandomAccessFile { // file descriptor. (Use DisableAutoClose to retain ownership.) FdFile(int fd, bool checkUsage); FdFile(int fd, const std::string& path, bool checkUsage); + FdFile(int fd, const std::string& path, bool checkUsage, bool read_only_mode); // Destroys an FdFile, closing the file descriptor if Close hasn't already // been called. (If you care about the return value of Close, call it @@ -68,6 +69,8 @@ class FdFile : public RandomAccessFile { // Bonus API. int Fd() const; + bool ReadOnlyMode() const; + bool CheckUsage() const; bool IsOpened() const; const std::string& GetPath() const { return file_path_; @@ -119,6 +122,7 @@ class FdFile : public RandomAccessFile { int fd_; std::string file_path_; bool auto_close_; + bool read_only_mode_; DISALLOW_COPY_AND_ASSIGN(FdFile); }; diff --git a/runtime/jit/offline_profiling_info.cc b/runtime/jit/offline_profiling_info.cc index 0aff1f7ec3..747b112f57 100644 --- a/runtime/jit/offline_profiling_info.cc +++ b/runtime/jit/offline_profiling_info.cc @@ -125,8 +125,8 @@ static constexpr const char kLineSeparator = '\n'; * app.apk,131232145,11,23,454,54 * app.apk:classes5.dex,218490184,39,13,49,1 **/ -bool ProfileCompilationInfo::Save(uint32_t fd) { - DCHECK_GE(fd, 0u); +bool ProfileCompilationInfo::Save(int fd) { + DCHECK_GE(fd, 0); // TODO(calin): Profile this and see how much memory it takes. If too much, // write to file directly. std::ostringstream os; @@ -232,8 +232,8 @@ static int GetLineFromBuffer(char* buffer, int n, int start_from, std::string& l return new_line_pos == -1 ? new_line_pos : new_line_pos + 1; } -bool ProfileCompilationInfo::Load(uint32_t fd) { - DCHECK_GE(fd, 0u); +bool ProfileCompilationInfo::Load(int fd) { + DCHECK_GE(fd, 0); std::string current_line; const int kBufferSize = 1024; @@ -343,7 +343,7 @@ std::string ProfileCompilationInfo::DumpInfo(const std::vector* return os.str(); } -bool ProfileCompilationInfo::Equals(ProfileCompilationInfo& other) { +bool ProfileCompilationInfo::Equals(const ProfileCompilationInfo& other) { return info_.Equals(other.info_); } diff --git a/runtime/jit/offline_profiling_info.h b/runtime/jit/offline_profiling_info.h index c388c4a42f..edc591c2eb 100644 --- a/runtime/jit/offline_profiling_info.h +++ b/runtime/jit/offline_profiling_info.h @@ -46,11 +46,11 @@ class ProfileCompilationInfo { const std::vector& methods); // Loads profile information from the given file descriptor. - bool Load(uint32_t fd); + bool Load(int fd); // Loads the data from another ProfileCompilationInfo object. bool Load(const ProfileCompilationInfo& info); // Saves the profile data to the given file descriptor. - bool Save(uint32_t fd); + bool Save(int fd); // Returns the number of methods that were profiled. uint32_t GetNumberOfMethods() const; @@ -65,8 +65,7 @@ class ProfileCompilationInfo { bool print_full_dex_location = true) const; // For testing purposes. - bool Equals(ProfileCompilationInfo& other); - // Exposed for testing purpose. + bool Equals(const ProfileCompilationInfo& other); static std::string GetProfileDexFileKey(const std::string& dex_location); private: diff --git a/runtime/utils.cc b/runtime/utils.cc index 07f94c0766..13564a6a0f 100644 --- a/runtime/utils.cc +++ b/runtime/utils.cc @@ -1392,9 +1392,8 @@ std::string GetSystemImageFilename(const char* location, const InstructionSet is return filename; } -bool Exec(std::vector& arg_vector, std::string* error_msg) { +int ExecAndReturnCode(std::vector& arg_vector, std::string* error_msg) { const std::string command_line(Join(arg_vector, ' ')); - CHECK_GE(arg_vector.size(), 1U) << command_line; // Convert the args to char pointers. @@ -1417,7 +1416,6 @@ bool Exec(std::vector& arg_vector, std::string* error_msg) { setpgid(0, 0); execv(program, &args[0]); - PLOG(ERROR) << "Failed to execv(" << command_line << ")"; // _exit to avoid atexit handlers in child. _exit(1); @@ -1425,23 +1423,32 @@ bool Exec(std::vector& arg_vector, std::string* error_msg) { if (pid == -1) { *error_msg = StringPrintf("Failed to execv(%s) because fork failed: %s", command_line.c_str(), strerror(errno)); - return false; + return -1; } // wait for subprocess to finish - int status; + int status = -1; pid_t got_pid = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0)); if (got_pid != pid) { *error_msg = StringPrintf("Failed after fork for execv(%s) because waitpid failed: " "wanted %d, got %d: %s", command_line.c_str(), pid, got_pid, strerror(errno)); - return false; + return -1; } - if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { - *error_msg = StringPrintf("Failed execv(%s) because non-0 exit status", - command_line.c_str()); - return false; + if (WIFEXITED(status)) { + return WEXITSTATUS(status); } + return -1; + } +} + +bool Exec(std::vector& arg_vector, std::string* error_msg) { + int status = ExecAndReturnCode(arg_vector, error_msg); + if (status != 0) { + const std::string command_line(Join(arg_vector, ' ')); + *error_msg = StringPrintf("Failed execv(%s) because non-0 exit status", + command_line.c_str()); + return false; } return true; } diff --git a/runtime/utils.h b/runtime/utils.h index c00db11c16..36f9abfe7b 100644 --- a/runtime/utils.h +++ b/runtime/utils.h @@ -287,6 +287,7 @@ std::string GetSystemImageFilename(const char* location, InstructionSet isa); // Wrapper on fork/execv to run a command in a subprocess. bool Exec(std::vector& arg_vector, std::string* error_msg); +int ExecAndReturnCode(std::vector& arg_vector, std::string* error_msg); // Returns true if the file exists. bool FileExists(const std::string& filename); @@ -343,7 +344,7 @@ static void ParseUintOption(const StringPiece& option, UsageFn Usage, bool is_long_option = true) { std::string option_prefix = option_name + (is_long_option ? "=" : ""); - DCHECK(option.starts_with(option_prefix)); + DCHECK(option.starts_with(option_prefix)) << option << " " << option_prefix; const char* value_string = option.substr(option_prefix.size()).data(); int64_t parsed_integer_value = 0; if (!ParseInt(value_string, &parsed_integer_value)) { -- GitLab From 7ca4b77c98ffdf7a4db26fd9f84b2cfcc274c4aa Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Tue, 23 Feb 2016 13:52:01 +0000 Subject: [PATCH 005/204] Don't allocate mspaces of less than a page. Fixes jit tests in debug mode. Change-Id: I34565e03683cee063e26975a461d8e75ad4a205f --- runtime/jit/jit_code_cache.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h index 087d1de196..74ce7b57fd 100644 --- a/runtime/jit/jit_code_cache.h +++ b/runtime/jit/jit_code_cache.h @@ -49,7 +49,7 @@ class JitCodeCache { static constexpr size_t kMaxCapacity = 64 * MB; // Put the default to a very low amount for debug builds to stress the code cache // collection. - static constexpr size_t kInitialCapacity = kIsDebugBuild ? 4 * KB : 64 * KB; + static constexpr size_t kInitialCapacity = kIsDebugBuild ? 8 * KB : 64 * KB; // By default, do not GC until reaching 256KB. static constexpr size_t kReservedCapacity = kInitialCapacity * 4; -- GitLab From cedd4f24ffe106bef57b85e465b1638e4cbd1168 Mon Sep 17 00:00:00 2001 From: Roland Levillain Date: Tue, 23 Feb 2016 13:49:35 +0000 Subject: [PATCH 006/204] Re-enable tests that were causing TimeoutExceptions on x86/x86-64. Some libcore tests have been failing with a java.util.concurrent.TimeoutException on ART Builbot's x86/x86-64 concurrent collector configurations. No longer ignore these failures to see whether recent changes (namely https://android-review.googlesource.com/#/c/204125) fixed the issue. Bug: 26507762 Change-Id: Ib188e194195fc11b539bd53a03db78ab80626f33 --- tools/libcore_failures_concurrent_collector.txt | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/tools/libcore_failures_concurrent_collector.txt b/tools/libcore_failures_concurrent_collector.txt index f347429ee1..19a61dc8cb 100644 --- a/tools/libcore_failures_concurrent_collector.txt +++ b/tools/libcore_failures_concurrent_collector.txt @@ -23,20 +23,6 @@ names: ["libcore.java.lang.OldSystemTest#test_gc"], bug: 26155567 }, -{ - description: "TimeoutException on host-{x86,x86-64}-concurrent-collector", - result: EXEC_FAILED, - modes: [host], - names: ["libcore.java.util.zip.DeflaterOutputStreamTest#testSyncFlushEnabled", - "libcore.java.util.zip.DeflaterOutputStreamTest#testSyncFlushDisabled", - "libcore.java.util.zip.GZIPInputStreamTest#testLongMessage", - "libcore.java.util.zip.GZIPOutputStreamTest#testSyncFlushEnabled", - "libcore.java.util.zip.OldAndroidGZIPStreamTest#testGZIPStream", - "libcore.java.util.zip.OldAndroidZipStreamTest#testZipStream", - "libcore.java.util.zip.ZipFileTest#testZipFileWithLotsOfEntries", - "libcore.java.util.zip.ZipInputStreamTest#testLongMessage"], - bug: 26507762 -}, { description: "TimeoutException on hammerhead-concurrent-collector", result: EXEC_FAILED, -- GitLab From 8d37250f7b9a7839b11488f45a1842b025026f94 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Tue, 23 Feb 2016 13:56:43 +0000 Subject: [PATCH 007/204] Introduce partial code cache collection. It will collect code that is known unused (because it deoptimized), and osr code. bug:26846185 Change-Id: Ic27dfeb944efb2ca464039007ba365c1e0d4a040 --- runtime/jit/jit_code_cache.cc | 141 ++++++++++++++++++++++++---------- runtime/jit/jit_code_cache.h | 16 +++- 2 files changed, 114 insertions(+), 43 deletions(-) diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index d5a9d66210..6348ddad58 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -123,7 +123,7 @@ JitCodeCache::JitCodeCache(MemMap* code_map, current_capacity_(initial_code_capacity + initial_data_capacity), code_end_(initial_code_capacity), data_end_(initial_data_capacity), - has_done_one_collection_(false), + has_done_full_collection_(false), last_update_time_ns_(0), garbage_collect_code_(garbage_collect_code), number_of_compilations_(0) { @@ -524,8 +524,56 @@ bool JitCodeCache::IncreaseCodeCacheCapacity() { return true; } +void JitCodeCache::MarkCompiledCodeOnThreadStacks(Thread* self) { + Barrier barrier(0); + size_t threads_running_checkpoint = 0; + MarkCodeClosure closure(this, &barrier); + threads_running_checkpoint = Runtime::Current()->GetThreadList()->RunCheckpoint(&closure); + // Now that we have run our checkpoint, move to a suspended state and wait + // for other threads to run the checkpoint. + ScopedThreadSuspension sts(self, kSuspended); + if (threads_running_checkpoint != 0) { + barrier.Increment(self, threads_running_checkpoint); + } +} + +void JitCodeCache::RemoveUnusedCode(Thread* self) { + // Clear the osr map, chances are most of the code in it is now dead. + { + MutexLock mu(self, lock_); + osr_code_map_.clear(); + } + + // Run a checkpoint on all threads to mark the JIT compiled code they are running. + MarkCompiledCodeOnThreadStacks(self); + + // Iterate over all compiled code and remove entries that are not marked and not + // the entrypoint of their corresponding ArtMethod. + { + MutexLock mu(self, lock_); + ScopedCodeCacheWrite scc(code_map_.get()); + for (auto it = method_code_map_.begin(); it != method_code_map_.end();) { + const void* code_ptr = it->first; + ArtMethod* method = it->second; + uintptr_t allocation = FromCodeToAllocation(code_ptr); + const OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromCodePointer(code_ptr); + if ((method->GetEntryPointFromQuickCompiledCode() != method_header->GetEntryPoint()) && + !GetLiveBitmap()->Test(allocation)) { + FreeCode(code_ptr, method); + it = method_code_map_.erase(it); + } else { + ++it; + } + } + } +} + void JitCodeCache::GarbageCollectCache(Thread* self) { - instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); + if (!garbage_collect_code_) { + MutexLock mu(self, lock_); + IncreaseCodeCacheCapacity(); + return; + } // Wait for an existing collection, or let everyone know we are starting one. { @@ -534,42 +582,75 @@ void JitCodeCache::GarbageCollectCache(Thread* self) { if (WaitForPotentialCollectionToComplete(self)) { return; } else { + live_bitmap_.reset(CodeCacheBitmap::Create( + "code-cache-bitmap", + reinterpret_cast(code_map_->Begin()), + reinterpret_cast(code_map_->Begin() + current_capacity_ / 2))); collection_in_progress_ = true; } } - // Check if we just need to grow the capacity. If we don't, allocate the bitmap while - // we hold the lock. + // Check if we want to do a full collection. + bool do_full_collection = true; + { + MutexLock mu(self, lock_); + if (current_capacity_ == max_capacity_) { + // Always do a full collection when the code cache is full. + do_full_collection = true; + } else if (current_capacity_ < kReservedCapacity) { + // Do a partial collection until we hit the reserved capacity limit. + do_full_collection = false; + } else if (has_done_full_collection_) { + // Do a partial collection if we have done a full collection in the last + // collection round. + do_full_collection = false; + } + } + + if (!kIsDebugBuild || VLOG_IS_ON(jit)) { + LOG(INFO) << "Do " + << (do_full_collection ? "full" : "partial") + << " code cache collection, code=" + << PrettySize(CodeCacheSize()) + << ", data=" << PrettySize(DataCacheSize()); + } + + if (do_full_collection) { + DoFullCollection(self); + } else { + RemoveUnusedCode(self); + } + { MutexLock mu(self, lock_); - if (!garbage_collect_code_ || current_capacity_ < kReservedCapacity) { + if (!do_full_collection) { + has_done_full_collection_ = false; IncreaseCodeCacheCapacity(); - NotifyCollectionDone(self); - return; - } else if (has_done_one_collection_ && IncreaseCodeCacheCapacity()) { - has_done_one_collection_ = false; - NotifyCollectionDone(self); - return; } else { - live_bitmap_.reset(CodeCacheBitmap::Create( - "code-cache-bitmap", - reinterpret_cast(code_map_->Begin()), - reinterpret_cast(code_map_->Begin() + current_capacity_ / 2))); + has_done_full_collection_ = true; } + live_bitmap_.reset(nullptr); + NotifyCollectionDone(self); } if (!kIsDebugBuild || VLOG_IS_ON(jit)) { - LOG(INFO) << "Clearing code cache, code=" + LOG(INFO) << "After code cache collection, code=" << PrettySize(CodeCacheSize()) << ", data=" << PrettySize(DataCacheSize()); } - // Walk over all compiled methods and set the entry points of these - // methods to interpreter. +} + +void JitCodeCache::DoFullCollection(Thread* self) { + instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); { MutexLock mu(self, lock_); + // Walk over all compiled methods and set the entry points of these + // methods to interpreter. for (auto& it : method_code_map_) { instrumentation->UpdateMethodsCode(it.second, GetQuickToInterpreterBridge()); } + + // Clear the profiling info of methods that are not being compiled. for (ProfilingInfo* info : profiling_infos_) { if (!info->IsMethodBeingCompiled()) { info->GetMethod()->SetProfilingInfo(nullptr); @@ -582,19 +663,7 @@ void JitCodeCache::GarbageCollectCache(Thread* self) { } // Run a checkpoint on all threads to mark the JIT compiled code they are running. - { - Barrier barrier(0); - size_t threads_running_checkpoint = 0; - MarkCodeClosure closure(this, &barrier); - threads_running_checkpoint = - Runtime::Current()->GetThreadList()->RunCheckpoint(&closure); - // Now that we have run our checkpoint, move to a suspended state and wait - // for other threads to run the checkpoint. - ScopedThreadSuspension sts(self, kSuspended); - if (threads_running_checkpoint != 0) { - barrier.Increment(self, threads_running_checkpoint); - } - } + MarkCompiledCodeOnThreadStacks(self); { MutexLock mu(self, lock_); @@ -628,16 +697,6 @@ void JitCodeCache::GarbageCollectCache(Thread* self) { return false; }); profiling_infos_.erase(profiling_kept_end, profiling_infos_.end()); - - live_bitmap_.reset(nullptr); - has_done_one_collection_ = true; - NotifyCollectionDone(self); - } - - if (!kIsDebugBuild || VLOG_IS_ON(jit)) { - LOG(INFO) << "After clearing code cache, code=" - << PrettySize(CodeCacheSize()) - << ", data=" << PrettySize(DataCacheSize()); } } diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h index 74ce7b57fd..e5b8e6ca17 100644 --- a/runtime/jit/jit_code_cache.h +++ b/runtime/jit/jit_code_cache.h @@ -234,6 +234,18 @@ class JitCodeCache { // Set the footprint limit of the code cache. void SetFootprintLimit(size_t new_footprint) REQUIRES(lock_); + void DoFullCollection(Thread* self) + REQUIRES(!lock_) + SHARED_REQUIRES(Locks::mutator_lock_); + + void RemoveUnusedCode(Thread* self) + REQUIRES(!lock_) + SHARED_REQUIRES(Locks::mutator_lock_); + + void MarkCompiledCodeOnThreadStacks(Thread* self) + REQUIRES(!lock_) + SHARED_REQUIRES(Locks::mutator_lock_); + // Lock for guarding allocations, collections, and the method_code_map_. Mutex lock_; // Condition to wait on during collection. @@ -269,8 +281,8 @@ class JitCodeCache { // The current footprint in bytes of the data portion of the code cache. size_t data_end_ GUARDED_BY(lock_); - // Whether a collection has already been done on the current capacity. - bool has_done_one_collection_ GUARDED_BY(lock_); + // Whether a full collection has already been done on the current capacity. + bool has_done_full_collection_ GUARDED_BY(lock_); // Last time the the code_cache was updated. // It is atomic to avoid locking when reading it. -- GitLab From fbeeb47fc63ec85891198b51b2309bb15f26527e Mon Sep 17 00:00:00 2001 From: Sebastien Hertz Date: Fri, 19 Feb 2016 16:54:05 +0100 Subject: [PATCH 008/204] Compile checker run-tests with Jack Bug: 25635944 Bug: 19467889 Change-Id: I3c3fcbcbd2f6c142dbea279a9ecd9d372e79b3b9 --- test/555-checker-regression-x86const/build | 10 ++++------ test/run-test | 6 +++--- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/test/555-checker-regression-x86const/build b/test/555-checker-regression-x86const/build index 09dcc363dd..92ddfc9a58 100644 --- a/test/555-checker-regression-x86const/build +++ b/test/555-checker-regression-x86const/build @@ -27,14 +27,12 @@ mkdir classes-ex mv classes/UnresolvedClass.class classes-ex if [ ${USE_JACK} = "true" ]; then - # Create .jack files from classes generated with javac. - ${JILL} classes --output classes.jack - ${JILL} classes-ex --output classes-ex.jack + jar cf classes.jill.jar -C classes . + jar cf classes-ex.jill.jar -C classes-ex . - # Create DEX files from .jack files. - ${JACK} --import classes.jack --output-dex . + ${JACK} --import classes.jill.jar --output-dex . zip $TEST_NAME.jar classes.dex - ${JACK} --import classes-ex.jack --output-dex . + ${JACK} --import classes-ex.jill.jar --output-dex . zip ${TEST_NAME}-ex.jar classes.dex else if [ ${NEED_DEX} = "true" ]; then diff --git a/test/run-test b/test/run-test index f1875d71a5..a7a9876bb6 100755 --- a/test/run-test +++ b/test/run-test @@ -677,9 +677,9 @@ function arch_supports_read_barrier() { # Tests named '-checker-*' will also have their CFGs verified with # Checker when compiled with Optimizing on host. if [[ "$TEST_NAME" =~ ^[0-9]+-checker- ]]; then - # Jack does not necessarily generate the same DEX output than dx. Because these tests depend - # on a particular DEX output, keep building them with dx for now (b/19467889). - USE_JACK="false" + # Disable optimizations where Jack may simplify statements with constants + # (like 'null instanceof SomeType'). + JACK="$JACK -D jack.optimization.expression-simplifier=false" if [ "$runtime" = "art" -a "$image_suffix" = "-optimizing" ]; then # Optimizing has read barrier support for certain architectures -- GitLab From de4fb63be26cd561111cad4574cac5e0c2314d11 Mon Sep 17 00:00:00 2001 From: Calin Juravle Date: Tue, 23 Feb 2016 16:53:30 +0000 Subject: [PATCH 009/204] Fix profman path for the gtest. Change-Id: Id332c50658b7dc1eb1edf6a58fcbdde4c2887d27 --- profman/profile_assistant_test.cc | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/profman/profile_assistant_test.cc b/profman/profile_assistant_test.cc index 543be5d181..3faa8eb53f 100644 --- a/profman/profile_assistant_test.cc +++ b/profman/profile_assistant_test.cc @@ -59,11 +59,7 @@ class ProfileAssistantTest : public CommonRuntimeTest { // Runs test with given arguments. int ProcessProfiles(const std::vector& profiles_fd, int reference_profile_fd) { std::string file_path = GetTestAndroidRoot(); - if (IsHost()) { - file_path += "/bin/profman"; - } else { - file_path += "/xbin/profman"; - } + file_path += "/bin/profman"; if (kIsDebugBuild) { file_path += "d"; } -- GitLab From 5c7e2606d1246460a8a4b227d894110e89d0a842 Mon Sep 17 00:00:00 2001 From: Roland Levillain Date: Tue, 23 Feb 2016 18:08:53 +0000 Subject: [PATCH 010/204] Please Clang with respect to stack frame limits in dex2oat. This change enables Clang to compile dex2oat on MIPS64 without complaining about stack frames larger than what `-Wframe-larger-than` allows. Bug: 27310199 Change-Id: I441a4be499959a089d3c2eae1135eb0273c1e80b --- dex2oat/dex2oat.cc | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index f7efdc5bac..1459c76a7a 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -2550,16 +2550,20 @@ static int dex2oat(int argc, char** argv) { TimingLogger timings("compiler", false, false); - Dex2Oat dex2oat(&timings); + // Allocate `dex2oat` on the heap instead of on the stack, as Clang + // might produce a stack frame too large for this function or for + // functions inlining it (such as main), that would not fit the + // requirements of the `-Wframe-larger-than` option. + std::unique_ptr dex2oat = MakeUnique(&timings); // Parse arguments. Argument mistakes will lead to exit(EXIT_FAILURE) in UsageError. - dex2oat.ParseArgs(argc, argv); + dex2oat->ParseArgs(argc, argv); // Process profile information and assess if we need to do a profile guided compilation. // This operation involves I/O. - if (dex2oat.UseProfileGuidedCompilation()) { - if (dex2oat.LoadProfile()) { - if (!dex2oat.ShouldCompileBasedOnProfiles()) { + if (dex2oat->UseProfileGuidedCompilation()) { + if (dex2oat->LoadProfile()) { + if (!dex2oat->ShouldCompileBasedOnProfiles()) { LOG(INFO) << "Skipped compilation because of insignificant profile delta"; return EXIT_SUCCESS; } @@ -2570,7 +2574,7 @@ static int dex2oat(int argc, char** argv) { } // Check early that the result of compilation can be written - if (!dex2oat.OpenFile()) { + if (!dex2oat->OpenFile()) { return EXIT_FAILURE; } @@ -2580,25 +2584,25 @@ static int dex2oat(int argc, char** argv) { // 3) Compiling with --host // 4) Compiling on the host (not a target build) // Otherwise, print a stripped command line. - if (kIsDebugBuild || dex2oat.IsBootImage() || dex2oat.IsHost() || !kIsTargetBuild) { + if (kIsDebugBuild || dex2oat->IsBootImage() || dex2oat->IsHost() || !kIsTargetBuild) { LOG(INFO) << CommandLine(); } else { LOG(INFO) << StrippedCommandLine(); } - if (!dex2oat.Setup()) { - dex2oat.EraseOatFiles(); + if (!dex2oat->Setup()) { + dex2oat->EraseOatFiles(); return EXIT_FAILURE; } bool result; - if (dex2oat.IsImage()) { - result = CompileImage(dex2oat); + if (dex2oat->IsImage()) { + result = CompileImage(*dex2oat); } else { - result = CompileApp(dex2oat); + result = CompileApp(*dex2oat); } - dex2oat.Shutdown(); + dex2oat->Shutdown(); return result; } } // namespace art -- GitLab From 32ae80251b6967cfc83461ece771f9f7e5c202bc Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Tue, 23 Feb 2016 18:17:46 +0000 Subject: [PATCH 011/204] Revert "Compile checker run-tests with Jack" Tests still faill. Bug: 25635944 Bug: 19467889 This reverts commit fbeeb47fc63ec85891198b51b2309bb15f26527e. Change-Id: I8f49dc6e8d3eb5f0de10636ef2685b7a2e08f297 --- test/555-checker-regression-x86const/build | 10 ++++++---- test/run-test | 6 +++--- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/test/555-checker-regression-x86const/build b/test/555-checker-regression-x86const/build index 92ddfc9a58..09dcc363dd 100644 --- a/test/555-checker-regression-x86const/build +++ b/test/555-checker-regression-x86const/build @@ -27,12 +27,14 @@ mkdir classes-ex mv classes/UnresolvedClass.class classes-ex if [ ${USE_JACK} = "true" ]; then - jar cf classes.jill.jar -C classes . - jar cf classes-ex.jill.jar -C classes-ex . + # Create .jack files from classes generated with javac. + ${JILL} classes --output classes.jack + ${JILL} classes-ex --output classes-ex.jack - ${JACK} --import classes.jill.jar --output-dex . + # Create DEX files from .jack files. + ${JACK} --import classes.jack --output-dex . zip $TEST_NAME.jar classes.dex - ${JACK} --import classes-ex.jill.jar --output-dex . + ${JACK} --import classes-ex.jack --output-dex . zip ${TEST_NAME}-ex.jar classes.dex else if [ ${NEED_DEX} = "true" ]; then diff --git a/test/run-test b/test/run-test index a7a9876bb6..f1875d71a5 100755 --- a/test/run-test +++ b/test/run-test @@ -677,9 +677,9 @@ function arch_supports_read_barrier() { # Tests named '-checker-*' will also have their CFGs verified with # Checker when compiled with Optimizing on host. if [[ "$TEST_NAME" =~ ^[0-9]+-checker- ]]; then - # Disable optimizations where Jack may simplify statements with constants - # (like 'null instanceof SomeType'). - JACK="$JACK -D jack.optimization.expression-simplifier=false" + # Jack does not necessarily generate the same DEX output than dx. Because these tests depend + # on a particular DEX output, keep building them with dx for now (b/19467889). + USE_JACK="false" if [ "$runtime" = "art" -a "$image_suffix" = "-optimizing" ]; then # Optimizing has read barrier support for certain architectures -- GitLab From 16e51beb9a771ffe7be833173e3dd5f2dac63892 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Tue, 23 Feb 2016 10:37:32 -0800 Subject: [PATCH 012/204] Fix allocation tracking race Need to instrument the stubs before calling Heap::SetAlloctrackingEnabled or else we can get this DCHECK failing DCHECK(!IsAllocTrackingEnabled()) Change-Id: I1fb5df217a2785baf22b4f57887cd323f13f0973 --- runtime/gc/allocation_record.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/runtime/gc/allocation_record.cc b/runtime/gc/allocation_record.cc index 83e5bad552..4de5388d8c 100644 --- a/runtime/gc/allocation_record.cc +++ b/runtime/gc/allocation_record.cc @@ -238,9 +238,12 @@ void AllocRecordObjectMap::SetAllocTrackingEnabled(bool enable) { << records->max_stack_depth_ << " frames, taking up to " << PrettySize(sz * records->alloc_record_max_) << ")"; heap->SetAllocationRecords(records); - heap->SetAllocTrackingEnabled(true); } Runtime::Current()->GetInstrumentation()->InstrumentQuickAllocEntryPoints(); + { + MutexLock mu(self, *Locks::alloc_tracker_lock_); + heap->SetAllocTrackingEnabled(true); + } } else { { MutexLock mu(self, *Locks::alloc_tracker_lock_); -- GitLab From bf3f1cf15a021ea1ff8ae860c55e8281da4619b3 Mon Sep 17 00:00:00 2001 From: Aart Bik Date: Mon, 22 Feb 2016 16:22:33 -0800 Subject: [PATCH 013/204] Improved instruction + offset hunting. Rationale: This is generally useful for anything using this method but in particular for deopting something like bs[ off] = (byte)(n >>> 24); bs[++off] = (byte)(n >>> 16); bs[++off] = (byte)(n >>> 8); bs[++off] = (byte)(n ); where the base + offset is hidden in the increments. Occurs quite often in real-life code. Change-Id: I3fa7d285a7368a179a26e590e8eee37f3b64c25d --- .../optimizing/bounds_check_elimination.cc | 24 ++++++++----- test/449-checker-bce/src/Main.java | 36 +++++++++++++++++++ 2 files changed, 52 insertions(+), 8 deletions(-) diff --git a/compiler/optimizing/bounds_check_elimination.cc b/compiler/optimizing/bounds_check_elimination.cc index ba1b1683d7..a7a1c0f2c4 100644 --- a/compiler/optimizing/bounds_check_elimination.cc +++ b/compiler/optimizing/bounds_check_elimination.cc @@ -67,20 +67,28 @@ class ValueBound : public ValueObject { static bool IsAddOrSubAConstant(HInstruction* instruction, /* out */ HInstruction** left_instruction, /* out */ int32_t* right_constant) { - if (instruction->IsAdd() || instruction->IsSub()) { + HInstruction* left_so_far = nullptr; + int32_t right_so_far = 0; + while (instruction->IsAdd() || instruction->IsSub()) { HBinaryOperation* bin_op = instruction->AsBinaryOperation(); HInstruction* left = bin_op->GetLeft(); HInstruction* right = bin_op->GetRight(); if (right->IsIntConstant()) { - *left_instruction = left; - int32_t c = right->AsIntConstant()->GetValue(); - *right_constant = instruction->IsAdd() ? c : -c; - return true; + int32_t v = right->AsIntConstant()->GetValue(); + int32_t c = instruction->IsAdd() ? v : -v; + if (!WouldAddOverflowOrUnderflow(right_so_far, c)) { + instruction = left; + left_so_far = left; + right_so_far += c; + continue; + } } + break; } - *left_instruction = nullptr; - *right_constant = 0; - return false; + // Return result: either false and "null+0" or true and "instr+constant". + *left_instruction = left_so_far; + *right_constant = right_so_far; + return left_so_far != nullptr; } // Expresses any instruction as a value bound. diff --git a/test/449-checker-bce/src/Main.java b/test/449-checker-bce/src/Main.java index 31bb94cb8c..32bbc5b61d 100644 --- a/test/449-checker-bce/src/Main.java +++ b/test/449-checker-bce/src/Main.java @@ -391,6 +391,36 @@ public class Main { array[base + 1] = 1; } + /// CHECK-START: void Main.constantIndexing10(int[], int) BCE (before) + /// CHECK: BoundsCheck + /// CHECK: ArraySet + /// CHECK: BoundsCheck + /// CHECK: ArraySet + /// CHECK: BoundsCheck + /// CHECK: ArraySet + /// CHECK: BoundsCheck + /// CHECK: ArraySet + + /// CHECK-START: void Main.constantIndexing10(int[], int) BCE (after) + /// CHECK: Deoptimize + /// CHECK: Deoptimize + /// CHECK-NOT: BoundsCheck + /// CHECK: ArraySet + /// CHECK-NOT: BoundsCheck + /// CHECK: ArraySet + /// CHECK-NOT: BoundsCheck + /// CHECK: ArraySet + /// CHECK-NOT: BoundsCheck + /// CHECK: ArraySet + + static void constantIndexing10(int[] array, int base) { + // Offset hidden in incremented base. + array[base] = 1; + array[++base] = 2; + array[++base] = 3; + array[++base] = 4; + } + static void runAllConstantIndices() { int[] a1 = { 0 }; int[] a6 = { 0, 0, 0, 0, 0, 0 }; @@ -502,6 +532,12 @@ public class Main { a6[3] != 3 || a6[4] != 40 || a6[5] != 10) { System.out.println("constant indices 9 failed!"); } + + constantIndexing10(a6, 0); + if (a6[0] != 1 || a6[1] != 2 || a6[2] != 3 || + a6[3] != 4 || a6[4] != 40 || a6[5] != 10) { + System.out.println("constant indices 10 failed!"); + } } // A helper into which the actual throwing function should be inlined. -- GitLab From 3612149aee482ab7a17da68b0ef5fef3879729a2 Mon Sep 17 00:00:00 2001 From: Alex Light Date: Mon, 22 Feb 2016 13:43:29 -0800 Subject: [PATCH 014/204] Make JNI work correctly with default methods. Also adds some tests for JNI and DefaultMethods. Bug: 27259142 Bug: 24618811 Change-Id: I31222e3e41059d803be1dbb0f40e1144ac4bf457 --- compiler/driver/compiler_driver.cc | 6 +- compiler/image_writer.cc | 2 +- compiler/oat_test.cc | 2 +- runtime/art_method.h | 9 ++- runtime/class_linker.cc | 24 +++--- runtime/class_linker_test.cc | 2 +- runtime/mirror/class-inl.h | 2 +- runtime/modifiers.h | 5 ++ test/004-JniTest/expected.txt | 27 +++++++ test/004-JniTest/jni_test.cc | 62 +++++++++++++++ .../004-JniTest/smali/AbstractInterface.smali | 26 +++++++ test/004-JniTest/smali/ConcreteClass.smali | 72 +++++++++++++++++ .../004-JniTest/smali/ConflictInterface.smali | 35 +++++++++ test/004-JniTest/smali/DefaultInterface.smali | 77 +++++++++++++++++++ test/004-JniTest/src/Main.java | 5 +- 15 files changed, 333 insertions(+), 23 deletions(-) create mode 100644 test/004-JniTest/smali/AbstractInterface.smali create mode 100644 test/004-JniTest/smali/ConcreteClass.smali create mode 100644 test/004-JniTest/smali/ConflictInterface.smali create mode 100644 test/004-JniTest/smali/DefaultInterface.smali diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index a51dd3209b..9ed5ec80b2 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -1077,10 +1077,8 @@ static void MaybeAddToImageClasses(Handle c, image_classes); } for (auto& m : c->GetVirtualMethods(pointer_size)) { - if (m.IsMiranda() || (true)) { - StackHandleScope<1> hs2(self); - MaybeAddToImageClasses(hs2.NewHandle(m.GetDeclaringClass()), image_classes); - } + StackHandleScope<1> hs2(self); + MaybeAddToImageClasses(hs2.NewHandle(m.GetDeclaringClass()), image_classes); } if (klass->IsArrayClass()) { StackHandleScope<1> hs2(self); diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc index d50528edee..3d3130962a 100644 --- a/compiler/image_writer.cc +++ b/compiler/image_writer.cc @@ -917,7 +917,7 @@ void ImageWriter::PruneNonImageClasses() { // Copied methods may be held live by a class which was not an image class but have a // declaring class which is an image class. Set it to the resolution method to be safe and // prevent dangling pointers. - if (method->MightBeCopied() || !KeepClass(declaring_class)) { + if (method->IsCopied() || !KeepClass(declaring_class)) { mirror::DexCache::SetElementPtrSize(resolved_methods, i, resolution_method, diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc index d3b404a3b6..fead839263 100644 --- a/compiler/oat_test.cc +++ b/compiler/oat_test.cc @@ -416,7 +416,7 @@ TEST_F(OatTest, WriteRead) { // TODO We should also check copied methods in this test. for (auto& m : klass->GetDeclaredVirtualMethods(pointer_size)) { if (!klass->IsInterface()) { - EXPECT_FALSE(m.MightBeCopied()); + EXPECT_FALSE(m.IsCopied()); } CheckMethod(&m, oat_class.GetOatMethod(method_index), dex_file); ++method_index; diff --git a/runtime/art_method.h b/runtime/art_method.h index f3e8d6bd0b..ec00a7b900 100644 --- a/runtime/art_method.h +++ b/runtime/art_method.h @@ -132,9 +132,12 @@ class ArtMethod FINAL { return (GetAccessFlags() & kAccFinal) != 0; } - // Returns true if this method might be copied from another class. - bool MightBeCopied() { - return IsMiranda() || IsDefault() || IsDefaultConflicting(); + bool IsCopied() { + const bool copied = (GetAccessFlags() & kAccCopied) != 0; + // (IsMiranda() || IsDefaultConflicting()) implies copied + DCHECK(!(IsMiranda() || IsDefaultConflicting()) || copied) + << "Miranda or default-conflict methods must always be copied."; + return copied; } bool IsMiranda() { diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 936c98875f..9ea082769a 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -759,7 +759,7 @@ static void SanityCheckArtMethod(ArtMethod* m, SHARED_REQUIRES(Locks::mutator_lock_) { if (m->IsRuntimeMethod()) { CHECK(m->GetDeclaringClass() == nullptr) << PrettyMethod(m); - } else if (m->MightBeCopied()) { + } else if (m->IsCopied()) { CHECK(m->GetDeclaringClass() != nullptr) << PrettyMethod(m); } else if (expected_class != nullptr) { CHECK_EQ(m->GetDeclaringClassUnchecked(), expected_class) << PrettyMethod(m); @@ -1137,18 +1137,18 @@ class FixupArtMethodArrayVisitor : public ArtMethodVisitor { virtual void Visit(ArtMethod* method) SHARED_REQUIRES(Locks::mutator_lock_) { GcRoot* resolved_types = method->GetDexCacheResolvedTypes(sizeof(void*)); - const bool maybe_copied = method->MightBeCopied(); + const bool is_copied = method->IsCopied(); if (resolved_types != nullptr) { bool in_image_space = false; - if (kIsDebugBuild || maybe_copied) { + if (kIsDebugBuild || is_copied) { in_image_space = header_.GetImageSection(ImageHeader::kSectionDexCacheArrays).Contains( reinterpret_cast(resolved_types) - header_.GetImageBegin()); } // Must be in image space for non-miranda method. - DCHECK(maybe_copied || in_image_space) + DCHECK(is_copied || in_image_space) << resolved_types << " is not in image starting at " << reinterpret_cast(header_.GetImageBegin()); - if (!maybe_copied || in_image_space) { + if (!is_copied || in_image_space) { // Go through the array so that we don't need to do a slow map lookup. method->SetDexCacheResolvedTypes(*reinterpret_cast**>(resolved_types), sizeof(void*)); @@ -1157,15 +1157,15 @@ class FixupArtMethodArrayVisitor : public ArtMethodVisitor { ArtMethod** resolved_methods = method->GetDexCacheResolvedMethods(sizeof(void*)); if (resolved_methods != nullptr) { bool in_image_space = false; - if (kIsDebugBuild || maybe_copied) { + if (kIsDebugBuild || is_copied) { in_image_space = header_.GetImageSection(ImageHeader::kSectionDexCacheArrays).Contains( reinterpret_cast(resolved_methods) - header_.GetImageBegin()); } // Must be in image space for non-miranda method. - DCHECK(maybe_copied || in_image_space) + DCHECK(is_copied || in_image_space) << resolved_methods << " is not in image starting at " << reinterpret_cast(header_.GetImageBegin()); - if (!maybe_copied || in_image_space) { + if (!is_copied || in_image_space) { // Go through the array so that we don't need to do a slow map lookup. method->SetDexCacheResolvedMethods(*reinterpret_cast(resolved_methods), sizeof(void*)); @@ -6459,7 +6459,7 @@ bool ClassLinker::LinkInterfaceMethods( for (ArtMethod* mir_method : miranda_methods) { ArtMethod& new_method = *out; new_method.CopyFrom(mir_method, image_pointer_size_); - new_method.SetAccessFlags(new_method.GetAccessFlags() | kAccMiranda); + new_method.SetAccessFlags(new_method.GetAccessFlags() | kAccMiranda | kAccCopied); DCHECK_NE(new_method.GetAccessFlags() & kAccAbstract, 0u) << "Miranda method should be abstract!"; move_table.emplace(mir_method, &new_method); @@ -6478,7 +6478,9 @@ bool ClassLinker::LinkInterfaceMethods( // yet it shouldn't have methods that are skipping access checks. // TODO This is rather arbitrary. We should maybe support classes where only some of its // methods are skip_access_checks. - new_method.SetAccessFlags((new_method.GetAccessFlags() | kAccDefault) & ~kAccSkipAccessChecks); + constexpr uint32_t kSetFlags = kAccDefault | kAccCopied; + constexpr uint32_t kMaskFlags = ~kAccSkipAccessChecks; + new_method.SetAccessFlags((new_method.GetAccessFlags() | kSetFlags) & kMaskFlags); move_table.emplace(def_method, &new_method); ++out; } @@ -6489,7 +6491,7 @@ bool ClassLinker::LinkInterfaceMethods( // this as a default, non-abstract method, since thats what it is. Also clear the // kAccSkipAccessChecks bit since this class hasn't been verified yet it shouldn't have // methods that are skipping access checks. - constexpr uint32_t kSetFlags = kAccDefault | kAccDefaultConflict; + constexpr uint32_t kSetFlags = kAccDefault | kAccDefaultConflict | kAccCopied; constexpr uint32_t kMaskFlags = ~(kAccAbstract | kAccSkipAccessChecks); new_method.SetAccessFlags((new_method.GetAccessFlags() | kSetFlags) & kMaskFlags); DCHECK(new_method.IsDefaultConflicting()); diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc index 5c3029a5f8..488826b6c4 100644 --- a/runtime/class_linker_test.cc +++ b/runtime/class_linker_test.cc @@ -263,7 +263,7 @@ class ClassLinkerTest : public CommonRuntimeTest { for (ArtMethod& method : klass->GetCopiedMethods(sizeof(void*))) { AssertMethod(&method); EXPECT_FALSE(method.IsDirect()); - EXPECT_TRUE(method.MightBeCopied()); + EXPECT_TRUE(method.IsCopied()); EXPECT_TRUE(method.GetDeclaringClass()->IsInterface()) << "declaring class: " << PrettyClass(method.GetDeclaringClass()); EXPECT_TRUE(method.GetDeclaringClass()->IsAssignableFrom(klass.Get())) diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h index 3f806d3ca1..19584edf7f 100644 --- a/runtime/mirror/class-inl.h +++ b/runtime/mirror/class-inl.h @@ -502,7 +502,7 @@ inline ArtMethod* Class::FindVirtualMethodForVirtualOrInterface(ArtMethod* metho if (method->IsDirect()) { return method; } - if (method->GetDeclaringClass()->IsInterface() && !method->IsMiranda()) { + if (method->GetDeclaringClass()->IsInterface() && !method->IsCopied()) { return FindVirtualMethodForInterface(method, pointer_size); } return FindVirtualMethodForVirtual(method, pointer_size); diff --git a/runtime/modifiers.h b/runtime/modifiers.h index ed4c5fc76c..c31b22ee89 100644 --- a/runtime/modifiers.h +++ b/runtime/modifiers.h @@ -50,6 +50,11 @@ static constexpr uint32_t kAccSkipAccessChecks = 0x00080000; // method (de // Used by a class to denote that the verifier has attempted to check it at least once. static constexpr uint32_t kAccVerificationAttempted = 0x00080000; // class (runtime) static constexpr uint32_t kAccFastNative = 0x00080000; // method (dex only) +// This is set by the class linker during LinkInterfaceMethods. It is used by a method to represent +// that it was copied from its declaring class into another class. All methods marked kAccMiranda +// and kAccDefaultConflict will have this bit set. Any kAccDefault method contained in the methods_ +// array of a concrete class will also have this bit set. +static constexpr uint32_t kAccCopied = 0x00100000; // method (runtime) static constexpr uint32_t kAccMiranda = 0x00200000; // method (dex only) static constexpr uint32_t kAccDefault = 0x00400000; // method (runtime) // This is set by the class linker during LinkInterfaceMethods. Prior to that point we do not know diff --git a/test/004-JniTest/expected.txt b/test/004-JniTest/expected.txt index 86ab37e1e5..155c6ae5f3 100644 --- a/test/004-JniTest/expected.txt +++ b/test/004-JniTest/expected.txt @@ -28,3 +28,30 @@ Subclass. RUNNING sub object, sub class, sub nonstatic Subclass.nonstaticMethod PASSED sub object, sub class, sub nonstatic +Calling method ConcreteClass->JniCallNonOverridenDefaultMethod on object of type ConcreteClass +DefaultInterface.JniCallNonOverridenDefaultMethod +Calling method ConcreteClass->JniCallOverridenDefaultMethod on object of type ConcreteClass +ConcreteClass.JniCallOverridenDefaultMethod +Calling method ConcreteClass->JniCallOverridenDefaultMethodWithSuper on object of type ConcreteClass +ConcreteClass.JniCallOverridenDefaultMethodWithSuper +DefaultInterface.JniCallOverridenDefaultMethod +Calling method ConcreteClass->JniCallOverridenAbstractMethod on object of type ConcreteClass +ConcreteClass.JniCallOverridenAbstractMethod +Calling method ConcreteClass->JniCallConflictDefaultMethod on object of type ConcreteClass +EXCEPTION OCCURED: java.lang.IncompatibleClassChangeError: Conflicting default method implementations void ConflictInterface.JniCallConflictDefaultMethod() +Calling method ConcreteClass->JniCallSoftConflictMethod on object of type ConcreteClass +DefaultInterface.JniCallSoftConflictMethod +Calling method DefaultInterface->JniCallNonOverridenDefaultMethod on object of type ConcreteClass +DefaultInterface.JniCallNonOverridenDefaultMethod +Calling method DefaultInterface->JniCallOverridenDefaultMethod on object of type ConcreteClass +ConcreteClass.JniCallOverridenDefaultMethod +Calling method DefaultInterface->JniCallOverridenAbstractMethod on object of type ConcreteClass +ConcreteClass.JniCallOverridenAbstractMethod +Calling method DefaultInterface->JniCallConflictDefaultMethod on object of type ConcreteClass +EXCEPTION OCCURED: java.lang.IncompatibleClassChangeError: Conflicting default method implementations void ConflictInterface.JniCallConflictDefaultMethod() +Calling method DefaultInterface->JniCallSoftConflictMethod on object of type ConcreteClass +DefaultInterface.JniCallSoftConflictMethod +Calling method AbstractInterface->JniCallSoftConflictMethod on object of type ConcreteClass +DefaultInterface.JniCallSoftConflictMethod +Calling method ConflictInterface->JniCallConflictDefaultMethod on object of type ConcreteClass +EXCEPTION OCCURED: java.lang.IncompatibleClassChangeError: Conflicting default method implementations void ConflictInterface.JniCallConflictDefaultMethod() diff --git a/test/004-JniTest/jni_test.cc b/test/004-JniTest/jni_test.cc index 70454828f1..f632331fe3 100644 --- a/test/004-JniTest/jni_test.cc +++ b/test/004-JniTest/jni_test.cc @@ -659,3 +659,65 @@ extern "C" JNIEXPORT void JNICALL Java_Main_enterJniCriticalSection(JNIEnv* env, env->ReleasePrimitiveArrayCritical(array0, data0, 0); } } + +class JniCallDefaultMethodsTest { + public: + explicit JniCallDefaultMethodsTest(JNIEnv* env) + : env_(env), concrete_class_(env_->FindClass("ConcreteClass")) { + assert(!env_->ExceptionCheck()); + assert(concrete_class_ != nullptr); + } + + void Test() { + TestCalls("ConcreteClass", { "JniCallNonOverridenDefaultMethod", + "JniCallOverridenDefaultMethod", + "JniCallOverridenDefaultMethodWithSuper", + "JniCallOverridenAbstractMethod", + "JniCallConflictDefaultMethod", + "JniCallSoftConflictMethod" }); + TestCalls("DefaultInterface", { "JniCallNonOverridenDefaultMethod", + "JniCallOverridenDefaultMethod", + "JniCallOverridenAbstractMethod", + "JniCallConflictDefaultMethod", + "JniCallSoftConflictMethod" }); + TestCalls("AbstractInterface", { "JniCallSoftConflictMethod" }); + TestCalls("ConflictInterface", { "JniCallConflictDefaultMethod" }); + } + + private: + void TestCalls(const char* declaring_class, std::vector methods) { + jmethodID new_method = env_->GetMethodID(concrete_class_, "", "()V"); + jobject obj = env_->NewObject(concrete_class_, new_method); + assert(!env_->ExceptionCheck()); + assert(obj != nullptr); + jclass decl_class = env_->FindClass(declaring_class); + assert(!env_->ExceptionCheck()); + assert(decl_class != nullptr); + for (const char* method : methods) { + jmethodID method_id = env_->GetMethodID(decl_class, method, "()V"); + assert(!env_->ExceptionCheck()); + printf("Calling method %s->%s on object of type ConcreteClass\n", declaring_class, method); + env_->CallVoidMethod(obj, method_id); + if (env_->ExceptionCheck()) { + jthrowable thrown = env_->ExceptionOccurred(); + env_->ExceptionClear(); + jmethodID to_string = env_->GetMethodID( + env_->FindClass("java/lang/Object"), "toString", "()Ljava/lang/String;"); + jstring exception_string = (jstring) env_->CallObjectMethod(thrown, to_string); + assert(!env_->ExceptionCheck()); + const char* exception_string_utf8 = env_->GetStringUTFChars(exception_string, nullptr); + assert(!env_->ExceptionCheck()); + assert(exception_string_utf8 != nullptr); + printf("EXCEPTION OCCURED: %s\n", exception_string_utf8); + env_->ReleaseStringUTFChars(exception_string, exception_string_utf8); + } + } + } + + JNIEnv* env_; + jclass concrete_class_; +}; + +extern "C" JNIEXPORT void JNICALL Java_Main_testCallDefaultMethods(JNIEnv* env) { + JniCallDefaultMethodsTest(env).Test(); +} diff --git a/test/004-JniTest/smali/AbstractInterface.smali b/test/004-JniTest/smali/AbstractInterface.smali new file mode 100644 index 0000000000..52b2fc537e --- /dev/null +++ b/test/004-JniTest/smali/AbstractInterface.smali @@ -0,0 +1,26 @@ +# /* +# * Copyright 2016 The Android Open Source Project +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +# */ + +.class public interface LAbstractInterface; +.super Ljava/lang/Object; + +# public interface AbstractInterface { +# public void JniCallSoftConflictMethod(); +# } + +.method public abstract JniCallSoftConflictMethod()V +.end method + diff --git a/test/004-JniTest/smali/ConcreteClass.smali b/test/004-JniTest/smali/ConcreteClass.smali new file mode 100644 index 0000000000..a9c072fc2f --- /dev/null +++ b/test/004-JniTest/smali/ConcreteClass.smali @@ -0,0 +1,72 @@ +# /* +# * Copyright 2016 The Android Open Source Project +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +# */ + +.class public LConcreteClass; +.super Ljava/lang/Object; +.implements LDefaultInterface; +.implements LConflictInterface; +.implements LAbstractInterface; + +# public class ConcreteClass implements DefaultInterface, ConflictInterface, AbstractInterface { +# public void JniCallOverridenAbstractMethod() { +# System.out.println("ConcreteClass.JniCallOverridenAbstractMethod"); +# } +# +# public void JniCallOverridenDefaultMethod() { +# System.out.println("ConcreteClass.JniCallOverridenDefaultMethod"); +# } +# +# public void JniCallOverridenDefaultMethodWithSuper() { +# System.out.println("ConcreteClass.JniCallOverridenDefaultMethodWithSuper"); +# DefaultInterface.super.JniCallOverridenDefaultMethod(); +# } +# } + +.method public constructor ()V + .registers 1 + invoke-direct {p0}, Ljava/lang/Object;->()V + return-void +.end method + +.method public JniCallOverridenAbstractMethod()V + .locals 2 + + const-string v0, "ConcreteClass.JniCallOverridenAbstractMethod" + sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream; + invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V + return-void +.end method + +.method public JniCallOverridenDefaultMethod()V + .locals 2 + + const-string v0, "ConcreteClass.JniCallOverridenDefaultMethod" + sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream; + invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V + return-void +.end method + +.method public JniCallOverridenDefaultMethodWithSuper()V + .locals 2 + + const-string v0, "ConcreteClass.JniCallOverridenDefaultMethodWithSuper" + sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream; + invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V + + invoke-super {p0}, LDefaultInterface;->JniCallOverridenDefaultMethod()V + + return-void +.end method diff --git a/test/004-JniTest/smali/ConflictInterface.smali b/test/004-JniTest/smali/ConflictInterface.smali new file mode 100644 index 0000000000..fc3d474df0 --- /dev/null +++ b/test/004-JniTest/smali/ConflictInterface.smali @@ -0,0 +1,35 @@ +# /* +# * Copyright 2016 The Android Open Source Project +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +# */ + +.class public interface LConflictInterface; +.super Ljava/lang/Object; + +# public interface ConflictInterface { +# public default void JniCallConflictDefaultMethod() { +# System.out.println("ConflictInterface.JniCallConflictDefaultMethod"); +# } +# +# } + +.method public JniCallConflictDefaultMethod()V + .locals 2 + + const-string v0, "ConflictInterface.JniCallConflictDefaultMethod" + sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream; + invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V + return-void +.end method + diff --git a/test/004-JniTest/smali/DefaultInterface.smali b/test/004-JniTest/smali/DefaultInterface.smali new file mode 100644 index 0000000000..1ee872154b --- /dev/null +++ b/test/004-JniTest/smali/DefaultInterface.smali @@ -0,0 +1,77 @@ +# /* +# * Copyright 2016 The Android Open Source Project +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +# */ + +.class public interface LDefaultInterface; +.super Ljava/lang/Object; + +# public interface DefaultInterface { +# public default void JniCallNonOverridenDefaultMethod() { +# System.out.println("DefaultInterface.JniCallNonOverridenDefaultMethod"); +# } +# +# public default void JniCallOverridenDefaultMethod() { +# System.out.println("DefaultInterface.JniCallOverridenDefaultMethod"); +# } +# +# public void JniCallOverridenAbstractMethod(); +# +# public default void JniCallConflictDefaultMethod() { +# System.out.println("DefaultInterface.JniCallConflictDefaultMethod"); +# } +# +# public default void JniCallSoftConflictMethod() { +# System.out.println("DefaultInterface.JniCallSoftConflictMethod"); +# } +# } + +.method public JniCallNonOverridenDefaultMethod()V + .locals 2 + + const-string v0, "DefaultInterface.JniCallNonOverridenDefaultMethod" + sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream; + invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V + return-void +.end method + +.method public JniCallOverridenDefaultMethod()V + .locals 2 + + const-string v0, "DefaultInterface.JniCallOverridenDefaultMethod" + sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream; + invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V + return-void +.end method + +.method public abstract JniCallOverridenAbstractMethod()V +.end method + +.method public JniCallConflictDefaultMethod()V + .locals 2 + + const-string v0, "DefaultInterface.JniCallConflictDefaultMethod" + sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream; + invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V + return-void +.end method + +.method public JniCallSoftConflictMethod()V + .locals 2 + + const-string v0, "DefaultInterface.JniCallSoftConflictMethod" + sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream; + invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V + return-void +.end method diff --git a/test/004-JniTest/src/Main.java b/test/004-JniTest/src/Main.java index 5c39ede687..9f4a8522e7 100644 --- a/test/004-JniTest/src/Main.java +++ b/test/004-JniTest/src/Main.java @@ -39,8 +39,11 @@ public class Main { testRemoveLocalObject(); testProxyGetMethodID(); testJniCriticalSectionAndGc(); + testCallDefaultMethods(); } + private static native void testCallDefaultMethods(); + private static native void testFindClassOnAttachedNativeThread(); private static boolean testFindFieldOnAttachedNativeThreadField; @@ -121,7 +124,7 @@ public class Main { private static void testRemoveLocalObject() { removeLocalObject(new Object()); } - + private static native short shortMethod(short s1, short s2, short s3, short s4, short s5, short s6, short s7, short s8, short s9, short s10); -- GitLab From 6ea1a0e2168c8d9b6d97c075c73a72d84080f45b Mon Sep 17 00:00:00 2001 From: Mingyao Yang Date: Fri, 29 Jan 2016 12:12:49 -0800 Subject: [PATCH 015/204] AOT compile framework code as non-debuggable When a debugger attaches, we patch method entry points in framework code to interpreter bridge. The code will later be jitted as debuggable. Change-Id: Id148069ccad95e2339ba214742ae3ef4f084f495 --- dex2oat/dex2oat.cc | 5 ----- runtime/class_linker.cc | 17 +++++------------ runtime/class_linker.h | 6 ------ runtime/debugger.cc | 32 ++++++++++++++++++++++++++++++++ runtime/gc/heap.cc | 9 +++++++++ runtime/gc/heap.h | 3 +++ runtime/instrumentation.cc | 23 ++++++++++++++++++++--- runtime/instrumentation.h | 5 +++++ runtime/oat_file.h | 4 ++++ 9 files changed, 78 insertions(+), 26 deletions(-) diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index f7efdc5bac..5756891617 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -695,11 +695,6 @@ class Dex2Oat FINAL { Usage("Can't have both --image and (--app-image-fd or --app-image-file)"); } - if (IsBootImage()) { - // We need the boot image to always be debuggable. - compiler_options_->debuggable_ = true; - } - if (oat_filenames_.empty() && oat_fd_ == -1) { Usage("Output must be supplied with either --oat-file or --oat-fd"); } diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 936c98875f..7397709ca9 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -2608,18 +2608,6 @@ const void* ClassLinker::GetOatMethodQuickCodeFor(ArtMethod* method) { return nullptr; } -const void* ClassLinker::GetQuickOatCodeFor(const DexFile& dex_file, - uint16_t class_def_idx, - uint32_t method_idx) { - bool found; - OatFile::OatClass oat_class = FindOatClass(dex_file, class_def_idx, &found); - if (!found) { - return nullptr; - } - uint32_t oat_method_idx = GetOatMethodIndexFromMethodIndex(dex_file, class_def_idx, method_idx); - return oat_class.GetOatMethod(oat_method_idx).GetQuickCode(); -} - bool ClassLinker::ShouldUseInterpreterEntrypoint(ArtMethod* method, const void* quick_code) { if (UNLIKELY(method->IsNative() || method->IsProxyMethod())) { return false; @@ -2650,6 +2638,11 @@ bool ClassLinker::ShouldUseInterpreterEntrypoint(ArtMethod* method, const void* return true; } + if (Dbg::IsDebuggerActive()) { + // Boot image classes are AOT-compiled as non-debuggable. + return runtime->GetHeap()->IsInBootImageOatFile(quick_code); + } + return false; } diff --git a/runtime/class_linker.h b/runtime/class_linker.h index a9448f732c..aa55dac7be 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -472,12 +472,6 @@ class ClassLinker { const void* GetQuickOatCodeFor(ArtMethod* method) SHARED_REQUIRES(Locks::mutator_lock_); - // Get the oat code for a method from a method index. - const void* GetQuickOatCodeFor(const DexFile& dex_file, - uint16_t class_def_idx, - uint32_t method_idx) - SHARED_REQUIRES(Locks::mutator_lock_); - // Get compiled code for a method, return null if no code // exists. This is unlike Get..OatCodeFor which will return a bridge // or interpreter entrypoint. diff --git a/runtime/debugger.cc b/runtime/debugger.cc index 904490aa8c..bc6589380c 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -28,6 +28,7 @@ #include "class_linker-inl.h" #include "dex_file-inl.h" #include "dex_instruction.h" +#include "entrypoints/runtime_asm_entrypoints.h" #include "gc/accounting/card_table-inl.h" #include "gc/allocation_record.h" #include "gc/scoped_gc_critical_section.h" @@ -570,6 +571,29 @@ bool Dbg::RequiresDeoptimization() { return !Runtime::Current()->GetInstrumentation()->IsForcedInterpretOnly(); } +// Used to patch boot image method entry point to interpreter bridge. +class UpdateEntryPointsClassVisitor : public ClassVisitor { + public: + explicit UpdateEntryPointsClassVisitor(instrumentation::Instrumentation* instrumentation) + : instrumentation_(instrumentation) {} + + bool operator()(mirror::Class* klass) OVERRIDE REQUIRES(Locks::mutator_lock_) { + auto pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); + for (auto& m : klass->GetMethods(pointer_size)) { + const void* code = m.GetEntryPointFromQuickCompiledCode(); + if (Runtime::Current()->GetHeap()->IsInBootImageOatFile(code) && + !m.IsNative() && + !m.IsProxyMethod()) { + instrumentation_->UpdateMethodsCode(&m, GetQuickToInterpreterBridge()); + } + } + return true; + } + + private: + instrumentation::Instrumentation* const instrumentation_; +}; + void Dbg::GoActive() { // Enable all debugging features, including scans for breakpoints. // This is a no-op if we're already active. @@ -598,6 +622,14 @@ void Dbg::GoActive() { } Runtime* runtime = Runtime::Current(); + // Since boot image code is AOT compiled as not debuggable, we need to patch + // entry points of methods in boot image to interpreter bridge. + if (!runtime->GetInstrumentation()->IsForcedInterpretOnly()) { + ScopedObjectAccess soa(self); + UpdateEntryPointsClassVisitor visitor(runtime->GetInstrumentation()); + runtime->GetClassLinker()->VisitClasses(&visitor); + } + ScopedSuspendAll ssa(__FUNCTION__); if (RequiresDeoptimization()) { runtime->GetInstrumentation()->EnableDeoptimization(); diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index a656fb8faf..4bee46285d 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -4058,6 +4058,15 @@ bool Heap::ObjectIsInBootImageSpace(mirror::Object* obj) const { return false; } +bool Heap::IsInBootImageOatFile(const void* p) const { + for (gc::space::ImageSpace* space : boot_image_spaces_) { + if (space->GetOatFile()->Contains(p)) { + return true; + } + } + return false; +} + void Heap::GetBootImagesSize(uint32_t* boot_image_begin, uint32_t* boot_image_end, uint32_t* boot_oat_begin, diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index a181e23b53..6edb548155 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -605,6 +605,9 @@ class Heap { bool ObjectIsInBootImageSpace(mirror::Object* obj) const SHARED_REQUIRES(Locks::mutator_lock_); + bool IsInBootImageOatFile(const void* p) const + SHARED_REQUIRES(Locks::mutator_lock_); + void GetBootImagesSize(uint32_t* boot_image_begin, uint32_t* boot_image_end, uint32_t* boot_oat_begin, diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc index 748463529e..b107b72b55 100644 --- a/runtime/instrumentation.cc +++ b/runtime/instrumentation.cc @@ -104,6 +104,14 @@ static void UpdateEntrypoints(ArtMethod* method, const void* quick_code) method->SetEntryPointFromQuickCompiledCode(quick_code); } +bool Instrumentation::NeedDebugVersionForBootImageCode(ArtMethod* method, const void* code) const + SHARED_REQUIRES(Locks::mutator_lock_) { + return Dbg::IsDebuggerActive() && + Runtime::Current()->GetHeap()->IsInBootImageOatFile(code) && + !method->IsNative() && + !method->IsProxyMethod(); +} + void Instrumentation::InstallStubsForMethod(ArtMethod* method) { if (!method->IsInvokable() || method->IsProxyMethod()) { // Do not change stubs for these methods. @@ -124,6 +132,9 @@ void Instrumentation::InstallStubsForMethod(ArtMethod* method) { new_quick_code = GetQuickToInterpreterBridge(); } else if (is_class_initialized || !method->IsStatic() || method->IsConstructor()) { new_quick_code = class_linker->GetQuickOatCodeFor(method); + if (NeedDebugVersionForBootImageCode(method, new_quick_code)) { + new_quick_code = GetQuickToInterpreterBridge(); + } } else { new_quick_code = GetQuickResolutionStub(); } @@ -136,10 +147,13 @@ void Instrumentation::InstallStubsForMethod(ArtMethod* method) { // class, all its static methods code will be set to the instrumentation entry point. // For more details, see ClassLinker::FixupStaticTrampolines. if (is_class_initialized || !method->IsStatic() || method->IsConstructor()) { - if (entry_exit_stubs_installed_) { + new_quick_code = class_linker->GetQuickOatCodeFor(method); + if (NeedDebugVersionForBootImageCode(method, new_quick_code)) { + // Oat code should not be used. Don't install instrumentation stub and + // use interpreter for instrumentation. + new_quick_code = GetQuickToInterpreterBridge(); + } else if (entry_exit_stubs_installed_) { new_quick_code = GetQuickInstrumentationEntryPoint(); - } else { - new_quick_code = class_linker->GetQuickOatCodeFor(method); } } else { new_quick_code = GetQuickResolutionStub(); @@ -775,6 +789,9 @@ void Instrumentation::Undeoptimize(ArtMethod* method) { UpdateEntrypoints(method, GetQuickResolutionStub()); } else { const void* quick_code = class_linker->GetQuickOatCodeFor(method); + if (NeedDebugVersionForBootImageCode(method, quick_code)) { + quick_code = GetQuickToInterpreterBridge(); + } UpdateEntrypoints(method, quick_code); } diff --git a/runtime/instrumentation.h b/runtime/instrumentation.h index e3cbf53873..2e4be6b689 100644 --- a/runtime/instrumentation.h +++ b/runtime/instrumentation.h @@ -247,6 +247,11 @@ class Instrumentation { return forced_interpret_only_; } + // Code is in boot image oat file which isn't compiled as debuggable. + // Need debug version (interpreter or jitted) if that's the case. + bool NeedDebugVersionForBootImageCode(ArtMethod* method, const void* code) const + SHARED_REQUIRES(Locks::mutator_lock_); + bool AreExitStubsInstalled() const { return instrumentation_stubs_installed_; } diff --git a/runtime/oat_file.h b/runtime/oat_file.h index 910163c787..fb91a8cdff 100644 --- a/runtime/oat_file.h +++ b/runtime/oat_file.h @@ -228,6 +228,10 @@ class OatFile { return End() - Begin(); } + bool Contains(const void* p) const { + return p >= Begin() && p < End(); + } + size_t BssSize() const { return BssEnd() - BssBegin(); } -- GitLab From 7ea5747d60d368727adf49023256d9cf43f56cfd Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Wed, 24 Feb 2016 09:53:09 +0000 Subject: [PATCH 016/204] Show battery level fo devices on buildbot. Change-Id: Ic8279d1905c8a6efe08ca190f1bb7332842e1904 --- tools/setup-buildbot-device.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/setup-buildbot-device.sh b/tools/setup-buildbot-device.sh index d5b89897e5..45b60dc647 100755 --- a/tools/setup-buildbot-device.sh +++ b/tools/setup-buildbot-device.sh @@ -34,6 +34,9 @@ adb shell getprop echo -e "${green}Uptime${nc}" adb shell uptime +echo -e "${green}Battery info${nc}" +adb shell dumpsys battery + echo -e "${green}Kill stalled dalvikvm processes${nc}" processes=$(adb shell "ps" | grep dalvikvm | awk '{print $2}') for i in $processes; do adb shell kill -9 $i; done -- GitLab From 2de973de1094b6598d4d2b3457a3490d40c5d4fb Mon Sep 17 00:00:00 2001 From: buzbee Date: Tue, 23 Feb 2016 13:25:00 -0800 Subject: [PATCH 017/204] ART: Enable JitProfiling for x86 Mterp Adds branch profiling and enables for x86. Change-Id: I875034d5bc6b639df08a0236e415195521994238 --- runtime/interpreter/mterp/mterp.cc | 2 +- runtime/interpreter/mterp/out/mterp_x86.S | 316 +++++++----------- runtime/interpreter/mterp/x86/bincmp.S | 17 +- runtime/interpreter/mterp/x86/footer.S | 16 +- runtime/interpreter/mterp/x86/header.S | 23 +- runtime/interpreter/mterp/x86/op_goto.S | 15 +- runtime/interpreter/mterp/x86/op_goto_16.S | 15 +- runtime/interpreter/mterp/x86/op_goto_32.S | 15 +- .../interpreter/mterp/x86/op_packed_switch.S | 14 +- runtime/interpreter/mterp/x86/zcmp.S | 17 +- 10 files changed, 196 insertions(+), 254 deletions(-) diff --git a/runtime/interpreter/mterp/mterp.cc b/runtime/interpreter/mterp/mterp.cc index 8f4741c3ef..b443c69718 100644 --- a/runtime/interpreter/mterp/mterp.cc +++ b/runtime/interpreter/mterp/mterp.cc @@ -149,7 +149,7 @@ extern "C" bool MterpShouldSwitchInterpreters() Runtime::Current()->GetInstrumentation(); bool unhandled_instrumentation; // TODO: enable for other targets after more extensive testing. - if ((kRuntimeISA == kArm64) || (kRuntimeISA == kArm)) { + if ((kRuntimeISA == kArm64) || (kRuntimeISA == kArm) || (kRuntimeISA == kX86)) { unhandled_instrumentation = instrumentation->NonJitProfilingActive(); } else { unhandled_instrumentation = instrumentation->IsActive(); diff --git a/runtime/interpreter/mterp/out/mterp_x86.S b/runtime/interpreter/mterp/out/mterp_x86.S index d365a4f986..b05360b6ae 100644 --- a/runtime/interpreter/mterp/out/mterp_x86.S +++ b/runtime/interpreter/mterp/out/mterp_x86.S @@ -163,13 +163,26 @@ unspecified registers or condition codes. #define OFF_FP_CODE_ITEM OFF_FP(SHADOWFRAME_CODE_ITEM_OFFSET) #define OFF_FP_SHADOWFRAME (-SHADOWFRAME_VREGS_OFFSET) +#define MTERP_PROFILE_BRANCHES 1 +#define MTERP_LOGGING 0 + /* - * - * The reference interpreter performs explicit suspect checks, which is somewhat wasteful. - * Dalvik's interpreter folded suspend checks into the jump table mechanism, and eventually - * mterp should do so as well. + * Profile branch. rINST should contain the offset. %eax is scratch. */ -#define MTERP_SUSPEND 0 +.macro MTERP_PROFILE_BRANCH +#ifdef MTERP_PROFILE_BRANCHES + EXPORT_PC + movl rSELF, %eax + movl %eax, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + movl rINST, OUT_ARG2(%esp) + call SYMBOL(MterpProfileBranch) + testb %al, %al + jnz MterpOnStackReplacement + RESTORE_IBASE +#endif +.endm /* * "export" the PC to dex_pc field in the shadow frame, f/b/o future exception objects. Must @@ -1076,17 +1089,12 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop * double to get a byte offset. */ /* goto +AA */ - movsbl rINSTbl, %eax # eax <- ssssssAA - addl %eax, %eax # eax <- AA * 2 - leal (rPC, %eax), rPC + movsbl rINSTbl, rINST # rINST <- ssssssAA + MTERP_PROFILE_BRANCH + addl rINST, rINST # rINST <- AA * 2 + leal (rPC, rINST), rPC FETCH_INST - jg 1f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -1: + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check GOTO_NEXT /* ------------------------------ */ @@ -1100,17 +1108,12 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop * double to get a byte offset. */ /* goto/16 +AAAA */ - movswl 2(rPC), %eax # eax <- ssssAAAA - addl %eax, %eax # eax <- AA * 2 - leal (rPC, %eax), rPC + movswl 2(rPC), rINST # rINST <- ssssAAAA + MTERP_PROFILE_BRANCH + addl rINST, rINST # rINST <- AA * 2 + leal (rPC, rINST), rPC FETCH_INST - jg 1f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -1: + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check GOTO_NEXT /* ------------------------------ */ @@ -1129,17 +1132,12 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop * offset to byte offset. */ /* goto/32 +AAAAAAAA */ - movl 2(rPC), %eax # eax <- AAAAAAAA - addl %eax, %eax # eax <- AA * 2 - leal (rPC, %eax), rPC + movl 2(rPC), rINST # rINST <- AAAAAAAA + MTERP_PROFILE_BRANCH + addl rINST, rINST # rINST <- AA * 2 + leal (rPC, rINST), rPC FETCH_INST - jg 1f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -1: + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check GOTO_NEXT /* ------------------------------ */ @@ -1162,17 +1160,13 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop movl %eax, OUT_ARG1(%esp) # ARG1 <- vAA movl %ecx, OUT_ARG0(%esp) # ARG0 <- switchData call SYMBOL(MterpDoPackedSwitch) - addl %eax, %eax - leal (rPC, %eax), rPC + movl %eax, rINST + MTERP_PROFILE_BRANCH + addl rINST, rINST + leal (rPC, rINST), rPC FETCH_INST REFRESH_IBASE - jg 1f -#if MTERP_SUSPEND - # REFRESH_IBASE - we did it above. -#else - jmp MterpCheckSuspendAndContinue -#endif -1: + jle MterpCheckSuspendAndContinue GOTO_NEXT /* ------------------------------ */ @@ -1196,17 +1190,13 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop movl %eax, OUT_ARG1(%esp) # ARG1 <- vAA movl %ecx, OUT_ARG0(%esp) # ARG0 <- switchData call SYMBOL(MterpDoSparseSwitch) - addl %eax, %eax - leal (rPC, %eax), rPC + movl %eax, rINST + MTERP_PROFILE_BRANCH + addl rINST, rINST + leal (rPC, rINST), rPC FETCH_INST REFRESH_IBASE - jg 1f -#if MTERP_SUSPEND - # REFRESH_IBASE - we did it above. -#else - jmp MterpCheckSuspendAndContinue -#endif -1: + jle MterpCheckSuspendAndContinue GOTO_NEXT @@ -1424,20 +1414,15 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop GET_VREG %eax, %ecx # eax <- vA sarl $4, rINST # rINST <- B cmpl VREG_ADDRESS(rINST), %eax # compare (vA, vB) - movl $2, %eax # assume not taken + movl $2, rINST jne 1f - movswl 2(rPC),%eax # Get signed branch offset + movswl 2(rPC), rINST # Get signed branch offset 1: - addl %eax, %eax # eax <- AA * 2 - leal (rPC, %eax), rPC + MTERP_PROFILE_BRANCH + addl rINST, rINST # eax <- AA * 2 + leal (rPC, rINST), rPC FETCH_INST - jg 2f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -2: + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check GOTO_NEXT @@ -1459,20 +1444,15 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop GET_VREG %eax, %ecx # eax <- vA sarl $4, rINST # rINST <- B cmpl VREG_ADDRESS(rINST), %eax # compare (vA, vB) - movl $2, %eax # assume not taken + movl $2, rINST je 1f - movswl 2(rPC),%eax # Get signed branch offset + movswl 2(rPC), rINST # Get signed branch offset 1: - addl %eax, %eax # eax <- AA * 2 - leal (rPC, %eax), rPC + MTERP_PROFILE_BRANCH + addl rINST, rINST # eax <- AA * 2 + leal (rPC, rINST), rPC FETCH_INST - jg 2f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -2: + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check GOTO_NEXT @@ -1494,20 +1474,15 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop GET_VREG %eax, %ecx # eax <- vA sarl $4, rINST # rINST <- B cmpl VREG_ADDRESS(rINST), %eax # compare (vA, vB) - movl $2, %eax # assume not taken + movl $2, rINST jge 1f - movswl 2(rPC),%eax # Get signed branch offset + movswl 2(rPC), rINST # Get signed branch offset 1: - addl %eax, %eax # eax <- AA * 2 - leal (rPC, %eax), rPC + MTERP_PROFILE_BRANCH + addl rINST, rINST # eax <- AA * 2 + leal (rPC, rINST), rPC FETCH_INST - jg 2f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -2: + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check GOTO_NEXT @@ -1529,20 +1504,15 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop GET_VREG %eax, %ecx # eax <- vA sarl $4, rINST # rINST <- B cmpl VREG_ADDRESS(rINST), %eax # compare (vA, vB) - movl $2, %eax # assume not taken + movl $2, rINST jl 1f - movswl 2(rPC),%eax # Get signed branch offset + movswl 2(rPC), rINST # Get signed branch offset 1: - addl %eax, %eax # eax <- AA * 2 - leal (rPC, %eax), rPC + MTERP_PROFILE_BRANCH + addl rINST, rINST # eax <- AA * 2 + leal (rPC, rINST), rPC FETCH_INST - jg 2f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -2: + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check GOTO_NEXT @@ -1564,20 +1534,15 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop GET_VREG %eax, %ecx # eax <- vA sarl $4, rINST # rINST <- B cmpl VREG_ADDRESS(rINST), %eax # compare (vA, vB) - movl $2, %eax # assume not taken + movl $2, rINST jle 1f - movswl 2(rPC),%eax # Get signed branch offset + movswl 2(rPC), rINST # Get signed branch offset 1: - addl %eax, %eax # eax <- AA * 2 - leal (rPC, %eax), rPC + MTERP_PROFILE_BRANCH + addl rINST, rINST # eax <- AA * 2 + leal (rPC, rINST), rPC FETCH_INST - jg 2f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -2: + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check GOTO_NEXT @@ -1599,20 +1564,15 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop GET_VREG %eax, %ecx # eax <- vA sarl $4, rINST # rINST <- B cmpl VREG_ADDRESS(rINST), %eax # compare (vA, vB) - movl $2, %eax # assume not taken + movl $2, rINST jg 1f - movswl 2(rPC),%eax # Get signed branch offset + movswl 2(rPC), rINST # Get signed branch offset 1: - addl %eax, %eax # eax <- AA * 2 - leal (rPC, %eax), rPC + MTERP_PROFILE_BRANCH + addl rINST, rINST # eax <- AA * 2 + leal (rPC, rINST), rPC FETCH_INST - jg 2f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -2: + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check GOTO_NEXT @@ -1630,20 +1590,15 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop */ /* if-cmp vAA, +BBBB */ cmpl $0, VREG_ADDRESS(rINST) # compare (vA, 0) - movl $2, %eax # assume branch not taken + movl $2, rINST jne 1f - movswl 2(rPC),%eax # fetch signed displacement + movswl 2(rPC), rINST # fetch signed displacement 1: - addl %eax, %eax # eax <- AA * 2 - leal (rPC, %eax), rPC + MTERP_PROFILE_BRANCH + addl rINST, rINST # eax <- AA * 2 + leal (rPC, rINST), rPC FETCH_INST - jg 2f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -2: + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check GOTO_NEXT @@ -1661,20 +1616,15 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop */ /* if-cmp vAA, +BBBB */ cmpl $0, VREG_ADDRESS(rINST) # compare (vA, 0) - movl $2, %eax # assume branch not taken + movl $2, rINST je 1f - movswl 2(rPC),%eax # fetch signed displacement + movswl 2(rPC), rINST # fetch signed displacement 1: - addl %eax, %eax # eax <- AA * 2 - leal (rPC, %eax), rPC + MTERP_PROFILE_BRANCH + addl rINST, rINST # eax <- AA * 2 + leal (rPC, rINST), rPC FETCH_INST - jg 2f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -2: + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check GOTO_NEXT @@ -1692,20 +1642,15 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop */ /* if-cmp vAA, +BBBB */ cmpl $0, VREG_ADDRESS(rINST) # compare (vA, 0) - movl $2, %eax # assume branch not taken + movl $2, rINST jge 1f - movswl 2(rPC),%eax # fetch signed displacement + movswl 2(rPC), rINST # fetch signed displacement 1: - addl %eax, %eax # eax <- AA * 2 - leal (rPC, %eax), rPC + MTERP_PROFILE_BRANCH + addl rINST, rINST # eax <- AA * 2 + leal (rPC, rINST), rPC FETCH_INST - jg 2f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -2: + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check GOTO_NEXT @@ -1723,20 +1668,15 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop */ /* if-cmp vAA, +BBBB */ cmpl $0, VREG_ADDRESS(rINST) # compare (vA, 0) - movl $2, %eax # assume branch not taken + movl $2, rINST jl 1f - movswl 2(rPC),%eax # fetch signed displacement + movswl 2(rPC), rINST # fetch signed displacement 1: - addl %eax, %eax # eax <- AA * 2 - leal (rPC, %eax), rPC + MTERP_PROFILE_BRANCH + addl rINST, rINST # eax <- AA * 2 + leal (rPC, rINST), rPC FETCH_INST - jg 2f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -2: + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check GOTO_NEXT @@ -1754,20 +1694,15 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop */ /* if-cmp vAA, +BBBB */ cmpl $0, VREG_ADDRESS(rINST) # compare (vA, 0) - movl $2, %eax # assume branch not taken + movl $2, rINST jle 1f - movswl 2(rPC),%eax # fetch signed displacement + movswl 2(rPC), rINST # fetch signed displacement 1: - addl %eax, %eax # eax <- AA * 2 - leal (rPC, %eax), rPC + MTERP_PROFILE_BRANCH + addl rINST, rINST # eax <- AA * 2 + leal (rPC, rINST), rPC FETCH_INST - jg 2f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -2: + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check GOTO_NEXT @@ -1785,20 +1720,15 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop */ /* if-cmp vAA, +BBBB */ cmpl $0, VREG_ADDRESS(rINST) # compare (vA, 0) - movl $2, %eax # assume branch not taken + movl $2, rINST jg 1f - movswl 2(rPC),%eax # fetch signed displacement + movswl 2(rPC), rINST # fetch signed displacement 1: - addl %eax, %eax # eax <- AA * 2 - leal (rPC, %eax), rPC + MTERP_PROFILE_BRANCH + addl rINST, rINST # eax <- AA * 2 + leal (rPC, rINST), rPC FETCH_INST - jg 2f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -2: + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check GOTO_NEXT @@ -12818,7 +12748,6 @@ SYMBOL(artMterpAsmAltInstructionEnd): * has not yet been thrown. Just bail out to the reference interpreter to deal with it. * TUNING: for consistency, we may want to just go ahead and handle these here. */ -#define MTERP_LOGGING 0 common_errDivideByZero: EXPORT_PC #if MTERP_LOGGING @@ -12948,6 +12877,21 @@ MterpCheckSuspendAndContinue: 1: GOTO_NEXT +/* + * On-stack replacement has happened, and now we've returned from the compiled method. + */ +MterpOnStackReplacement: +#if MTERP_LOGGING + movl rSELF, %eax + movl %eax, OUT_ARG0(%esp) + lea OFF_FP_SHADOWFRAME(rFP), %ecx + movl %ecx, OUT_ARG1(%esp) + movl rINST, OUT_ARG2(%esp) + call SYMBOL(MterpLogOSR) +#endif + movl $1, %eax + jmp MterpDone + /* * Bail out to reference interpreter. */ diff --git a/runtime/interpreter/mterp/x86/bincmp.S b/runtime/interpreter/mterp/x86/bincmp.S index 27cf6ea6d4..c72a5cf9d4 100644 --- a/runtime/interpreter/mterp/x86/bincmp.S +++ b/runtime/interpreter/mterp/x86/bincmp.S @@ -11,18 +11,13 @@ GET_VREG %eax, %ecx # eax <- vA sarl $$4, rINST # rINST <- B cmpl VREG_ADDRESS(rINST), %eax # compare (vA, vB) - movl $$2, %eax # assume not taken + movl $$2, rINST j${revcmp} 1f - movswl 2(rPC),%eax # Get signed branch offset + movswl 2(rPC), rINST # Get signed branch offset 1: - addl %eax, %eax # eax <- AA * 2 - leal (rPC, %eax), rPC + MTERP_PROFILE_BRANCH + addl rINST, rINST # eax <- AA * 2 + leal (rPC, rINST), rPC FETCH_INST - jg 2f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -2: + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check GOTO_NEXT diff --git a/runtime/interpreter/mterp/x86/footer.S b/runtime/interpreter/mterp/x86/footer.S index a1532fa710..c67491e577 100644 --- a/runtime/interpreter/mterp/x86/footer.S +++ b/runtime/interpreter/mterp/x86/footer.S @@ -12,7 +12,6 @@ * has not yet been thrown. Just bail out to the reference interpreter to deal with it. * TUNING: for consistency, we may want to just go ahead and handle these here. */ -#define MTERP_LOGGING 0 common_errDivideByZero: EXPORT_PC #if MTERP_LOGGING @@ -142,6 +141,21 @@ MterpCheckSuspendAndContinue: 1: GOTO_NEXT +/* + * On-stack replacement has happened, and now we've returned from the compiled method. + */ +MterpOnStackReplacement: +#if MTERP_LOGGING + movl rSELF, %eax + movl %eax, OUT_ARG0(%esp) + lea OFF_FP_SHADOWFRAME(rFP), %ecx + movl %ecx, OUT_ARG1(%esp) + movl rINST, OUT_ARG2(%esp) + call SYMBOL(MterpLogOSR) +#endif + movl $$1, %eax + jmp MterpDone + /* * Bail out to reference interpreter. */ diff --git a/runtime/interpreter/mterp/x86/header.S b/runtime/interpreter/mterp/x86/header.S index 3fbbbf955e..6bddaf9344 100644 --- a/runtime/interpreter/mterp/x86/header.S +++ b/runtime/interpreter/mterp/x86/header.S @@ -156,13 +156,26 @@ unspecified registers or condition codes. #define OFF_FP_CODE_ITEM OFF_FP(SHADOWFRAME_CODE_ITEM_OFFSET) #define OFF_FP_SHADOWFRAME (-SHADOWFRAME_VREGS_OFFSET) +#define MTERP_PROFILE_BRANCHES 1 +#define MTERP_LOGGING 0 + /* - * - * The reference interpreter performs explicit suspect checks, which is somewhat wasteful. - * Dalvik's interpreter folded suspend checks into the jump table mechanism, and eventually - * mterp should do so as well. + * Profile branch. rINST should contain the offset. %eax is scratch. */ -#define MTERP_SUSPEND 0 +.macro MTERP_PROFILE_BRANCH +#ifdef MTERP_PROFILE_BRANCHES + EXPORT_PC + movl rSELF, %eax + movl %eax, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + movl rINST, OUT_ARG2(%esp) + call SYMBOL(MterpProfileBranch) + testb %al, %al + jnz MterpOnStackReplacement + RESTORE_IBASE +#endif +.endm /* * "export" the PC to dex_pc field in the shadow frame, f/b/o future exception objects. Must diff --git a/runtime/interpreter/mterp/x86/op_goto.S b/runtime/interpreter/mterp/x86/op_goto.S index 411399d3ad..9a87361c8e 100644 --- a/runtime/interpreter/mterp/x86/op_goto.S +++ b/runtime/interpreter/mterp/x86/op_goto.S @@ -5,15 +5,10 @@ * double to get a byte offset. */ /* goto +AA */ - movsbl rINSTbl, %eax # eax <- ssssssAA - addl %eax, %eax # eax <- AA * 2 - leal (rPC, %eax), rPC + movsbl rINSTbl, rINST # rINST <- ssssssAA + MTERP_PROFILE_BRANCH + addl rINST, rINST # rINST <- AA * 2 + leal (rPC, rINST), rPC FETCH_INST - jg 1f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -1: + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check GOTO_NEXT diff --git a/runtime/interpreter/mterp/x86/op_goto_16.S b/runtime/interpreter/mterp/x86/op_goto_16.S index 4f04f9e479..a25c31b2d0 100644 --- a/runtime/interpreter/mterp/x86/op_goto_16.S +++ b/runtime/interpreter/mterp/x86/op_goto_16.S @@ -5,15 +5,10 @@ * double to get a byte offset. */ /* goto/16 +AAAA */ - movswl 2(rPC), %eax # eax <- ssssAAAA - addl %eax, %eax # eax <- AA * 2 - leal (rPC, %eax), rPC + movswl 2(rPC), rINST # rINST <- ssssAAAA + MTERP_PROFILE_BRANCH + addl rINST, rINST # rINST <- AA * 2 + leal (rPC, rINST), rPC FETCH_INST - jg 1f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -1: + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check GOTO_NEXT diff --git a/runtime/interpreter/mterp/x86/op_goto_32.S b/runtime/interpreter/mterp/x86/op_goto_32.S index 48f6e5afd7..159128be1c 100644 --- a/runtime/interpreter/mterp/x86/op_goto_32.S +++ b/runtime/interpreter/mterp/x86/op_goto_32.S @@ -10,15 +10,10 @@ * offset to byte offset. */ /* goto/32 +AAAAAAAA */ - movl 2(rPC), %eax # eax <- AAAAAAAA - addl %eax, %eax # eax <- AA * 2 - leal (rPC, %eax), rPC + movl 2(rPC), rINST # rINST <- AAAAAAAA + MTERP_PROFILE_BRANCH + addl rINST, rINST # rINST <- AA * 2 + leal (rPC, rINST), rPC FETCH_INST - jg 1f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -1: + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check GOTO_NEXT diff --git a/runtime/interpreter/mterp/x86/op_packed_switch.S b/runtime/interpreter/mterp/x86/op_packed_switch.S index 230b58e02b..e33cf75499 100644 --- a/runtime/interpreter/mterp/x86/op_packed_switch.S +++ b/runtime/interpreter/mterp/x86/op_packed_switch.S @@ -15,15 +15,11 @@ movl %eax, OUT_ARG1(%esp) # ARG1 <- vAA movl %ecx, OUT_ARG0(%esp) # ARG0 <- switchData call SYMBOL($func) - addl %eax, %eax - leal (rPC, %eax), rPC + movl %eax, rINST + MTERP_PROFILE_BRANCH + addl rINST, rINST + leal (rPC, rINST), rPC FETCH_INST REFRESH_IBASE - jg 1f -#if MTERP_SUSPEND - # REFRESH_IBASE - we did it above. -#else - jmp MterpCheckSuspendAndContinue -#endif -1: + jle MterpCheckSuspendAndContinue GOTO_NEXT diff --git a/runtime/interpreter/mterp/x86/zcmp.S b/runtime/interpreter/mterp/x86/zcmp.S index 5ce4f0f6a7..0f28d1acd8 100644 --- a/runtime/interpreter/mterp/x86/zcmp.S +++ b/runtime/interpreter/mterp/x86/zcmp.S @@ -7,18 +7,13 @@ */ /* if-cmp vAA, +BBBB */ cmpl $$0, VREG_ADDRESS(rINST) # compare (vA, 0) - movl $$2, %eax # assume branch not taken + movl $$2, rINST j${revcmp} 1f - movswl 2(rPC),%eax # fetch signed displacement + movswl 2(rPC), rINST # fetch signed displacement 1: - addl %eax, %eax # eax <- AA * 2 - leal (rPC, %eax), rPC + MTERP_PROFILE_BRANCH + addl rINST, rINST # eax <- AA * 2 + leal (rPC, rINST), rPC FETCH_INST - jg 2f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -2: + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check GOTO_NEXT -- GitLab From c7098ff991bb4e00a800d315d1c36f52a9cb0149 Mon Sep 17 00:00:00 2001 From: David Srbecky Date: Tue, 9 Feb 2016 14:30:11 +0000 Subject: [PATCH 018/204] Remove HNativeDebugInfo from start of basic blocks. We do not require full environment at the start of basic block. The dex pc contained in basic block is sufficient for line mapping. Change-Id: I5ba9e5f5acbc4a783ad544769f9a73bb33e2bafa --- compiler/optimizing/builder.cc | 17 ++++------------- compiler/optimizing/code_generator.cc | 17 ++++++++++++++++- compiler/optimizing/code_generator.h | 4 ++++ compiler/optimizing/code_generator_arm.cc | 10 +++++----- compiler/optimizing/code_generator_arm.h | 2 ++ compiler/optimizing/code_generator_arm64.cc | 10 +++++----- compiler/optimizing/code_generator_arm64.h | 2 ++ compiler/optimizing/code_generator_mips.cc | 10 +++++----- compiler/optimizing/code_generator_mips.h | 2 ++ compiler/optimizing/code_generator_mips64.cc | 10 +++++----- compiler/optimizing/code_generator_mips64.h | 2 ++ compiler/optimizing/code_generator_x86.cc | 10 +++++----- compiler/optimizing/code_generator_x86.h | 1 + compiler/optimizing/code_generator_x86_64.cc | 10 +++++----- compiler/optimizing/code_generator_x86_64.h | 2 ++ 15 files changed, 65 insertions(+), 44 deletions(-) diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc index 05e1356ed8..35ec7d41ff 100644 --- a/compiler/optimizing/builder.cc +++ b/compiler/optimizing/builder.cc @@ -368,7 +368,6 @@ GraphAnalysisResult HGraphBuilder::BuildGraph(const DexFile::CodeItem& code_item if (native_debuggable) { const uint32_t num_instructions = code_item.insns_size_in_code_units_; native_debug_info_locations = new (arena_) ArenaBitVector (arena_, num_instructions, false); - native_debug_info_locations->ClearAllBits(); FindNativeDebugInfoLocations(code_item, native_debug_info_locations); } @@ -443,23 +442,15 @@ void HGraphBuilder::FindNativeDebugInfoLocations(const DexFile::CodeItem& code_i } }; dex_file_->DecodeDebugPositionInfo(&code_item, Callback::Position, locations); - // Add native debug info at the start of every basic block. - for (uint32_t pc = 0; pc < code_item.insns_size_in_code_units_; pc++) { - if (FindBlockStartingAt(pc) != nullptr) { - locations->SetBit(pc); - } - } // Instruction-specific tweaks. const Instruction* const begin = Instruction::At(code_item.insns_); const Instruction* const end = begin->RelativeAt(code_item.insns_size_in_code_units_); for (const Instruction* inst = begin; inst < end; inst = inst->Next()) { switch (inst->Opcode()) { - case Instruction::MOVE_EXCEPTION: - case Instruction::MOVE_RESULT: - case Instruction::MOVE_RESULT_WIDE: - case Instruction::MOVE_RESULT_OBJECT: { - // The compiler checks that there are no instructions before those. - // So generate HNativeDebugInfo after them instead. + case Instruction::MOVE_EXCEPTION: { + // Stop in native debugger after the exception has been moved. + // The compiler also expects the move at the start of basic block so + // we do not want to interfere by inserting native-debug-info before it. locations->ClearBit(inst->GetDexPc(code_item.insns_)); const Instruction* next = inst->Next(); if (next < end) { diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index c2c8ccfc56..c67efc06c1 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -226,6 +226,10 @@ void CodeGenerator::Compile(CodeAllocator* allocator) { // errors where we reference that label. if (block->IsSingleJump()) continue; Bind(block); + // This ensures that we have correct native line mapping for all native instructions. + // It is necessary to make stepping over a statement work. Otherwise, any initial + // instructions (e.g. moves) would be assumed to be the start of next statement. + MaybeRecordNativeDebugInfo(nullptr /* instruction */, block->GetDexPc()); for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) { HInstruction* current = it.Current(); DisassemblyScope disassembly_scope(current, *this); @@ -733,7 +737,8 @@ void CodeGenerator::RecordPcInfo(HInstruction* instruction, uint32_t native_pc = GetAssembler()->CodeSize(); if (instruction == nullptr) { - // For stack overflow checks. + // For stack overflow checks and native-debug-info entries without dex register + // mapping (i.e. start of basic block or start of slow path). stack_map_stream_.BeginStackMapEntry(outer_dex_pc, native_pc, 0, 0, 0, 0); stack_map_stream_.EndStackMapEntry(); return; @@ -808,6 +813,16 @@ bool CodeGenerator::HasStackMapAtCurrentPc() { return count > 0 && stack_map_stream_.GetStackMap(count - 1).native_pc_offset == pc; } +void CodeGenerator::MaybeRecordNativeDebugInfo(HInstruction* instruction, uint32_t dex_pc) { + if (GetCompilerOptions().GetNativeDebuggable() && dex_pc != kNoDexPc) { + if (HasStackMapAtCurrentPc()) { + // Ensure that we do not collide with the stack map of the previous instruction. + GenerateNop(); + } + RecordPcInfo(instruction, dex_pc); + } +} + void CodeGenerator::RecordCatchBlockInfo() { ArenaAllocator* arena = graph_->GetArena(); diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h index 49c193e7bf..789bf4019f 100644 --- a/compiler/optimizing/code_generator.h +++ b/compiler/optimizing/code_generator.h @@ -267,6 +267,8 @@ class CodeGenerator { void RecordPcInfo(HInstruction* instruction, uint32_t dex_pc, SlowPathCode* slow_path = nullptr); // Check whether we have already recorded mapping at this PC. bool HasStackMapAtCurrentPc(); + // Record extra stack maps if we support native debugging. + void MaybeRecordNativeDebugInfo(HInstruction* instruction, uint32_t dex_pc); bool CanMoveNullCheckToUser(HNullCheck* null_check); void MaybeRecordImplicitNullCheck(HInstruction* instruction); @@ -440,6 +442,8 @@ class CodeGenerator { // Copy the result of a call into the given target. virtual void MoveFromReturnRegister(Location trg, Primitive::Type type) = 0; + virtual void GenerateNop() = 0; + protected: // Method patch info used for recording locations of required linker patches and // target methods. The target method can be used for various purposes, whether for diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index 87f52c6f21..f60c5e9d86 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -1557,11 +1557,11 @@ void LocationsBuilderARM::VisitNativeDebugInfo(HNativeDebugInfo* info) { } void InstructionCodeGeneratorARM::VisitNativeDebugInfo(HNativeDebugInfo* info) { - if (codegen_->HasStackMapAtCurrentPc()) { - // Ensure that we do not collide with the stack map of the previous instruction. - __ nop(); - } - codegen_->RecordPcInfo(info, info->GetDexPc()); + codegen_->MaybeRecordNativeDebugInfo(info, info->GetDexPc()); +} + +void CodeGeneratorARM::GenerateNop() { + __ nop(); } void LocationsBuilderARM::HandleCondition(HCondition* cond) { diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h index cfd7a3bc14..2e4dc1e014 100644 --- a/compiler/optimizing/code_generator_arm.h +++ b/compiler/optimizing/code_generator_arm.h @@ -510,6 +510,8 @@ class CodeGeneratorARM : public CodeGenerator { // artReadBarrierForRootSlow. void GenerateReadBarrierForRootSlow(HInstruction* instruction, Location out, Location root); + void GenerateNop(); + private: // Factored implementation of GenerateFieldLoadWithBakerReadBarrier // and GenerateArrayLoadWithBakerReadBarrier. diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index 435ae5e954..0c2e9cf373 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -3057,11 +3057,11 @@ void LocationsBuilderARM64::VisitNativeDebugInfo(HNativeDebugInfo* info) { } void InstructionCodeGeneratorARM64::VisitNativeDebugInfo(HNativeDebugInfo* info) { - if (codegen_->HasStackMapAtCurrentPc()) { - // Ensure that we do not collide with the stack map of the previous instruction. - __ Nop(); - } - codegen_->RecordPcInfo(info, info->GetDexPc()); + codegen_->MaybeRecordNativeDebugInfo(info, info->GetDexPc()); +} + +void CodeGeneratorARM64::GenerateNop() { + __ Nop(); } void LocationsBuilderARM64::VisitInstanceFieldGet(HInstanceFieldGet* instruction) { diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h index 360488eb4a..fea87ab6dc 100644 --- a/compiler/optimizing/code_generator_arm64.h +++ b/compiler/optimizing/code_generator_arm64.h @@ -532,6 +532,8 @@ class CodeGeneratorARM64 : public CodeGenerator { // artReadBarrierForRootSlow. void GenerateReadBarrierForRootSlow(HInstruction* instruction, Location out, Location root); + void GenerateNop(); + private: // Factored implementation of GenerateFieldLoadWithBakerReadBarrier // and GenerateArrayLoadWithBakerReadBarrier. diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc index 3eda8639c1..23ca703b89 100644 --- a/compiler/optimizing/code_generator_mips.cc +++ b/compiler/optimizing/code_generator_mips.cc @@ -3406,11 +3406,11 @@ void LocationsBuilderMIPS::VisitNativeDebugInfo(HNativeDebugInfo* info) { } void InstructionCodeGeneratorMIPS::VisitNativeDebugInfo(HNativeDebugInfo* info) { - if (codegen_->HasStackMapAtCurrentPc()) { - // Ensure that we do not collide with the stack map of the previous instruction. - __ Nop(); - } - codegen_->RecordPcInfo(info, info->GetDexPc()); + codegen_->MaybeRecordNativeDebugInfo(info, info->GetDexPc()); +} + +void CodeGeneratorMIPS::GenerateNop() { + __ Nop(); } void LocationsBuilderMIPS::HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info) { diff --git a/compiler/optimizing/code_generator_mips.h b/compiler/optimizing/code_generator_mips.h index 12964b0b6a..752bf3b986 100644 --- a/compiler/optimizing/code_generator_mips.h +++ b/compiler/optimizing/code_generator_mips.h @@ -363,6 +363,8 @@ class CodeGeneratorMIPS : public CodeGenerator { UNIMPLEMENTED(FATAL) << "Not implemented on MIPS"; } + void GenerateNop(); + private: // Labels for each block that will be compiled. MipsLabel* block_labels_; diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc index 119084e026..db85dbeba6 100644 --- a/compiler/optimizing/code_generator_mips64.cc +++ b/compiler/optimizing/code_generator_mips64.cc @@ -2733,11 +2733,11 @@ void LocationsBuilderMIPS64::VisitNativeDebugInfo(HNativeDebugInfo* info) { } void InstructionCodeGeneratorMIPS64::VisitNativeDebugInfo(HNativeDebugInfo* info) { - if (codegen_->HasStackMapAtCurrentPc()) { - // Ensure that we do not collide with the stack map of the previous instruction. - __ Nop(); - } - codegen_->RecordPcInfo(info, info->GetDexPc()); + codegen_->MaybeRecordNativeDebugInfo(info, info->GetDexPc()); +} + +void CodeGeneratorMIPS64::GenerateNop() { + __ Nop(); } void LocationsBuilderMIPS64::HandleFieldGet(HInstruction* instruction, diff --git a/compiler/optimizing/code_generator_mips64.h b/compiler/optimizing/code_generator_mips64.h index 1161253792..1ba44dfc54 100644 --- a/compiler/optimizing/code_generator_mips64.h +++ b/compiler/optimizing/code_generator_mips64.h @@ -352,6 +352,8 @@ class CodeGeneratorMIPS64 : public CodeGenerator { UNIMPLEMENTED(FATAL); } + void GenerateNop(); + private: // Labels for each block that will be compiled. Mips64Label* block_labels_; // Indexed by block id. diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 07edd97c1f..143dad8085 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -1536,11 +1536,11 @@ void LocationsBuilderX86::VisitNativeDebugInfo(HNativeDebugInfo* info) { } void InstructionCodeGeneratorX86::VisitNativeDebugInfo(HNativeDebugInfo* info) { - if (codegen_->HasStackMapAtCurrentPc()) { - // Ensure that we do not collide with the stack map of the previous instruction. - __ nop(); - } - codegen_->RecordPcInfo(info, info->GetDexPc()); + codegen_->MaybeRecordNativeDebugInfo(info, info->GetDexPc()); +} + +void CodeGeneratorX86::GenerateNop() { + __ nop(); } void LocationsBuilderX86::VisitLocal(HLocal* local) { diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h index 2fb6d60ad5..858fe17e72 100644 --- a/compiler/optimizing/code_generator_x86.h +++ b/compiler/optimizing/code_generator_x86.h @@ -539,6 +539,7 @@ class CodeGeneratorX86 : public CodeGenerator { } } + void GenerateNop(); private: // Factored implementation of GenerateFieldLoadWithBakerReadBarrier diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index a53a6be3de..e79c1fb227 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -1632,11 +1632,11 @@ void LocationsBuilderX86_64::VisitNativeDebugInfo(HNativeDebugInfo* info) { } void InstructionCodeGeneratorX86_64::VisitNativeDebugInfo(HNativeDebugInfo* info) { - if (codegen_->HasStackMapAtCurrentPc()) { - // Ensure that we do not collide with the stack map of the previous instruction. - __ nop(); - } - codegen_->RecordPcInfo(info, info->GetDexPc()); + codegen_->MaybeRecordNativeDebugInfo(info, info->GetDexPc()); +} + +void CodeGeneratorX86_64::GenerateNop() { + __ nop(); } void LocationsBuilderX86_64::VisitLocal(HLocal* local) { diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h index 97f6f84236..b3d27e194a 100644 --- a/compiler/optimizing/code_generator_x86_64.h +++ b/compiler/optimizing/code_generator_x86_64.h @@ -513,6 +513,8 @@ class CodeGeneratorX86_64 : public CodeGenerator { } } + void GenerateNop(); + private: // Factored implementation of GenerateFieldLoadWithBakerReadBarrier // and GenerateArrayLoadWithBakerReadBarrier. -- GitLab From 9cd6d378bd573cdc14d049d32bdd22a97fa4d84a Mon Sep 17 00:00:00 2001 From: David Srbecky Date: Tue, 9 Feb 2016 15:24:47 +0000 Subject: [PATCH 019/204] Associate slow paths with the instruction that they belong to. Almost all slow paths already know the instruction they belong to, this CL just moves the knowledge to the base class as well. This is needed to be be able to get the corresponding dex pc for slow path, which allows us generate better native line numbers, which in turn fixes some native debugging stepping issues. Change-Id: I568dbe78a7cea6a43a4a71a014b3ad135782c270 --- compiler/optimizing/code_generator.cc | 2 + compiler/optimizing/code_generator.h | 8 +++- compiler/optimizing/code_generator_arm.cc | 41 ++++++---------- compiler/optimizing/code_generator_arm64.cc | 42 ++++++----------- compiler/optimizing/code_generator_arm64.h | 3 +- compiler/optimizing/code_generator_mips.cc | 30 ++++-------- compiler/optimizing/code_generator_mips.h | 3 +- compiler/optimizing/code_generator_mips64.cc | 30 ++++-------- compiler/optimizing/code_generator_mips64.h | 3 +- compiler/optimizing/code_generator_x86.cc | 49 +++++++------------- compiler/optimizing/code_generator_x86_64.cc | 48 +++++++------------ compiler/optimizing/intrinsics_arm64.cc | 3 +- compiler/optimizing/intrinsics_mips.cc | 2 +- compiler/optimizing/intrinsics_mips64.cc | 3 +- compiler/optimizing/intrinsics_utils.h | 2 +- 15 files changed, 104 insertions(+), 165 deletions(-) diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index c67efc06c1..967d156cf6 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -195,6 +195,8 @@ void CodeGenerator::GenerateSlowPaths() { if (disasm_info_ != nullptr) { code_start = GetAssembler()->CodeSize(); } + // Record the dex pc at start of slow path (required for java line number mapping). + MaybeRecordNativeDebugInfo(nullptr /* instruction */, slow_path->GetDexPc()); slow_path->EmitNativeCode(this); if (disasm_info_ != nullptr) { disasm_info_->AddSlowPathInterval(slow_path, code_start, GetAssembler()->CodeSize()); diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h index 789bf4019f..9297fc956f 100644 --- a/compiler/optimizing/code_generator.h +++ b/compiler/optimizing/code_generator.h @@ -69,7 +69,7 @@ class CodeAllocator { class SlowPathCode : public ArenaObject { public: - SlowPathCode() { + explicit SlowPathCode(HInstruction* instruction) : instruction_(instruction) { for (size_t i = 0; i < kMaximumNumberOfExpectedRegisters; ++i) { saved_core_stack_offsets_[i] = kRegisterNotSaved; saved_fpu_stack_offsets_[i] = kRegisterNotSaved; @@ -106,9 +106,15 @@ class SlowPathCode : public ArenaObject { Label* GetEntryLabel() { return &entry_label_; } Label* GetExitLabel() { return &exit_label_; } + uint32_t GetDexPc() const { + return instruction_ != nullptr ? instruction_->GetDexPc() : kNoDexPc; + } + protected: static constexpr size_t kMaximumNumberOfExpectedRegisters = 32; static constexpr uint32_t kRegisterNotSaved = -1; + // The instruction where this slow path is happening. + HInstruction* instruction_; uint32_t saved_core_stack_offsets_[kMaximumNumberOfExpectedRegisters]; uint32_t saved_fpu_stack_offsets_[kMaximumNumberOfExpectedRegisters]; diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index f60c5e9d86..cdbb9c31aa 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -64,7 +64,7 @@ static constexpr uint32_t kPackedSwitchCompareJumpThreshold = 7; class NullCheckSlowPathARM : public SlowPathCode { public: - explicit NullCheckSlowPathARM(HNullCheck* instruction) : instruction_(instruction) {} + explicit NullCheckSlowPathARM(HNullCheck* instruction) : SlowPathCode(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorARM* arm_codegen = down_cast(codegen); @@ -83,13 +83,12 @@ class NullCheckSlowPathARM : public SlowPathCode { const char* GetDescription() const OVERRIDE { return "NullCheckSlowPathARM"; } private: - HNullCheck* const instruction_; DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathARM); }; class DivZeroCheckSlowPathARM : public SlowPathCode { public: - explicit DivZeroCheckSlowPathARM(HDivZeroCheck* instruction) : instruction_(instruction) {} + explicit DivZeroCheckSlowPathARM(HDivZeroCheck* instruction) : SlowPathCode(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorARM* arm_codegen = down_cast(codegen); @@ -108,14 +107,13 @@ class DivZeroCheckSlowPathARM : public SlowPathCode { const char* GetDescription() const OVERRIDE { return "DivZeroCheckSlowPathARM"; } private: - HDivZeroCheck* const instruction_; DISALLOW_COPY_AND_ASSIGN(DivZeroCheckSlowPathARM); }; class SuspendCheckSlowPathARM : public SlowPathCode { public: SuspendCheckSlowPathARM(HSuspendCheck* instruction, HBasicBlock* successor) - : instruction_(instruction), successor_(successor) {} + : SlowPathCode(instruction), successor_(successor) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorARM* arm_codegen = down_cast(codegen); @@ -144,7 +142,6 @@ class SuspendCheckSlowPathARM : public SlowPathCode { const char* GetDescription() const OVERRIDE { return "SuspendCheckSlowPathARM"; } private: - HSuspendCheck* const instruction_; // If not null, the block to branch to after the suspend check. HBasicBlock* const successor_; @@ -157,7 +154,7 @@ class SuspendCheckSlowPathARM : public SlowPathCode { class BoundsCheckSlowPathARM : public SlowPathCode { public: explicit BoundsCheckSlowPathARM(HBoundsCheck* instruction) - : instruction_(instruction) {} + : SlowPathCode(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorARM* arm_codegen = down_cast(codegen); @@ -188,8 +185,6 @@ class BoundsCheckSlowPathARM : public SlowPathCode { const char* GetDescription() const OVERRIDE { return "BoundsCheckSlowPathARM"; } private: - HBoundsCheck* const instruction_; - DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathARM); }; @@ -199,7 +194,7 @@ class LoadClassSlowPathARM : public SlowPathCode { HInstruction* at, uint32_t dex_pc, bool do_clinit) - : cls_(cls), at_(at), dex_pc_(dex_pc), do_clinit_(do_clinit) { + : SlowPathCode(at), cls_(cls), at_(at), dex_pc_(dex_pc), do_clinit_(do_clinit) { DCHECK(at->IsLoadClass() || at->IsClinitCheck()); } @@ -253,7 +248,7 @@ class LoadClassSlowPathARM : public SlowPathCode { class LoadStringSlowPathARM : public SlowPathCode { public: - explicit LoadStringSlowPathARM(HLoadString* instruction) : instruction_(instruction) {} + explicit LoadStringSlowPathARM(HLoadString* instruction) : SlowPathCode(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { LocationSummary* locations = instruction_->GetLocations(); @@ -264,7 +259,8 @@ class LoadStringSlowPathARM : public SlowPathCode { SaveLiveRegisters(codegen, locations); InvokeRuntimeCallingConvention calling_convention; - __ LoadImmediate(calling_convention.GetRegisterAt(0), instruction_->GetStringIndex()); + const uint32_t string_index = instruction_->AsLoadString()->GetStringIndex(); + __ LoadImmediate(calling_convention.GetRegisterAt(0), string_index); arm_codegen->InvokeRuntime( QUICK_ENTRY_POINT(pResolveString), instruction_, instruction_->GetDexPc(), this); CheckEntrypointTypes(); @@ -277,15 +273,13 @@ class LoadStringSlowPathARM : public SlowPathCode { const char* GetDescription() const OVERRIDE { return "LoadStringSlowPathARM"; } private: - HLoadString* const instruction_; - DISALLOW_COPY_AND_ASSIGN(LoadStringSlowPathARM); }; class TypeCheckSlowPathARM : public SlowPathCode { public: TypeCheckSlowPathARM(HInstruction* instruction, bool is_fatal) - : instruction_(instruction), is_fatal_(is_fatal) {} + : SlowPathCode(instruction), is_fatal_(is_fatal) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { LocationSummary* locations = instruction_->GetLocations(); @@ -340,7 +334,6 @@ class TypeCheckSlowPathARM : public SlowPathCode { bool IsFatal() const OVERRIDE { return is_fatal_; } private: - HInstruction* const instruction_; const bool is_fatal_; DISALLOW_COPY_AND_ASSIGN(TypeCheckSlowPathARM); @@ -349,7 +342,7 @@ class TypeCheckSlowPathARM : public SlowPathCode { class DeoptimizationSlowPathARM : public SlowPathCode { public: explicit DeoptimizationSlowPathARM(HDeoptimize* instruction) - : instruction_(instruction) {} + : SlowPathCode(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorARM* arm_codegen = down_cast(codegen); @@ -365,13 +358,12 @@ class DeoptimizationSlowPathARM : public SlowPathCode { const char* GetDescription() const OVERRIDE { return "DeoptimizationSlowPathARM"; } private: - HDeoptimize* const instruction_; DISALLOW_COPY_AND_ASSIGN(DeoptimizationSlowPathARM); }; class ArraySetSlowPathARM : public SlowPathCode { public: - explicit ArraySetSlowPathARM(HInstruction* instruction) : instruction_(instruction) {} + explicit ArraySetSlowPathARM(HInstruction* instruction) : SlowPathCode(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { LocationSummary* locations = instruction_->GetLocations(); @@ -410,8 +402,6 @@ class ArraySetSlowPathARM : public SlowPathCode { const char* GetDescription() const OVERRIDE { return "ArraySetSlowPathARM"; } private: - HInstruction* const instruction_; - DISALLOW_COPY_AND_ASSIGN(ArraySetSlowPathARM); }; @@ -419,7 +409,7 @@ class ArraySetSlowPathARM : public SlowPathCode { class ReadBarrierMarkSlowPathARM : public SlowPathCode { public: ReadBarrierMarkSlowPathARM(HInstruction* instruction, Location out, Location obj) - : instruction_(instruction), out_(out), obj_(obj) { + : SlowPathCode(instruction), out_(out), obj_(obj) { DCHECK(kEmitCompilerReadBarrier); } @@ -458,7 +448,6 @@ class ReadBarrierMarkSlowPathARM : public SlowPathCode { } private: - HInstruction* const instruction_; const Location out_; const Location obj_; @@ -474,7 +463,7 @@ class ReadBarrierForHeapReferenceSlowPathARM : public SlowPathCode { Location obj, uint32_t offset, Location index) - : instruction_(instruction), + : SlowPathCode(instruction), out_(out), ref_(ref), obj_(obj), @@ -629,7 +618,6 @@ class ReadBarrierForHeapReferenceSlowPathARM : public SlowPathCode { UNREACHABLE(); } - HInstruction* const instruction_; const Location out_; const Location ref_; const Location obj_; @@ -646,7 +634,7 @@ class ReadBarrierForHeapReferenceSlowPathARM : public SlowPathCode { class ReadBarrierForRootSlowPathARM : public SlowPathCode { public: ReadBarrierForRootSlowPathARM(HInstruction* instruction, Location out, Location root) - : instruction_(instruction), out_(out), root_(root) { + : SlowPathCode(instruction), out_(out), root_(root) { DCHECK(kEmitCompilerReadBarrier); } @@ -679,7 +667,6 @@ class ReadBarrierForRootSlowPathARM : public SlowPathCode { const char* GetDescription() const OVERRIDE { return "ReadBarrierForRootSlowPathARM"; } private: - HInstruction* const instruction_; const Location out_; const Location root_; diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index 0c2e9cf373..814f8b4d51 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -219,7 +219,7 @@ void SlowPathCodeARM64::RestoreLiveRegisters(CodeGenerator* codegen, LocationSum class BoundsCheckSlowPathARM64 : public SlowPathCodeARM64 { public: - explicit BoundsCheckSlowPathARM64(HBoundsCheck* instruction) : instruction_(instruction) {} + explicit BoundsCheckSlowPathARM64(HBoundsCheck* instruction) : SlowPathCodeARM64(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { LocationSummary* locations = instruction_->GetLocations(); @@ -246,14 +246,12 @@ class BoundsCheckSlowPathARM64 : public SlowPathCodeARM64 { const char* GetDescription() const OVERRIDE { return "BoundsCheckSlowPathARM64"; } private: - HBoundsCheck* const instruction_; - DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathARM64); }; class DivZeroCheckSlowPathARM64 : public SlowPathCodeARM64 { public: - explicit DivZeroCheckSlowPathARM64(HDivZeroCheck* instruction) : instruction_(instruction) {} + explicit DivZeroCheckSlowPathARM64(HDivZeroCheck* instruction) : SlowPathCodeARM64(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorARM64* arm64_codegen = down_cast(codegen); @@ -272,7 +270,6 @@ class DivZeroCheckSlowPathARM64 : public SlowPathCodeARM64 { const char* GetDescription() const OVERRIDE { return "DivZeroCheckSlowPathARM64"; } private: - HDivZeroCheck* const instruction_; DISALLOW_COPY_AND_ASSIGN(DivZeroCheckSlowPathARM64); }; @@ -282,7 +279,7 @@ class LoadClassSlowPathARM64 : public SlowPathCodeARM64 { HInstruction* at, uint32_t dex_pc, bool do_clinit) - : cls_(cls), at_(at), dex_pc_(dex_pc), do_clinit_(do_clinit) { + : SlowPathCodeARM64(at), cls_(cls), at_(at), dex_pc_(dex_pc), do_clinit_(do_clinit) { DCHECK(at->IsLoadClass() || at->IsClinitCheck()); } @@ -337,7 +334,7 @@ class LoadClassSlowPathARM64 : public SlowPathCodeARM64 { class LoadStringSlowPathARM64 : public SlowPathCodeARM64 { public: - explicit LoadStringSlowPathARM64(HLoadString* instruction) : instruction_(instruction) {} + explicit LoadStringSlowPathARM64(HLoadString* instruction) : SlowPathCodeARM64(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { LocationSummary* locations = instruction_->GetLocations(); @@ -348,7 +345,8 @@ class LoadStringSlowPathARM64 : public SlowPathCodeARM64 { SaveLiveRegisters(codegen, locations); InvokeRuntimeCallingConvention calling_convention; - __ Mov(calling_convention.GetRegisterAt(0).W(), instruction_->GetStringIndex()); + const uint32_t string_index = instruction_->AsLoadString()->GetStringIndex(); + __ Mov(calling_convention.GetRegisterAt(0).W(), string_index); arm64_codegen->InvokeRuntime( QUICK_ENTRY_POINT(pResolveString), instruction_, instruction_->GetDexPc(), this); CheckEntrypointTypes(); @@ -362,14 +360,12 @@ class LoadStringSlowPathARM64 : public SlowPathCodeARM64 { const char* GetDescription() const OVERRIDE { return "LoadStringSlowPathARM64"; } private: - HLoadString* const instruction_; - DISALLOW_COPY_AND_ASSIGN(LoadStringSlowPathARM64); }; class NullCheckSlowPathARM64 : public SlowPathCodeARM64 { public: - explicit NullCheckSlowPathARM64(HNullCheck* instr) : instruction_(instr) {} + explicit NullCheckSlowPathARM64(HNullCheck* instr) : SlowPathCodeARM64(instr) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorARM64* arm64_codegen = down_cast(codegen); @@ -388,15 +384,13 @@ class NullCheckSlowPathARM64 : public SlowPathCodeARM64 { const char* GetDescription() const OVERRIDE { return "NullCheckSlowPathARM64"; } private: - HNullCheck* const instruction_; - DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathARM64); }; class SuspendCheckSlowPathARM64 : public SlowPathCodeARM64 { public: SuspendCheckSlowPathARM64(HSuspendCheck* instruction, HBasicBlock* successor) - : instruction_(instruction), successor_(successor) {} + : SlowPathCodeARM64(instruction), successor_(successor) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorARM64* arm64_codegen = down_cast(codegen); @@ -425,7 +419,6 @@ class SuspendCheckSlowPathARM64 : public SlowPathCodeARM64 { const char* GetDescription() const OVERRIDE { return "SuspendCheckSlowPathARM64"; } private: - HSuspendCheck* const instruction_; // If not null, the block to branch to after the suspend check. HBasicBlock* const successor_; @@ -438,7 +431,7 @@ class SuspendCheckSlowPathARM64 : public SlowPathCodeARM64 { class TypeCheckSlowPathARM64 : public SlowPathCodeARM64 { public: TypeCheckSlowPathARM64(HInstruction* instruction, bool is_fatal) - : instruction_(instruction), is_fatal_(is_fatal) {} + : SlowPathCodeARM64(instruction), is_fatal_(is_fatal) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { LocationSummary* locations = instruction_->GetLocations(); @@ -487,7 +480,6 @@ class TypeCheckSlowPathARM64 : public SlowPathCodeARM64 { bool IsFatal() const { return is_fatal_; } private: - HInstruction* const instruction_; const bool is_fatal_; DISALLOW_COPY_AND_ASSIGN(TypeCheckSlowPathARM64); @@ -496,7 +488,7 @@ class TypeCheckSlowPathARM64 : public SlowPathCodeARM64 { class DeoptimizationSlowPathARM64 : public SlowPathCodeARM64 { public: explicit DeoptimizationSlowPathARM64(HDeoptimize* instruction) - : instruction_(instruction) {} + : SlowPathCodeARM64(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorARM64* arm64_codegen = down_cast(codegen); @@ -512,13 +504,12 @@ class DeoptimizationSlowPathARM64 : public SlowPathCodeARM64 { const char* GetDescription() const OVERRIDE { return "DeoptimizationSlowPathARM64"; } private: - HDeoptimize* const instruction_; DISALLOW_COPY_AND_ASSIGN(DeoptimizationSlowPathARM64); }; class ArraySetSlowPathARM64 : public SlowPathCodeARM64 { public: - explicit ArraySetSlowPathARM64(HInstruction* instruction) : instruction_(instruction) {} + explicit ArraySetSlowPathARM64(HInstruction* instruction) : SlowPathCodeARM64(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { LocationSummary* locations = instruction_->GetLocations(); @@ -557,8 +548,6 @@ class ArraySetSlowPathARM64 : public SlowPathCodeARM64 { const char* GetDescription() const OVERRIDE { return "ArraySetSlowPathARM64"; } private: - HInstruction* const instruction_; - DISALLOW_COPY_AND_ASSIGN(ArraySetSlowPathARM64); }; @@ -588,7 +577,7 @@ void JumpTableARM64::EmitTable(CodeGeneratorARM64* codegen) { class ReadBarrierMarkSlowPathARM64 : public SlowPathCodeARM64 { public: ReadBarrierMarkSlowPathARM64(HInstruction* instruction, Location out, Location obj) - : instruction_(instruction), out_(out), obj_(obj) { + : SlowPathCodeARM64(instruction), out_(out), obj_(obj) { DCHECK(kEmitCompilerReadBarrier); } @@ -627,7 +616,6 @@ class ReadBarrierMarkSlowPathARM64 : public SlowPathCodeARM64 { } private: - HInstruction* const instruction_; const Location out_; const Location obj_; @@ -643,7 +631,7 @@ class ReadBarrierForHeapReferenceSlowPathARM64 : public SlowPathCodeARM64 { Location obj, uint32_t offset, Location index) - : instruction_(instruction), + : SlowPathCodeARM64(instruction), out_(out), ref_(ref), obj_(obj), @@ -804,7 +792,6 @@ class ReadBarrierForHeapReferenceSlowPathARM64 : public SlowPathCodeARM64 { UNREACHABLE(); } - HInstruction* const instruction_; const Location out_; const Location ref_; const Location obj_; @@ -821,7 +808,7 @@ class ReadBarrierForHeapReferenceSlowPathARM64 : public SlowPathCodeARM64 { class ReadBarrierForRootSlowPathARM64 : public SlowPathCodeARM64 { public: ReadBarrierForRootSlowPathARM64(HInstruction* instruction, Location out, Location root) - : instruction_(instruction), out_(out), root_(root) { + : SlowPathCodeARM64(instruction), out_(out), root_(root) { DCHECK(kEmitCompilerReadBarrier); } @@ -865,7 +852,6 @@ class ReadBarrierForRootSlowPathARM64 : public SlowPathCodeARM64 { const char* GetDescription() const OVERRIDE { return "ReadBarrierForRootSlowPathARM64"; } private: - HInstruction* const instruction_; const Location out_; const Location root_; diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h index fea87ab6dc..3527261835 100644 --- a/compiler/optimizing/code_generator_arm64.h +++ b/compiler/optimizing/code_generator_arm64.h @@ -66,7 +66,8 @@ Location ARM64ReturnLocation(Primitive::Type return_type); class SlowPathCodeARM64 : public SlowPathCode { public: - SlowPathCodeARM64() : entry_label_(), exit_label_() {} + explicit SlowPathCodeARM64(HInstruction* instruction) + : SlowPathCode(instruction), entry_label_(), exit_label_() {} vixl::Label* GetEntryLabel() { return &entry_label_; } vixl::Label* GetExitLabel() { return &exit_label_; } diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc index 9dd7c519aa..a47cf93708 100644 --- a/compiler/optimizing/code_generator_mips.cc +++ b/compiler/optimizing/code_generator_mips.cc @@ -149,7 +149,7 @@ Location InvokeRuntimeCallingConvention::GetReturnLocation(Primitive::Type type) class BoundsCheckSlowPathMIPS : public SlowPathCodeMIPS { public: - explicit BoundsCheckSlowPathMIPS(HBoundsCheck* instruction) : instruction_(instruction) {} + explicit BoundsCheckSlowPathMIPS(HBoundsCheck* instruction) : SlowPathCodeMIPS(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { LocationSummary* locations = instruction_->GetLocations(); @@ -181,14 +181,12 @@ class BoundsCheckSlowPathMIPS : public SlowPathCodeMIPS { const char* GetDescription() const OVERRIDE { return "BoundsCheckSlowPathMIPS"; } private: - HBoundsCheck* const instruction_; - DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathMIPS); }; class DivZeroCheckSlowPathMIPS : public SlowPathCodeMIPS { public: - explicit DivZeroCheckSlowPathMIPS(HDivZeroCheck* instruction) : instruction_(instruction) {} + explicit DivZeroCheckSlowPathMIPS(HDivZeroCheck* instruction) : SlowPathCodeMIPS(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorMIPS* mips_codegen = down_cast(codegen); @@ -210,7 +208,6 @@ class DivZeroCheckSlowPathMIPS : public SlowPathCodeMIPS { const char* GetDescription() const OVERRIDE { return "DivZeroCheckSlowPathMIPS"; } private: - HDivZeroCheck* const instruction_; DISALLOW_COPY_AND_ASSIGN(DivZeroCheckSlowPathMIPS); }; @@ -220,7 +217,7 @@ class LoadClassSlowPathMIPS : public SlowPathCodeMIPS { HInstruction* at, uint32_t dex_pc, bool do_clinit) - : cls_(cls), at_(at), dex_pc_(dex_pc), do_clinit_(do_clinit) { + : SlowPathCodeMIPS(at), cls_(cls), at_(at), dex_pc_(dex_pc), do_clinit_(do_clinit) { DCHECK(at->IsLoadClass() || at->IsClinitCheck()); } @@ -279,7 +276,7 @@ class LoadClassSlowPathMIPS : public SlowPathCodeMIPS { class LoadStringSlowPathMIPS : public SlowPathCodeMIPS { public: - explicit LoadStringSlowPathMIPS(HLoadString* instruction) : instruction_(instruction) {} + explicit LoadStringSlowPathMIPS(HLoadString* instruction) : SlowPathCodeMIPS(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { LocationSummary* locations = instruction_->GetLocations(); @@ -290,7 +287,8 @@ class LoadStringSlowPathMIPS : public SlowPathCodeMIPS { SaveLiveRegisters(codegen, locations); InvokeRuntimeCallingConvention calling_convention; - __ LoadConst32(calling_convention.GetRegisterAt(0), instruction_->GetStringIndex()); + const uint32_t string_index = instruction_->AsLoadString()->GetStringIndex(); + __ LoadConst32(calling_convention.GetRegisterAt(0), string_index); mips_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pResolveString), instruction_, instruction_->GetDexPc(), @@ -309,14 +307,12 @@ class LoadStringSlowPathMIPS : public SlowPathCodeMIPS { const char* GetDescription() const OVERRIDE { return "LoadStringSlowPathMIPS"; } private: - HLoadString* const instruction_; - DISALLOW_COPY_AND_ASSIGN(LoadStringSlowPathMIPS); }; class NullCheckSlowPathMIPS : public SlowPathCodeMIPS { public: - explicit NullCheckSlowPathMIPS(HNullCheck* instr) : instruction_(instr) {} + explicit NullCheckSlowPathMIPS(HNullCheck* instr) : SlowPathCodeMIPS(instr) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorMIPS* mips_codegen = down_cast(codegen); @@ -338,15 +334,13 @@ class NullCheckSlowPathMIPS : public SlowPathCodeMIPS { const char* GetDescription() const OVERRIDE { return "NullCheckSlowPathMIPS"; } private: - HNullCheck* const instruction_; - DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathMIPS); }; class SuspendCheckSlowPathMIPS : public SlowPathCodeMIPS { public: SuspendCheckSlowPathMIPS(HSuspendCheck* instruction, HBasicBlock* successor) - : instruction_(instruction), successor_(successor) {} + : SlowPathCodeMIPS(instruction), successor_(successor) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorMIPS* mips_codegen = down_cast(codegen); @@ -374,7 +368,6 @@ class SuspendCheckSlowPathMIPS : public SlowPathCodeMIPS { const char* GetDescription() const OVERRIDE { return "SuspendCheckSlowPathMIPS"; } private: - HSuspendCheck* const instruction_; // If not null, the block to branch to after the suspend check. HBasicBlock* const successor_; @@ -386,7 +379,7 @@ class SuspendCheckSlowPathMIPS : public SlowPathCodeMIPS { class TypeCheckSlowPathMIPS : public SlowPathCodeMIPS { public: - explicit TypeCheckSlowPathMIPS(HInstruction* instruction) : instruction_(instruction) {} + explicit TypeCheckSlowPathMIPS(HInstruction* instruction) : SlowPathCodeMIPS(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { LocationSummary* locations = instruction_->GetLocations(); @@ -437,15 +430,13 @@ class TypeCheckSlowPathMIPS : public SlowPathCodeMIPS { const char* GetDescription() const OVERRIDE { return "TypeCheckSlowPathMIPS"; } private: - HInstruction* const instruction_; - DISALLOW_COPY_AND_ASSIGN(TypeCheckSlowPathMIPS); }; class DeoptimizationSlowPathMIPS : public SlowPathCodeMIPS { public: explicit DeoptimizationSlowPathMIPS(HDeoptimize* instruction) - : instruction_(instruction) {} + : SlowPathCodeMIPS(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorMIPS* mips_codegen = down_cast(codegen); @@ -462,7 +453,6 @@ class DeoptimizationSlowPathMIPS : public SlowPathCodeMIPS { const char* GetDescription() const OVERRIDE { return "DeoptimizationSlowPathMIPS"; } private: - HDeoptimize* const instruction_; DISALLOW_COPY_AND_ASSIGN(DeoptimizationSlowPathMIPS); }; diff --git a/compiler/optimizing/code_generator_mips.h b/compiler/optimizing/code_generator_mips.h index 49c958335b..605c794421 100644 --- a/compiler/optimizing/code_generator_mips.h +++ b/compiler/optimizing/code_generator_mips.h @@ -152,7 +152,8 @@ class ParallelMoveResolverMIPS : public ParallelMoveResolverWithSwap { class SlowPathCodeMIPS : public SlowPathCode { public: - SlowPathCodeMIPS() : entry_label_(), exit_label_() {} + explicit SlowPathCodeMIPS(HInstruction* instruction) + : SlowPathCode(instruction), entry_label_(), exit_label_() {} MipsLabel* GetEntryLabel() { return &entry_label_; } MipsLabel* GetExitLabel() { return &exit_label_; } diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc index 2c0ae9ba90..dea6322e5b 100644 --- a/compiler/optimizing/code_generator_mips64.cc +++ b/compiler/optimizing/code_generator_mips64.cc @@ -110,7 +110,7 @@ Location InvokeRuntimeCallingConvention::GetReturnLocation(Primitive::Type type) class BoundsCheckSlowPathMIPS64 : public SlowPathCodeMIPS64 { public: - explicit BoundsCheckSlowPathMIPS64(HBoundsCheck* instruction) : instruction_(instruction) {} + explicit BoundsCheckSlowPathMIPS64(HBoundsCheck* instruction) : SlowPathCodeMIPS64(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { LocationSummary* locations = instruction_->GetLocations(); @@ -141,14 +141,12 @@ class BoundsCheckSlowPathMIPS64 : public SlowPathCodeMIPS64 { const char* GetDescription() const OVERRIDE { return "BoundsCheckSlowPathMIPS64"; } private: - HBoundsCheck* const instruction_; - DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathMIPS64); }; class DivZeroCheckSlowPathMIPS64 : public SlowPathCodeMIPS64 { public: - explicit DivZeroCheckSlowPathMIPS64(HDivZeroCheck* instruction) : instruction_(instruction) {} + explicit DivZeroCheckSlowPathMIPS64(HDivZeroCheck* instruction) : SlowPathCodeMIPS64(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorMIPS64* mips64_codegen = down_cast(codegen); @@ -169,7 +167,6 @@ class DivZeroCheckSlowPathMIPS64 : public SlowPathCodeMIPS64 { const char* GetDescription() const OVERRIDE { return "DivZeroCheckSlowPathMIPS64"; } private: - HDivZeroCheck* const instruction_; DISALLOW_COPY_AND_ASSIGN(DivZeroCheckSlowPathMIPS64); }; @@ -179,7 +176,7 @@ class LoadClassSlowPathMIPS64 : public SlowPathCodeMIPS64 { HInstruction* at, uint32_t dex_pc, bool do_clinit) - : cls_(cls), at_(at), dex_pc_(dex_pc), do_clinit_(do_clinit) { + : SlowPathCodeMIPS64(at), cls_(cls), at_(at), dex_pc_(dex_pc), do_clinit_(do_clinit) { DCHECK(at->IsLoadClass() || at->IsClinitCheck()); } @@ -234,7 +231,7 @@ class LoadClassSlowPathMIPS64 : public SlowPathCodeMIPS64 { class LoadStringSlowPathMIPS64 : public SlowPathCodeMIPS64 { public: - explicit LoadStringSlowPathMIPS64(HLoadString* instruction) : instruction_(instruction) {} + explicit LoadStringSlowPathMIPS64(HLoadString* instruction) : SlowPathCodeMIPS64(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { LocationSummary* locations = instruction_->GetLocations(); @@ -245,7 +242,8 @@ class LoadStringSlowPathMIPS64 : public SlowPathCodeMIPS64 { SaveLiveRegisters(codegen, locations); InvokeRuntimeCallingConvention calling_convention; - __ LoadConst32(calling_convention.GetRegisterAt(0), instruction_->GetStringIndex()); + const uint32_t string_index = instruction_->AsLoadString()->GetStringIndex(); + __ LoadConst32(calling_convention.GetRegisterAt(0), string_index); mips64_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pResolveString), instruction_, instruction_->GetDexPc(), @@ -263,14 +261,12 @@ class LoadStringSlowPathMIPS64 : public SlowPathCodeMIPS64 { const char* GetDescription() const OVERRIDE { return "LoadStringSlowPathMIPS64"; } private: - HLoadString* const instruction_; - DISALLOW_COPY_AND_ASSIGN(LoadStringSlowPathMIPS64); }; class NullCheckSlowPathMIPS64 : public SlowPathCodeMIPS64 { public: - explicit NullCheckSlowPathMIPS64(HNullCheck* instr) : instruction_(instr) {} + explicit NullCheckSlowPathMIPS64(HNullCheck* instr) : SlowPathCodeMIPS64(instr) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorMIPS64* mips64_codegen = down_cast(codegen); @@ -291,15 +287,13 @@ class NullCheckSlowPathMIPS64 : public SlowPathCodeMIPS64 { const char* GetDescription() const OVERRIDE { return "NullCheckSlowPathMIPS64"; } private: - HNullCheck* const instruction_; - DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathMIPS64); }; class SuspendCheckSlowPathMIPS64 : public SlowPathCodeMIPS64 { public: SuspendCheckSlowPathMIPS64(HSuspendCheck* instruction, HBasicBlock* successor) - : instruction_(instruction), successor_(successor) {} + : SlowPathCodeMIPS64(instruction), successor_(successor) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorMIPS64* mips64_codegen = down_cast(codegen); @@ -326,7 +320,6 @@ class SuspendCheckSlowPathMIPS64 : public SlowPathCodeMIPS64 { const char* GetDescription() const OVERRIDE { return "SuspendCheckSlowPathMIPS64"; } private: - HSuspendCheck* const instruction_; // If not null, the block to branch to after the suspend check. HBasicBlock* const successor_; @@ -338,7 +331,7 @@ class SuspendCheckSlowPathMIPS64 : public SlowPathCodeMIPS64 { class TypeCheckSlowPathMIPS64 : public SlowPathCodeMIPS64 { public: - explicit TypeCheckSlowPathMIPS64(HInstruction* instruction) : instruction_(instruction) {} + explicit TypeCheckSlowPathMIPS64(HInstruction* instruction) : SlowPathCodeMIPS64(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { LocationSummary* locations = instruction_->GetLocations(); @@ -384,15 +377,13 @@ class TypeCheckSlowPathMIPS64 : public SlowPathCodeMIPS64 { const char* GetDescription() const OVERRIDE { return "TypeCheckSlowPathMIPS64"; } private: - HInstruction* const instruction_; - DISALLOW_COPY_AND_ASSIGN(TypeCheckSlowPathMIPS64); }; class DeoptimizationSlowPathMIPS64 : public SlowPathCodeMIPS64 { public: explicit DeoptimizationSlowPathMIPS64(HDeoptimize* instruction) - : instruction_(instruction) {} + : SlowPathCodeMIPS64(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorMIPS64* mips64_codegen = down_cast(codegen); @@ -408,7 +399,6 @@ class DeoptimizationSlowPathMIPS64 : public SlowPathCodeMIPS64 { const char* GetDescription() const OVERRIDE { return "DeoptimizationSlowPathMIPS64"; } private: - HDeoptimize* const instruction_; DISALLOW_COPY_AND_ASSIGN(DeoptimizationSlowPathMIPS64); }; diff --git a/compiler/optimizing/code_generator_mips64.h b/compiler/optimizing/code_generator_mips64.h index c298097a46..ba9eaff46f 100644 --- a/compiler/optimizing/code_generator_mips64.h +++ b/compiler/optimizing/code_generator_mips64.h @@ -152,7 +152,8 @@ class ParallelMoveResolverMIPS64 : public ParallelMoveResolverWithSwap { class SlowPathCodeMIPS64 : public SlowPathCode { public: - SlowPathCodeMIPS64() : entry_label_(), exit_label_() {} + explicit SlowPathCodeMIPS64(HInstruction* instruction) + : SlowPathCode(instruction), entry_label_(), exit_label_() {} Mips64Label* GetEntryLabel() { return &entry_label_; } Mips64Label* GetExitLabel() { return &exit_label_; } diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 236dea1bba..88e42f3faf 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -52,7 +52,7 @@ static constexpr int kFakeReturnRegister = Register(8); class NullCheckSlowPathX86 : public SlowPathCode { public: - explicit NullCheckSlowPathX86(HNullCheck* instruction) : instruction_(instruction) {} + explicit NullCheckSlowPathX86(HNullCheck* instruction) : SlowPathCode(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorX86* x86_codegen = down_cast(codegen); @@ -73,13 +73,12 @@ class NullCheckSlowPathX86 : public SlowPathCode { const char* GetDescription() const OVERRIDE { return "NullCheckSlowPathX86"; } private: - HNullCheck* const instruction_; DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathX86); }; class DivZeroCheckSlowPathX86 : public SlowPathCode { public: - explicit DivZeroCheckSlowPathX86(HDivZeroCheck* instruction) : instruction_(instruction) {} + explicit DivZeroCheckSlowPathX86(HDivZeroCheck* instruction) : SlowPathCode(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorX86* x86_codegen = down_cast(codegen); @@ -100,13 +99,13 @@ class DivZeroCheckSlowPathX86 : public SlowPathCode { const char* GetDescription() const OVERRIDE { return "DivZeroCheckSlowPathX86"; } private: - HDivZeroCheck* const instruction_; DISALLOW_COPY_AND_ASSIGN(DivZeroCheckSlowPathX86); }; class DivRemMinusOneSlowPathX86 : public SlowPathCode { public: - DivRemMinusOneSlowPathX86(Register reg, bool is_div) : reg_(reg), is_div_(is_div) {} + DivRemMinusOneSlowPathX86(HInstruction* instruction, Register reg, bool is_div) + : SlowPathCode(instruction), reg_(reg), is_div_(is_div) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { __ Bind(GetEntryLabel()); @@ -128,7 +127,7 @@ class DivRemMinusOneSlowPathX86 : public SlowPathCode { class BoundsCheckSlowPathX86 : public SlowPathCode { public: - explicit BoundsCheckSlowPathX86(HBoundsCheck* instruction) : instruction_(instruction) {} + explicit BoundsCheckSlowPathX86(HBoundsCheck* instruction) : SlowPathCode(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { LocationSummary* locations = instruction_->GetLocations(); @@ -160,15 +159,13 @@ class BoundsCheckSlowPathX86 : public SlowPathCode { const char* GetDescription() const OVERRIDE { return "BoundsCheckSlowPathX86"; } private: - HBoundsCheck* const instruction_; - DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathX86); }; class SuspendCheckSlowPathX86 : public SlowPathCode { public: SuspendCheckSlowPathX86(HSuspendCheck* instruction, HBasicBlock* successor) - : instruction_(instruction), successor_(successor) {} + : SlowPathCode(instruction), successor_(successor) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorX86* x86_codegen = down_cast(codegen); @@ -199,7 +196,6 @@ class SuspendCheckSlowPathX86 : public SlowPathCode { const char* GetDescription() const OVERRIDE { return "SuspendCheckSlowPathX86"; } private: - HSuspendCheck* const instruction_; HBasicBlock* const successor_; Label return_label_; @@ -208,7 +204,7 @@ class SuspendCheckSlowPathX86 : public SlowPathCode { class LoadStringSlowPathX86 : public SlowPathCode { public: - explicit LoadStringSlowPathX86(HLoadString* instruction) : instruction_(instruction) {} + explicit LoadStringSlowPathX86(HLoadString* instruction): SlowPathCode(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { LocationSummary* locations = instruction_->GetLocations(); @@ -219,7 +215,8 @@ class LoadStringSlowPathX86 : public SlowPathCode { SaveLiveRegisters(codegen, locations); InvokeRuntimeCallingConvention calling_convention; - __ movl(calling_convention.GetRegisterAt(0), Immediate(instruction_->GetStringIndex())); + const uint32_t string_index = instruction_->AsLoadString()->GetStringIndex(); + __ movl(calling_convention.GetRegisterAt(0), Immediate(string_index)); x86_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pResolveString), instruction_, instruction_->GetDexPc(), @@ -234,8 +231,6 @@ class LoadStringSlowPathX86 : public SlowPathCode { const char* GetDescription() const OVERRIDE { return "LoadStringSlowPathX86"; } private: - HLoadString* const instruction_; - DISALLOW_COPY_AND_ASSIGN(LoadStringSlowPathX86); }; @@ -245,7 +240,7 @@ class LoadClassSlowPathX86 : public SlowPathCode { HInstruction* at, uint32_t dex_pc, bool do_clinit) - : cls_(cls), at_(at), dex_pc_(dex_pc), do_clinit_(do_clinit) { + : SlowPathCode(at), cls_(cls), at_(at), dex_pc_(dex_pc), do_clinit_(do_clinit) { DCHECK(at->IsLoadClass() || at->IsClinitCheck()); } @@ -299,7 +294,7 @@ class LoadClassSlowPathX86 : public SlowPathCode { class TypeCheckSlowPathX86 : public SlowPathCode { public: TypeCheckSlowPathX86(HInstruction* instruction, bool is_fatal) - : instruction_(instruction), is_fatal_(is_fatal) {} + : SlowPathCode(instruction), is_fatal_(is_fatal) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { LocationSummary* locations = instruction_->GetLocations(); @@ -356,7 +351,6 @@ class TypeCheckSlowPathX86 : public SlowPathCode { bool IsFatal() const OVERRIDE { return is_fatal_; } private: - HInstruction* const instruction_; const bool is_fatal_; DISALLOW_COPY_AND_ASSIGN(TypeCheckSlowPathX86); @@ -365,7 +359,7 @@ class TypeCheckSlowPathX86 : public SlowPathCode { class DeoptimizationSlowPathX86 : public SlowPathCode { public: explicit DeoptimizationSlowPathX86(HDeoptimize* instruction) - : instruction_(instruction) {} + : SlowPathCode(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorX86* x86_codegen = down_cast(codegen); @@ -381,13 +375,12 @@ class DeoptimizationSlowPathX86 : public SlowPathCode { const char* GetDescription() const OVERRIDE { return "DeoptimizationSlowPathX86"; } private: - HDeoptimize* const instruction_; DISALLOW_COPY_AND_ASSIGN(DeoptimizationSlowPathX86); }; class ArraySetSlowPathX86 : public SlowPathCode { public: - explicit ArraySetSlowPathX86(HInstruction* instruction) : instruction_(instruction) {} + explicit ArraySetSlowPathX86(HInstruction* instruction) : SlowPathCode(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { LocationSummary* locations = instruction_->GetLocations(); @@ -426,8 +419,6 @@ class ArraySetSlowPathX86 : public SlowPathCode { const char* GetDescription() const OVERRIDE { return "ArraySetSlowPathX86"; } private: - HInstruction* const instruction_; - DISALLOW_COPY_AND_ASSIGN(ArraySetSlowPathX86); }; @@ -435,7 +426,7 @@ class ArraySetSlowPathX86 : public SlowPathCode { class ReadBarrierMarkSlowPathX86 : public SlowPathCode { public: ReadBarrierMarkSlowPathX86(HInstruction* instruction, Location out, Location obj) - : instruction_(instruction), out_(out), obj_(obj) { + : SlowPathCode(instruction), out_(out), obj_(obj) { DCHECK(kEmitCompilerReadBarrier); } @@ -474,7 +465,6 @@ class ReadBarrierMarkSlowPathX86 : public SlowPathCode { } private: - HInstruction* const instruction_; const Location out_; const Location obj_; @@ -490,7 +480,7 @@ class ReadBarrierForHeapReferenceSlowPathX86 : public SlowPathCode { Location obj, uint32_t offset, Location index) - : instruction_(instruction), + : SlowPathCode(instruction), out_(out), ref_(ref), obj_(obj), @@ -645,7 +635,6 @@ class ReadBarrierForHeapReferenceSlowPathX86 : public SlowPathCode { UNREACHABLE(); } - HInstruction* const instruction_; const Location out_; const Location ref_; const Location obj_; @@ -662,7 +651,7 @@ class ReadBarrierForHeapReferenceSlowPathX86 : public SlowPathCode { class ReadBarrierForRootSlowPathX86 : public SlowPathCode { public: ReadBarrierForRootSlowPathX86(HInstruction* instruction, Location out, Location root) - : instruction_(instruction), out_(out), root_(root) { + : SlowPathCode(instruction), out_(out), root_(root) { DCHECK(kEmitCompilerReadBarrier); } @@ -695,7 +684,6 @@ class ReadBarrierForRootSlowPathX86 : public SlowPathCode { const char* GetDescription() const OVERRIDE { return "ReadBarrierForRootSlowPathX86"; } private: - HInstruction* const instruction_; const Location out_; const Location root_; @@ -3453,9 +3441,8 @@ void InstructionCodeGeneratorX86::GenerateDivRemIntegral(HBinaryOperation* instr GenerateDivRemWithAnyConstant(instruction); } } else { - SlowPathCode* slow_path = - new (GetGraph()->GetArena()) DivRemMinusOneSlowPathX86(out.AsRegister(), - is_div); + SlowPathCode* slow_path = new (GetGraph()->GetArena()) DivRemMinusOneSlowPathX86( + instruction, out.AsRegister(), is_div); codegen_->AddSlowPath(slow_path); Register second_reg = second.AsRegister(); diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index 8def1de14a..bb24c6f59c 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -56,7 +56,7 @@ static constexpr int kC2ConditionMask = 0x400; class NullCheckSlowPathX86_64 : public SlowPathCode { public: - explicit NullCheckSlowPathX86_64(HNullCheck* instruction) : instruction_(instruction) {} + explicit NullCheckSlowPathX86_64(HNullCheck* instruction) : SlowPathCode(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorX86_64* x86_64_codegen = down_cast(codegen); @@ -77,13 +77,12 @@ class NullCheckSlowPathX86_64 : public SlowPathCode { const char* GetDescription() const OVERRIDE { return "NullCheckSlowPathX86_64"; } private: - HNullCheck* const instruction_; DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathX86_64); }; class DivZeroCheckSlowPathX86_64 : public SlowPathCode { public: - explicit DivZeroCheckSlowPathX86_64(HDivZeroCheck* instruction) : instruction_(instruction) {} + explicit DivZeroCheckSlowPathX86_64(HDivZeroCheck* instruction) : SlowPathCode(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorX86_64* x86_64_codegen = down_cast(codegen); @@ -104,14 +103,13 @@ class DivZeroCheckSlowPathX86_64 : public SlowPathCode { const char* GetDescription() const OVERRIDE { return "DivZeroCheckSlowPathX86_64"; } private: - HDivZeroCheck* const instruction_; DISALLOW_COPY_AND_ASSIGN(DivZeroCheckSlowPathX86_64); }; class DivRemMinusOneSlowPathX86_64 : public SlowPathCode { public: - DivRemMinusOneSlowPathX86_64(Register reg, Primitive::Type type, bool is_div) - : cpu_reg_(CpuRegister(reg)), type_(type), is_div_(is_div) {} + DivRemMinusOneSlowPathX86_64(HInstruction* at, Register reg, Primitive::Type type, bool is_div) + : SlowPathCode(at), cpu_reg_(CpuRegister(reg)), type_(type), is_div_(is_div) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { __ Bind(GetEntryLabel()); @@ -145,7 +143,7 @@ class DivRemMinusOneSlowPathX86_64 : public SlowPathCode { class SuspendCheckSlowPathX86_64 : public SlowPathCode { public: SuspendCheckSlowPathX86_64(HSuspendCheck* instruction, HBasicBlock* successor) - : instruction_(instruction), successor_(successor) {} + : SlowPathCode(instruction), successor_(successor) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorX86_64* x86_64_codegen = down_cast(codegen); @@ -176,7 +174,6 @@ class SuspendCheckSlowPathX86_64 : public SlowPathCode { const char* GetDescription() const OVERRIDE { return "SuspendCheckSlowPathX86_64"; } private: - HSuspendCheck* const instruction_; HBasicBlock* const successor_; Label return_label_; @@ -186,7 +183,7 @@ class SuspendCheckSlowPathX86_64 : public SlowPathCode { class BoundsCheckSlowPathX86_64 : public SlowPathCode { public: explicit BoundsCheckSlowPathX86_64(HBoundsCheck* instruction) - : instruction_(instruction) {} + : SlowPathCode(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { LocationSummary* locations = instruction_->GetLocations(); @@ -218,8 +215,6 @@ class BoundsCheckSlowPathX86_64 : public SlowPathCode { const char* GetDescription() const OVERRIDE { return "BoundsCheckSlowPathX86_64"; } private: - HBoundsCheck* const instruction_; - DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathX86_64); }; @@ -229,7 +224,7 @@ class LoadClassSlowPathX86_64 : public SlowPathCode { HInstruction* at, uint32_t dex_pc, bool do_clinit) - : cls_(cls), at_(at), dex_pc_(dex_pc), do_clinit_(do_clinit) { + : SlowPathCode(at), cls_(cls), at_(at), dex_pc_(dex_pc), do_clinit_(do_clinit) { DCHECK(at->IsLoadClass() || at->IsClinitCheck()); } @@ -286,7 +281,7 @@ class LoadClassSlowPathX86_64 : public SlowPathCode { class LoadStringSlowPathX86_64 : public SlowPathCode { public: - explicit LoadStringSlowPathX86_64(HLoadString* instruction) : instruction_(instruction) {} + explicit LoadStringSlowPathX86_64(HLoadString* instruction) : SlowPathCode(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { LocationSummary* locations = instruction_->GetLocations(); @@ -297,8 +292,8 @@ class LoadStringSlowPathX86_64 : public SlowPathCode { SaveLiveRegisters(codegen, locations); InvokeRuntimeCallingConvention calling_convention; - __ movl(CpuRegister(calling_convention.GetRegisterAt(0)), - Immediate(instruction_->GetStringIndex())); + const uint32_t string_index = instruction_->AsLoadString()->GetStringIndex(); + __ movl(CpuRegister(calling_convention.GetRegisterAt(0)), Immediate(string_index)); x86_64_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pResolveString), instruction_, instruction_->GetDexPc(), @@ -312,15 +307,13 @@ class LoadStringSlowPathX86_64 : public SlowPathCode { const char* GetDescription() const OVERRIDE { return "LoadStringSlowPathX86_64"; } private: - HLoadString* const instruction_; - DISALLOW_COPY_AND_ASSIGN(LoadStringSlowPathX86_64); }; class TypeCheckSlowPathX86_64 : public SlowPathCode { public: TypeCheckSlowPathX86_64(HInstruction* instruction, bool is_fatal) - : instruction_(instruction), is_fatal_(is_fatal) {} + : SlowPathCode(instruction), is_fatal_(is_fatal) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { LocationSummary* locations = instruction_->GetLocations(); @@ -379,7 +372,6 @@ class TypeCheckSlowPathX86_64 : public SlowPathCode { bool IsFatal() const OVERRIDE { return is_fatal_; } private: - HInstruction* const instruction_; const bool is_fatal_; DISALLOW_COPY_AND_ASSIGN(TypeCheckSlowPathX86_64); @@ -388,7 +380,7 @@ class TypeCheckSlowPathX86_64 : public SlowPathCode { class DeoptimizationSlowPathX86_64 : public SlowPathCode { public: explicit DeoptimizationSlowPathX86_64(HDeoptimize* instruction) - : instruction_(instruction) {} + : SlowPathCode(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorX86_64* x86_64_codegen = down_cast(codegen); @@ -404,13 +396,12 @@ class DeoptimizationSlowPathX86_64 : public SlowPathCode { const char* GetDescription() const OVERRIDE { return "DeoptimizationSlowPathX86_64"; } private: - HDeoptimize* const instruction_; DISALLOW_COPY_AND_ASSIGN(DeoptimizationSlowPathX86_64); }; class ArraySetSlowPathX86_64 : public SlowPathCode { public: - explicit ArraySetSlowPathX86_64(HInstruction* instruction) : instruction_(instruction) {} + explicit ArraySetSlowPathX86_64(HInstruction* instruction) : SlowPathCode(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { LocationSummary* locations = instruction_->GetLocations(); @@ -449,8 +440,6 @@ class ArraySetSlowPathX86_64 : public SlowPathCode { const char* GetDescription() const OVERRIDE { return "ArraySetSlowPathX86_64"; } private: - HInstruction* const instruction_; - DISALLOW_COPY_AND_ASSIGN(ArraySetSlowPathX86_64); }; @@ -458,7 +447,7 @@ class ArraySetSlowPathX86_64 : public SlowPathCode { class ReadBarrierMarkSlowPathX86_64 : public SlowPathCode { public: ReadBarrierMarkSlowPathX86_64(HInstruction* instruction, Location out, Location obj) - : instruction_(instruction), out_(out), obj_(obj) { + : SlowPathCode(instruction), out_(out), obj_(obj) { DCHECK(kEmitCompilerReadBarrier); } @@ -497,7 +486,6 @@ class ReadBarrierMarkSlowPathX86_64 : public SlowPathCode { } private: - HInstruction* const instruction_; const Location out_; const Location obj_; @@ -513,7 +501,7 @@ class ReadBarrierForHeapReferenceSlowPathX86_64 : public SlowPathCode { Location obj, uint32_t offset, Location index) - : instruction_(instruction), + : SlowPathCode(instruction), out_(out), ref_(ref), obj_(obj), @@ -667,7 +655,6 @@ class ReadBarrierForHeapReferenceSlowPathX86_64 : public SlowPathCode { UNREACHABLE(); } - HInstruction* const instruction_; const Location out_; const Location ref_; const Location obj_; @@ -684,7 +671,7 @@ class ReadBarrierForHeapReferenceSlowPathX86_64 : public SlowPathCode { class ReadBarrierForRootSlowPathX86_64 : public SlowPathCode { public: ReadBarrierForRootSlowPathX86_64(HInstruction* instruction, Location out, Location root) - : instruction_(instruction), out_(out), root_(root) { + : SlowPathCode(instruction), out_(out), root_(root) { DCHECK(kEmitCompilerReadBarrier); } @@ -716,7 +703,6 @@ class ReadBarrierForRootSlowPathX86_64 : public SlowPathCode { const char* GetDescription() const OVERRIDE { return "ReadBarrierForRootSlowPathX86_64"; } private: - HInstruction* const instruction_; const Location out_; const Location root_; @@ -3546,7 +3532,7 @@ void InstructionCodeGeneratorX86_64::GenerateDivRemIntegral(HBinaryOperation* in } else { SlowPathCode* slow_path = new (GetGraph()->GetArena()) DivRemMinusOneSlowPathX86_64( - out.AsRegister(), type, is_div); + instruction, out.AsRegister(), type, is_div); codegen_->AddSlowPath(slow_path); CpuRegister second_reg = second.AsRegister(); diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc index 8741fd284f..b5f15fe22d 100644 --- a/compiler/optimizing/intrinsics_arm64.cc +++ b/compiler/optimizing/intrinsics_arm64.cc @@ -99,7 +99,8 @@ static void MoveArguments(HInvoke* invoke, CodeGeneratorARM64* codegen) { // restored! class IntrinsicSlowPathARM64 : public SlowPathCodeARM64 { public: - explicit IntrinsicSlowPathARM64(HInvoke* invoke) : invoke_(invoke) { } + explicit IntrinsicSlowPathARM64(HInvoke* invoke) + : SlowPathCodeARM64(invoke), invoke_(invoke) { } void EmitNativeCode(CodeGenerator* codegen_in) OVERRIDE { CodeGeneratorARM64* codegen = down_cast(codegen_in); diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc index c8629644b6..ca9a9dd366 100644 --- a/compiler/optimizing/intrinsics_mips.cc +++ b/compiler/optimizing/intrinsics_mips.cc @@ -99,7 +99,7 @@ static void MoveArguments(HInvoke* invoke, CodeGeneratorMIPS* codegen) { // restored! class IntrinsicSlowPathMIPS : public SlowPathCodeMIPS { public: - explicit IntrinsicSlowPathMIPS(HInvoke* invoke) : invoke_(invoke) { } + explicit IntrinsicSlowPathMIPS(HInvoke* invoke) : SlowPathCodeMIPS(invoke), invoke_(invoke) { } void EmitNativeCode(CodeGenerator* codegen_in) OVERRIDE { CodeGeneratorMIPS* codegen = down_cast(codegen_in); diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc index cf3a3657de..457e247d6f 100644 --- a/compiler/optimizing/intrinsics_mips64.cc +++ b/compiler/optimizing/intrinsics_mips64.cc @@ -87,7 +87,8 @@ static void MoveArguments(HInvoke* invoke, CodeGeneratorMIPS64* codegen) { // restored! class IntrinsicSlowPathMIPS64 : public SlowPathCodeMIPS64 { public: - explicit IntrinsicSlowPathMIPS64(HInvoke* invoke) : invoke_(invoke) { } + explicit IntrinsicSlowPathMIPS64(HInvoke* invoke) + : SlowPathCodeMIPS64(invoke), invoke_(invoke) { } void EmitNativeCode(CodeGenerator* codegen_in) OVERRIDE { CodeGeneratorMIPS64* codegen = down_cast(codegen_in); diff --git a/compiler/optimizing/intrinsics_utils.h b/compiler/optimizing/intrinsics_utils.h index e70afd29f0..c1f9ae6425 100644 --- a/compiler/optimizing/intrinsics_utils.h +++ b/compiler/optimizing/intrinsics_utils.h @@ -39,7 +39,7 @@ namespace art { template class IntrinsicSlowPath : public SlowPathCode { public: - explicit IntrinsicSlowPath(HInvoke* invoke) : invoke_(invoke) { } + explicit IntrinsicSlowPath(HInvoke* invoke) : SlowPathCode(invoke), invoke_(invoke) { } Location MoveArguments(CodeGenerator* codegen) { TDexCallingConvention calling_convention_visitor; -- GitLab From 944da603cde59a4277f3bbc31d860a90842a1a2a Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Fri, 19 Feb 2016 12:27:55 +0000 Subject: [PATCH 020/204] ART: Allow method references across oat files for multi-image, 2nd attempt. These were disabled because we didn't have sufficient information about the multi-image layout when processing link-time patches in OatWriter. This CL refactors the ELF file creation so that the information is available. Also clean up ImageWriter to use oat file indexes instead of filenames and avoid reopening the oat file to retrieve the checksum. Change-Id: Icc7b528deca29da1e473c8f079521a36d6c4892f --- build/Android.gtest.mk | 1 + compiler/Android.mk | 1 + compiler/common_compiler_test.cc | 11 +- compiler/dex/quick/quick_cfi_test.cc | 21 +- .../dex/quick/x86/quick_assemble_x86_test.cc | 23 +- .../driver/compiled_method_storage_test.cc | 26 +- compiler/driver/compiler_driver.cc | 18 +- compiler/driver/compiler_driver.h | 25 +- compiler/elf_builder.h | 298 ++++++++++---- compiler/elf_writer.h | 9 +- compiler/elf_writer_quick.cc | 52 ++- compiler/image_test.cc | 31 +- compiler/image_writer.cc | 387 ++++++------------ compiler/image_writer.h | 119 +++--- compiler/jit/jit_compiler.cc | 1 - .../linker/arm/relative_patcher_arm_base.cc | 5 + .../linker/arm/relative_patcher_arm_base.h | 15 +- .../linker/arm/relative_patcher_thumb2.cc | 6 +- compiler/linker/arm/relative_patcher_thumb2.h | 12 +- .../linker/arm64/relative_patcher_arm64.cc | 6 +- .../linker/arm64/relative_patcher_arm64.h | 15 +- compiler/linker/multi_oat_relative_patcher.cc | 72 ++++ compiler/linker/multi_oat_relative_patcher.h | 146 +++++++ .../linker/multi_oat_relative_patcher_test.cc | 299 ++++++++++++++ compiler/linker/relative_patcher.cc | 3 +- compiler/linker/relative_patcher.h | 20 +- compiler/linker/relative_patcher_test.h | 26 +- compiler/linker/x86/relative_patcher_x86.h | 6 +- .../linker/x86/relative_patcher_x86_base.cc | 6 +- .../linker/x86/relative_patcher_x86_base.h | 6 +- .../linker/x86_64/relative_patcher_x86_64.cc | 3 +- .../linker/x86_64/relative_patcher_x86_64.h | 6 +- compiler/oat_test.cc | 29 +- compiler/oat_writer.cc | 97 +++-- compiler/oat_writer.h | 26 +- dex2oat/dex2oat.cc | 72 ++-- oatdump/oatdump.cc | 4 +- runtime/safe_map.h | 8 +- 38 files changed, 1287 insertions(+), 624 deletions(-) create mode 100644 compiler/linker/multi_oat_relative_patcher.cc create mode 100644 compiler/linker/multi_oat_relative_patcher.h create mode 100644 compiler/linker/multi_oat_relative_patcher_test.cc diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index 704d69a37a..cb9a7e6e77 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -251,6 +251,7 @@ COMPILER_GTEST_COMMON_SRC_FILES := \ compiler/elf_writer_test.cc \ compiler/image_test.cc \ compiler/jni/jni_compiler_test.cc \ + compiler/linker/multi_oat_relative_patcher_test.cc \ compiler/linker/output_stream_test.cc \ compiler/oat_test.cc \ compiler/optimizing/bounds_check_elimination_test.cc \ diff --git a/compiler/Android.mk b/compiler/Android.mk index 159e9cfb5e..33b89bbe42 100644 --- a/compiler/Android.mk +++ b/compiler/Android.mk @@ -61,6 +61,7 @@ LIBART_COMPILER_SRC_FILES := \ driver/dex_compilation_unit.cc \ linker/buffered_output_stream.cc \ linker/file_output_stream.cc \ + linker/multi_oat_relative_patcher.cc \ linker/output_stream.cc \ linker/vector_output_stream.cc \ linker/relative_patcher.cc \ diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc index e4bfac9ee7..239bc590e9 100644 --- a/compiler/common_compiler_test.cc +++ b/compiler/common_compiler_test.cc @@ -194,16 +194,15 @@ void CommonCompilerTest::CreateCompilerDriver(Compiler::Kind kind, InstructionSe kind, isa, instruction_set_features_.get(), - true, + /* boot_image */ true, GetImageClasses(), GetCompiledClasses(), GetCompiledMethods(), - 2, - true, - true, + /* thread_count */ 2, + /* dump_stats */ true, + /* dump_passes */ true, timer_.get(), - -1, - /* dex_to_oat_map */ nullptr, + /* swap_fd */ -1, GetProfileCompilationInfo())); // We typically don't generate an image in unit tests, disable this optimization by default. compiler_driver_->SetSupportBootImageFixup(false); diff --git a/compiler/dex/quick/quick_cfi_test.cc b/compiler/dex/quick/quick_cfi_test.cc index 0cd41bbf4c..6c6c9cfb1e 100644 --- a/compiler/dex/quick/quick_cfi_test.cc +++ b/compiler/dex/quick/quick_cfi_test.cc @@ -84,17 +84,16 @@ class QuickCFITest : public CFITest { Compiler::kQuick, isa, isa_features.get(), - false, - nullptr, - nullptr, - nullptr, - 0, - false, - false, - 0, - -1, - nullptr, - nullptr); + /* boot_image */ false, + /* image_classes */ nullptr, + /* compiled_classes */ nullptr, + /* compiled_methods */ nullptr, + /* thread_count */ 0, + /* dump_stats */ false, + /* dump_passes */ false, + /* timer */ nullptr, + /* swap_fd */ -1, + /* profile_compilation_info */ nullptr); ClassLinker* linker = nullptr; CompilationUnit cu(&pool, isa, &driver, linker); DexFile::CodeItem code_item { 0, 0, 0, 0, 0, 0, { 0 } }; // NOLINT diff --git a/compiler/dex/quick/x86/quick_assemble_x86_test.cc b/compiler/dex/quick/x86/quick_assemble_x86_test.cc index efdc333261..ff0ecea94c 100644 --- a/compiler/dex/quick/x86/quick_assemble_x86_test.cc +++ b/compiler/dex/quick/x86/quick_assemble_x86_test.cc @@ -64,18 +64,17 @@ class QuickAssembleX86TestBase : public testing::Test { method_inliner_map_.get(), Compiler::kQuick, isa_, - nullptr, - false, - nullptr, - nullptr, - nullptr, - 0, - false, - false, - 0, - -1, - nullptr, - nullptr)); + /* instruction_set_features*/ nullptr, + /* boot_image */ false, + /* image_classes */ nullptr, + /* compiled_classes */ nullptr, + /* compiled_methods */ nullptr, + /* thread_count */ 0, + /* dump_stats */ false, + /* dump_passes */ false, + /* timer */ nullptr, + /* swap_fd */ -1, + /* profile_compilation_info */ nullptr)); cu_.reset(new CompilationUnit(pool_.get(), isa_, compiler_driver_.get(), nullptr)); DexFile::CodeItem* code_item = static_cast( cu_->arena.Alloc(sizeof(DexFile::CodeItem), kArenaAllocMisc)); diff --git a/compiler/driver/compiled_method_storage_test.cc b/compiler/driver/compiled_method_storage_test.cc index 2e2d1f99f3..0695cb56b3 100644 --- a/compiler/driver/compiled_method_storage_test.cc +++ b/compiler/driver/compiled_method_storage_test.cc @@ -32,19 +32,19 @@ TEST(CompiledMethodStorage, Deduplicate) { CompilerDriver driver(&compiler_options, &verification_results, &method_inliner_map, - Compiler::kOptimizing, kNone, - nullptr, - false, - nullptr, - nullptr, - nullptr, - 1u, - false, - false, - nullptr, - -1, - nullptr, - nullptr); + Compiler::kOptimizing, + /* instruction_set_ */ kNone, + /* instruction_set_features */ nullptr, + /* boot_image */ false, + /* image_classes */ nullptr, + /* compiled_classes */ nullptr, + /* compiled_methods */ nullptr, + /* thread_count */ 1u, + /* dump_stats */ false, + /* dump_passes */ false, + /* timer */ nullptr, + /* swap_fd */ -1, + /* profile_compilation_info */ nullptr); CompiledMethodStorage* storage = driver.GetCompiledMethodStorage(); ASSERT_TRUE(storage->DedupeEnabled()); // The default. diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 670fe94988..e80730fe11 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -342,12 +342,15 @@ CompilerDriver::CompilerDriver( Compiler::Kind compiler_kind, InstructionSet instruction_set, const InstructionSetFeatures* instruction_set_features, - bool boot_image, std::unordered_set* image_classes, + bool boot_image, + std::unordered_set* image_classes, std::unordered_set* compiled_classes, std::unordered_set* compiled_methods, - size_t thread_count, bool dump_stats, bool dump_passes, - CumulativeLogger* timer, int swap_fd, - const std::unordered_map* dex_to_oat_map, + size_t thread_count, + bool dump_stats, + bool dump_passes, + CumulativeLogger* timer, + int swap_fd, const ProfileCompilationInfo* profile_compilation_info) : compiler_options_(compiler_options), verification_results_(verification_results), @@ -374,7 +377,6 @@ CompilerDriver::CompilerDriver( compiler_context_(nullptr), support_boot_image_fixup_(instruction_set != kMips && instruction_set != kMips64), dex_files_for_oat_file_(nullptr), - dex_file_oat_filename_map_(dex_to_oat_map), compiled_method_storage_(swap_fd), profile_compilation_info_(profile_compilation_info) { DCHECK(compiler_options_ != nullptr); @@ -1678,12 +1680,6 @@ void CompilerDriver::GetCodeAndMethodForDirectCall(InvokeType* type, InvokeType use_dex_cache = true; } } - if (!use_dex_cache && IsBootImage()) { - if (!AreInSameOatFile(&(const_cast(referrer_class)->GetDexFile()), - &declaring_class->GetDexFile())) { - use_dex_cache = true; - } - } // The method is defined not within this dex file. We need a dex cache slot within the current // dex file or direct pointers. bool must_use_direct_pointers = false; diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index 5e35cbb309..ca340ee92c 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -94,9 +94,11 @@ class CompilerDriver { bool boot_image, std::unordered_set* image_classes, std::unordered_set* compiled_classes, std::unordered_set* compiled_methods, - size_t thread_count, bool dump_stats, bool dump_passes, - CumulativeLogger* timer, int swap_fd, - const std::unordered_map* dex_to_oat_map, + size_t thread_count, + bool dump_stats, + bool dump_passes, + CumulativeLogger* timer, + int swap_fd, const ProfileCompilationInfo* profile_compilation_info); ~CompilerDriver(); @@ -113,20 +115,6 @@ class CompilerDriver { : ArrayRef(); } - // Are the given dex files compiled into the same oat file? Should only be called after - // GetDexFilesForOatFile, as the conservative answer (when we don't have a map) is true. - bool AreInSameOatFile(const DexFile* d1, const DexFile* d2) { - if (dex_file_oat_filename_map_ == nullptr) { - // TODO: Check for this wrt/ apps and boot image calls. - return true; - } - auto it1 = dex_file_oat_filename_map_->find(d1); - DCHECK(it1 != dex_file_oat_filename_map_->end()); - auto it2 = dex_file_oat_filename_map_->find(d2); - DCHECK(it2 != dex_file_oat_filename_map_->end()); - return it1->second == it2->second; - } - void CompileAll(jobject class_loader, const std::vector& dex_files, TimingLogger* timings) @@ -700,9 +688,6 @@ class CompilerDriver { // List of dex files that will be stored in the oat file. const std::vector* dex_files_for_oat_file_; - // Map from dex files to the oat file (name) they will be compiled into. - const std::unordered_map* dex_file_oat_filename_map_; - CompiledMethodStorage compiled_method_storage_; // Info for profile guided compilation. diff --git a/compiler/elf_builder.h b/compiler/elf_builder.h index b673eeb3b6..f7da609e5d 100644 --- a/compiler/elf_builder.h +++ b/compiler/elf_builder.h @@ -86,12 +86,24 @@ class ElfBuilder FINAL { // Base class of all sections. class Section : public OutputStream { public: - Section(ElfBuilder* owner, const std::string& name, - Elf_Word type, Elf_Word flags, const Section* link, - Elf_Word info, Elf_Word align, Elf_Word entsize) - : OutputStream(name), owner_(owner), header_(), - section_index_(0), name_(name), link_(link), - started_(false), finished_(false), phdr_flags_(PF_R), phdr_type_(0) { + Section(ElfBuilder* owner, + const std::string& name, + Elf_Word type, + Elf_Word flags, + const Section* link, + Elf_Word info, + Elf_Word align, + Elf_Word entsize) + : OutputStream(name), + owner_(owner), + header_(), + section_index_(0), + name_(name), + link_(link), + started_(false), + finished_(false), + phdr_flags_(PF_R), + phdr_type_(0) { DCHECK_GE(align, 1u); header_.sh_type = type; header_.sh_flags = flags; @@ -228,12 +240,84 @@ class ElfBuilder FINAL { DISALLOW_COPY_AND_ASSIGN(Section); }; - // Writer of .dynstr .strtab and .shstrtab sections. + class CachedSection : public Section { + public: + CachedSection(ElfBuilder* owner, + const std::string& name, + Elf_Word type, + Elf_Word flags, + const Section* link, + Elf_Word info, + Elf_Word align, + Elf_Word entsize) + : Section(owner, name, type, flags, link, info, align, entsize), cache_() { } + + Elf_Word Add(const void* data, size_t length) { + Elf_Word offset = cache_.size(); + const uint8_t* d = reinterpret_cast(data); + cache_.insert(cache_.end(), d, d + length); + return offset; + } + + Elf_Word GetCacheSize() { + return cache_.size(); + } + + void Write() { + this->WriteFully(cache_.data(), cache_.size()); + cache_.clear(); + cache_.shrink_to_fit(); + } + + void WriteCachedSection() { + this->Start(); + Write(); + this->End(); + } + + private: + std::vector cache_; + }; + + // Writer of .dynstr section. + class CachedStringSection FINAL : public CachedSection { + public: + CachedStringSection(ElfBuilder* owner, + const std::string& name, + Elf_Word flags, + Elf_Word align) + : CachedSection(owner, + name, + SHT_STRTAB, + flags, + /* link */ nullptr, + /* info */ 0, + align, + /* entsize */ 0) { } + + Elf_Word Add(const std::string& name) { + if (CachedSection::GetCacheSize() == 0u) { + DCHECK(name.empty()); + } + return CachedSection::Add(name.c_str(), name.length() + 1); + } + }; + + // Writer of .strtab and .shstrtab sections. class StringSection FINAL : public Section { public: - StringSection(ElfBuilder* owner, const std::string& name, - Elf_Word flags, Elf_Word align) - : Section(owner, name, SHT_STRTAB, flags, nullptr, 0, align, 0), + StringSection(ElfBuilder* owner, + const std::string& name, + Elf_Word flags, + Elf_Word align) + : Section(owner, + name, + SHT_STRTAB, + flags, + /* link */ nullptr, + /* info */ 0, + align, + /* entsize */ 0), current_offset_(0) { } @@ -252,42 +336,60 @@ class ElfBuilder FINAL { }; // Writer of .dynsym and .symtab sections. - class SymbolSection FINAL : public Section { + class SymbolSection FINAL : public CachedSection { public: - SymbolSection(ElfBuilder* owner, const std::string& name, - Elf_Word type, Elf_Word flags, StringSection* strtab) - : Section(owner, name, type, flags, strtab, 0, - sizeof(Elf_Off), sizeof(Elf_Sym)) { + SymbolSection(ElfBuilder* owner, + const std::string& name, + Elf_Word type, + Elf_Word flags, + Section* strtab) + : CachedSection(owner, + name, + type, + flags, + strtab, + /* info */ 0, + sizeof(Elf_Off), + sizeof(Elf_Sym)) { + // The symbol table always has to start with NULL symbol. + Elf_Sym null_symbol = Elf_Sym(); + CachedSection::Add(&null_symbol, sizeof(null_symbol)); } // Buffer symbol for this section. It will be written later. // If the symbol's section is null, it will be considered absolute (SHN_ABS). // (we use this in JIT to reference code which is stored outside the debug ELF file) - void Add(Elf_Word name, const Section* section, - Elf_Addr addr, bool is_relative, Elf_Word size, - uint8_t binding, uint8_t type, uint8_t other = 0) { + void Add(Elf_Word name, + const Section* section, + Elf_Addr addr, + bool is_relative, + Elf_Word size, + uint8_t binding, + uint8_t type, + uint8_t other = 0) { + DCHECK(section != nullptr || !is_relative); + Elf_Addr abs_addr = addr + (is_relative ? section->GetAddress() : 0); + Elf_Word section_index = + (section != nullptr) ? section->GetSectionIndex() : static_cast(SHN_ABS); + Add(name, section_index, abs_addr, size, binding, type, other); + } + + void Add(Elf_Word name, + Elf_Word section_index, + Elf_Addr addr, + Elf_Word size, + uint8_t binding, + uint8_t type, + uint8_t other = 0) { Elf_Sym sym = Elf_Sym(); sym.st_name = name; - sym.st_value = addr + (is_relative ? section->GetAddress() : 0); + sym.st_value = addr; sym.st_size = size; sym.st_other = other; - sym.st_shndx = (section != nullptr ? section->GetSectionIndex() - : static_cast(SHN_ABS)); + sym.st_shndx = section_index; sym.st_info = (binding << 4) + (type & 0xf); - symbols_.push_back(sym); - } - - void Write() { - // The symbol table always has to start with NULL symbol. - Elf_Sym null_symbol = Elf_Sym(); - this->WriteFully(&null_symbol, sizeof(null_symbol)); - this->WriteFully(symbols_.data(), symbols_.size() * sizeof(symbols_[0])); - symbols_.clear(); - symbols_.shrink_to_fit(); + CachedSection::Add(&sym, sizeof(sym)); } - - private: - std::vector symbols_; }; ElfBuilder(InstructionSet isa, OutputStream* output) @@ -309,6 +411,8 @@ class ElfBuilder FINAL { debug_line_(this, ".debug_line", SHT_PROGBITS, 0, nullptr, 0, 1, 0), shstrtab_(this, ".shstrtab", 0, 1), started_(false), + write_program_headers_(false), + loaded_size_(0u), virtual_address_(0) { text_.phdr_flags_ = PF_R | PF_X; bss_.phdr_flags_ = PF_R | PF_W; @@ -380,6 +484,14 @@ class ElfBuilder FINAL { void End() { DCHECK(started_); + // Note: loaded_size_ == 0 for tests that don't write .rodata, .text, .bss, + // .dynstr, dynsym, .hash and .dynamic. These tests should not read loaded_size_. + // TODO: Either refactor the .eh_frame creation so that it counts towards loaded_size_, + // or remove all support for .eh_frame. (The currently unused .eh_frame counts towards + // the virtual_address_ but we don't consider it for loaded_size_.) + CHECK(loaded_size_ == 0 || loaded_size_ == RoundUp(virtual_address_, kPageSize)) + << loaded_size_ << " " << virtual_address_; + // Write section names and finish the section headers. shstrtab_.Start(); shstrtab_.Write(""); @@ -434,45 +546,58 @@ class ElfBuilder FINAL { // information like the address and size of .rodata and .text. // It also contains other metadata like the SONAME. // The .dynamic section is found using the PT_DYNAMIC program header. - void WriteDynamicSection(const std::string& elf_file_path) { + void PrepareDynamicSection(const std::string& elf_file_path, + Elf_Word rodata_size, + Elf_Word text_size, + Elf_Word bss_size) { std::string soname(elf_file_path); size_t directory_separator_pos = soname.rfind('/'); if (directory_separator_pos != std::string::npos) { soname = soname.substr(directory_separator_pos + 1); } - dynstr_.Start(); - dynstr_.Write(""); // dynstr should start with empty string. - dynsym_.Add(dynstr_.Write("oatdata"), &rodata_, 0, true, - rodata_.GetSize(), STB_GLOBAL, STT_OBJECT); - if (text_.GetSize() != 0u) { - dynsym_.Add(dynstr_.Write("oatexec"), &text_, 0, true, - text_.GetSize(), STB_GLOBAL, STT_OBJECT); - dynsym_.Add(dynstr_.Write("oatlastword"), &text_, text_.GetSize() - 4, - true, 4, STB_GLOBAL, STT_OBJECT); - } else if (rodata_.GetSize() != 0) { + // Calculate addresses of .text, .bss and .dynstr. + DCHECK_EQ(rodata_.header_.sh_addralign, static_cast(kPageSize)); + DCHECK_EQ(text_.header_.sh_addralign, static_cast(kPageSize)); + DCHECK_EQ(bss_.header_.sh_addralign, static_cast(kPageSize)); + DCHECK_EQ(dynstr_.header_.sh_addralign, static_cast(kPageSize)); + Elf_Word rodata_address = rodata_.GetAddress(); + Elf_Word text_address = RoundUp(rodata_address + rodata_size, kPageSize); + Elf_Word bss_address = RoundUp(text_address + text_size, kPageSize); + Elf_Word dynstr_address = RoundUp(bss_address + bss_size, kPageSize); + + // Cache .dynstr, .dynsym and .hash data. + dynstr_.Add(""); // dynstr should start with empty string. + Elf_Word rodata_index = rodata_.GetSectionIndex(); + Elf_Word oatdata = dynstr_.Add("oatdata"); + dynsym_.Add(oatdata, rodata_index, rodata_address, rodata_size, STB_GLOBAL, STT_OBJECT); + if (text_size != 0u) { + Elf_Word text_index = rodata_index + 1u; + Elf_Word oatexec = dynstr_.Add("oatexec"); + dynsym_.Add(oatexec, text_index, text_address, text_size, STB_GLOBAL, STT_OBJECT); + Elf_Word oatlastword = dynstr_.Add("oatlastword"); + Elf_Word oatlastword_address = text_address + text_size - 4; + dynsym_.Add(oatlastword, text_index, oatlastword_address, 4, STB_GLOBAL, STT_OBJECT); + } else if (rodata_size != 0) { // rodata_ can be size 0 for dwarf_test. - dynsym_.Add(dynstr_.Write("oatlastword"), &rodata_, rodata_.GetSize() - 4, - true, 4, STB_GLOBAL, STT_OBJECT); + Elf_Word oatlastword = dynstr_.Add("oatlastword"); + Elf_Word oatlastword_address = rodata_address + rodata_size - 4; + dynsym_.Add(oatlastword, rodata_index, oatlastword_address, 4, STB_GLOBAL, STT_OBJECT); } - if (bss_.finished_) { - dynsym_.Add(dynstr_.Write("oatbss"), &bss_, - 0, true, bss_.GetSize(), STB_GLOBAL, STT_OBJECT); - dynsym_.Add(dynstr_.Write("oatbsslastword"), &bss_, - bss_.GetSize() - 4, true, 4, STB_GLOBAL, STT_OBJECT); + if (bss_size != 0u) { + Elf_Word bss_index = rodata_index + 1u + (text_size != 0 ? 1u : 0u); + Elf_Word oatbss = dynstr_.Add("oatbss"); + dynsym_.Add(oatbss, bss_index, bss_address, bss_size, STB_GLOBAL, STT_OBJECT); + Elf_Word oatbsslastword = dynstr_.Add("oatbsslastword"); + Elf_Word bsslastword_address = bss_address + bss_size - 4; + dynsym_.Add(oatbsslastword, bss_index, bsslastword_address, 4, STB_GLOBAL, STT_OBJECT); } - Elf_Word soname_offset = dynstr_.Write(soname); - dynstr_.End(); - - dynsym_.Start(); - dynsym_.Write(); - dynsym_.End(); + Elf_Word soname_offset = dynstr_.Add(soname); // We do not really need a hash-table since there is so few entries. // However, the hash-table is the only way the linker can actually // determine the number of symbols in .dynsym so it is required. - hash_.Start(); - int count = dynsym_.GetSize() / sizeof(Elf_Sym); // Includes NULL. + int count = dynsym_.GetCacheSize() / sizeof(Elf_Sym); // Includes NULL. std::vector hash; hash.push_back(1); // Number of buckets. hash.push_back(count); // Number of chains. @@ -484,21 +609,44 @@ class ElfBuilder FINAL { hash.push_back(i + 1); // Each symbol points to the next one. } hash.push_back(0); // Last symbol terminates the chain. - hash_.WriteFully(hash.data(), hash.size() * sizeof(hash[0])); - hash_.End(); + hash_.Add(hash.data(), hash.size() * sizeof(hash[0])); + + // Calculate addresses of .dynsym, .hash and .dynamic. + DCHECK_EQ(dynstr_.header_.sh_flags, dynsym_.header_.sh_flags); + DCHECK_EQ(dynsym_.header_.sh_flags, hash_.header_.sh_flags); + Elf_Word dynsym_address = + RoundUp(dynstr_address + dynstr_.GetCacheSize(), dynsym_.header_.sh_addralign); + Elf_Word hash_address = + RoundUp(dynsym_address + dynsym_.GetCacheSize(), hash_.header_.sh_addralign); + DCHECK_EQ(dynamic_.header_.sh_addralign, static_cast(kPageSize)); + Elf_Word dynamic_address = RoundUp(hash_address + dynsym_.GetCacheSize(), kPageSize); - dynamic_.Start(); Elf_Dyn dyns[] = { - { DT_HASH, { hash_.GetAddress() } }, - { DT_STRTAB, { dynstr_.GetAddress() } }, - { DT_SYMTAB, { dynsym_.GetAddress() } }, + { DT_HASH, { hash_address } }, + { DT_STRTAB, { dynstr_address } }, + { DT_SYMTAB, { dynsym_address } }, { DT_SYMENT, { sizeof(Elf_Sym) } }, - { DT_STRSZ, { dynstr_.GetSize() } }, + { DT_STRSZ, { dynstr_.GetCacheSize() } }, { DT_SONAME, { soname_offset } }, { DT_NULL, { 0 } }, }; - dynamic_.WriteFully(&dyns, sizeof(dyns)); - dynamic_.End(); + dynamic_.Add(&dyns, sizeof(dyns)); + + loaded_size_ = RoundUp(dynamic_address + dynamic_.GetCacheSize(), kPageSize); + } + + void WriteDynamicSection() { + dynstr_.WriteCachedSection(); + dynsym_.WriteCachedSection(); + hash_.WriteCachedSection(); + dynamic_.WriteCachedSection(); + + CHECK_EQ(loaded_size_, RoundUp(dynamic_.GetAddress() + dynamic_.GetSize(), kPageSize)); + } + + Elf_Word GetLoadedSize() { + CHECK_NE(loaded_size_, 0u); + return loaded_size_; } // Returns true if all writes and seeks on the output stream succeeded. @@ -676,10 +824,10 @@ class ElfBuilder FINAL { Section rodata_; Section text_; Section bss_; - StringSection dynstr_; + CachedStringSection dynstr_; SymbolSection dynsym_; - Section hash_; - Section dynamic_; + CachedSection hash_; + CachedSection dynamic_; Section eh_frame_; Section eh_frame_hdr_; StringSection strtab_; @@ -694,12 +842,14 @@ class ElfBuilder FINAL { std::vector sections_; bool started_; + bool write_program_headers_; + + // The size of the memory taken by the ELF file when loaded. + size_t loaded_size_; // Used for allocation of virtual address space. Elf_Addr virtual_address_; - size_t write_program_headers_; - DISALLOW_COPY_AND_ASSIGN(ElfBuilder); }; diff --git a/compiler/elf_writer.h b/compiler/elf_writer.h index d50a08cb20..c9ea0083d5 100644 --- a/compiler/elf_writer.h +++ b/compiler/elf_writer.h @@ -52,14 +52,12 @@ class ElfWriter { virtual ~ElfWriter() {} virtual void Start() = 0; - virtual void PrepareDebugInfo(size_t rodata_section_size, - size_t text_section_size, - const ArrayRef& method_infos) = 0; + virtual void SetLoadedSectionSizes(size_t rodata_size, size_t text_size, size_t bss_size) = 0; + virtual void PrepareDebugInfo(const ArrayRef& method_infos) = 0; virtual OutputStream* StartRoData() = 0; virtual void EndRoData(OutputStream* rodata) = 0; virtual OutputStream* StartText() = 0; virtual void EndText(OutputStream* text) = 0; - virtual void SetBssSize(size_t bss_size) = 0; virtual void WriteDynamicSection() = 0; virtual void WriteDebugInfo(const ArrayRef& method_infos) = 0; virtual void WritePatchLocations(const ArrayRef& patch_locations) = 0; @@ -70,6 +68,9 @@ class ElfWriter { // should Seek() back to the position where the stream was before this operation. virtual OutputStream* GetStream() = 0; + // Get the size that the loaded ELF file will occupy in memory. + virtual size_t GetLoadedSize() = 0; + protected: ElfWriter() = default; }; diff --git a/compiler/elf_writer_quick.cc b/compiler/elf_writer_quick.cc index 1d71e572d7..19346ecc2b 100644 --- a/compiler/elf_writer_quick.cc +++ b/compiler/elf_writer_quick.cc @@ -88,14 +88,12 @@ class ElfWriterQuick FINAL : public ElfWriter { ~ElfWriterQuick(); void Start() OVERRIDE; - void PrepareDebugInfo(size_t rodata_section_size, - size_t text_section_size, - const ArrayRef& method_infos) OVERRIDE; + void SetLoadedSectionSizes(size_t rodata_size, size_t text_size, size_t bss_size) OVERRIDE; + void PrepareDebugInfo(const ArrayRef& method_infos) OVERRIDE; OutputStream* StartRoData() OVERRIDE; void EndRoData(OutputStream* rodata) OVERRIDE; OutputStream* StartText() OVERRIDE; void EndText(OutputStream* text) OVERRIDE; - void SetBssSize(size_t bss_size) OVERRIDE; void WriteDynamicSection() OVERRIDE; void WriteDebugInfo(const ArrayRef& method_infos) OVERRIDE; void WritePatchLocations(const ArrayRef& patch_locations) OVERRIDE; @@ -103,12 +101,17 @@ class ElfWriterQuick FINAL : public ElfWriter { virtual OutputStream* GetStream() OVERRIDE; + size_t GetLoadedSize() OVERRIDE; + static void EncodeOatPatches(const std::vector& locations, std::vector* buffer); private: const CompilerOptions* const compiler_options_; File* const elf_file_; + size_t rodata_size_; + size_t text_size_; + size_t bss_size_; std::unique_ptr output_stream_; std::unique_ptr> builder_; std::unique_ptr debug_info_task_; @@ -134,6 +137,9 @@ ElfWriterQuick::ElfWriterQuick(InstructionSet instruction_set, : ElfWriter(), compiler_options_(compiler_options), elf_file_(elf_file), + rodata_size_(0u), + text_size_(0u), + bss_size_(0u), output_stream_(MakeUnique(MakeUnique(elf_file))), builder_(new ElfBuilder(instruction_set, output_stream_.get())) {} @@ -145,6 +151,19 @@ void ElfWriterQuick::Start() { builder_->Start(); } +template +void ElfWriterQuick::SetLoadedSectionSizes(size_t rodata_size, + size_t text_size, + size_t bss_size) { + DCHECK_EQ(rodata_size_, 0u); + rodata_size_ = rodata_size; + DCHECK_EQ(text_size_, 0u); + text_size_ = text_size; + DCHECK_EQ(bss_size_, 0u); + bss_size_ = bss_size; + builder_->PrepareDynamicSection(elf_file_->GetPath(), rodata_size_, text_size_, bss_size_); +} + template OutputStream* ElfWriterQuick::StartRoData() { auto* rodata = builder_->GetRoData(); @@ -171,32 +190,22 @@ void ElfWriterQuick::EndText(OutputStream* text) { builder_->GetText()->End(); } -template -void ElfWriterQuick::SetBssSize(size_t bss_size) { - auto* bss = builder_->GetBss(); - if (bss_size != 0u) { - bss->WriteNoBitsSection(bss_size); - } -} - template void ElfWriterQuick::WriteDynamicSection() { - builder_->WriteDynamicSection(elf_file_->GetPath()); + if (bss_size_ != 0u) { + builder_->GetBss()->WriteNoBitsSection(bss_size_); + } + builder_->WriteDynamicSection(); } template void ElfWriterQuick::PrepareDebugInfo( - size_t rodata_section_size, - size_t text_section_size, const ArrayRef& method_infos) { if (!method_infos.empty() && compiler_options_->GetGenerateMiniDebugInfo()) { // Prepare the mini-debug-info in background while we do other I/O. Thread* self = Thread::Current(); debug_info_task_ = std::unique_ptr( - new DebugInfoTask(builder_->GetIsa(), - rodata_section_size, - text_section_size, - method_infos)); + new DebugInfoTask(builder_->GetIsa(), rodata_size_, text_size_, method_infos)); debug_info_thread_pool_ = std::unique_ptr( new ThreadPool("Mini-debug-info writer", 1)); debug_info_thread_pool_->AddTask(self, debug_info_task_.get()); @@ -245,6 +254,11 @@ OutputStream* ElfWriterQuick::GetStream() { return builder_->GetStream(); } +template +size_t ElfWriterQuick::GetLoadedSize() { + return builder_->GetLoadedSize(); +} + // Explicit instantiations template class ElfWriterQuick; template class ElfWriterQuick; diff --git a/compiler/image_test.cc b/compiler/image_test.cc index 4920f9baa5..992af29545 100644 --- a/compiler/image_test.cc +++ b/compiler/image_test.cc @@ -28,6 +28,7 @@ #include "elf_writer_quick.h" #include "gc/space/image_space.h" #include "image_writer.h" +#include "linker/multi_oat_relative_patcher.h" #include "lock_word.h" #include "mirror/object-inl.h" #include "oat_writer.h" @@ -72,10 +73,10 @@ void ImageTest::TestWriteRead(ImageHeader::StorageMode storage_mode) { ScratchFile oat_file(OS::CreateEmptyFile(oat_filename.c_str())); const uintptr_t requested_image_base = ART_BASE_ADDRESS; - std::unordered_map dex_file_to_oat_filename_map; + std::unordered_map dex_file_to_oat_index_map; std::vector oat_filename_vector(1, oat_filename.c_str()); for (const DexFile* dex_file : class_linker->GetBootClassPath()) { - dex_file_to_oat_filename_map.emplace(dex_file, oat_filename.c_str()); + dex_file_to_oat_index_map.emplace(dex_file, 0); } std::unique_ptr writer(new ImageWriter(*compiler_driver_, requested_image_base, @@ -83,7 +84,7 @@ void ImageTest::TestWriteRead(ImageHeader::StorageMode storage_mode) { /*compile_app_image*/false, storage_mode, oat_filename_vector, - dex_file_to_oat_filename_map)); + dex_file_to_oat_index_map)); // TODO: compile_pic should be a test argument. { { @@ -123,10 +124,22 @@ void ImageTest::TestWriteRead(ImageHeader::StorageMode storage_mode) { &opened_dex_files_map, &opened_dex_files); ASSERT_TRUE(dex_files_ok); - oat_writer.PrepareLayout(compiler_driver_.get(), writer.get(), dex_files); + bool image_space_ok = writer->PrepareImageAddressSpace(); ASSERT_TRUE(image_space_ok); + linker::MultiOatRelativePatcher patcher(compiler_driver_->GetInstructionSet(), + instruction_set_features_.get()); + oat_writer.PrepareLayout(compiler_driver_.get(), writer.get(), dex_files, &patcher); + size_t rodata_size = oat_writer.GetOatHeader().GetExecutableOffset(); + size_t text_size = oat_writer.GetSize() - rodata_size; + elf_writer->SetLoadedSectionSizes(rodata_size, text_size, oat_writer.GetBssSize()); + + writer->UpdateOatFileLayout(/* oat_index */ 0u, + elf_writer->GetLoadedSize(), + oat_writer.GetOatDataOffset(), + oat_writer.GetSize()); + bool rodata_ok = oat_writer.WriteRodata(rodata); ASSERT_TRUE(rodata_ok); elf_writer->EndRoData(rodata); @@ -139,13 +152,13 @@ void ImageTest::TestWriteRead(ImageHeader::StorageMode storage_mode) { bool header_ok = oat_writer.WriteHeader(elf_writer->GetStream(), 0u, 0u, 0u); ASSERT_TRUE(header_ok); - elf_writer->SetBssSize(oat_writer.GetBssSize()); + writer->UpdateOatFileHeader(/* oat_index */ 0u, oat_writer.GetOatHeader()); + elf_writer->WriteDynamicSection(); elf_writer->WriteDebugInfo(oat_writer.GetMethodDebugInfo()); elf_writer->WritePatchLocations(oat_writer.GetAbsolutePatchLocations()); bool success = elf_writer->End(); - ASSERT_TRUE(success); } } @@ -158,12 +171,10 @@ void ImageTest::TestWriteRead(ImageHeader::StorageMode storage_mode) { std::vector dup_image_filename(1, image_file.GetFilename().c_str()); bool success_image = writer->Write(kInvalidFd, dup_image_filename, - kInvalidFd, - dup_oat_filename, - dup_oat_filename[0]); + dup_oat_filename); ASSERT_TRUE(success_image); bool success_fixup = ElfWriter::Fixup(dup_oat.get(), - writer->GetOatDataBegin(dup_oat_filename[0])); + writer->GetOatDataBegin(0)); ASSERT_TRUE(success_fixup); ASSERT_EQ(dup_oat->FlushCloseOrErase(), 0) << "Could not flush and close oat file " diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc index a8de86d059..97ad9b3377 100644 --- a/compiler/image_writer.cc +++ b/compiler/image_writer.cc @@ -159,9 +159,7 @@ bool ImageWriter::PrepareImageAddressSpace() { bool ImageWriter::Write(int image_fd, const std::vector& image_filenames, - int oat_fd, - const std::vector& oat_filenames, - const std::string& oat_location) { + const std::vector& oat_filenames) { // If image_fd or oat_fd are not kInvalidFd then we may have empty strings in image_filenames or // oat_filenames. CHECK(!image_filenames.empty()); @@ -169,95 +167,13 @@ bool ImageWriter::Write(int image_fd, CHECK_EQ(image_filenames.size(), 1u); } CHECK(!oat_filenames.empty()); - if (oat_fd != kInvalidFd) { - CHECK_EQ(oat_filenames.size(), 1u); - } CHECK_EQ(image_filenames.size(), oat_filenames.size()); - size_t oat_file_offset = 0; - - for (size_t i = 0; i < oat_filenames.size(); ++i) { - const char* oat_filename = oat_filenames[i]; - std::unique_ptr oat_file; - - if (oat_fd != -1) { - if (strlen(oat_filename) == 0u) { - oat_file.reset(new File(oat_fd, false)); - } else { - oat_file.reset(new File(oat_fd, oat_filename, false)); - } - int length = oat_file->GetLength(); - if (length < 0) { - PLOG(ERROR) << "Oat file has negative length " << length; - return false; - } else { - // Leave the fd open since dex2oat still needs to write out the oat file with the fd. - oat_file->DisableAutoClose(); - } - } else { - oat_file.reset(OS::OpenFileReadWrite(oat_filename)); - } - if (oat_file == nullptr) { - PLOG(ERROR) << "Failed to open oat file " << oat_filename; - return false; - } - std::string error_msg; - oat_file_ = OatFile::OpenReadable(oat_file.get(), oat_filename, nullptr, &error_msg); - if (oat_file_ == nullptr) { - PLOG(ERROR) << "Failed to open writable oat file " << oat_filename; - oat_file->Erase(); - return false; - } - Runtime::Current()->GetOatFileManager().RegisterOatFile( - std::unique_ptr(oat_file_)); - - const OatHeader& oat_header = oat_file_->GetOatHeader(); - ImageInfo& image_info = GetImageInfo(oat_filename); - - size_t oat_loaded_size = 0; - size_t oat_data_offset = 0; - ElfWriter::GetOatElfInformation(oat_file.get(), &oat_loaded_size, &oat_data_offset); - - DCHECK_EQ(image_info.oat_offset_, oat_file_offset); - oat_file_offset += oat_loaded_size; - - if (i == 0) { - // Primary oat file, read the trampolines. - image_info.oat_address_offsets_[kOatAddressInterpreterToInterpreterBridge] = - oat_header.GetInterpreterToInterpreterBridgeOffset(); - image_info.oat_address_offsets_[kOatAddressInterpreterToCompiledCodeBridge] = - oat_header.GetInterpreterToCompiledCodeBridgeOffset(); - image_info.oat_address_offsets_[kOatAddressJNIDlsymLookup] = - oat_header.GetJniDlsymLookupOffset(); - image_info.oat_address_offsets_[kOatAddressQuickGenericJNITrampoline] = - oat_header.GetQuickGenericJniTrampolineOffset(); - image_info.oat_address_offsets_[kOatAddressQuickIMTConflictTrampoline] = - oat_header.GetQuickImtConflictTrampolineOffset(); - image_info.oat_address_offsets_[kOatAddressQuickResolutionTrampoline] = - oat_header.GetQuickResolutionTrampolineOffset(); - image_info.oat_address_offsets_[kOatAddressQuickToInterpreterBridge] = - oat_header.GetQuickToInterpreterBridgeOffset(); - } - - - { - ScopedObjectAccess soa(Thread::Current()); - CreateHeader(oat_loaded_size, oat_data_offset); - CopyAndFixupNativeData(); - } - - SetOatChecksumFromElfFile(oat_file.get()); - - if (oat_fd != -1) { - // Leave fd open for caller. - if (oat_file->Flush() != 0) { - LOG(ERROR) << "Failed to flush oat file " << oat_filename << " for " << oat_location; - return false; - } - } else if (oat_file->FlushCloseOrErase() != 0) { - LOG(ERROR) << "Failed to flush and close oat file " << oat_filename - << " for " << oat_location; - return false; + { + ScopedObjectAccess soa(Thread::Current()); + for (size_t i = 0; i < oat_filenames.size(); ++i) { + CreateHeader(i); + CopyAndFixupNativeData(i); } } @@ -270,8 +186,7 @@ bool ImageWriter::Write(int image_fd, for (size_t i = 0; i < image_filenames.size(); ++i) { const char* image_filename = image_filenames[i]; - const char* oat_filename = oat_filenames[i]; - ImageInfo& image_info = GetImageInfo(oat_filename); + ImageInfo& image_info = GetImageInfo(i); std::unique_ptr image_file; if (image_fd != kInvalidFd) { if (strlen(image_filename) == 0u) { @@ -393,8 +308,8 @@ void ImageWriter::AssignImageOffset(mirror::Object* object, ImageWriter::BinSlot DCHECK(object != nullptr); DCHECK_NE(image_objects_offset_begin_, 0u); - const char* oat_filename = GetOatFilename(object); - ImageInfo& image_info = GetImageInfo(oat_filename); + size_t oat_index = GetOatIndex(object); + ImageInfo& image_info = GetImageInfo(oat_index); size_t bin_slot_offset = image_info.bin_slot_offsets_[bin_slot.GetBin()]; size_t new_offset = bin_slot_offset + bin_slot.GetIndex(); DCHECK_ALIGNED(new_offset, kObjectAlignment); @@ -414,8 +329,8 @@ size_t ImageWriter::GetImageOffset(mirror::Object* object) const { DCHECK(IsImageOffsetAssigned(object)); LockWord lock_word = object->GetLockWord(false); size_t offset = lock_word.ForwardingAddress(); - const char* oat_filename = GetOatFilename(object); - const ImageInfo& image_info = GetConstImageInfo(oat_filename); + size_t oat_index = GetOatIndex(object); + const ImageInfo& image_info = GetImageInfo(oat_index); DCHECK_LT(offset, image_info.image_end_); return offset; } @@ -458,8 +373,8 @@ void ImageWriter::PrepareDexCacheArraySlots() { // Set the slot size early to avoid DCHECK() failures in IsImageBinSlotAssigned() // when AssignImageBinSlot() assigns their indexes out or order. for (const DexFile* dex_file : compiler_driver_.GetDexFilesForOatFile()) { - auto it = dex_file_oat_filename_map_.find(dex_file); - DCHECK(it != dex_file_oat_filename_map_.end()) << dex_file->GetLocation(); + auto it = dex_file_oat_index_map_.find(dex_file); + DCHECK(it != dex_file_oat_index_map_.end()) << dex_file->GetLocation(); ImageInfo& image_info = GetImageInfo(it->second); image_info.dex_cache_array_starts_.Put(dex_file, image_info.bin_slot_sizes_[kBinDexCacheArray]); DexCacheArraysLayout layout(target_ptr_size_, dex_file); @@ -478,8 +393,8 @@ void ImageWriter::PrepareDexCacheArraySlots() { const DexFile* dex_file = dex_cache->GetDexFile(); DexCacheArraysLayout layout(target_ptr_size_, dex_file); DCHECK(layout.Valid()); - const char* oat_filename = GetOatFilenameForDexCache(dex_cache); - ImageInfo& image_info = GetImageInfo(oat_filename); + size_t oat_index = GetOatIndexForDexCache(dex_cache); + ImageInfo& image_info = GetImageInfo(oat_index); uint32_t start = image_info.dex_cache_array_starts_.Get(dex_file); DCHECK_EQ(dex_file->NumTypeIds() != 0u, dex_cache->GetResolvedTypes() != nullptr); AddDexCacheArrayRelocation(dex_cache->GetResolvedTypes(), @@ -501,9 +416,9 @@ void ImageWriter::PrepareDexCacheArraySlots() { void ImageWriter::AddDexCacheArrayRelocation(void* array, size_t offset, DexCache* dex_cache) { if (array != nullptr) { DCHECK(!IsInBootImage(array)); - const char* oat_filename = GetOatFilenameForDexCache(dex_cache); + size_t oat_index = GetOatIndexForDexCache(dex_cache); native_object_relocations_.emplace(array, - NativeObjectRelocation { oat_filename, offset, kNativeObjectRelocationTypeDexCacheArray }); + NativeObjectRelocation { oat_index, offset, kNativeObjectRelocationTypeDexCacheArray }); } } @@ -618,8 +533,8 @@ void ImageWriter::AssignImageBinSlot(mirror::Object* object) { } // else bin = kBinRegular } - const char* oat_filename = GetOatFilename(object); - ImageInfo& image_info = GetImageInfo(oat_filename); + size_t oat_index = GetOatIndex(object); + ImageInfo& image_info = GetImageInfo(oat_index); size_t offset_delta = RoundUp(object_size, kObjectAlignment); // 64-bit alignment current_offset = image_info.bin_slot_sizes_[bin]; // How many bytes the current bin is at (aligned). @@ -655,8 +570,8 @@ bool ImageWriter::IsImageBinSlotAssigned(mirror::Object* object) const { LockWord lock_word = object->GetLockWord(false); size_t offset = lock_word.ForwardingAddress(); BinSlot bin_slot(offset); - const char* oat_filename = GetOatFilename(object); - const ImageInfo& image_info = GetConstImageInfo(oat_filename); + size_t oat_index = GetOatIndex(object); + const ImageInfo& image_info = GetImageInfo(oat_index); DCHECK_LT(bin_slot.GetIndex(), image_info.bin_slot_sizes_[bin_slot.GetBin()]) << "bin slot offset should not exceed the size of that bin"; } @@ -672,16 +587,15 @@ ImageWriter::BinSlot ImageWriter::GetImageBinSlot(mirror::Object* object) const DCHECK_LE(offset, std::numeric_limits::max()); BinSlot bin_slot(static_cast(offset)); - const char* oat_filename = GetOatFilename(object); - const ImageInfo& image_info = GetConstImageInfo(oat_filename); + size_t oat_index = GetOatIndex(object); + const ImageInfo& image_info = GetImageInfo(oat_index); DCHECK_LT(bin_slot.GetIndex(), image_info.bin_slot_sizes_[bin_slot.GetBin()]); return bin_slot; } bool ImageWriter::AllocMemory() { - for (const char* oat_filename : oat_filenames_) { - ImageInfo& image_info = GetImageInfo(oat_filename); + for (ImageInfo& image_info : image_infos_) { ImageSection unused_sections[ImageHeader::kSectionCount]; const size_t length = RoundUp( image_info.CreateImageSections(target_ptr_size_, unused_sections), @@ -970,8 +884,7 @@ void ImageWriter::DumpImageClasses() { mirror::String* ImageWriter::FindInternedString(mirror::String* string) { Thread* const self = Thread::Current(); - for (auto& pair : image_info_map_) { - const ImageInfo& image_info = pair.second; + for (const ImageInfo& image_info : image_infos_) { mirror::String* const found = image_info.intern_table_->LookupStrong(self, string); DCHECK(image_info.intern_table_->LookupWeak(self, string) == nullptr) << string->ToModifiedUtf8(); @@ -998,8 +911,8 @@ void ImageWriter::CalculateObjectBinSlots(Object* obj) { DCHECK(obj != nullptr); // if it is a string, we want to intern it if its not interned. if (obj->GetClass()->IsStringClass()) { - const char* oat_filename = GetOatFilename(obj); - ImageInfo& image_info = GetImageInfo(oat_filename); + size_t oat_index = GetOatIndex(obj); + ImageInfo& image_info = GetImageInfo(oat_index); // we must be an interned string that was forward referenced and already assigned if (IsImageBinSlotAssigned(obj)) { @@ -1028,7 +941,7 @@ void ImageWriter::CalculateObjectBinSlots(Object* obj) { AssignImageBinSlot(obj); } -ObjectArray* ImageWriter::CreateImageRoots(const char* oat_filename) const { +ObjectArray* ImageWriter::CreateImageRoots(size_t oat_index) const { Runtime* runtime = Runtime::Current(); ClassLinker* class_linker = runtime->GetClassLinker(); Thread* self = Thread::Current(); @@ -1037,10 +950,10 @@ ObjectArray* ImageWriter::CreateImageRoots(const char* oat_filename) con class_linker->FindSystemClass(self, "[Ljava/lang/Object;"))); std::unordered_set image_dex_files; - for (auto& pair : dex_file_oat_filename_map_) { + for (auto& pair : dex_file_oat_index_map_) { const DexFile* image_dex_file = pair.first; - const char* image_oat_filename = pair.second; - if (strcmp(oat_filename, image_oat_filename) == 0) { + size_t image_oat_index = pair.second; + if (oat_index == image_oat_index) { image_dex_files.insert(image_dex_file); } } @@ -1165,8 +1078,8 @@ void ImageWriter::WalkFieldsInOrder(mirror::Object* obj) { LengthPrefixedArray* fields[] = { as_klass->GetSFieldsPtr(), as_klass->GetIFieldsPtr(), }; - const char* oat_file = GetOatFilenameForDexCache(dex_cache); - ImageInfo& image_info = GetImageInfo(oat_file); + size_t oat_index = GetOatIndexForDexCache(dex_cache); + ImageInfo& image_info = GetImageInfo(oat_index); { // Note: This table is only accessed from the image writer, so the lock is technically // unnecessary. @@ -1184,8 +1097,11 @@ void ImageWriter::WalkFieldsInOrder(mirror::Object* obj) { << " already forwarded"; size_t& offset = image_info.bin_slot_sizes_[kBinArtField]; DCHECK(!IsInBootImage(cur_fields)); - native_object_relocations_.emplace(cur_fields, - NativeObjectRelocation {oat_file, offset, kNativeObjectRelocationTypeArtFieldArray }); + native_object_relocations_.emplace( + cur_fields, + NativeObjectRelocation { + oat_index, offset, kNativeObjectRelocationTypeArtFieldArray + }); offset += header_size; // Forward individual fields so that we can quickly find where they belong. for (size_t i = 0, count = cur_fields->size(); i < count; ++i) { @@ -1195,8 +1111,9 @@ void ImageWriter::WalkFieldsInOrder(mirror::Object* obj) { CHECK(it2 == native_object_relocations_.end()) << "Field at index=" << i << " already assigned " << PrettyField(field) << " static=" << field->IsStatic(); DCHECK(!IsInBootImage(field)); - native_object_relocations_.emplace(field, - NativeObjectRelocation {oat_file, offset, kNativeObjectRelocationTypeArtField }); + native_object_relocations_.emplace( + field, + NativeObjectRelocation { oat_index, offset, kNativeObjectRelocationTypeArtField }); offset += sizeof(ArtField); } } @@ -1229,13 +1146,13 @@ void ImageWriter::WalkFieldsInOrder(mirror::Object* obj) { DCHECK(!IsInBootImage(array)); native_object_relocations_.emplace(array, NativeObjectRelocation { - oat_file, + oat_index, offset, any_dirty ? kNativeObjectRelocationTypeArtMethodArrayDirty : kNativeObjectRelocationTypeArtMethodArrayClean }); offset += header_size; for (auto& m : as_klass->GetMethods(target_ptr_size_)) { - AssignMethodOffset(&m, type, oat_file); + AssignMethodOffset(&m, type, oat_index); } (any_dirty ? dirty_methods_ : clean_methods_) += num_methods; } @@ -1263,14 +1180,14 @@ void ImageWriter::WalkFieldsInOrder(mirror::Object* obj) { void ImageWriter::AssignMethodOffset(ArtMethod* method, NativeObjectRelocationType type, - const char* oat_filename) { + size_t oat_index) { DCHECK(!IsInBootImage(method)); auto it = native_object_relocations_.find(method); CHECK(it == native_object_relocations_.end()) << "Method " << method << " already assigned " << PrettyMethod(method); - ImageInfo& image_info = GetImageInfo(oat_filename); + ImageInfo& image_info = GetImageInfo(oat_index); size_t& offset = image_info.bin_slot_sizes_[BinTypeForNativeRelocationType(type)]; - native_object_relocations_.emplace(method, NativeObjectRelocation { oat_filename, offset, type }); + native_object_relocations_.emplace(method, NativeObjectRelocation { oat_index, offset, type }); offset += ArtMethod::Size(target_ptr_size_); } @@ -1305,9 +1222,8 @@ void ImageWriter::CalculateNewObjectOffsets() { Thread* const self = Thread::Current(); StackHandleScopeCollection handles(self); std::vector>> image_roots; - for (const char* oat_filename : oat_filenames_) { - std::string image_filename = oat_filename; - image_roots.push_back(handles.NewHandle(CreateImageRoots(image_filename.c_str()))); + for (size_t i = 0, size = oat_filenames_.size(); i != size; ++i) { + image_roots.push_back(handles.NewHandle(CreateImageRoots(i))); } auto* runtime = Runtime::Current(); @@ -1333,12 +1249,12 @@ void ImageWriter::CalculateNewObjectOffsets() { const auto image_method_type = kNativeObjectRelocationTypeArtMethodArrayClean; auto it = native_object_relocations_.find(&image_method_array_); CHECK(it == native_object_relocations_.end()); - ImageInfo& default_image_info = GetImageInfo(default_oat_filename_); + ImageInfo& default_image_info = GetImageInfo(GetDefaultOatIndex()); size_t& offset = default_image_info.bin_slot_sizes_[BinTypeForNativeRelocationType(image_method_type)]; if (!compile_app_image_) { native_object_relocations_.emplace(&image_method_array_, - NativeObjectRelocation { default_oat_filename_, offset, image_method_type }); + NativeObjectRelocation { GetDefaultOatIndex(), offset, image_method_type }); } size_t method_alignment = ArtMethod::Alignment(target_ptr_size_); const size_t array_size = LengthPrefixedArray::ComputeSize( @@ -1350,15 +1266,14 @@ void ImageWriter::CalculateNewObjectOffsets() { CHECK(m->IsRuntimeMethod()); DCHECK_EQ(compile_app_image_, IsInBootImage(m)) << "Trampolines should be in boot image"; if (!IsInBootImage(m)) { - AssignMethodOffset(m, kNativeObjectRelocationTypeArtMethodClean, default_oat_filename_); + AssignMethodOffset(m, kNativeObjectRelocationTypeArtMethodClean, GetDefaultOatIndex()); } } // Calculate size of the dex cache arrays slot and prepare offsets. PrepareDexCacheArraySlots(); // Calculate the sizes of the intern tables and class tables. - for (const char* oat_filename : oat_filenames_) { - ImageInfo& image_info = GetImageInfo(oat_filename); + for (ImageInfo& image_info : image_infos_) { // Calculate how big the intern table will be after being serialized. InternTable* const intern_table = image_info.intern_table_.get(); CHECK_EQ(intern_table->WeakSize(), 0u) << " should have strong interned all the strings"; @@ -1369,8 +1284,7 @@ void ImageWriter::CalculateNewObjectOffsets() { } // Calculate bin slot offsets. - for (const char* oat_filename : oat_filenames_) { - ImageInfo& image_info = GetImageInfo(oat_filename); + for (ImageInfo& image_info : image_infos_) { size_t bin_offset = image_objects_offset_begin_; for (size_t i = 0; i != kBinSize; ++i) { image_info.bin_slot_offsets_[i] = bin_offset; @@ -1390,8 +1304,7 @@ void ImageWriter::CalculateNewObjectOffsets() { // Calculate image offsets. size_t image_offset = 0; - for (const char* oat_filename : oat_filenames_) { - ImageInfo& image_info = GetImageInfo(oat_filename); + for (ImageInfo& image_info : image_infos_) { image_info.image_begin_ = global_image_begin_ + image_offset; image_info.image_offset_ = image_offset; ImageSection unused_sections[ImageHeader::kSectionCount]; @@ -1408,8 +1321,7 @@ void ImageWriter::CalculateNewObjectOffsets() { // DCHECK_EQ(image_end_, GetBinSizeSum(kBinMirrorCount) + image_objects_offset_begin_); size_t i = 0; - for (const char* oat_filename : oat_filenames_) { - ImageInfo& image_info = GetImageInfo(oat_filename); + for (ImageInfo& image_info : image_infos_) { image_info.image_roots_address_ = PointerToLowMemUInt32(GetImageAddress(image_roots[i].Get())); i++; } @@ -1418,7 +1330,7 @@ void ImageWriter::CalculateNewObjectOffsets() { for (auto& pair : native_object_relocations_) { NativeObjectRelocation& relocation = pair.second; Bin bin_type = BinTypeForNativeRelocationType(relocation.type); - ImageInfo& image_info = GetImageInfo(relocation.oat_filename); + ImageInfo& image_info = GetImageInfo(relocation.oat_index); relocation.offset += image_info.bin_slot_offsets_[bin_type]; } @@ -1467,15 +1379,11 @@ size_t ImageWriter::ImageInfo::CreateImageSections(size_t target_ptr_size, return cur_pos; } -void ImageWriter::CreateHeader(size_t oat_loaded_size, size_t oat_data_offset) { - CHECK_NE(0U, oat_loaded_size); - const char* oat_filename = oat_file_->GetLocation().c_str(); - ImageInfo& image_info = GetImageInfo(oat_filename); - const uint8_t* oat_file_begin = GetOatFileBegin(oat_filename); - const uint8_t* oat_file_end = oat_file_begin + oat_loaded_size; - image_info.oat_data_begin_ = const_cast(oat_file_begin) + oat_data_offset; - const uint8_t* oat_data_end = image_info.oat_data_begin_ + oat_file_->Size(); - image_info.oat_size_ = oat_file_->Size(); +void ImageWriter::CreateHeader(size_t oat_index) { + ImageInfo& image_info = GetImageInfo(oat_index); + const uint8_t* oat_file_begin = image_info.oat_file_begin_; + const uint8_t* oat_file_end = oat_file_begin + image_info.oat_loaded_size_; + const uint8_t* oat_data_end = image_info.oat_data_begin_ + image_info.oat_size_; // Create the image sections. ImageSection sections[ImageHeader::kSectionCount]; @@ -1486,7 +1394,7 @@ void ImageWriter::CreateHeader(size_t oat_loaded_size, size_t oat_data_offset) { auto* bitmap_section = §ions[ImageHeader::kSectionImageBitmap]; *bitmap_section = ImageSection(RoundUp(image_end, kPageSize), RoundUp(bitmap_bytes, kPageSize)); if (VLOG_IS_ON(compiler)) { - LOG(INFO) << "Creating header for " << oat_filename; + LOG(INFO) << "Creating header for " << oat_filenames_[oat_index]; size_t idx = 0; for (const ImageSection& section : sections) { LOG(INFO) << static_cast(idx) << " " << section; @@ -1515,7 +1423,7 @@ void ImageWriter::CreateHeader(size_t oat_loaded_size, size_t oat_data_offset) { image_end, sections, image_info.image_roots_address_, - oat_file_->GetOatHeader().GetChecksum(), + image_info.oat_checksum_, PointerToLowMemUInt32(oat_file_begin), PointerToLowMemUInt32(image_info.oat_data_begin_), PointerToLowMemUInt32(oat_data_end), @@ -1534,8 +1442,8 @@ void ImageWriter::CreateHeader(size_t oat_loaded_size, size_t oat_data_offset) { ArtMethod* ImageWriter::GetImageMethodAddress(ArtMethod* method) { auto it = native_object_relocations_.find(method); CHECK(it != native_object_relocations_.end()) << PrettyMethod(method) << " @ " << method; - const char* oat_filename = GetOatFilename(method->GetDexCache()); - ImageInfo& image_info = GetImageInfo(oat_filename); + size_t oat_index = GetOatIndex(method->GetDexCache()); + ImageInfo& image_info = GetImageInfo(oat_index); CHECK_GE(it->second.offset, image_info.image_end_) << "ArtMethods should be after Objects"; return reinterpret_cast(image_info.image_begin_ + it->second.offset); } @@ -1564,14 +1472,13 @@ class FixupRootVisitor : public RootVisitor { ImageWriter* const image_writer_; }; -void ImageWriter::CopyAndFixupNativeData() { - const char* oat_filename = oat_file_->GetLocation().c_str(); - ImageInfo& image_info = GetImageInfo(oat_filename); +void ImageWriter::CopyAndFixupNativeData(size_t oat_index) { + ImageInfo& image_info = GetImageInfo(oat_index); // Copy ArtFields and methods to their locations and update the array for convenience. for (auto& pair : native_object_relocations_) { NativeObjectRelocation& relocation = pair.second; // Only work with fields and methods that are in the current oat file. - if (strcmp(relocation.oat_filename, oat_filename) != 0) { + if (relocation.oat_index != oat_index) { continue; } auto* dest = image_info.image_->Begin() + relocation.offset; @@ -1617,7 +1524,7 @@ void ImageWriter::CopyAndFixupNativeData() { ArtMethod* method = image_methods_[i]; CHECK(method != nullptr); // Only place runtime methods in the image of the default oat file. - if (method->IsRuntimeMethod() && strcmp(default_oat_filename_, oat_filename) != 0) { + if (method->IsRuntimeMethod() && oat_index != GetDefaultOatIndex()) { continue; } if (!IsInBootImage(method)) { @@ -1722,7 +1629,7 @@ void ImageWriter::FixupPointerArray(mirror::Object* dst, mirror::PointerArray* a } UNREACHABLE(); } else { - ImageInfo& image_info = GetImageInfo(it->second.oat_filename); + ImageInfo& image_info = GetImageInfo(it->second.oat_index); elem = image_info.image_begin_ + it->second.offset; } } @@ -1735,8 +1642,8 @@ void ImageWriter::CopyAndFixupObject(Object* obj) { return; } size_t offset = GetImageOffset(obj); - const char* oat_filename = GetOatFilename(obj); - ImageInfo& image_info = GetImageInfo(oat_filename); + size_t oat_index = GetOatIndex(obj); + ImageInfo& image_info = GetImageInfo(oat_index); auto* dst = reinterpret_cast(image_info.image_->Begin() + offset); DCHECK_LT(offset, image_info.image_end_); const auto* src = reinterpret_cast(obj); @@ -1828,7 +1735,7 @@ T* ImageWriter::NativeLocationInImage(T* obj) { CHECK(it != native_object_relocations_.end()) << obj << " spaces " << Runtime::Current()->GetHeap()->DumpSpaces(); const NativeObjectRelocation& relocation = it->second; - ImageInfo& image_info = GetImageInfo(relocation.oat_filename); + ImageInfo& image_info = GetImageInfo(relocation.oat_index); return reinterpret_cast(image_info.image_begin_ + relocation.offset); } } @@ -1838,8 +1745,8 @@ T* ImageWriter::NativeCopyLocation(T* obj, mirror::DexCache* dex_cache) { if (obj == nullptr || IsInBootImage(obj)) { return obj; } else { - const char* oat_filename = GetOatFilenameForDexCache(dex_cache); - ImageInfo& image_info = GetImageInfo(oat_filename); + size_t oat_index = GetOatIndexForDexCache(dex_cache); + ImageInfo& image_info = GetImageInfo(oat_index); return reinterpret_cast(image_info.image_->Begin() + NativeOffsetInImage(obj)); } } @@ -2122,34 +2029,6 @@ void ImageWriter::CopyAndFixupMethod(ArtMethod* orig, } } -static OatHeader* GetOatHeaderFromElf(ElfFile* elf) { - uint64_t data_sec_offset; - bool has_data_sec = elf->GetSectionOffsetAndSize(".rodata", &data_sec_offset, nullptr); - if (!has_data_sec) { - return nullptr; - } - return reinterpret_cast(elf->Begin() + data_sec_offset); -} - -void ImageWriter::SetOatChecksumFromElfFile(File* elf_file) { - std::string error_msg; - std::unique_ptr elf(ElfFile::Open(elf_file, - PROT_READ | PROT_WRITE, - MAP_SHARED, - &error_msg)); - if (elf.get() == nullptr) { - LOG(FATAL) << "Unable open oat file: " << error_msg; - return; - } - OatHeader* oat_header = GetOatHeaderFromElf(elf.get()); - CHECK(oat_header != nullptr); - CHECK(oat_header->IsValid()); - - ImageInfo& image_info = GetImageInfo(oat_file_->GetLocation().c_str()); - ImageHeader* image_header = reinterpret_cast(image_info.image_->Begin()); - image_header->SetOatChecksum(oat_header->GetChecksum()); -} - size_t ImageWriter::GetBinSizeSum(ImageWriter::ImageInfo& image_info, ImageWriter::Bin up_to) const { DCHECK_LE(up_to, kBinSize); return std::accumulate(&image_info.bin_slot_sizes_[0], @@ -2180,19 +2059,6 @@ uint32_t ImageWriter::BinSlot::GetIndex() const { return lockword_ & ~kBinMask; } -uint8_t* ImageWriter::GetOatFileBegin(const char* oat_filename) const { - uintptr_t last_image_end = 0; - for (const char* oat_fn : oat_filenames_) { - const ImageInfo& image_info = GetConstImageInfo(oat_fn); - DCHECK(image_info.image_begin_ != nullptr); - uintptr_t this_end = reinterpret_cast(image_info.image_begin_) + - image_info.image_size_; - last_image_end = std::max(this_end, last_image_end); - } - const ImageInfo& image_info = GetConstImageInfo(oat_filename); - return reinterpret_cast(last_image_end) + image_info.oat_offset_; -} - ImageWriter::Bin ImageWriter::BinTypeForNativeRelocationType(NativeObjectRelocationType type) { switch (type) { case kNativeObjectRelocationTypeArtField: @@ -2210,91 +2076,110 @@ ImageWriter::Bin ImageWriter::BinTypeForNativeRelocationType(NativeObjectRelocat UNREACHABLE(); } -const char* ImageWriter::GetOatFilename(mirror::Object* obj) const { +size_t ImageWriter::GetOatIndex(mirror::Object* obj) const { if (compile_app_image_) { - return default_oat_filename_; + return GetDefaultOatIndex(); } else { - return GetOatFilenameForDexCache(obj->IsDexCache() ? obj->AsDexCache() : - obj->IsClass() ? obj->AsClass()->GetDexCache() : obj->GetClass()->GetDexCache()); + mirror::DexCache* dex_cache = + obj->IsDexCache() ? obj->AsDexCache() + : obj->IsClass() ? obj->AsClass()->GetDexCache() + : obj->GetClass()->GetDexCache(); + return GetOatIndexForDexCache(dex_cache); } } -const char* ImageWriter::GetOatFilenameForDexCache(mirror::DexCache* dex_cache) const { - if (compile_app_image_ || dex_cache == nullptr) { - return default_oat_filename_; +size_t ImageWriter::GetOatIndexForDexFile(const DexFile* dex_file) const { + if (compile_app_image_) { + return GetDefaultOatIndex(); } else { - auto it = dex_file_oat_filename_map_.find(dex_cache->GetDexFile()); - DCHECK(it != dex_file_oat_filename_map_.end()) << dex_cache->GetDexFile()->GetLocation(); + auto it = dex_file_oat_index_map_.find(dex_file); + DCHECK(it != dex_file_oat_index_map_.end()) << dex_file->GetLocation(); return it->second; } } -ImageWriter::ImageInfo& ImageWriter::GetImageInfo(const char* oat_filename) { - auto it = image_info_map_.find(oat_filename); - DCHECK(it != image_info_map_.end()); - return it->second; +size_t ImageWriter::GetOatIndexForDexCache(mirror::DexCache* dex_cache) const { + if (dex_cache == nullptr) { + return GetDefaultOatIndex(); + } else { + return GetOatIndexForDexFile(dex_cache->GetDexFile()); + } } -const ImageWriter::ImageInfo& ImageWriter::GetConstImageInfo(const char* oat_filename) const { - auto it = image_info_map_.find(oat_filename); - DCHECK(it != image_info_map_.end()); - return it->second; -} +void ImageWriter::UpdateOatFileLayout(size_t oat_index, + size_t oat_loaded_size, + size_t oat_data_offset, + size_t oat_data_size) { + const uint8_t* images_end = image_infos_.back().image_begin_ + image_infos_.back().image_size_; + for (const ImageInfo& info : image_infos_) { + DCHECK_LE(info.image_begin_ + info.image_size_, images_end); + } + DCHECK(images_end != nullptr); // Image space must be ready. -const ImageWriter::ImageInfo& ImageWriter::GetImageInfo(size_t index) const { - DCHECK_LT(index, oat_filenames_.size()); - return GetConstImageInfo(oat_filenames_[index]); -} + ImageInfo& cur_image_info = GetImageInfo(oat_index); + cur_image_info.oat_file_begin_ = images_end + cur_image_info.oat_offset_; + cur_image_info.oat_loaded_size_ = oat_loaded_size; + cur_image_info.oat_data_begin_ = cur_image_info.oat_file_begin_ + oat_data_offset; + cur_image_info.oat_size_ = oat_data_size; -void ImageWriter::UpdateOatFile(File* oat_file, const char* oat_filename) { - DCHECK(oat_file != nullptr); if (compile_app_image_) { CHECK_EQ(oat_filenames_.size(), 1u) << "App image should have no next image."; return; } - ImageInfo& cur_image_info = GetImageInfo(oat_filename); // Update the oat_offset of the next image info. - auto it = std::find(oat_filenames_.begin(), oat_filenames_.end(), oat_filename); - DCHECK(it != oat_filenames_.end()); - - it++; - if (it != oat_filenames_.end()) { - size_t oat_loaded_size = 0; - size_t oat_data_offset = 0; - ElfWriter::GetOatElfInformation(oat_file, &oat_loaded_size, &oat_data_offset); + if (oat_index + 1u != oat_filenames_.size()) { // There is a following one. - ImageInfo& next_image_info = GetImageInfo(*it); + ImageInfo& next_image_info = GetImageInfo(oat_index + 1u); next_image_info.oat_offset_ = cur_image_info.oat_offset_ + oat_loaded_size; } } +void ImageWriter::UpdateOatFileHeader(size_t oat_index, const OatHeader& oat_header) { + ImageInfo& cur_image_info = GetImageInfo(oat_index); + cur_image_info.oat_checksum_ = oat_header.GetChecksum(); + + if (oat_index == GetDefaultOatIndex()) { + // Primary oat file, read the trampolines. + cur_image_info.oat_address_offsets_[kOatAddressInterpreterToInterpreterBridge] = + oat_header.GetInterpreterToInterpreterBridgeOffset(); + cur_image_info.oat_address_offsets_[kOatAddressInterpreterToCompiledCodeBridge] = + oat_header.GetInterpreterToCompiledCodeBridgeOffset(); + cur_image_info.oat_address_offsets_[kOatAddressJNIDlsymLookup] = + oat_header.GetJniDlsymLookupOffset(); + cur_image_info.oat_address_offsets_[kOatAddressQuickGenericJNITrampoline] = + oat_header.GetQuickGenericJniTrampolineOffset(); + cur_image_info.oat_address_offsets_[kOatAddressQuickIMTConflictTrampoline] = + oat_header.GetQuickImtConflictTrampolineOffset(); + cur_image_info.oat_address_offsets_[kOatAddressQuickResolutionTrampoline] = + oat_header.GetQuickResolutionTrampolineOffset(); + cur_image_info.oat_address_offsets_[kOatAddressQuickToInterpreterBridge] = + oat_header.GetQuickToInterpreterBridgeOffset(); + } +} + ImageWriter::ImageWriter( const CompilerDriver& compiler_driver, uintptr_t image_begin, bool compile_pic, bool compile_app_image, ImageHeader::StorageMode image_storage_mode, - const std::vector oat_filenames, - const std::unordered_map& dex_file_oat_filename_map) + const std::vector& oat_filenames, + const std::unordered_map& dex_file_oat_index_map) : compiler_driver_(compiler_driver), global_image_begin_(reinterpret_cast(image_begin)), image_objects_offset_begin_(0), - oat_file_(nullptr), compile_pic_(compile_pic), compile_app_image_(compile_app_image), target_ptr_size_(InstructionSetPointerSize(compiler_driver_.GetInstructionSet())), + image_infos_(oat_filenames.size()), image_method_array_(ImageHeader::kImageMethodsCount), dirty_methods_(0u), clean_methods_(0u), image_storage_mode_(image_storage_mode), - dex_file_oat_filename_map_(dex_file_oat_filename_map), oat_filenames_(oat_filenames), - default_oat_filename_(oat_filenames[0]) { + dex_file_oat_index_map_(dex_file_oat_index_map) { CHECK_NE(image_begin, 0U); - for (const char* oat_filename : oat_filenames) { - image_info_map_.emplace(oat_filename, ImageInfo()); - } std::fill_n(image_methods_, arraysize(image_methods_), nullptr); } diff --git a/compiler/image_writer.h b/compiler/image_writer.h index b227c44519..34d47bc5ba 100644 --- a/compiler/image_writer.h +++ b/compiler/image_writer.h @@ -27,6 +27,7 @@ #include #include "base/bit_utils.h" +#include "base/dchecked_vector.h" #include "base/length_prefixed_array.h" #include "base/macros.h" #include "driver/compiler_driver.h" @@ -59,20 +60,19 @@ class ImageWriter FINAL { bool compile_pic, bool compile_app_image, ImageHeader::StorageMode image_storage_mode, - const std::vector oat_filenames, - const std::unordered_map& dex_file_oat_filename_map); + const std::vector& oat_filenames, + const std::unordered_map& dex_file_oat_index_map); bool PrepareImageAddressSpace(); bool IsImageAddressSpaceReady() const { - bool ready = !image_info_map_.empty(); - for (auto& pair : image_info_map_) { - const ImageInfo& image_info = pair.second; + DCHECK(!image_infos_.empty()); + for (const ImageInfo& image_info : image_infos_) { if (image_info.image_roots_address_ == 0u) { return false; } } - return ready; + return true; } template @@ -80,8 +80,8 @@ class ImageWriter FINAL { if (object == nullptr || IsInBootImage(object)) { return object; } else { - const char* oat_filename = GetOatFilename(object); - const ImageInfo& image_info = GetConstImageInfo(oat_filename); + size_t oat_index = GetOatIndex(object); + const ImageInfo& image_info = GetImageInfo(oat_index); return reinterpret_cast(image_info.image_begin_ + GetImageOffset(object)); } } @@ -91,9 +91,9 @@ class ImageWriter FINAL { template PtrType GetDexCacheArrayElementImageAddress(const DexFile* dex_file, uint32_t offset) const SHARED_REQUIRES(Locks::mutator_lock_) { - auto oat_it = dex_file_oat_filename_map_.find(dex_file); - DCHECK(oat_it != dex_file_oat_filename_map_.end()); - const ImageInfo& image_info = GetConstImageInfo(oat_it->second); + auto oat_it = dex_file_oat_index_map_.find(dex_file); + DCHECK(oat_it != dex_file_oat_index_map_.end()); + const ImageInfo& image_info = GetImageInfo(oat_it->second); auto it = image_info.dex_cache_array_starts_.find(dex_file); DCHECK(it != image_info.dex_cache_array_starts_.end()); return reinterpret_cast( @@ -101,7 +101,13 @@ class ImageWriter FINAL { it->second + offset); } - uint8_t* GetOatFileBegin(const char* oat_filename) const; + size_t GetOatFileOffset(size_t oat_index) const { + return GetImageInfo(oat_index).oat_offset_; + } + + const uint8_t* GetOatFileBegin(size_t oat_index) const { + return GetImageInfo(oat_index).oat_file_begin_; + } // If image_fd is not kInvalidFd, then we use that for the image file. Otherwise we open // the names in image_filenames. @@ -109,21 +115,32 @@ class ImageWriter FINAL { // the names in oat_filenames. bool Write(int image_fd, const std::vector& image_filenames, - int oat_fd, - const std::vector& oat_filenames, - const std::string& oat_location) + const std::vector& oat_filenames) REQUIRES(!Locks::mutator_lock_); - uintptr_t GetOatDataBegin(const char* oat_filename) { - return reinterpret_cast(GetImageInfo(oat_filename).oat_data_begin_); + uintptr_t GetOatDataBegin(size_t oat_index) { + return reinterpret_cast(GetImageInfo(oat_index).oat_data_begin_); } - const char* GetOatFilenameForDexCache(mirror::DexCache* dex_cache) const + // Get the index of the oat file containing the dex file. + // + // This "oat_index" is used to retrieve information about the the memory layout + // of the oat file and its associated image file, needed for link-time patching + // of references to the image or across oat files. + size_t GetOatIndexForDexFile(const DexFile* dex_file) const; + + // Get the index of the oat file containing the dex file served by the dex cache. + size_t GetOatIndexForDexCache(mirror::DexCache* dex_cache) const SHARED_REQUIRES(Locks::mutator_lock_); - // Update the oat size for the given oat file. This will make the oat_offset for the next oat - // file valid. - void UpdateOatFile(File* oat_file, const char* oat_filename); + // Update the oat layout for the given oat file. + // This will make the oat_offset for the next oat file valid. + void UpdateOatFileLayout(size_t oat_index, + size_t oat_loaded_size, + size_t oat_data_offset, + size_t oat_data_size); + // Update information about the oat header, i.e. checksum and trampoline offsets. + void UpdateOatFileHeader(size_t oat_index, const OatHeader& oat_header); private: bool AllocMemory(); @@ -247,10 +264,13 @@ class ImageWriter FINAL { // Offset of the oat file for this image from start of oat files. This is // valid when the previous oat file has been written. size_t oat_offset_ = 0; - // Start of oatdata in the corresponding oat file. This is - // valid when the images have been layed out. - uint8_t* oat_data_begin_ = nullptr; + // Layout of the loaded ELF file containing the oat file, valid after UpdateOatFileLayout(). + const uint8_t* oat_file_begin_ = nullptr; + size_t oat_loaded_size_ = 0; + const uint8_t* oat_data_begin_ = nullptr; size_t oat_size_ = 0; // Size of the corresponding oat data. + // The oat header checksum, valid after UpdateOatFileHeader(). + uint32_t oat_checksum_ = 0u; // Image bitmap which lets us know where the objects inside of the image reside. std::unique_ptr image_bitmap_; @@ -310,8 +330,8 @@ class ImageWriter FINAL { mirror::Object* GetLocalAddress(mirror::Object* object) const SHARED_REQUIRES(Locks::mutator_lock_) { size_t offset = GetImageOffset(object); - const char* oat_filename = GetOatFilename(object); - const ImageInfo& image_info = GetConstImageInfo(oat_filename); + size_t oat_index = GetOatIndex(object); + const ImageInfo& image_info = GetImageInfo(oat_index); uint8_t* dst = image_info.image_->Begin() + offset; return reinterpret_cast(dst); } @@ -348,9 +368,9 @@ class ImageWriter FINAL { // Lays out where the image objects will be at runtime. void CalculateNewObjectOffsets() SHARED_REQUIRES(Locks::mutator_lock_); - void CreateHeader(size_t oat_loaded_size, size_t oat_data_offset) + void CreateHeader(size_t oat_index) SHARED_REQUIRES(Locks::mutator_lock_); - mirror::ObjectArray* CreateImageRoots(const char* oat_filename) const + mirror::ObjectArray* CreateImageRoots(size_t oat_index) const SHARED_REQUIRES(Locks::mutator_lock_); void CalculateObjectBinSlots(mirror::Object* obj) SHARED_REQUIRES(Locks::mutator_lock_); @@ -367,7 +387,7 @@ class ImageWriter FINAL { SHARED_REQUIRES(Locks::mutator_lock_); // Creates the contiguous image in memory and adjusts pointers. - void CopyAndFixupNativeData() SHARED_REQUIRES(Locks::mutator_lock_); + void CopyAndFixupNativeData(size_t oat_index) SHARED_REQUIRES(Locks::mutator_lock_); void CopyAndFixupObjects() SHARED_REQUIRES(Locks::mutator_lock_); static void CopyAndFixupObjectsCallback(mirror::Object* obj, void* arg) SHARED_REQUIRES(Locks::mutator_lock_); @@ -392,9 +412,6 @@ class ImageWriter FINAL { bool* quick_is_interpreted) SHARED_REQUIRES(Locks::mutator_lock_); - // Patches references in OatFile to expect runtime addresses. - void SetOatChecksumFromElfFile(File* elf_file); - // Calculate the sum total of the bin slot sizes in [0, up_to). Defaults to all bins. size_t GetBinSizeSum(ImageInfo& image_info, Bin up_to = kBinSize) const; @@ -404,7 +421,7 @@ class ImageWriter FINAL { // Assign the offset for an ArtMethod. void AssignMethodOffset(ArtMethod* method, NativeObjectRelocationType type, - const char* oat_filename) + size_t oat_index) SHARED_REQUIRES(Locks::mutator_lock_); // Return true if klass is loaded by the boot class loader but not in the boot image. @@ -441,15 +458,21 @@ class ImageWriter FINAL { // Return true if ptr is within the boot oat file. bool IsInBootOatFile(const void* ptr) const; - const char* GetOatFilename(mirror::Object* object) const SHARED_REQUIRES(Locks::mutator_lock_); + // Get the index of the oat file associated with the object. + size_t GetOatIndex(mirror::Object* object) const SHARED_REQUIRES(Locks::mutator_lock_); - const char* GetDefaultOatFilename() const { - return default_oat_filename_; + // The oat index for shared data in multi-image and all data in single-image compilation. + size_t GetDefaultOatIndex() const { + return 0u; } - ImageInfo& GetImageInfo(const char* oat_filename); - const ImageInfo& GetConstImageInfo(const char* oat_filename) const; - const ImageInfo& GetImageInfo(size_t index) const; + ImageInfo& GetImageInfo(size_t oat_index) { + return image_infos_[oat_index]; + } + + const ImageInfo& GetImageInfo(size_t oat_index) const { + return image_infos_[oat_index]; + } // Find an already strong interned string in the other images or in the boot image. Used to // remove duplicates in the multi image and app image case. @@ -463,9 +486,6 @@ class ImageWriter FINAL { // Offset from image_begin_ to where the first object is in image_. size_t image_objects_offset_begin_; - // oat file with code for this image - OatFile* oat_file_; - // Pointer arrays that need to be updated. Since these are only some int and long arrays, we need // to keep track. These include vtable arrays, iftable arrays, and dex caches. std::unordered_map pointer_arrays_; @@ -481,14 +501,14 @@ class ImageWriter FINAL { // Size of pointers on the target architecture. size_t target_ptr_size_; - // Mapping of oat filename to image data. - std::unordered_map image_info_map_; + // Image data indexed by the oat file index. + dchecked_vector image_infos_; // ArtField, ArtMethod relocating map. These are allocated as array of structs but we want to // have one entry per art field for convenience. ArtFields are placed right after the end of the // image objects (aka sum of bin_slot_sizes_). ArtMethods are placed right after the ArtFields. struct NativeObjectRelocation { - const char* oat_filename; + size_t oat_index; uintptr_t offset; NativeObjectRelocationType type; @@ -520,10 +540,11 @@ class ImageWriter FINAL { // Which mode the image is stored as, see image.h const ImageHeader::StorageMode image_storage_mode_; - // Map of dex files to the oat filenames that they were compiled into. - const std::unordered_map& dex_file_oat_filename_map_; - const std::vector oat_filenames_; - const char* default_oat_filename_; + // The file names of oat files. + const std::vector& oat_filenames_; + + // Map of dex files to the indexes of oat files that they were compiled into. + const std::unordered_map& dex_file_oat_index_map_; friend class ContainsBootClassLoaderNonImageClassVisitor; friend class FixupClassVisitor; diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc index 3fe786141e..c2b29eb01a 100644 --- a/compiler/jit/jit_compiler.cc +++ b/compiler/jit/jit_compiler.cc @@ -172,7 +172,6 @@ JitCompiler::JitCompiler() : total_time_(0) { /* dump_passes */ false, cumulative_logger_.get(), /* swap_fd */ -1, - /* dex to oat map */ nullptr, /* profile_compilation_info */ nullptr)); // Disable dedupe so we can remove compiled methods. compiler_driver_->SetDedupeEnabled(false); diff --git a/compiler/linker/arm/relative_patcher_arm_base.cc b/compiler/linker/arm/relative_patcher_arm_base.cc index 73b0facf4b..682b008219 100644 --- a/compiler/linker/arm/relative_patcher_arm_base.cc +++ b/compiler/linker/arm/relative_patcher_arm_base.cc @@ -40,6 +40,11 @@ uint32_t ArmBaseRelativePatcher::ReserveSpaceEnd(uint32_t offset) { MethodReference(nullptr, 0u), aligned_offset); if (needs_thunk) { + // All remaining patches will be handled by this thunk. + DCHECK(!unprocessed_patches_.empty()); + DCHECK_LE(aligned_offset - unprocessed_patches_.front().second, max_positive_displacement_); + unprocessed_patches_.clear(); + thunk_locations_.push_back(aligned_offset); offset = CompiledMethod::AlignCode(aligned_offset + thunk_code_.size(), instruction_set_); } diff --git a/compiler/linker/arm/relative_patcher_arm_base.h b/compiler/linker/arm/relative_patcher_arm_base.h index f80dd962ce..25fd35e1d6 100644 --- a/compiler/linker/arm/relative_patcher_arm_base.h +++ b/compiler/linker/arm/relative_patcher_arm_base.h @@ -27,18 +27,23 @@ namespace linker { class ArmBaseRelativePatcher : public RelativePatcher { public: - uint32_t ReserveSpace(uint32_t offset, const CompiledMethod* compiled_method, + uint32_t ReserveSpace(uint32_t offset, + const CompiledMethod* compiled_method, MethodReference method_ref) OVERRIDE; uint32_t ReserveSpaceEnd(uint32_t offset) OVERRIDE; uint32_t WriteThunks(OutputStream* out, uint32_t offset) OVERRIDE; protected: ArmBaseRelativePatcher(RelativePatcherTargetProvider* provider, - InstructionSet instruction_set, std::vector thunk_code, - uint32_t max_positive_displacement, uint32_t max_negative_displacement); + InstructionSet instruction_set, + std::vector thunk_code, + uint32_t max_positive_displacement, + uint32_t max_negative_displacement); - uint32_t ReserveSpaceInternal(uint32_t offset, const CompiledMethod* compiled_method, - MethodReference method_ref, uint32_t max_extra_space); + uint32_t ReserveSpaceInternal(uint32_t offset, + const CompiledMethod* compiled_method, + MethodReference method_ref, + uint32_t max_extra_space); uint32_t CalculateDisplacement(uint32_t patch_offset, uint32_t target_offset); private: diff --git a/compiler/linker/arm/relative_patcher_thumb2.cc b/compiler/linker/arm/relative_patcher_thumb2.cc index 5f4f760c14..c090dffc55 100644 --- a/compiler/linker/arm/relative_patcher_thumb2.cc +++ b/compiler/linker/arm/relative_patcher_thumb2.cc @@ -28,8 +28,10 @@ Thumb2RelativePatcher::Thumb2RelativePatcher(RelativePatcherTargetProvider* prov kMaxPositiveDisplacement, kMaxNegativeDisplacement) { } -void Thumb2RelativePatcher::PatchCall(std::vector* code, uint32_t literal_offset, - uint32_t patch_offset, uint32_t target_offset) { +void Thumb2RelativePatcher::PatchCall(std::vector* code, + uint32_t literal_offset, + uint32_t patch_offset, + uint32_t target_offset) { DCHECK_LE(literal_offset + 4u, code->size()); DCHECK_EQ(literal_offset & 1u, 0u); DCHECK_EQ(patch_offset & 1u, 0u); diff --git a/compiler/linker/arm/relative_patcher_thumb2.h b/compiler/linker/arm/relative_patcher_thumb2.h index 006d6fb9d5..0d903c0b41 100644 --- a/compiler/linker/arm/relative_patcher_thumb2.h +++ b/compiler/linker/arm/relative_patcher_thumb2.h @@ -26,10 +26,14 @@ class Thumb2RelativePatcher FINAL : public ArmBaseRelativePatcher { public: explicit Thumb2RelativePatcher(RelativePatcherTargetProvider* provider); - void PatchCall(std::vector* code, uint32_t literal_offset, - uint32_t patch_offset, uint32_t target_offset) OVERRIDE; - void PatchDexCacheReference(std::vector* code, const LinkerPatch& patch, - uint32_t patch_offset, uint32_t target_offset) OVERRIDE; + void PatchCall(std::vector* code, + uint32_t literal_offset, + uint32_t patch_offset, + uint32_t target_offset) OVERRIDE; + void PatchDexCacheReference(std::vector* code, + const LinkerPatch& patch, + uint32_t patch_offset, + uint32_t target_offset) OVERRIDE; private: static std::vector CompileThunkCode(); diff --git a/compiler/linker/arm64/relative_patcher_arm64.cc b/compiler/linker/arm64/relative_patcher_arm64.cc index 3d4c2184f1..a81c85c707 100644 --- a/compiler/linker/arm64/relative_patcher_arm64.cc +++ b/compiler/linker/arm64/relative_patcher_arm64.cc @@ -131,8 +131,10 @@ uint32_t Arm64RelativePatcher::WriteThunks(OutputStream* out, uint32_t offset) { return ArmBaseRelativePatcher::WriteThunks(out, offset); } -void Arm64RelativePatcher::PatchCall(std::vector* code, uint32_t literal_offset, - uint32_t patch_offset, uint32_t target_offset) { +void Arm64RelativePatcher::PatchCall(std::vector* code, + uint32_t literal_offset, + uint32_t patch_offset, uint32_t + target_offset) { DCHECK_LE(literal_offset + 4u, code->size()); DCHECK_EQ(literal_offset & 3u, 0u); DCHECK_EQ(patch_offset & 3u, 0u); diff --git a/compiler/linker/arm64/relative_patcher_arm64.h b/compiler/linker/arm64/relative_patcher_arm64.h index 2d07e75c85..f9b76e6250 100644 --- a/compiler/linker/arm64/relative_patcher_arm64.h +++ b/compiler/linker/arm64/relative_patcher_arm64.h @@ -28,14 +28,19 @@ class Arm64RelativePatcher FINAL : public ArmBaseRelativePatcher { Arm64RelativePatcher(RelativePatcherTargetProvider* provider, const Arm64InstructionSetFeatures* features); - uint32_t ReserveSpace(uint32_t offset, const CompiledMethod* compiled_method, + uint32_t ReserveSpace(uint32_t offset, + const CompiledMethod* compiled_method, MethodReference method_ref) OVERRIDE; uint32_t ReserveSpaceEnd(uint32_t offset) OVERRIDE; uint32_t WriteThunks(OutputStream* out, uint32_t offset) OVERRIDE; - void PatchCall(std::vector* code, uint32_t literal_offset, - uint32_t patch_offset, uint32_t target_offset) OVERRIDE; - void PatchDexCacheReference(std::vector* code, const LinkerPatch& patch, - uint32_t patch_offset, uint32_t target_offset) OVERRIDE; + void PatchCall(std::vector* code, + uint32_t literal_offset, + uint32_t patch_offset, + uint32_t target_offset) OVERRIDE; + void PatchDexCacheReference(std::vector* code, + const LinkerPatch& patch, + uint32_t patch_offset, + uint32_t target_offset) OVERRIDE; private: static std::vector CompileThunkCode(); diff --git a/compiler/linker/multi_oat_relative_patcher.cc b/compiler/linker/multi_oat_relative_patcher.cc new file mode 100644 index 0000000000..e9e242b658 --- /dev/null +++ b/compiler/linker/multi_oat_relative_patcher.cc @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "multi_oat_relative_patcher.h" + +#include "globals.h" +#include "base/bit_utils.h" +#include "base/logging.h" + +namespace art { +namespace linker { + +MultiOatRelativePatcher::MultiOatRelativePatcher(InstructionSet instruction_set, + const InstructionSetFeatures* features) + : method_offset_map_(), + relative_patcher_( + linker::RelativePatcher::Create(instruction_set, features, &method_offset_map_)), + adjustment_(0u), + instruction_set_(instruction_set), + start_size_code_alignment_(0u), + start_size_relative_call_thunks_(0u), + start_size_misc_thunks_(0u) { +} + +void MultiOatRelativePatcher::StartOatFile(uint32_t adjustment) { + DCHECK_ALIGNED(adjustment, kPageSize); + adjustment_ = adjustment; + + start_size_code_alignment_ = relative_patcher_->CodeAlignmentSize(); + start_size_relative_call_thunks_ = relative_patcher_->RelativeCallThunksSize(); + start_size_misc_thunks_ = relative_patcher_->MiscThunksSize(); +} + +uint32_t MultiOatRelativePatcher::CodeAlignmentSize() const { + DCHECK_GE(relative_patcher_->CodeAlignmentSize(), start_size_code_alignment_); + return relative_patcher_->CodeAlignmentSize() - start_size_code_alignment_; +} + +uint32_t MultiOatRelativePatcher::RelativeCallThunksSize() const { + DCHECK_GE(relative_patcher_->RelativeCallThunksSize(), start_size_relative_call_thunks_); + return relative_patcher_->RelativeCallThunksSize() - start_size_relative_call_thunks_; +} + +uint32_t MultiOatRelativePatcher::MiscThunksSize() const { + DCHECK_GE(relative_patcher_->MiscThunksSize(), start_size_misc_thunks_); + return relative_patcher_->MiscThunksSize() - start_size_misc_thunks_; +} + +std::pair MultiOatRelativePatcher::MethodOffsetMap::FindMethodOffset( + MethodReference ref) { + auto it = map.find(ref); + if (it == map.end()) { + return std::pair(false, 0u); + } else { + return std::pair(true, it->second); + } +} +} // namespace linker +} // namespace art diff --git a/compiler/linker/multi_oat_relative_patcher.h b/compiler/linker/multi_oat_relative_patcher.h new file mode 100644 index 0000000000..1727d529fc --- /dev/null +++ b/compiler/linker/multi_oat_relative_patcher.h @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_COMPILER_LINKER_MULTI_OAT_RELATIVE_PATCHER_H_ +#define ART_COMPILER_LINKER_MULTI_OAT_RELATIVE_PATCHER_H_ + +#include "arch/instruction_set.h" +#include "method_reference.h" +#include "relative_patcher.h" +#include "safe_map.h" + +namespace art { + +class CompiledMethod; +class LinkerPatch; +class InstructionSetFeatures; + +namespace linker { + +// MultiOatRelativePatcher is a helper class for handling patching across +// any number of oat files. It provides storage for method code offsets +// and wraps RelativePatcher calls, adjusting relative offsets according +// to the value set by SetAdjustment(). +class MultiOatRelativePatcher FINAL { + public: + using const_iterator = + SafeMap::const_iterator; + + MultiOatRelativePatcher(InstructionSet instruction_set, const InstructionSetFeatures* features); + + // Mark the start of a new oat file (for statistics retrieval) and set the + // adjustment for a new oat file to apply to all relative offsets that are + // passed to the MultiOatRelativePatcher. + // + // The adjustment should be the global offset of the base from which relative + // offsets are calculated, such as the start of .rodata for the current oat file. + // It must must never point directly to a method's code to avoid relative offsets + // with value 0 because this value is used as a missing offset indication in + // GetOffset() and an error indication in WriteThunks(). Additionally, it must be + // page-aligned, so that it does not skew alignment calculations, say arm64 ADRP. + void StartOatFile(uint32_t adjustment); + + // Get relative offset. Returns 0 when the offset has not been set yet. + uint32_t GetOffset(MethodReference method_ref) { + auto it = method_offset_map_.map.find(method_ref); + return (it != method_offset_map_.map.end()) ? it->second - adjustment_ : 0u; + } + + // Set the offset. + void SetOffset(MethodReference method_ref, uint32_t offset) { + method_offset_map_.map.Put(method_ref, offset + adjustment_); + } + + // Wrapper around RelativePatcher::ReserveSpace(), doing offset adjustment. + uint32_t ReserveSpace(uint32_t offset, + const CompiledMethod* compiled_method, + MethodReference method_ref) { + offset += adjustment_; + offset = relative_patcher_->ReserveSpace(offset, compiled_method, method_ref); + offset -= adjustment_; + return offset; + } + + // Wrapper around RelativePatcher::ReserveSpaceEnd(), doing offset adjustment. + uint32_t ReserveSpaceEnd(uint32_t offset) { + offset += adjustment_; + offset = relative_patcher_->ReserveSpaceEnd(offset); + offset -= adjustment_; + return offset; + } + + // Wrapper around RelativePatcher::WriteThunks(), doing offset adjustment. + uint32_t WriteThunks(OutputStream* out, uint32_t offset) { + offset += adjustment_; + offset = relative_patcher_->WriteThunks(out, offset); + if (offset != 0u) { // 0u indicates write error. + offset -= adjustment_; + } + return offset; + } + + // Wrapper around RelativePatcher::PatchCall(), doing offset adjustment. + void PatchCall(std::vector* code, + uint32_t literal_offset, + uint32_t patch_offset, + uint32_t target_offset) { + patch_offset += adjustment_; + target_offset += adjustment_; + relative_patcher_->PatchCall(code, literal_offset, patch_offset, target_offset); + } + + // Wrapper around RelativePatcher::PatchDexCacheReference(), doing offset adjustment. + void PatchDexCacheReference(std::vector* code, + const LinkerPatch& patch, + uint32_t patch_offset, + uint32_t target_offset) { + patch_offset += adjustment_; + target_offset += adjustment_; + relative_patcher_->PatchDexCacheReference(code, patch, patch_offset, target_offset); + } + + // Wrappers around RelativePatcher for statistics retrieval. + uint32_t CodeAlignmentSize() const; + uint32_t RelativeCallThunksSize() const; + uint32_t MiscThunksSize() const; + + private: + // Map method reference to assigned offset. + // Wrap the map in a class implementing linker::RelativePatcherTargetProvider. + class MethodOffsetMap : public linker::RelativePatcherTargetProvider { + public: + std::pair FindMethodOffset(MethodReference ref) OVERRIDE; + SafeMap map; + }; + + MethodOffsetMap method_offset_map_; + std::unique_ptr relative_patcher_; + uint32_t adjustment_; + InstructionSet instruction_set_; + + uint32_t start_size_code_alignment_; + uint32_t start_size_relative_call_thunks_; + uint32_t start_size_misc_thunks_; + + friend class MultiOatRelativePatcherTest; + + DISALLOW_COPY_AND_ASSIGN(MultiOatRelativePatcher); +}; + +} // namespace linker +} // namespace art + +#endif // ART_COMPILER_LINKER_MULTI_OAT_RELATIVE_PATCHER_H_ diff --git a/compiler/linker/multi_oat_relative_patcher_test.cc b/compiler/linker/multi_oat_relative_patcher_test.cc new file mode 100644 index 0000000000..792cdfe8e9 --- /dev/null +++ b/compiler/linker/multi_oat_relative_patcher_test.cc @@ -0,0 +1,299 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "compiled_method.h" +#include "gtest/gtest.h" +#include "multi_oat_relative_patcher.h" +#include "vector_output_stream.h" + +namespace art { +namespace linker { + +static const MethodReference kNullMethodRef = MethodReference(nullptr, 0u); + +static bool EqualRef(MethodReference lhs, MethodReference rhs) { + return lhs.dex_file == rhs.dex_file && lhs.dex_method_index == rhs.dex_method_index; +} + +class MultiOatRelativePatcherTest : public testing::Test { + protected: + class MockPatcher : public RelativePatcher { + public: + MockPatcher() { } + + uint32_t ReserveSpace(uint32_t offset, + const CompiledMethod* compiled_method ATTRIBUTE_UNUSED, + MethodReference method_ref) OVERRIDE { + last_reserve_offset_ = offset; + last_reserve_method_ = method_ref; + offset += next_reserve_adjustment_; + next_reserve_adjustment_ = 0u; + return offset; + } + + uint32_t ReserveSpaceEnd(uint32_t offset) OVERRIDE { + last_reserve_offset_ = offset; + last_reserve_method_ = kNullMethodRef; + offset += next_reserve_adjustment_; + next_reserve_adjustment_ = 0u; + return offset; + } + + uint32_t WriteThunks(OutputStream* out, uint32_t offset) OVERRIDE { + last_write_offset_ = offset; + if (next_write_alignment_ != 0u) { + offset += next_write_alignment_; + bool success = WriteCodeAlignment(out, next_write_alignment_); + CHECK(success); + next_write_alignment_ = 0u; + } + if (next_write_call_thunk_ != 0u) { + offset += next_write_call_thunk_; + std::vector thunk(next_write_call_thunk_, 'c'); + bool success = WriteRelCallThunk(out, ArrayRef(thunk)); + CHECK(success); + next_write_call_thunk_ = 0u; + } + if (next_write_misc_thunk_ != 0u) { + offset += next_write_misc_thunk_; + std::vector thunk(next_write_misc_thunk_, 'm'); + bool success = WriteMiscThunk(out, ArrayRef(thunk)); + CHECK(success); + next_write_misc_thunk_ = 0u; + } + return offset; + } + + void PatchCall(std::vector* code ATTRIBUTE_UNUSED, + uint32_t literal_offset, + uint32_t patch_offset, + uint32_t target_offset) OVERRIDE { + last_literal_offset_ = literal_offset; + last_patch_offset_ = patch_offset; + last_target_offset_ = target_offset; + } + + void PatchDexCacheReference(std::vector* code ATTRIBUTE_UNUSED, + const LinkerPatch& patch, + uint32_t patch_offset, + uint32_t target_offset) OVERRIDE { + last_literal_offset_ = patch.LiteralOffset(); + last_patch_offset_ = patch_offset; + last_target_offset_ = target_offset; + } + + uint32_t last_reserve_offset_ = 0u; + MethodReference last_reserve_method_ = kNullMethodRef; + uint32_t next_reserve_adjustment_ = 0u; + + uint32_t last_write_offset_ = 0u; + uint32_t next_write_alignment_ = 0u; + uint32_t next_write_call_thunk_ = 0u; + uint32_t next_write_misc_thunk_ = 0u; + + uint32_t last_literal_offset_ = 0u; + uint32_t last_patch_offset_ = 0u; + uint32_t last_target_offset_ = 0u; + }; + + MultiOatRelativePatcherTest() + : instruction_set_features_(InstructionSetFeatures::FromCppDefines()), + patcher_(kRuntimeISA, instruction_set_features_.get()) { + std::unique_ptr mock(new MockPatcher()); + mock_ = mock.get(); + patcher_.relative_patcher_ = std::move(mock); + } + + std::unique_ptr instruction_set_features_; + MultiOatRelativePatcher patcher_; + MockPatcher* mock_; +}; + +TEST_F(MultiOatRelativePatcherTest, Offsets) { + const DexFile* dex_file = reinterpret_cast(1); + MethodReference ref1(dex_file, 1u); + MethodReference ref2(dex_file, 2u); + EXPECT_EQ(0u, patcher_.GetOffset(ref1)); + EXPECT_EQ(0u, patcher_.GetOffset(ref2)); + + uint32_t adjustment1 = 0x1000; + patcher_.StartOatFile(adjustment1); + EXPECT_EQ(0u, patcher_.GetOffset(ref1)); + EXPECT_EQ(0u, patcher_.GetOffset(ref2)); + + uint32_t off1 = 0x1234; + patcher_.SetOffset(ref1, off1); + EXPECT_EQ(off1, patcher_.GetOffset(ref1)); + EXPECT_EQ(0u, patcher_.GetOffset(ref2)); + + uint32_t adjustment2 = 0x30000; + patcher_.StartOatFile(adjustment2); + EXPECT_EQ(off1 + adjustment1 - adjustment2, patcher_.GetOffset(ref1)); + EXPECT_EQ(0u, patcher_.GetOffset(ref2)); + + uint32_t off2 = 0x4321; + patcher_.SetOffset(ref2, off2); + EXPECT_EQ(off1 + adjustment1 - adjustment2, patcher_.GetOffset(ref1)); + EXPECT_EQ(off2, patcher_.GetOffset(ref2)); + + uint32_t adjustment3 = 0x78000; + patcher_.StartOatFile(adjustment3); + EXPECT_EQ(off1 + adjustment1 - adjustment3, patcher_.GetOffset(ref1)); + EXPECT_EQ(off2 + adjustment2 - adjustment3, patcher_.GetOffset(ref2)); +} + +TEST_F(MultiOatRelativePatcherTest, OffsetsInReserve) { + const DexFile* dex_file = reinterpret_cast(1); + MethodReference ref1(dex_file, 1u); + MethodReference ref2(dex_file, 2u); + MethodReference ref3(dex_file, 3u); + const CompiledMethod* method = reinterpret_cast(-1); + + uint32_t adjustment1 = 0x1000; + patcher_.StartOatFile(adjustment1); + + uint32_t method1_offset = 0x100; + uint32_t method1_offset_check = patcher_.ReserveSpace(method1_offset, method, ref1); + ASSERT_EQ(adjustment1 + method1_offset, mock_->last_reserve_offset_); + ASSERT_TRUE(EqualRef(ref1, mock_->last_reserve_method_)); + ASSERT_EQ(method1_offset, method1_offset_check); + + uint32_t method2_offset = 0x1230; + uint32_t method2_reserve_adjustment = 0x10; + mock_->next_reserve_adjustment_ = method2_reserve_adjustment; + uint32_t method2_offset_adjusted = patcher_.ReserveSpace(method2_offset, method, ref2); + ASSERT_EQ(adjustment1 + method2_offset, mock_->last_reserve_offset_); + ASSERT_TRUE(EqualRef(ref2, mock_->last_reserve_method_)); + ASSERT_EQ(method2_offset + method2_reserve_adjustment, method2_offset_adjusted); + + uint32_t end1_offset = 0x4320; + uint32_t end1_offset_check = patcher_.ReserveSpaceEnd(end1_offset); + ASSERT_EQ(adjustment1 + end1_offset, mock_->last_reserve_offset_); + ASSERT_TRUE(EqualRef(kNullMethodRef, mock_->last_reserve_method_)); + ASSERT_EQ(end1_offset, end1_offset_check); + + uint32_t adjustment2 = 0xd000; + patcher_.StartOatFile(adjustment2); + + uint32_t method3_offset = 0xf00; + uint32_t method3_offset_check = patcher_.ReserveSpace(method3_offset, method, ref3); + ASSERT_EQ(adjustment2 + method3_offset, mock_->last_reserve_offset_); + ASSERT_TRUE(EqualRef(ref3, mock_->last_reserve_method_)); + ASSERT_EQ(method3_offset, method3_offset_check); + + uint32_t end2_offset = 0x2400; + uint32_t end2_reserve_adjustment = 0x20; + mock_->next_reserve_adjustment_ = end2_reserve_adjustment; + uint32_t end2_offset_adjusted = patcher_.ReserveSpaceEnd(end2_offset); + ASSERT_EQ(adjustment2 + end2_offset, mock_->last_reserve_offset_); + ASSERT_TRUE(EqualRef(kNullMethodRef, mock_->last_reserve_method_)); + ASSERT_EQ(end2_offset + end2_reserve_adjustment, end2_offset_adjusted); +} + +TEST_F(MultiOatRelativePatcherTest, Write) { + std::vector output; + VectorOutputStream vos("output", &output); + + uint32_t adjustment1 = 0x1000; + patcher_.StartOatFile(adjustment1); + + uint32_t method1_offset = 0x100; + uint32_t method1_offset_check = patcher_.WriteThunks(&vos, method1_offset); + ASSERT_EQ(adjustment1 + method1_offset, mock_->last_write_offset_); + ASSERT_EQ(method1_offset, method1_offset_check); + vos.WriteFully("1", 1); // Mark method1. + + uint32_t method2_offset = 0x1230; + uint32_t method2_alignment_size = 1; + uint32_t method2_call_thunk_size = 2; + mock_->next_write_alignment_ = method2_alignment_size; + mock_->next_write_call_thunk_ = method2_call_thunk_size; + uint32_t method2_offset_adjusted = patcher_.WriteThunks(&vos, method2_offset); + ASSERT_EQ(adjustment1 + method2_offset, mock_->last_write_offset_); + ASSERT_EQ(method2_offset + method2_alignment_size + method2_call_thunk_size, + method2_offset_adjusted); + vos.WriteFully("2", 1); // Mark method2. + + EXPECT_EQ(method2_alignment_size, patcher_.CodeAlignmentSize()); + EXPECT_EQ(method2_call_thunk_size, patcher_.RelativeCallThunksSize()); + + uint32_t adjustment2 = 0xd000; + patcher_.StartOatFile(adjustment2); + + uint32_t method3_offset = 0xf00; + uint32_t method3_alignment_size = 2; + uint32_t method3_misc_thunk_size = 1; + mock_->next_write_alignment_ = method3_alignment_size; + mock_->next_write_misc_thunk_ = method3_misc_thunk_size; + uint32_t method3_offset_adjusted = patcher_.WriteThunks(&vos, method3_offset); + ASSERT_EQ(adjustment2 + method3_offset, mock_->last_write_offset_); + ASSERT_EQ(method3_offset + method3_alignment_size + method3_misc_thunk_size, + method3_offset_adjusted); + vos.WriteFully("3", 1); // Mark method3. + + EXPECT_EQ(method3_alignment_size, patcher_.CodeAlignmentSize()); + EXPECT_EQ(method3_misc_thunk_size, patcher_.MiscThunksSize()); + + uint8_t expected_output[] = { + '1', + 0, 'c', 'c', '2', + 0, 0, 'm', '3', + }; + ASSERT_EQ(arraysize(expected_output), output.size()); + for (size_t i = 0; i != arraysize(expected_output); ++i) { + ASSERT_EQ(expected_output[i], output[i]) << i; + } +} + +TEST_F(MultiOatRelativePatcherTest, Patch) { + std::vector code(16); + + uint32_t adjustment1 = 0x1000; + patcher_.StartOatFile(adjustment1); + + uint32_t method1_literal_offset = 4u; + uint32_t method1_patch_offset = 0x1234u; + uint32_t method1_target_offset = 0x8888u; + patcher_.PatchCall(&code, method1_literal_offset, method1_patch_offset, method1_target_offset); + DCHECK_EQ(method1_literal_offset, mock_->last_literal_offset_); + DCHECK_EQ(method1_patch_offset + adjustment1, mock_->last_patch_offset_); + DCHECK_EQ(method1_target_offset + adjustment1, mock_->last_target_offset_); + + uint32_t method2_literal_offset = 12u; + uint32_t method2_patch_offset = 0x7654u; + uint32_t method2_target_offset = 0xccccu; + LinkerPatch method2_patch = + LinkerPatch::DexCacheArrayPatch(method2_literal_offset, nullptr, 0u, 1234u); + patcher_.PatchDexCacheReference( + &code, method2_patch, method2_patch_offset, method2_target_offset); + DCHECK_EQ(method2_literal_offset, mock_->last_literal_offset_); + DCHECK_EQ(method2_patch_offset + adjustment1, mock_->last_patch_offset_); + DCHECK_EQ(method2_target_offset + adjustment1, mock_->last_target_offset_); + + uint32_t adjustment2 = 0xd000; + patcher_.StartOatFile(adjustment2); + + uint32_t method3_literal_offset = 8u; + uint32_t method3_patch_offset = 0x108u; + uint32_t method3_target_offset = 0x200u; + patcher_.PatchCall(&code, method3_literal_offset, method3_patch_offset, method3_target_offset); + DCHECK_EQ(method3_literal_offset, mock_->last_literal_offset_); + DCHECK_EQ(method3_patch_offset + adjustment2, mock_->last_patch_offset_); + DCHECK_EQ(method3_target_offset + adjustment2, mock_->last_target_offset_); +} + +} // namespace linker +} // namespace art diff --git a/compiler/linker/relative_patcher.cc b/compiler/linker/relative_patcher.cc index 82702dcf25..6727c17583 100644 --- a/compiler/linker/relative_patcher.cc +++ b/compiler/linker/relative_patcher.cc @@ -34,7 +34,8 @@ namespace art { namespace linker { std::unique_ptr RelativePatcher::Create( - InstructionSet instruction_set, const InstructionSetFeatures* features, + InstructionSet instruction_set, + const InstructionSetFeatures* features, RelativePatcherTargetProvider* provider) { class RelativePatcherNone FINAL : public RelativePatcher { public: diff --git a/compiler/linker/relative_patcher.h b/compiler/linker/relative_patcher.h index 8a9f3f8364..ba374512a1 100644 --- a/compiler/linker/relative_patcher.h +++ b/compiler/linker/relative_patcher.h @@ -83,23 +83,31 @@ class RelativePatcher { } // Reserve space for thunks if needed before a method, return adjusted offset. - virtual uint32_t ReserveSpace(uint32_t offset, const CompiledMethod* compiled_method, + virtual uint32_t ReserveSpace(uint32_t offset, + const CompiledMethod* compiled_method, MethodReference method_ref) = 0; // Reserve space for thunks if needed after the last method, return adjusted offset. + // The caller may use this method to preemptively force thunk space reservation and + // then resume reservation for more methods. This is useful when there is a gap in + // the .text segment, for example when going to the next oat file for multi-image. virtual uint32_t ReserveSpaceEnd(uint32_t offset) = 0; - // Write relative call thunks if needed, return adjusted offset. + // Write relative call thunks if needed, return adjusted offset. Returns 0 on write failure. virtual uint32_t WriteThunks(OutputStream* out, uint32_t offset) = 0; // Patch method code. The input displacement is relative to the patched location, // the patcher may need to adjust it if the correct base is different. - virtual void PatchCall(std::vector* code, uint32_t literal_offset, - uint32_t patch_offset, uint32_t target_offset) = 0; + virtual void PatchCall(std::vector* code, + uint32_t literal_offset, + uint32_t patch_offset, + uint32_t target_offset) = 0; // Patch a reference to a dex cache location. - virtual void PatchDexCacheReference(std::vector* code, const LinkerPatch& patch, - uint32_t patch_offset, uint32_t target_offset) = 0; + virtual void PatchDexCacheReference(std::vector* code, + const LinkerPatch& patch, + uint32_t patch_offset, + uint32_t target_offset) = 0; protected: RelativePatcher() diff --git a/compiler/linker/relative_patcher_test.h b/compiler/linker/relative_patcher_test.h index bf8e786f64..704135a7b5 100644 --- a/compiler/linker/relative_patcher_test.h +++ b/compiler/linker/relative_patcher_test.h @@ -44,10 +44,22 @@ class RelativePatcherTest : public testing::Test { : compiler_options_(), verification_results_(&compiler_options_), inliner_map_(), - driver_(&compiler_options_, &verification_results_, &inliner_map_, - Compiler::kQuick, instruction_set, nullptr, - false, nullptr, nullptr, nullptr, 1u, - false, false, nullptr, -1, nullptr, nullptr), + driver_(&compiler_options_, + &verification_results_, + &inliner_map_, + Compiler::kQuick, + instruction_set, + /* instruction_set_features*/ nullptr, + /* boot_image */ false, + /* image_classes */ nullptr, + /* compiled_classes */ nullptr, + /* compiled_methods */ nullptr, + /* thread_count */ 1u, + /* dump_stats */ false, + /* dump_passes */ false, + /* timer */ nullptr, + /* swap_fd */ -1, + /* profile_compilation_info */ nullptr), error_msg_(), instruction_set_(instruction_set), features_(InstructionSetFeatures::FromVariant(instruction_set, variant, &error_msg_)), @@ -138,8 +150,10 @@ class RelativePatcherTest : public testing::Test { offset + patch.LiteralOffset(), target_offset); } else if (patch.Type() == kLinkerPatchDexCacheArray) { uint32_t target_offset = dex_cache_arrays_begin_ + patch.TargetDexCacheElementOffset(); - patcher_->PatchDexCacheReference(&patched_code_, patch, - offset + patch.LiteralOffset(), target_offset); + patcher_->PatchDexCacheReference(&patched_code_, + patch, + offset + patch.LiteralOffset(), + target_offset); } else { LOG(FATAL) << "Bad patch type."; } diff --git a/compiler/linker/x86/relative_patcher_x86.h b/compiler/linker/x86/relative_patcher_x86.h index 0c881f00ba..ddc244c269 100644 --- a/compiler/linker/x86/relative_patcher_x86.h +++ b/compiler/linker/x86/relative_patcher_x86.h @@ -26,8 +26,10 @@ class X86RelativePatcher FINAL : public X86BaseRelativePatcher { public: X86RelativePatcher() { } - void PatchDexCacheReference(std::vector* code, const LinkerPatch& patch, - uint32_t patch_offset, uint32_t target_offset) OVERRIDE; + void PatchDexCacheReference(std::vector* code, + const LinkerPatch& patch, + uint32_t patch_offset, + uint32_t target_offset) OVERRIDE; }; } // namespace linker diff --git a/compiler/linker/x86/relative_patcher_x86_base.cc b/compiler/linker/x86/relative_patcher_x86_base.cc index bc285a7849..bf3a648218 100644 --- a/compiler/linker/x86/relative_patcher_x86_base.cc +++ b/compiler/linker/x86/relative_patcher_x86_base.cc @@ -34,8 +34,10 @@ uint32_t X86BaseRelativePatcher::WriteThunks(OutputStream* out ATTRIBUTE_UNUSED, return offset; // No thunks added; no limit on relative call distance. } -void X86BaseRelativePatcher::PatchCall(std::vector* code, uint32_t literal_offset, - uint32_t patch_offset, uint32_t target_offset) { +void X86BaseRelativePatcher::PatchCall(std::vector* code, + uint32_t literal_offset, + uint32_t patch_offset, + uint32_t target_offset) { DCHECK_LE(literal_offset + 4u, code->size()); // Unsigned arithmetic with its well-defined overflow behavior is just fine here. uint32_t displacement = target_offset - patch_offset; diff --git a/compiler/linker/x86/relative_patcher_x86_base.h b/compiler/linker/x86/relative_patcher_x86_base.h index 9200709398..ca83a72f48 100644 --- a/compiler/linker/x86/relative_patcher_x86_base.h +++ b/compiler/linker/x86/relative_patcher_x86_base.h @@ -29,8 +29,10 @@ class X86BaseRelativePatcher : public RelativePatcher { MethodReference method_ref) OVERRIDE; uint32_t ReserveSpaceEnd(uint32_t offset) OVERRIDE; uint32_t WriteThunks(OutputStream* out, uint32_t offset) OVERRIDE; - void PatchCall(std::vector* code, uint32_t literal_offset, - uint32_t patch_offset, uint32_t target_offset) OVERRIDE; + void PatchCall(std::vector* code, + uint32_t literal_offset, + uint32_t patch_offset, + uint32_t target_offset) OVERRIDE; protected: X86BaseRelativePatcher() { } diff --git a/compiler/linker/x86_64/relative_patcher_x86_64.cc b/compiler/linker/x86_64/relative_patcher_x86_64.cc index 598f3ac4a8..e571f50d2f 100644 --- a/compiler/linker/x86_64/relative_patcher_x86_64.cc +++ b/compiler/linker/x86_64/relative_patcher_x86_64.cc @@ -23,7 +23,8 @@ namespace linker { void X86_64RelativePatcher::PatchDexCacheReference(std::vector* code, const LinkerPatch& patch, - uint32_t patch_offset, uint32_t target_offset) { + uint32_t patch_offset, + uint32_t target_offset) { DCHECK_LE(patch.LiteralOffset() + 4u, code->size()); // Unsigned arithmetic with its well-defined overflow behavior is just fine here. uint32_t displacement = target_offset - patch_offset; diff --git a/compiler/linker/x86_64/relative_patcher_x86_64.h b/compiler/linker/x86_64/relative_patcher_x86_64.h index af687b4a2f..feecb3a2ad 100644 --- a/compiler/linker/x86_64/relative_patcher_x86_64.h +++ b/compiler/linker/x86_64/relative_patcher_x86_64.h @@ -26,8 +26,10 @@ class X86_64RelativePatcher FINAL : public X86BaseRelativePatcher { public: X86_64RelativePatcher() { } - void PatchDexCacheReference(std::vector* code, const LinkerPatch& patch, - uint32_t patch_offset, uint32_t target_offset) OVERRIDE; + void PatchDexCacheReference(std::vector* code, + const LinkerPatch& patch, + uint32_t patch_offset, + uint32_t target_offset) OVERRIDE; }; } // namespace linker diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc index d3b404a3b6..21ddb804fb 100644 --- a/compiler/oat_test.cc +++ b/compiler/oat_test.cc @@ -31,6 +31,7 @@ #include "elf_writer.h" #include "elf_writer_quick.h" #include "entrypoints/quick/quick_entrypoints.h" +#include "linker/multi_oat_relative_patcher.h" #include "linker/vector_output_stream.h" #include "mirror/class-inl.h" #include "mirror/object-inl.h" @@ -111,17 +112,16 @@ class OatTest : public CommonCompilerTest { compiler_kind, insn_set, insn_features_.get(), - false, - nullptr, - nullptr, - nullptr, - 2, - true, - true, + /* boot_image */ false, + /* image_classes */ nullptr, + /* compiled_classes */ nullptr, + /* compiled_methods */ nullptr, + /* thread_count */ 2, + /* dump_stats */ true, + /* dump_passes */ true, timer_.get(), - -1, - nullptr, - nullptr)); + /* swap_fd */ -1, + /* profile_compilation_info */ nullptr)); } bool WriteElf(File* file, @@ -200,7 +200,13 @@ class OatTest : public CommonCompilerTest { ScopedObjectAccess soa(Thread::Current()); class_linker->RegisterDexFile(*dex_file, runtime->GetLinearAlloc()); } - oat_writer.PrepareLayout(compiler_driver_.get(), nullptr, dex_files); + linker::MultiOatRelativePatcher patcher(compiler_driver_->GetInstructionSet(), + instruction_set_features_.get()); + oat_writer.PrepareLayout(compiler_driver_.get(), nullptr, dex_files, &patcher); + size_t rodata_size = oat_writer.GetOatHeader().GetExecutableOffset(); + size_t text_size = oat_writer.GetSize() - rodata_size; + elf_writer->SetLoadedSectionSizes(rodata_size, text_size, oat_writer.GetBssSize()); + if (!oat_writer.WriteRodata(rodata)) { return false; } @@ -216,7 +222,6 @@ class OatTest : public CommonCompilerTest { return false; } - elf_writer->SetBssSize(oat_writer.GetBssSize()); elf_writer->WriteDynamicSection(); elf_writer->WriteDebugInfo(oat_writer.GetMethodDebugInfo()); elf_writer->WritePatchLocations(oat_writer.GetAbsolutePatchLocations()); diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc index 47dcfd56f8..c60b02a227 100644 --- a/compiler/oat_writer.cc +++ b/compiler/oat_writer.cc @@ -38,8 +38,8 @@ #include "gc/space/space.h" #include "handle_scope-inl.h" #include "image_writer.h" +#include "linker/multi_oat_relative_patcher.h" #include "linker/output_stream.h" -#include "linker/relative_patcher.h" #include "mirror/array.h" #include "mirror/class_loader.h" #include "mirror/dex_cache-inl.h" @@ -292,7 +292,8 @@ OatWriter::OatWriter(bool compiling_boot_image, TimingLogger* timings) size_oat_class_status_(0), size_oat_class_method_bitmaps_(0), size_oat_class_method_offsets_(0), - method_offset_map_() { + relative_patcher_(nullptr), + absolute_patch_locations_() { } bool OatWriter::AddDexFileSource(const char* filename, @@ -438,21 +439,21 @@ bool OatWriter::WriteAndOpenDexFiles( void OatWriter::PrepareLayout(const CompilerDriver* compiler, ImageWriter* image_writer, - const std::vector& dex_files) { + const std::vector& dex_files, + linker::MultiOatRelativePatcher* relative_patcher) { CHECK(write_state_ == WriteState::kPrepareLayout); - dex_files_ = &dex_files; - compiler_driver_ = compiler; image_writer_ = image_writer; + dex_files_ = &dex_files; + relative_patcher_ = relative_patcher; + SetMultiOatRelativePatcherAdjustment(); + if (compiling_boot_image_) { CHECK(image_writer_ != nullptr); } InstructionSet instruction_set = compiler_driver_->GetInstructionSet(); CHECK_EQ(instruction_set, oat_header_->GetInstructionSet()); - const InstructionSetFeatures* features = compiler_driver_->GetInstructionSetFeatures(); - relative_patcher_ = linker::RelativePatcher::Create(instruction_set, features, - &method_offset_map_); uint32_t offset = size_; { @@ -727,13 +728,11 @@ class OatWriter::InitCodeMethodVisitor : public OatDexMethodVisitor { // Deduplicate code arrays if we are not producing debuggable code. bool deduped = false; MethodReference method_ref(dex_file_, it.GetMemberIndex()); - auto method_lb = writer_->method_offset_map_.map.lower_bound(method_ref); if (debuggable_) { - if (method_lb != writer_->method_offset_map_.map.end() && - !writer_->method_offset_map_.map.key_comp()(method_ref, method_lb->first)) { + quick_code_offset = writer_->relative_patcher_->GetOffset(method_ref); + if (quick_code_offset != 0u) { // Duplicate methods, we want the same code for both of them so that the oat writer puts // the same code in both ArtMethods so that we do not get different oat code at runtime. - quick_code_offset = method_lb->second; deduped = true; } else { quick_code_offset = NewQuickCodeOffset(compiled_method, it, thumb_offset); @@ -750,14 +749,14 @@ class OatWriter::InitCodeMethodVisitor : public OatDexMethodVisitor { } if (code_size != 0) { - if (method_lb != writer_->method_offset_map_.map.end() && - !writer_->method_offset_map_.map.key_comp()(method_ref, method_lb->first)) { + if (writer_->relative_patcher_->GetOffset(method_ref) != 0u) { // TODO: Should this be a hard failure? LOG(WARNING) << "Multiple definitions of " << PrettyMethod(method_ref.dex_method_index, *method_ref.dex_file) - << " offsets " << method_lb->second << " " << quick_code_offset; + << " offsets " << writer_->relative_patcher_->GetOffset(method_ref) + << " " << quick_code_offset; } else { - writer_->method_offset_map_.map.PutBefore(method_lb, method_ref, quick_code_offset); + writer_->relative_patcher_->SetOffset(method_ref, quick_code_offset); } } @@ -1106,27 +1105,29 @@ class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor { patched_code_.assign(quick_code.begin(), quick_code.end()); quick_code = ArrayRef(patched_code_); for (const LinkerPatch& patch : compiled_method->GetPatches()) { + uint32_t literal_offset = patch.LiteralOffset(); if (patch.Type() == kLinkerPatchCallRelative) { // NOTE: Relative calls across oat files are not supported. uint32_t target_offset = GetTargetOffset(patch); - uint32_t literal_offset = patch.LiteralOffset(); - writer_->relative_patcher_->PatchCall(&patched_code_, literal_offset, - offset_ + literal_offset, target_offset); + writer_->relative_patcher_->PatchCall(&patched_code_, + literal_offset, + offset_ + literal_offset, + target_offset); } else if (patch.Type() == kLinkerPatchDexCacheArray) { uint32_t target_offset = GetDexCacheOffset(patch); - uint32_t literal_offset = patch.LiteralOffset(); - writer_->relative_patcher_->PatchDexCacheReference(&patched_code_, patch, + writer_->relative_patcher_->PatchDexCacheReference(&patched_code_, + patch, offset_ + literal_offset, target_offset); } else if (patch.Type() == kLinkerPatchCall) { uint32_t target_offset = GetTargetOffset(patch); - PatchCodeAddress(&patched_code_, patch.LiteralOffset(), target_offset); + PatchCodeAddress(&patched_code_, literal_offset, target_offset); } else if (patch.Type() == kLinkerPatchMethod) { ArtMethod* method = GetTargetMethod(patch); - PatchMethodAddress(&patched_code_, patch.LiteralOffset(), method); + PatchMethodAddress(&patched_code_, literal_offset, method); } else if (patch.Type() == kLinkerPatchType) { mirror::Class* type = GetTargetType(patch); - PatchObjectAddress(&patched_code_, patch.LiteralOffset(), type); + PatchObjectAddress(&patched_code_, literal_offset, type); } } } @@ -1172,16 +1173,16 @@ class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor { } uint32_t GetTargetOffset(const LinkerPatch& patch) SHARED_REQUIRES(Locks::mutator_lock_) { - auto target_it = writer_->method_offset_map_.map.find(patch.TargetMethod()); - uint32_t target_offset = - (target_it != writer_->method_offset_map_.map.end()) ? target_it->second : 0u; - // If there's no compiled code, point to the correct trampoline. + uint32_t target_offset = writer_->relative_patcher_->GetOffset(patch.TargetMethod()); + // If there's no new compiled code, either we're compiling an app and the target method + // is in the boot image, or we need to point to the correct trampoline. if (UNLIKELY(target_offset == 0)) { ArtMethod* target = GetTargetMethod(patch); DCHECK(target != nullptr); size_t size = GetInstructionSetPointerSize(writer_->compiler_driver_->GetInstructionSet()); const void* oat_code_offset = target->GetEntryPointFromQuickCompiledCodePtrSize(size); if (oat_code_offset != 0) { + DCHECK(!writer_->HasBootImage()); DCHECK(!Runtime::Current()->GetClassLinker()->IsQuickResolutionStub(oat_code_offset)); DCHECK(!Runtime::Current()->GetClassLinker()->IsQuickToInterpreterBridge(oat_code_offset)); DCHECK(!Runtime::Current()->GetClassLinker()->IsQuickGenericJniStub(oat_code_offset)); @@ -1206,11 +1207,10 @@ class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor { uint32_t GetDexCacheOffset(const LinkerPatch& patch) SHARED_REQUIRES(Locks::mutator_lock_) { if (writer_->HasBootImage()) { - auto* element = writer_->image_writer_->GetDexCacheArrayElementImageAddress( - patch.TargetDexCacheDexFile(), patch.TargetDexCacheElementOffset()); - const char* oat_filename = writer_->image_writer_->GetOatFilenameForDexCache(dex_cache_); - const uint8_t* oat_data = - writer_->image_writer_->GetOatFileBegin(oat_filename) + file_offset_; + uintptr_t element = writer_->image_writer_->GetDexCacheArrayElementImageAddress( + patch.TargetDexCacheDexFile(), patch.TargetDexCacheElementOffset()); + size_t oat_index = writer_->image_writer_->GetOatIndexForDexCache(dex_cache_); + uintptr_t oat_data = writer_->image_writer_->GetOatDataBegin(oat_index); return element - oat_data; } else { size_t start = writer_->dex_cache_arrays_offsets_.Get(patch.TargetDexCacheDexFile()); @@ -1270,9 +1270,13 @@ class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor { SHARED_REQUIRES(Locks::mutator_lock_) { uint32_t address = target_offset; if (writer_->HasBootImage()) { - const char* oat_filename = writer_->image_writer_->GetOatFilenameForDexCache(dex_cache_); - address = PointerToLowMemUInt32(writer_->image_writer_->GetOatFileBegin(oat_filename) + - writer_->oat_data_offset_ + target_offset); + size_t oat_index = writer_->image_writer_->GetOatIndexForDexCache(dex_cache_); + // TODO: Clean up offset types. + // The target_offset must be treated as signed for cross-oat patching. + const void* target = reinterpret_cast( + writer_->image_writer_->GetOatDataBegin(oat_index) + + static_cast(target_offset)); + address = PointerToLowMemUInt32(target); } DCHECK_LE(offset + 4, code->size()); uint8_t* data = &(*code)[offset]; @@ -1540,6 +1544,8 @@ bool OatWriter::WriteRodata(OutputStream* out) { bool OatWriter::WriteCode(OutputStream* out) { CHECK(write_state_ == WriteState::kWriteText); + SetMultiOatRelativePatcherAdjustment(); + const size_t file_offset = oat_data_offset_; size_t relative_offset = oat_header_->GetExecutableOffset(); DCHECK_OFFSET(); @@ -1781,7 +1787,7 @@ size_t OatWriter::WriteCodeDexFiles(OutputStream* out, return relative_offset; } -bool OatWriter::GetOatDataOffset(OutputStream* out) { +bool OatWriter::RecordOatDataOffset(OutputStream* out) { // Get the elf file offset of the oat file. const off_t raw_file_offset = out->Seek(0, kSeekCurrent); if (raw_file_offset == static_cast(-1)) { @@ -1833,7 +1839,7 @@ bool OatWriter::WriteDexFiles(OutputStream* rodata, File* file) { TimingLogger::ScopedTiming split("WriteDexFiles", timings_); // Get the elf file offset of the oat file. - if (!GetOatDataOffset(rodata)) { + if (!RecordOatDataOffset(rodata)) { return false; } @@ -2261,12 +2267,15 @@ bool OatWriter::WriteData(OutputStream* out, const void* data, size_t size) { return out->WriteFully(data, size); } -std::pair OatWriter::MethodOffsetMap::FindMethodOffset(MethodReference ref) { - auto it = map.find(ref); - if (it == map.end()) { - return std::pair(false, 0u); - } else { - return std::pair(true, it->second); +void OatWriter::SetMultiOatRelativePatcherAdjustment() { + DCHECK(dex_files_ != nullptr); + DCHECK(relative_patcher_ != nullptr); + DCHECK_NE(oat_data_offset_, 0u); + if (image_writer_ != nullptr && !dex_files_->empty()) { + // The oat data begin may not be initialized yet but the oat file offset is ready. + size_t oat_index = image_writer_->GetOatIndexForDexFile(dex_files_->front()); + size_t elf_file_offset = image_writer_->GetOatFileOffset(oat_index); + relative_patcher_->StartOatFile(elf_file_offset + oat_data_offset_); } } diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h index 5a55fc6c95..74aab4efd0 100644 --- a/compiler/oat_writer.h +++ b/compiler/oat_writer.h @@ -47,6 +47,10 @@ namespace debug { struct MethodDebugInfo; } // namespace debug +namespace linker { +class MultiOatRelativePatcher; +} // namespace linker + // OatHeader variable length with count of D OatDexFiles // // OatDexFile[0] one variable sized OatDexFile with offsets to Dex and OatClasses @@ -153,7 +157,8 @@ class OatWriter { // Prepare layout of remaining data. void PrepareLayout(const CompilerDriver* compiler, ImageWriter* image_writer, - const std::vector& dex_files); + const std::vector& dex_files, + linker::MultiOatRelativePatcher* relative_patcher); // Write the rest of .rodata section (ClassOffsets[], OatClass[], maps). bool WriteRodata(OutputStream* out); // Write the code to the .text section. @@ -187,6 +192,10 @@ class OatWriter { return bss_size_; } + size_t GetOatDataOffset() const { + return oat_data_offset_; + } + ArrayRef GetAbsolutePatchLocations() const { return ArrayRef(absolute_patch_locations_); } @@ -249,7 +258,7 @@ class OatWriter { size_t WriteCode(OutputStream* out, const size_t file_offset, size_t relative_offset); size_t WriteCodeDexFiles(OutputStream* out, const size_t file_offset, size_t relative_offset); - bool GetOatDataOffset(OutputStream* out); + bool RecordOatDataOffset(OutputStream* out); bool ReadDexFileHeader(File* file, OatDexFile* oat_dex_file); bool ValidateDexFileHeader(const uint8_t* raw_header, const char* location); bool WriteDexFiles(OutputStream* rodata, File* file); @@ -268,6 +277,7 @@ class OatWriter { const std::vector>& opened_dex_files); bool WriteCodeAlignment(OutputStream* out, uint32_t aligned_code_delta); bool WriteData(OutputStream* out, const void* data, size_t size); + void SetMultiOatRelativePatcherAdjustment(); enum class WriteState { kAddingDexFileSources, @@ -358,20 +368,12 @@ class OatWriter { uint32_t size_oat_class_method_bitmaps_; uint32_t size_oat_class_method_offsets_; - std::unique_ptr relative_patcher_; + // The helper for processing relative patches is external so that we can patch across oat files. + linker::MultiOatRelativePatcher* relative_patcher_; // The locations of absolute patches relative to the start of the executable section. dchecked_vector absolute_patch_locations_; - // Map method reference to assigned offset. - // Wrap the map in a class implementing linker::RelativePatcherTargetProvider. - class MethodOffsetMap FINAL : public linker::RelativePatcherTargetProvider { - public: - std::pair FindMethodOffset(MethodReference ref) OVERRIDE; - SafeMap map; - }; - MethodOffsetMap method_offset_map_; - DISALLOW_COPY_AND_ASSIGN(OatWriter); }; diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 541fb5a423..9427eaf3d8 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -65,6 +65,7 @@ #include "interpreter/unstarted_runtime.h" #include "jit/offline_profiling_info.h" #include "leb128.h" +#include "linker/multi_oat_relative_patcher.h" #include "mirror/class-inl.h" #include "mirror/class_loader.h" #include "mirror/object-inl.h" @@ -1408,7 +1409,7 @@ class Dex2Oat FINAL { if (opened_dex_files_map != nullptr) { opened_dex_files_maps_.push_back(std::move(opened_dex_files_map)); for (std::unique_ptr& dex_file : opened_dex_files) { - dex_file_oat_filename_map_.emplace(dex_file.get(), oat_filenames_[i]); + dex_file_oat_index_map_.emplace(dex_file.get(), i); opened_dex_files_.push_back(std::move(dex_file)); } } else { @@ -1557,13 +1558,12 @@ class Dex2Oat FINAL { IsBootImage(), image_classes_.release(), compiled_classes_.release(), - nullptr, + /* compiled_methods */ nullptr, thread_count_, dump_stats_, dump_passes_, compiler_phases_timings_.get(), swap_fd_, - &dex_file_oat_filename_map_, profile_compilation_info_.get())); driver_->SetDexFilesForOatFile(dex_files_); driver_->CompileAll(class_loader_, dex_files_, timings_); @@ -1667,7 +1667,7 @@ class Dex2Oat FINAL { IsAppImage(), image_storage_mode_, oat_filenames_, - dex_file_oat_filename_map_)); + dex_file_oat_index_map_)); // We need to prepare method offsets in the image address space for direct method patching. TimingLogger::ScopedTiming t2("dex2oat Prepare image address space", timings_); @@ -1677,21 +1677,39 @@ class Dex2Oat FINAL { } } + linker::MultiOatRelativePatcher patcher(instruction_set_, instruction_set_features_.get()); { TimingLogger::ScopedTiming t2("dex2oat Write ELF", timings_); for (size_t i = 0, size = oat_files_.size(); i != size; ++i) { - std::unique_ptr& oat_file = oat_files_[i]; std::unique_ptr& elf_writer = elf_writers_[i]; std::unique_ptr& oat_writer = oat_writers_[i]; std::vector& dex_files = dex_files_per_oat_file_[i]; - oat_writer->PrepareLayout(driver_.get(), image_writer_.get(), dex_files); + oat_writer->PrepareLayout(driver_.get(), image_writer_.get(), dex_files, &patcher); - // We need to mirror the layout of the ELF file in the compressed debug-info. - // Therefore we need to propagate the sizes of at least those sections. size_t rodata_size = oat_writer->GetOatHeader().GetExecutableOffset(); size_t text_size = oat_writer->GetSize() - rodata_size; - elf_writer->PrepareDebugInfo(rodata_size, text_size, oat_writer->GetMethodDebugInfo()); + elf_writer->SetLoadedSectionSizes(rodata_size, text_size, oat_writer->GetBssSize()); + + if (IsImage()) { + // Update oat layout. + DCHECK(image_writer_ != nullptr); + DCHECK_LT(i, oat_filenames_.size()); + image_writer_->UpdateOatFileLayout(i, + elf_writer->GetLoadedSize(), + oat_writer->GetOatDataOffset(), + oat_writer->GetSize()); + } + } + + for (size_t i = 0, size = oat_files_.size(); i != size; ++i) { + std::unique_ptr& oat_file = oat_files_[i]; + std::unique_ptr& elf_writer = elf_writers_[i]; + std::unique_ptr& oat_writer = oat_writers_[i]; + + // We need to mirror the layout of the ELF file in the compressed debug-info. + // Therefore PrepareDebugInfo() relies on the SetLoadedSectionSizes() call further above. + elf_writer->PrepareDebugInfo(oat_writer->GetMethodDebugInfo()); OutputStream*& rodata = rodata_[i]; DCHECK(rodata != nullptr); @@ -1717,7 +1735,13 @@ class Dex2Oat FINAL { return false; } - elf_writer->SetBssSize(oat_writer->GetBssSize()); + if (IsImage()) { + // Update oat header information. + DCHECK(image_writer_ != nullptr); + DCHECK_LT(i, oat_filenames_.size()); + image_writer_->UpdateOatFileHeader(i, oat_writer->GetOatHeader()); + } + elf_writer->WriteDynamicSection(); elf_writer->WriteDebugInfo(oat_writer->GetMethodDebugInfo()); elf_writer->WritePatchLocations(oat_writer->GetAbsolutePatchLocations()); @@ -1731,19 +1755,10 @@ class Dex2Oat FINAL { if (oat_files_[i] != nullptr) { if (oat_files_[i]->Flush() != 0) { PLOG(ERROR) << "Failed to flush oat file: " << oat_filenames_[i]; - oat_files_[i]->Erase(); return false; } } - if (IsImage()) { - // Update oat estimates. - DCHECK(image_writer_ != nullptr); - DCHECK_LT(i, oat_filenames_.size()); - - image_writer_->UpdateOatFile(oat_file.get(), oat_filenames_[i]); - } - VLOG(compiler) << "Oat file written successfully: " << oat_filenames_[i]; oat_writer.reset(); @@ -2226,22 +2241,21 @@ class Dex2Oat FINAL { } if (!image_writer_->Write(app_image_fd_, image_filenames_, - oat_fd_, - oat_filenames_, - oat_location_)) { + oat_filenames_)) { LOG(ERROR) << "Failure during image file creation"; return false; } // We need the OatDataBegin entries. - std::map oat_data_begins; - for (const char* oat_filename : oat_filenames_) { - oat_data_begins.emplace(oat_filename, image_writer_->GetOatDataBegin(oat_filename)); + dchecked_vector oat_data_begins; + for (size_t i = 0, size = oat_filenames_.size(); i != size; ++i) { + oat_data_begins.push_back(image_writer_->GetOatDataBegin(i)); } // Destroy ImageWriter before doing FixupElf. image_writer_.reset(); - for (const char* oat_filename : oat_filenames_) { + for (size_t i = 0, size = oat_filenames_.size(); i != size; ++i) { + const char* oat_filename = oat_filenames_[i]; // Do not fix up the ELF file if we are --compile-pic or compiling the app image if (!compiler_options_->GetCompilePic() && IsBootImage()) { std::unique_ptr oat_file(OS::OpenFileReadWrite(oat_filename)); @@ -2250,9 +2264,7 @@ class Dex2Oat FINAL { return false; } - uintptr_t oat_data_begin = oat_data_begins.find(oat_filename)->second; - - if (!ElfWriter::Fixup(oat_file.get(), oat_data_begin)) { + if (!ElfWriter::Fixup(oat_file.get(), oat_data_begins[i])) { oat_file->Erase(); LOG(ERROR) << "Failed to fixup ELF file " << oat_file->GetPath(); return false; @@ -2468,7 +2480,7 @@ class Dex2Oat FINAL { TimingLogger* timings_; std::unique_ptr compiler_phases_timings_; std::vector> dex_files_per_oat_file_; - std::unordered_map dex_file_oat_filename_map_; + std::unordered_map dex_file_oat_index_map_; // Backing storage. std::vector char_backing_storage_; diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index af08fc4e93..e30b968a96 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -145,7 +145,9 @@ class OatSymbolizer FINAL { bss->WriteNoBitsSection(oat_file_->BssSize()); } - builder_->WriteDynamicSection(elf_file->GetPath()); + builder_->PrepareDynamicSection( + elf_file->GetPath(), rodata_size, text_size, oat_file_->BssSize()); + builder_->WriteDynamicSection(); Walk(&art::OatSymbolizer::RegisterForDedup); diff --git a/runtime/safe_map.h b/runtime/safe_map.h index a8b48ee4dc..0e5b503e26 100644 --- a/runtime/safe_map.h +++ b/runtime/safe_map.h @@ -99,16 +99,16 @@ class SafeMap { } // Used to insert a new mapping at a known position for better performance. - iterator PutBefore(iterator pos, const K& k, const V& v) { + iterator PutBefore(const_iterator pos, const K& k, const V& v) { // Check that we're using the correct position and the key is not in the map. DCHECK(pos == map_.end() || map_.key_comp()(k, pos->first)); - DCHECK(pos == map_.begin() || map_.key_comp()((--iterator(pos))->first, k)); + DCHECK(pos == map_.begin() || map_.key_comp()((--const_iterator(pos))->first, k)); return map_.emplace_hint(pos, k, v); } - iterator PutBefore(iterator pos, const K& k, V&& v) { + iterator PutBefore(const_iterator pos, const K& k, V&& v) { // Check that we're using the correct position and the key is not in the map. DCHECK(pos == map_.end() || map_.key_comp()(k, pos->first)); - DCHECK(pos == map_.begin() || map_.key_comp()((--iterator(pos))->first, k)); + DCHECK(pos == map_.begin() || map_.key_comp()((--const_iterator(pos))->first, k)); return map_.emplace_hint(pos, k, std::move(v)); } -- GitLab From dad095ad2e6171e8b75378d31eae292d4fefa000 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Wed, 24 Feb 2016 12:04:23 +0000 Subject: [PATCH 021/204] Add a script to symbolize buildbot crashes. Change-Id: I6e85a2f37db84a8d06c7258de1c60eaffc78e44b --- tools/symbolize-buildbot-crashes.sh | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100755 tools/symbolize-buildbot-crashes.sh diff --git a/tools/symbolize-buildbot-crashes.sh b/tools/symbolize-buildbot-crashes.sh new file mode 100755 index 0000000000..8df82691b7 --- /dev/null +++ b/tools/symbolize-buildbot-crashes.sh @@ -0,0 +1,20 @@ +#!/bin/bash +# +# Copyright (C) 2016 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# We push art and its dependencies to '/data/local/tmp', but the 'stack' +# script expect things to be in '/'. So we just remove the +# '/data/local/tmp' prefix. +adb logcat -d | sed 's,/data/local/tmp,,g' | development/scripts/stack -- GitLab From c257da7b0fb6737f65aba426add8831e45404755 Mon Sep 17 00:00:00 2001 From: Artem Serov Date: Tue, 2 Feb 2016 13:49:43 +0000 Subject: [PATCH 022/204] ARM: Implement Reverse bits and bytes intrinsic. - IntegerReverse - LongReverse - IntegerReverseBytes - LongReverseBytes - ShortReverseBytes Change-Id: I3ec202696b245148a0237ff6e46ac3f1a3f8402a --- compiler/optimizing/intrinsics_arm.cc | 89 +++++++++++++++++++-- compiler/utils/arm/assembler_arm.h | 3 + compiler/utils/arm/assembler_arm32.cc | 44 ++++++++++ compiler/utils/arm/assembler_arm32.h | 8 ++ compiler/utils/arm/assembler_arm32_test.cc | 12 +++ compiler/utils/arm/assembler_thumb2.cc | 77 +++++++++++++++--- compiler/utils/arm/assembler_thumb2.h | 14 ++++ compiler/utils/arm/assembler_thumb2_test.cc | 24 ++++++ 8 files changed, 256 insertions(+), 15 deletions(-) diff --git a/compiler/optimizing/intrinsics_arm.cc b/compiler/optimizing/intrinsics_arm.cc index ea8669fa18..8cbdcbbcaf 100644 --- a/compiler/optimizing/intrinsics_arm.cc +++ b/compiler/optimizing/intrinsics_arm.cc @@ -1825,6 +1825,90 @@ void IntrinsicCodeGeneratorARM::VisitMathNextAfter(HInvoke* invoke) { GenFPFPToFPCall(invoke, GetAssembler(), codegen_, kQuickNextAfter); } +void IntrinsicLocationsBuilderARM::VisitIntegerReverse(HInvoke* invoke) { + CreateIntToIntLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorARM::VisitIntegerReverse(HInvoke* invoke) { + ArmAssembler* assembler = GetAssembler(); + LocationSummary* locations = invoke->GetLocations(); + + Register out = locations->Out().AsRegister(); + Register in = locations->InAt(0).AsRegister(); + + __ rbit(out, in); +} + +void IntrinsicLocationsBuilderARM::VisitLongReverse(HInvoke* invoke) { + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kNoCall, + kIntrinsified); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); +} + +void IntrinsicCodeGeneratorARM::VisitLongReverse(HInvoke* invoke) { + ArmAssembler* assembler = GetAssembler(); + LocationSummary* locations = invoke->GetLocations(); + + Register in_reg_lo = locations->InAt(0).AsRegisterPairLow(); + Register in_reg_hi = locations->InAt(0).AsRegisterPairHigh(); + Register out_reg_lo = locations->Out().AsRegisterPairLow(); + Register out_reg_hi = locations->Out().AsRegisterPairHigh(); + + __ rbit(out_reg_lo, in_reg_hi); + __ rbit(out_reg_hi, in_reg_lo); +} + +void IntrinsicLocationsBuilderARM::VisitIntegerReverseBytes(HInvoke* invoke) { + CreateIntToIntLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorARM::VisitIntegerReverseBytes(HInvoke* invoke) { + ArmAssembler* assembler = GetAssembler(); + LocationSummary* locations = invoke->GetLocations(); + + Register out = locations->Out().AsRegister(); + Register in = locations->InAt(0).AsRegister(); + + __ rev(out, in); +} + +void IntrinsicLocationsBuilderARM::VisitLongReverseBytes(HInvoke* invoke) { + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kNoCall, + kIntrinsified); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); +} + +void IntrinsicCodeGeneratorARM::VisitLongReverseBytes(HInvoke* invoke) { + ArmAssembler* assembler = GetAssembler(); + LocationSummary* locations = invoke->GetLocations(); + + Register in_reg_lo = locations->InAt(0).AsRegisterPairLow(); + Register in_reg_hi = locations->InAt(0).AsRegisterPairHigh(); + Register out_reg_lo = locations->Out().AsRegisterPairLow(); + Register out_reg_hi = locations->Out().AsRegisterPairHigh(); + + __ rev(out_reg_lo, in_reg_hi); + __ rev(out_reg_hi, in_reg_lo); +} + +void IntrinsicLocationsBuilderARM::VisitShortReverseBytes(HInvoke* invoke) { + CreateIntToIntLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorARM::VisitShortReverseBytes(HInvoke* invoke) { + ArmAssembler* assembler = GetAssembler(); + LocationSummary* locations = invoke->GetLocations(); + + Register out = locations->Out().AsRegister(); + Register in = locations->InAt(0).AsRegister(); + + __ revsh(out, in); +} + // Unimplemented intrinsics. #define UNIMPLEMENTED_INTRINSIC(Name) \ @@ -1834,12 +1918,7 @@ void IntrinsicCodeGeneratorARM::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) } UNIMPLEMENTED_INTRINSIC(IntegerBitCount) -UNIMPLEMENTED_INTRINSIC(IntegerReverse) -UNIMPLEMENTED_INTRINSIC(IntegerReverseBytes) UNIMPLEMENTED_INTRINSIC(LongBitCount) -UNIMPLEMENTED_INTRINSIC(LongReverse) -UNIMPLEMENTED_INTRINSIC(LongReverseBytes) -UNIMPLEMENTED_INTRINSIC(ShortReverseBytes) UNIMPLEMENTED_INTRINSIC(MathMinDoubleDouble) UNIMPLEMENTED_INTRINSIC(MathMinFloatFloat) UNIMPLEMENTED_INTRINSIC(MathMaxDoubleDouble) diff --git a/compiler/utils/arm/assembler_arm.h b/compiler/utils/arm/assembler_arm.h index f96376d9fe..a894565425 100644 --- a/compiler/utils/arm/assembler_arm.h +++ b/compiler/utils/arm/assembler_arm.h @@ -545,6 +545,9 @@ class ArmAssembler : public Assembler { virtual void movw(Register rd, uint16_t imm16, Condition cond = AL) = 0; virtual void movt(Register rd, uint16_t imm16, Condition cond = AL) = 0; virtual void rbit(Register rd, Register rm, Condition cond = AL) = 0; + virtual void rev(Register rd, Register rm, Condition cond = AL) = 0; + virtual void rev16(Register rd, Register rm, Condition cond = AL) = 0; + virtual void revsh(Register rd, Register rm, Condition cond = AL) = 0; // Multiply instructions. virtual void mul(Register rd, Register rn, Register rm, Condition cond = AL) = 0; diff --git a/compiler/utils/arm/assembler_arm32.cc b/compiler/utils/arm/assembler_arm32.cc index ebca25bbf9..0a227b21cd 100644 --- a/compiler/utils/arm/assembler_arm32.cc +++ b/compiler/utils/arm/assembler_arm32.cc @@ -750,6 +750,35 @@ void Arm32Assembler::movt(Register rd, uint16_t imm16, Condition cond) { } +void Arm32Assembler::EmitMiscellaneous(Condition cond, uint8_t op1, + uint8_t op2, uint32_t a_part, + uint32_t rest) { + int32_t encoding = (static_cast(cond) << kConditionShift) | + B26 | B25 | B23 | + (op1 << 20) | + (a_part << 16) | + (op2 << 5) | + B4 | + rest; + Emit(encoding); +} + + +void Arm32Assembler::EmitReverseBytes(Register rd, Register rm, Condition cond, + uint8_t op1, uint8_t op2) { + CHECK_NE(rd, kNoRegister); + CHECK_NE(rm, kNoRegister); + CHECK_NE(cond, kNoCondition); + CHECK_NE(rd, PC); + CHECK_NE(rm, PC); + + int32_t encoding = (static_cast(rd) << kRdShift) | + (0b1111 << 8) | + static_cast(rm); + EmitMiscellaneous(cond, op1, op2, 0b1111, encoding); +} + + void Arm32Assembler::rbit(Register rd, Register rm, Condition cond) { CHECK_NE(rd, kNoRegister); CHECK_NE(rm, kNoRegister); @@ -764,6 +793,21 @@ void Arm32Assembler::rbit(Register rd, Register rm, Condition cond) { } +void Arm32Assembler::rev(Register rd, Register rm, Condition cond) { + EmitReverseBytes(rd, rm, cond, 0b011, 0b001); +} + + +void Arm32Assembler::rev16(Register rd, Register rm, Condition cond) { + EmitReverseBytes(rd, rm, cond, 0b011, 0b101); +} + + +void Arm32Assembler::revsh(Register rd, Register rm, Condition cond) { + EmitReverseBytes(rd, rm, cond, 0b111, 0b101); +} + + void Arm32Assembler::EmitMulOp(Condition cond, int32_t opcode, Register rd, Register rn, Register rm, Register rs) { diff --git a/compiler/utils/arm/assembler_arm32.h b/compiler/utils/arm/assembler_arm32.h index bf332feb62..e3e05caf92 100644 --- a/compiler/utils/arm/assembler_arm32.h +++ b/compiler/utils/arm/assembler_arm32.h @@ -91,6 +91,9 @@ class Arm32Assembler FINAL : public ArmAssembler { void movw(Register rd, uint16_t imm16, Condition cond = AL) OVERRIDE; void movt(Register rd, uint16_t imm16, Condition cond = AL) OVERRIDE; void rbit(Register rd, Register rm, Condition cond = AL) OVERRIDE; + void rev(Register rd, Register rm, Condition cond = AL) OVERRIDE; + void rev16(Register rd, Register rm, Condition cond = AL) OVERRIDE; + void revsh(Register rd, Register rm, Condition cond = AL) OVERRIDE; // Multiply instructions. void mul(Register rd, Register rn, Register rm, Condition cond = AL) OVERRIDE; @@ -388,6 +391,11 @@ class Arm32Assembler FINAL : public ArmAssembler { void EmitVPushPop(uint32_t reg, int nregs, bool push, bool dbl, Condition cond); + void EmitMiscellaneous(Condition cond, uint8_t op1, uint8_t op2, + uint32_t a_part, uint32_t rest); + void EmitReverseBytes(Register rd, Register rm, Condition cond, + uint8_t op1, uint8_t op2); + void EmitBranch(Condition cond, Label* label, bool link); static int32_t EncodeBranchOffset(int offset, int32_t inst); static int DecodeBranchOffset(int32_t inst); diff --git a/compiler/utils/arm/assembler_arm32_test.cc b/compiler/utils/arm/assembler_arm32_test.cc index 43805966a9..e570e22fca 100644 --- a/compiler/utils/arm/assembler_arm32_test.cc +++ b/compiler/utils/arm/assembler_arm32_test.cc @@ -887,4 +887,16 @@ TEST_F(AssemblerArm32Test, rbit) { T3Helper(&arm::Arm32Assembler::rbit, true, "rbit{cond} {reg1}, {reg2}", "rbit"); } +TEST_F(AssemblerArm32Test, rev) { + T3Helper(&arm::Arm32Assembler::rev, true, "rev{cond} {reg1}, {reg2}", "rev"); +} + +TEST_F(AssemblerArm32Test, rev16) { + T3Helper(&arm::Arm32Assembler::rev16, true, "rev16{cond} {reg1}, {reg2}", "rev16"); +} + +TEST_F(AssemblerArm32Test, revsh) { + T3Helper(&arm::Arm32Assembler::revsh, true, "revsh{cond} {reg1}, {reg2}", "revsh"); +} + } // namespace art diff --git a/compiler/utils/arm/assembler_thumb2.cc b/compiler/utils/arm/assembler_thumb2.cc index 52023a67ee..15298b390b 100644 --- a/compiler/utils/arm/assembler_thumb2.cc +++ b/compiler/utils/arm/assembler_thumb2.cc @@ -2569,20 +2569,36 @@ void Thumb2Assembler::EmitBranch(Condition cond, Label* label, bool link, bool x } +void Thumb2Assembler::Emit32Miscellaneous(uint8_t op1, + uint8_t op2, + uint32_t rest_encoding) { + int32_t encoding = B31 | B30 | B29 | B28 | B27 | B25 | B23 | + op1 << 20 | + 0xf << 12 | + B7 | + op2 << 4 | + rest_encoding; + Emit32(encoding); +} + + +void Thumb2Assembler::Emit16Miscellaneous(uint32_t rest_encoding) { + int16_t encoding = B15 | B13 | B12 | + rest_encoding; + Emit16(encoding); +} + void Thumb2Assembler::clz(Register rd, Register rm, Condition cond) { CHECK_NE(rd, kNoRegister); CHECK_NE(rm, kNoRegister); CheckCondition(cond); CHECK_NE(rd, PC); CHECK_NE(rm, PC); - int32_t encoding = B31 | B30 | B29 | B28 | B27 | - B25 | B23 | B21 | B20 | + int32_t encoding = static_cast(rm) << 16 | - 0xf << 12 | static_cast(rd) << 8 | - B7 | static_cast(rm); - Emit32(encoding); + Emit32Miscellaneous(0b11, 0b00, encoding); } @@ -2630,14 +2646,55 @@ void Thumb2Assembler::rbit(Register rd, Register rm, Condition cond) { CHECK_NE(rm, PC); CHECK_NE(rd, SP); CHECK_NE(rm, SP); - int32_t encoding = B31 | B30 | B29 | B28 | B27 | - B25 | B23 | B20 | + int32_t encoding = static_cast(rm) << 16 | - 0xf << 12 | static_cast(rd) << 8 | - B7 | B5 | static_cast(rm); - Emit32(encoding); + + Emit32Miscellaneous(0b01, 0b10, encoding); +} + + +void Thumb2Assembler::EmitReverseBytes(Register rd, Register rm, + uint32_t op) { + CHECK_NE(rd, kNoRegister); + CHECK_NE(rm, kNoRegister); + CHECK_NE(rd, PC); + CHECK_NE(rm, PC); + CHECK_NE(rd, SP); + CHECK_NE(rm, SP); + + if (!IsHighRegister(rd) && !IsHighRegister(rm) && !force_32bit_) { + uint16_t t1_op = B11 | B9 | (op << 6); + int16_t encoding = t1_op | + static_cast(rm) << 3 | + static_cast(rd); + Emit16Miscellaneous(encoding); + } else { + int32_t encoding = + static_cast(rm) << 16 | + static_cast(rd) << 8 | + static_cast(rm); + Emit32Miscellaneous(0b01, op, encoding); + } +} + + +void Thumb2Assembler::rev(Register rd, Register rm, Condition cond) { + CheckCondition(cond); + EmitReverseBytes(rd, rm, 0b00); +} + + +void Thumb2Assembler::rev16(Register rd, Register rm, Condition cond) { + CheckCondition(cond); + EmitReverseBytes(rd, rm, 0b01); +} + + +void Thumb2Assembler::revsh(Register rd, Register rm, Condition cond) { + CheckCondition(cond); + EmitReverseBytes(rd, rm, 0b11); } diff --git a/compiler/utils/arm/assembler_thumb2.h b/compiler/utils/arm/assembler_thumb2.h index bf07b2dbf8..6b61acafac 100644 --- a/compiler/utils/arm/assembler_thumb2.h +++ b/compiler/utils/arm/assembler_thumb2.h @@ -117,6 +117,9 @@ class Thumb2Assembler FINAL : public ArmAssembler { void movw(Register rd, uint16_t imm16, Condition cond = AL) OVERRIDE; void movt(Register rd, uint16_t imm16, Condition cond = AL) OVERRIDE; void rbit(Register rd, Register rm, Condition cond = AL) OVERRIDE; + void rev(Register rd, Register rm, Condition cond = AL) OVERRIDE; + void rev16(Register rd, Register rm, Condition cond = AL) OVERRIDE; + void revsh(Register rd, Register rm, Condition cond = AL) OVERRIDE; // Multiply instructions. void mul(Register rd, Register rn, Register rm, Condition cond = AL) OVERRIDE; @@ -644,6 +647,17 @@ class Thumb2Assembler FINAL : public ArmAssembler { Register rd, const ShifterOperand& so); + // Emit a single 32 bit miscellaneous instruction. + void Emit32Miscellaneous(uint8_t op1, + uint8_t op2, + uint32_t rest_encoding); + + // Emit reverse byte instructions: rev, rev16, revsh. + void EmitReverseBytes(Register rd, Register rm, uint32_t op); + + // Emit a single 16 bit miscellaneous instruction. + void Emit16Miscellaneous(uint32_t rest_encoding); + // Must the instruction be 32 bits or can it possibly be encoded // in 16 bits? bool Is32BitDataProcessing(Condition cond, diff --git a/compiler/utils/arm/assembler_thumb2_test.cc b/compiler/utils/arm/assembler_thumb2_test.cc index 7b32b0fd26..650b08900b 100644 --- a/compiler/utils/arm/assembler_thumb2_test.cc +++ b/compiler/utils/arm/assembler_thumb2_test.cc @@ -1331,4 +1331,28 @@ TEST_F(AssemblerThumb2Test, rbit) { DriverStr(expected, "rbit"); } +TEST_F(AssemblerThumb2Test, rev) { + __ rev(arm::R1, arm::R0); + + const char* expected = "rev r1, r0\n"; + + DriverStr(expected, "rev"); +} + +TEST_F(AssemblerThumb2Test, rev16) { + __ rev16(arm::R1, arm::R0); + + const char* expected = "rev16 r1, r0\n"; + + DriverStr(expected, "rev16"); +} + +TEST_F(AssemblerThumb2Test, revsh) { + __ revsh(arm::R1, arm::R0); + + const char* expected = "revsh r1, r0\n"; + + DriverStr(expected, "revsh"); +} + } // namespace art -- GitLab From 970e19164326d93947441f9f0c2c754225dfb6e8 Mon Sep 17 00:00:00 2001 From: Bilyan Borisov Date: Wed, 10 Feb 2016 11:51:17 +0000 Subject: [PATCH 023/204] Made art/runtime/arch/stub_test.cc compile with -O2 again. The test file art/runtime/arch/stub_test.cc wasn't compiling with -O2 as the optimisations interacted with -fstack-protector-strong. The aarch64 _asm_ block in the Invoke3WithReferrerAndHidden function was clobbering all possible registers, and clang requires at least one register to be live across an _asm_ block to do the checking. The fix was to remove a callee-saved register, x20, from the clobber list of the aarch64 asm block of Invoke3WithReferrerAndHidden. The block was also modified to save and restore x20 to ensure that it won't be clobbered by the stubs invoked by the blr instruction. Also added some comments above the clobber list. Change-Id: I03597fd2d14cf2d6e32edf02835aee2eb68bab17 --- build/Android.gtest.mk | 3 --- runtime/arch/stub_test.cc | 21 +++++++++++++++------ 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index 852dcf18eb..b3832ac3b6 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -578,9 +578,6 @@ define define-art-gtest LOCAL_MODULE_PATH_64 := $$(ART_TARGET_NATIVETEST_OUT)/$$(ART_TARGET_ARCH_64) LOCAL_MULTILIB := both LOCAL_CLANG_CFLAGS += -Wno-used-but-marked-unused -Wno-deprecated -Wno-missing-noreturn # gtest issue - # clang fails to compile art/runtime/arch/stub_test.cc for arm64 without -O1 - # b/26275713 - LOCAL_CLANG_CFLAGS_arm64 += -O1 include $$(BUILD_EXECUTABLE) library_path := 2nd_library_path := diff --git a/runtime/arch/stub_test.cc b/runtime/arch/stub_test.cc index d4b873e441..d5807e27b5 100644 --- a/runtime/arch/stub_test.cc +++ b/runtime/arch/stub_test.cc @@ -175,12 +175,16 @@ class StubTest : public CommonRuntimeTest { #elif defined(__aarch64__) __asm__ __volatile__( // Spill x0-x7 which we say we don't clobber. May contain args. - "sub sp, sp, #64\n\t" - ".cfi_adjust_cfa_offset 64\n\t" + "sub sp, sp, #80\n\t" + ".cfi_adjust_cfa_offset 80\n\t" "stp x0, x1, [sp]\n\t" "stp x2, x3, [sp, #16]\n\t" "stp x4, x5, [sp, #32]\n\t" "stp x6, x7, [sp, #48]\n\t" + // To be extra defensive, store x20. We do this because some of the stubs might make a + // transition into the runtime via the blr instruction below and *not* save x20. + "str x20, [sp, #64]\n\t" + // 8 byte buffer "sub sp, sp, #16\n\t" // Reserve stack space, 16B aligned ".cfi_adjust_cfa_offset 16\n\t" @@ -279,8 +283,9 @@ class StubTest : public CommonRuntimeTest { "ldp x2, x3, [sp, #16]\n\t" "ldp x4, x5, [sp, #32]\n\t" "ldp x6, x7, [sp, #48]\n\t" - "add sp, sp, #64\n\t" // Free stack space, now sp as on entry - ".cfi_adjust_cfa_offset -64\n\t" + "ldr x20, [sp, #64]\n\t" + "add sp, sp, #80\n\t" // Free stack space, now sp as on entry + ".cfi_adjust_cfa_offset -80\n\t" "str x9, %[fpr_result]\n\t" // Store the FPR comparison result "mov %[result], x8\n\t" // Store the call result @@ -298,13 +303,17 @@ class StubTest : public CommonRuntimeTest { // Use the result from r0 : [arg0] "0"(arg0), [arg1] "r"(arg1), [arg2] "r"(arg2), [code] "r"(code), [self] "r"(self), [referrer] "r"(referrer), [hidden] "r"(hidden), [fpr_result] "m" (fpr_result) - : "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "x19", "x20", + // Leave one register unclobbered, which is needed for compiling with + // -fstack-protector-strong. According to AAPCS64 registers x9-x15 are caller-saved, + // which means we should unclobber one of the callee-saved registers that are unused. + // Here we use x20. + : "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "x19", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28", "x30", "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "d8", "d9", "d10", "d11", "d12", "d13", "d14", "d15", "d16", "d17", "d18", "d19", "d20", "d21", "d22", "d23", "d24", "d25", "d26", "d27", "d28", "d29", "d30", "d31", - "memory"); // clobber. + "memory"); #elif defined(__mips__) && !defined(__LP64__) __asm__ __volatile__ ( // Spill a0-a3 and t0-t7 which we say we don't clobber. May contain args. -- GitLab From e53bd8160ad2892f33849108d3b1099992a311fd Mon Sep 17 00:00:00 2001 From: Roland Levillain Date: Wed, 24 Feb 2016 14:54:18 +0000 Subject: [PATCH 024/204] Remove unreachable code paths in constant folding. Change-Id: I7ffb361711c87f6b1b98d172d2cfdf9b2ba65607 --- compiler/optimizing/nodes.cc | 12 ++++----- compiler/optimizing/nodes.h | 52 +++++------------------------------- 2 files changed, 13 insertions(+), 51 deletions(-) diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index f36dc6e2fd..f9acb089ee 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -1178,19 +1178,19 @@ HConstant* HUnaryOperation::TryStaticEvaluation() const { } HConstant* HBinaryOperation::TryStaticEvaluation() const { - if (GetLeft()->IsIntConstant()) { - if (GetRight()->IsIntConstant()) { - return Evaluate(GetLeft()->AsIntConstant(), GetRight()->AsIntConstant()); - } else if (GetRight()->IsLongConstant()) { - return Evaluate(GetLeft()->AsIntConstant(), GetRight()->AsLongConstant()); - } + if (GetLeft()->IsIntConstant() && GetRight()->IsIntConstant()) { + return Evaluate(GetLeft()->AsIntConstant(), GetRight()->AsIntConstant()); } else if (GetLeft()->IsLongConstant()) { if (GetRight()->IsIntConstant()) { + // The binop(long, int) case is only valid for shifts and rotations. + DCHECK(IsShl() || IsShr() || IsUShr() || IsRor()) << DebugName(); return Evaluate(GetLeft()->AsLongConstant(), GetRight()->AsIntConstant()); } else if (GetRight()->IsLongConstant()) { return Evaluate(GetLeft()->AsLongConstant(), GetRight()->AsLongConstant()); } } else if (GetLeft()->IsNullConstant() && GetRight()->IsNullConstant()) { + // The binop(null, null) case is only valid for equal and not-equal conditions. + DCHECK(IsEqual() || IsNotEqual()) << DebugName(); return Evaluate(GetLeft()->AsNullConstant(), GetRight()->AsNullConstant()); } else if (kEnableFloatingPointStaticEvaluation) { if (GetLeft()->IsFloatConstant() && GetRight()->IsFloatConstant()) { diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 399afabea6..b355883a72 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -2821,20 +2821,15 @@ class HBinaryOperation : public HExpression<2> { // Apply this operation to `x` and `y`. virtual HConstant* Evaluate(HNullConstant* x ATTRIBUTE_UNUSED, HNullConstant* y ATTRIBUTE_UNUSED) const { - VLOG(compiler) << DebugName() << " is not defined for the (null, null) case."; - return nullptr; + LOG(FATAL) << DebugName() << " is not defined for the (null, null) case."; + UNREACHABLE(); } virtual HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const = 0; virtual HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const = 0; - virtual HConstant* Evaluate(HIntConstant* x ATTRIBUTE_UNUSED, - HLongConstant* y ATTRIBUTE_UNUSED) const { - VLOG(compiler) << DebugName() << " is not defined for the (int, long) case."; - return nullptr; - } virtual HConstant* Evaluate(HLongConstant* x ATTRIBUTE_UNUSED, HIntConstant* y ATTRIBUTE_UNUSED) const { - VLOG(compiler) << DebugName() << " is not defined for the (long, int) case."; - return nullptr; + LOG(FATAL) << DebugName() << " is not defined for the (long, int) case."; + UNREACHABLE(); } virtual HConstant* Evaluate(HFloatConstant* x, HFloatConstant* y) const = 0; virtual HConstant* Evaluate(HDoubleConstant* x, HDoubleConstant* y) const = 0; @@ -4305,8 +4300,6 @@ class HShl : public HBinaryOperation { return GetBlock()->GetGraph()->GetIntConstant( Compute(x->GetValue(), y->GetValue(), kMaxIntShiftValue), GetDexPc()); } - // There is no `Evaluate(HIntConstant* x, HLongConstant* y)`, as this - // case is handled as `x << static_cast(y)`. HConstant* Evaluate(HLongConstant* x, HIntConstant* y) const OVERRIDE { return GetBlock()->GetGraph()->GetLongConstant( Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue), GetDexPc()); @@ -4351,8 +4344,6 @@ class HShr : public HBinaryOperation { return GetBlock()->GetGraph()->GetIntConstant( Compute(x->GetValue(), y->GetValue(), kMaxIntShiftValue), GetDexPc()); } - // There is no `Evaluate(HIntConstant* x, HLongConstant* y)`, as this - // case is handled as `x >> static_cast(y)`. HConstant* Evaluate(HLongConstant* x, HIntConstant* y) const OVERRIDE { return GetBlock()->GetGraph()->GetLongConstant( Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue), GetDexPc()); @@ -4398,8 +4389,6 @@ class HUShr : public HBinaryOperation { return GetBlock()->GetGraph()->GetIntConstant( Compute(x->GetValue(), y->GetValue(), kMaxIntShiftValue), GetDexPc()); } - // There is no `Evaluate(HIntConstant* x, HLongConstant* y)`, as this - // case is handled as `x >>> static_cast(y)`. HConstant* Evaluate(HLongConstant* x, HIntConstant* y) const OVERRIDE { return GetBlock()->GetGraph()->GetLongConstant( Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue), GetDexPc()); @@ -4435,21 +4424,12 @@ class HAnd : public HBinaryOperation { bool IsCommutative() const OVERRIDE { return true; } - template - auto Compute(T x, U y) const -> decltype(x & y) { return x & y; } + template T Compute(T x, T y) const { return x & y; } HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE { return GetBlock()->GetGraph()->GetIntConstant( Compute(x->GetValue(), y->GetValue()), GetDexPc()); } - HConstant* Evaluate(HIntConstant* x, HLongConstant* y) const OVERRIDE { - return GetBlock()->GetGraph()->GetLongConstant( - Compute(x->GetValue(), y->GetValue()), GetDexPc()); - } - HConstant* Evaluate(HLongConstant* x, HIntConstant* y) const OVERRIDE { - return GetBlock()->GetGraph()->GetLongConstant( - Compute(x->GetValue(), y->GetValue()), GetDexPc()); - } HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE { return GetBlock()->GetGraph()->GetLongConstant( Compute(x->GetValue(), y->GetValue()), GetDexPc()); @@ -4481,21 +4461,12 @@ class HOr : public HBinaryOperation { bool IsCommutative() const OVERRIDE { return true; } - template - auto Compute(T x, U y) const -> decltype(x | y) { return x | y; } + template T Compute(T x, T y) const { return x | y; } HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE { return GetBlock()->GetGraph()->GetIntConstant( Compute(x->GetValue(), y->GetValue()), GetDexPc()); } - HConstant* Evaluate(HIntConstant* x, HLongConstant* y) const OVERRIDE { - return GetBlock()->GetGraph()->GetLongConstant( - Compute(x->GetValue(), y->GetValue()), GetDexPc()); - } - HConstant* Evaluate(HLongConstant* x, HIntConstant* y) const OVERRIDE { - return GetBlock()->GetGraph()->GetLongConstant( - Compute(x->GetValue(), y->GetValue()), GetDexPc()); - } HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE { return GetBlock()->GetGraph()->GetLongConstant( Compute(x->GetValue(), y->GetValue()), GetDexPc()); @@ -4527,21 +4498,12 @@ class HXor : public HBinaryOperation { bool IsCommutative() const OVERRIDE { return true; } - template - auto Compute(T x, U y) const -> decltype(x ^ y) { return x ^ y; } + template T Compute(T x, T y) const { return x ^ y; } HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE { return GetBlock()->GetGraph()->GetIntConstant( Compute(x->GetValue(), y->GetValue()), GetDexPc()); } - HConstant* Evaluate(HIntConstant* x, HLongConstant* y) const OVERRIDE { - return GetBlock()->GetGraph()->GetLongConstant( - Compute(x->GetValue(), y->GetValue()), GetDexPc()); - } - HConstant* Evaluate(HLongConstant* x, HIntConstant* y) const OVERRIDE { - return GetBlock()->GetGraph()->GetLongConstant( - Compute(x->GetValue(), y->GetValue()), GetDexPc()); - } HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE { return GetBlock()->GetGraph()->GetLongConstant( Compute(x->GetValue(), y->GetValue()), GetDexPc()); -- GitLab From fb7fc7b1e11523b6e4f444f0f54de0b49cd0c4e1 Mon Sep 17 00:00:00 2001 From: Roland Levillain Date: Wed, 24 Feb 2016 15:41:20 +0000 Subject: [PATCH 025/204] Fix indentation of copyright headers in some ART run-tests. Reported by Kevin Brodsky . Change-Id: Iec3d69aa33d3b6a31f1298607e0ee6bc4cb6d9c0 --- .../src/Main.java | 28 +++++++++---------- .../551-checker-shifter-operand/src/Main.java | 28 +++++++++---------- .../src/Main.java | 28 +++++++++---------- 3 files changed, 42 insertions(+), 42 deletions(-) diff --git a/test/550-checker-multiply-accumulate/src/Main.java b/test/550-checker-multiply-accumulate/src/Main.java index 2d0688d57e..87a89bd9dc 100644 --- a/test/550-checker-multiply-accumulate/src/Main.java +++ b/test/550-checker-multiply-accumulate/src/Main.java @@ -1,18 +1,18 @@ /* -* Copyright (C) 2015 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ public class Main { diff --git a/test/551-checker-shifter-operand/src/Main.java b/test/551-checker-shifter-operand/src/Main.java index 8d73d69db9..9c86154bd4 100644 --- a/test/551-checker-shifter-operand/src/Main.java +++ b/test/551-checker-shifter-operand/src/Main.java @@ -1,18 +1,18 @@ /* -* Copyright (C) 2015 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ public class Main { diff --git a/test/565-checker-doublenegbitwise/src/Main.java b/test/565-checker-doublenegbitwise/src/Main.java index 41af97b3b7..2d70e111aa 100644 --- a/test/565-checker-doublenegbitwise/src/Main.java +++ b/test/565-checker-doublenegbitwise/src/Main.java @@ -1,18 +1,18 @@ /* -* Copyright (C) 2016 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ public class Main { -- GitLab From b0d220835f2ddc03510f17fcc4fb4c64b426a0f5 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Wed, 24 Feb 2016 17:18:25 +0000 Subject: [PATCH 026/204] Forgot to initialize JIT memory use fields. Change-Id: I82ae97adffd8e633de048f044cdbdb953b2f9763 --- runtime/jit/jit_code_cache.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index 6348ddad58..478b164597 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -126,6 +126,8 @@ JitCodeCache::JitCodeCache(MemMap* code_map, has_done_full_collection_(false), last_update_time_ns_(0), garbage_collect_code_(garbage_collect_code), + used_memory_for_data_(0), + used_memory_for_code_(0), number_of_compilations_(0) { DCHECK_GE(max_capacity, initial_code_capacity + initial_data_capacity); -- GitLab From 6d3d9bbf077aa9f1b9d11416a8f623241c72260b Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Wed, 24 Feb 2016 17:52:08 +0000 Subject: [PATCH 027/204] Don't make this script fail a buildbot run. Change-Id: I000d69c1a33eca6ca98f7fd1096bc9af7d22b4e8 --- tools/symbolize-buildbot-crashes.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/symbolize-buildbot-crashes.sh b/tools/symbolize-buildbot-crashes.sh index 8df82691b7..8dc4e27885 100755 --- a/tools/symbolize-buildbot-crashes.sh +++ b/tools/symbolize-buildbot-crashes.sh @@ -18,3 +18,6 @@ # script expect things to be in '/'. So we just remove the # '/data/local/tmp' prefix. adb logcat -d | sed 's,/data/local/tmp,,g' | development/scripts/stack + +# Always return 0 to avoid having the buildbot complain about wrong stacks. +exit 0 -- GitLab From e530539656fd7c95c914885b4f67c404daea2ea2 Mon Sep 17 00:00:00 2001 From: Aart Bik Date: Wed, 24 Feb 2016 10:49:37 -0800 Subject: [PATCH 028/204] Make sure constantIndexing2 is optimized with DEOPT. Rationale: This particular method is also used in foo() to see if assertIsManaged() holds after going out of bounds. During my last rewrite (which added a driver to make sure more code is actually executed), I changed the method to show a non-deopt case. However, that would miss this test. I moved the non-deopt case into a new test. Change-Id: Iaf3e77e33a82ff6cd300753f9e5211529353401c --- test/449-checker-bce/src/Main.java | 74 ++++++++++++++++++++++-------- 1 file changed, 54 insertions(+), 20 deletions(-) diff --git a/test/449-checker-bce/src/Main.java b/test/449-checker-bce/src/Main.java index 32bbc5b61d..66e1d92cc2 100644 --- a/test/449-checker-bce/src/Main.java +++ b/test/449-checker-bce/src/Main.java @@ -137,20 +137,16 @@ public class Main { /// CHECK: ArraySet /// CHECK: BoundsCheck /// CHECK: ArraySet - /// CHECK: BoundsCheck - /// CHECK: ArraySet /// CHECK-START: void Main.$opt$noinline$constantIndexing2(int[]) BCE (after) - /// CHECK-NOT: Deoptimize - /// CHECK: BoundsCheck - /// CHECK: ArraySet - /// CHECK: BoundsCheck + /// CHECK: Deoptimize + /// CHECK-NOT: BoundsCheck /// CHECK: ArraySet - /// CHECK: BoundsCheck + /// CHECK-NOT: BoundsCheck /// CHECK: ArraySet - /// CHECK: BoundsCheck + /// CHECK-NOT: BoundsCheck /// CHECK: ArraySet - /// CHECK: BoundsCheck + /// CHECK-NOT: BoundsCheck /// CHECK: ArraySet static void $opt$noinline$constantIndexing2(int[] array) { @@ -158,8 +154,7 @@ public class Main { array[2] = 1; array[3] = 1; array[4] = 1; - array[-1] = 1; // prevents the whole opt on [-1:4] - if (array[1] == 1) { + if (array[1] != 1) { throw new Error(""); } } @@ -173,8 +168,41 @@ public class Main { /// CHECK: ArraySet /// CHECK: BoundsCheck /// CHECK: ArraySet + /// CHECK: BoundsCheck + /// CHECK: ArraySet /// CHECK-START: void Main.constantIndexing2b(int[]) BCE (after) + /// CHECK-NOT: Deoptimize + /// CHECK: BoundsCheck + /// CHECK: ArraySet + /// CHECK: BoundsCheck + /// CHECK: ArraySet + /// CHECK: BoundsCheck + /// CHECK: ArraySet + /// CHECK: BoundsCheck + /// CHECK: ArraySet + /// CHECK: BoundsCheck + /// CHECK: ArraySet + + static void constantIndexing2b(int[] array) { + array[0] = 6; + array[1] = 6; + array[2] = 6; + array[3] = 6; + array[-1] = 1; // prevents the whole opt on [-1:4] + } + + /// CHECK-START: void Main.constantIndexing2c(int[]) BCE (before) + /// CHECK: BoundsCheck + /// CHECK: ArraySet + /// CHECK: BoundsCheck + /// CHECK: ArraySet + /// CHECK: BoundsCheck + /// CHECK: ArraySet + /// CHECK: BoundsCheck + /// CHECK: ArraySet + + /// CHECK-START: void Main.constantIndexing2c(int[]) BCE (after) /// CHECK: Deoptimize /// CHECK-NOT: BoundsCheck /// CHECK: ArraySet @@ -185,7 +213,7 @@ public class Main { /// CHECK-NOT: BoundsCheck /// CHECK: ArraySet - static void constantIndexing2b(int[] array) { + static void constantIndexing2c(int[] array) { array[0] = 7; array[1] = 7; array[2] = 7; @@ -440,31 +468,37 @@ public class Main { System.out.println("constant indices 1 failed!"); } + $opt$noinline$constantIndexing2(a6); + if (a6[0] != 0 || a6[1] != 1 || a6[2] != 1 || + a6[3] != 1 || a6[4] != 1 || a6[5] != 11) { + System.out.println("constant indices 2 failed!"); + } + caught = false; try { - $opt$noinline$constantIndexing2(a6); + constantIndexing2b(a6); } catch (ArrayIndexOutOfBoundsException e) { caught = true; } - if (!caught || a6[0] != 0 || a6[1] != 1 || a6[2] != 1 || - a6[3] != 1 || a6[4] != 1 || a6[5] != 11) { - System.out.println("constant indices 2 failed!"); + if (!caught || a6[0] != 6 || a6[1] != 6 || a6[2] != 6 || + a6[3] != 6 || a6[4] != 1 || a6[5] != 11) { + System.out.println("constant indices 2b failed!"); } caught = false; try { - constantIndexing2b(a1); + constantIndexing2c(a1); } catch (ArrayIndexOutOfBoundsException e) { caught = true; } if (!caught || a1[0] != 7) { - System.out.println("constant indices 2b failed!"); + System.out.println("constant indices 2c failed!"); } - constantIndexing2b(a6); + constantIndexing2c(a6); if (a6[0] != 7 || a6[1] != 7 || a6[2] != 7 || a6[3] != 7 || a6[4] != 1 || a6[5] != 11) { - System.out.println("constant indices 2b failed!"); + System.out.println("constant indices 2c failed!"); } int[] b4 = new int[4]; -- GitLab From 83723aedac536fd8a3cd6e1662dbd6260e576194 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Wed, 24 Feb 2016 10:09:23 -0800 Subject: [PATCH 029/204] Add MapAnonymous handling for null error_str We use MapAnonymous with null error_str for app image loading when we want to fail quickly. Also avoid doing CheckNonOverlapping in CheckMapRequest if error_msg is null. The result from CheckNonOverlapping is unused and CheckNonOverlapping is slow since it creates a backtrace map. Bug: 22858531 Bug: 26746779 Change-Id: I8605ec0b9d9ae554a4b74f2606fa7dd81ade9021 --- runtime/mem_map.cc | 34 ++++++++++++++++++++-------------- runtime/mem_map_test.cc | 13 +++++++++++++ 2 files changed, 33 insertions(+), 14 deletions(-) diff --git a/runtime/mem_map.cc b/runtime/mem_map.cc index c908b3920a..11156c6229 100644 --- a/runtime/mem_map.cc +++ b/runtime/mem_map.cc @@ -230,17 +230,16 @@ static bool CheckMapRequest(uint8_t* expected_ptr, void* actual_ptr, size_t byte PLOG(WARNING) << StringPrintf("munmap(%p, %zd) failed", actual_ptr, byte_count); } - // We call this here so that we can try and generate a full error - // message with the overlapping mapping. There's no guarantee that - // that there will be an overlap though, since - // - The kernel is not *required* to honor expected_ptr unless MAP_FIXED is - // true, even if there is no overlap - // - There might have been an overlap at the point of mmap, but the - // overlapping region has since been unmapped. - std::string error_detail; - CheckNonOverlapping(expected, limit, &error_detail); - if (error_msg != nullptr) { + // We call this here so that we can try and generate a full error + // message with the overlapping mapping. There's no guarantee that + // that there will be an overlap though, since + // - The kernel is not *required* to honor expected_ptr unless MAP_FIXED is + // true, even if there is no overlap + // - There might have been an overlap at the point of mmap, but the + // overlapping region has since been unmapped. + std::string error_detail; + CheckNonOverlapping(expected, limit, &error_detail); std::ostringstream os; os << StringPrintf("Failed to mmap at expected address, mapped at " "0x%08" PRIxPTR " instead of 0x%08" PRIxPTR, @@ -338,11 +337,18 @@ MemMap* MemMap::MapAnonymous(const char* name, saved_errno = errno; if (actual == MAP_FAILED) { - PrintFileToLog("/proc/self/maps", LogSeverity::WARNING); + if (error_msg != nullptr) { + PrintFileToLog("/proc/self/maps", LogSeverity::WARNING); - *error_msg = StringPrintf("Failed anonymous mmap(%p, %zd, 0x%x, 0x%x, %d, 0): %s. See process " - "maps in the log.", expected_ptr, page_aligned_byte_count, prot, - flags, fd.get(), strerror(saved_errno)); + *error_msg = StringPrintf("Failed anonymous mmap(%p, %zd, 0x%x, 0x%x, %d, 0): %s. " + "See process maps in the log.", + expected_ptr, + page_aligned_byte_count, + prot, + flags, + fd.get(), + strerror(saved_errno)); + } return nullptr; } std::ostringstream check_map_request_error_msg; diff --git a/runtime/mem_map_test.cc b/runtime/mem_map_test.cc index 81c855e736..e703b78cfa 100644 --- a/runtime/mem_map_test.cc +++ b/runtime/mem_map_test.cc @@ -164,6 +164,19 @@ TEST_F(MemMapTest, MapAnonymousEmpty) { ASSERT_TRUE(error_msg.empty()); } +TEST_F(MemMapTest, MapAnonymousFailNullError) { + CommonInit(); + // Test that we don't crash with a null error_str when mapping at an invalid location. + std::unique_ptr map(MemMap::MapAnonymous("MapAnonymousInvalid", + reinterpret_cast(kPageSize), + 0x20000, + PROT_READ | PROT_WRITE, + false, + false, + nullptr)); + ASSERT_EQ(nullptr, map.get()); +} + #ifdef __LP64__ TEST_F(MemMapTest, MapAnonymousEmpty32bit) { CommonInit(); -- GitLab From 97412c92afb3f6630c4f0eafe6d6161862bfb4c1 Mon Sep 17 00:00:00 2001 From: Aart Bik Date: Fri, 19 Feb 2016 20:14:38 -0800 Subject: [PATCH 030/204] Use range analysis for better trip count analysis Rationale: Marking more loops as always-taken avoids generating unnecessary new top tests while marking more loops are non-infinite enables more optimizations. This CL helps with these improvements. Also, some more code is shared between induction and range analysis and a bug with refining ranges has been fixed. Bug: 27151190 Change-Id: Iecc0d7f32ae4779ee5424cda9dcc20816220935e --- compiler/optimizing/induction_var_analysis.cc | 98 +++-- compiler/optimizing/induction_var_analysis.h | 4 +- compiler/optimizing/induction_var_range.cc | 359 ++++++++++-------- compiler/optimizing/induction_var_range.h | 25 +- .../optimizing/induction_var_range_test.cc | 101 ++++- test/530-checker-loops/src/Main.java | 230 ++++++++++- 6 files changed, 586 insertions(+), 231 deletions(-) diff --git a/compiler/optimizing/induction_var_analysis.cc b/compiler/optimizing/induction_var_analysis.cc index 37f2d79536..a1e1cde9df 100644 --- a/compiler/optimizing/induction_var_analysis.cc +++ b/compiler/optimizing/induction_var_analysis.cc @@ -379,7 +379,7 @@ HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferShl(Inducti Primitive::Type type) { // Transfer over a shift left: treat shift by restricted constant as equivalent multiplication. int64_t value = -1; - if (a != nullptr && IsIntAndGet(b, &value)) { + if (a != nullptr && IsExact(b, &value)) { // Obtain the constant needed for the multiplication. This yields an existing instruction // if the constants is already there. Otherwise, this has a side effect on the HIR. // The restriction on the shift factor avoids generating a negative constant @@ -546,9 +546,10 @@ void HInductionVarAnalysis::VisitCondition(HLoopInformation* loop, // Analyze condition with induction at left-hand-side (e.g. i < U). InductionInfo* lower_expr = a->op_b; InductionInfo* upper_expr = b; - InductionInfo* stride = a->op_a; + InductionInfo* stride_expr = a->op_a; + // Constant stride? int64_t stride_value = 0; - if (!IsIntAndGet(stride, &stride_value)) { + if (!IsExact(stride_expr, &stride_value)) { return; } // Rewrite condition i != U into i < U or i > U if end condition is reached exactly. @@ -561,7 +562,7 @@ void HInductionVarAnalysis::VisitCondition(HLoopInformation* loop, // stride < 0, either i > U or i >= U if ((stride_value > 0 && (cmp == kCondLT || cmp == kCondLE)) || (stride_value < 0 && (cmp == kCondGT || cmp == kCondGE))) { - VisitTripCount(loop, lower_expr, upper_expr, stride, stride_value, type, cmp); + VisitTripCount(loop, lower_expr, upper_expr, stride_expr, stride_value, type, cmp); } } } @@ -569,7 +570,7 @@ void HInductionVarAnalysis::VisitCondition(HLoopInformation* loop, void HInductionVarAnalysis::VisitTripCount(HLoopInformation* loop, InductionInfo* lower_expr, InductionInfo* upper_expr, - InductionInfo* stride, + InductionInfo* stride_expr, int64_t stride_value, Primitive::Type type, IfCondition cmp) { @@ -612,9 +613,10 @@ void HInductionVarAnalysis::VisitTripCount(HLoopInformation* loop, trip_count = CreateInvariantOp(kAdd, trip_count, CreateConstant(1, type)); } // Compensate for stride. - trip_count = CreateInvariantOp(kAdd, trip_count, stride); + trip_count = CreateInvariantOp(kAdd, trip_count, stride_expr); } - trip_count = CreateInvariantOp(kDiv, CreateInvariantOp(kSub, trip_count, lower_expr), stride); + trip_count = CreateInvariantOp( + kDiv, CreateInvariantOp(kSub, trip_count, lower_expr), stride_expr); // Assign the trip-count expression to the loop control. Clients that use the information // should be aware that the expression is only valid under the conditions listed above. InductionOp tcKind = kTripCountInBodyUnsafe; // needs both tests @@ -644,14 +646,25 @@ bool HInductionVarAnalysis::IsTaken(InductionInfo* lower_expr, IfCondition cmp) { int64_t lower_value; int64_t upper_value; - if (IsIntAndGet(lower_expr, &lower_value) && IsIntAndGet(upper_expr, &upper_value)) { - switch (cmp) { - case kCondLT: return lower_value < upper_value; - case kCondLE: return lower_value <= upper_value; - case kCondGT: return lower_value > upper_value; - case kCondGE: return lower_value >= upper_value; - default: LOG(FATAL) << "CONDITION UNREACHABLE"; - } + switch (cmp) { + case kCondLT: + return IsAtMost(lower_expr, &lower_value) + && IsAtLeast(upper_expr, &upper_value) + && lower_value < upper_value; + case kCondLE: + return IsAtMost(lower_expr, &lower_value) + && IsAtLeast(upper_expr, &upper_value) + && lower_value <= upper_value; + case kCondGT: + return IsAtLeast(lower_expr, &lower_value) + && IsAtMost(upper_expr, &upper_value) + && lower_value > upper_value; + case kCondGE: + return IsAtLeast(lower_expr, &lower_value) + && IsAtMost(upper_expr, &upper_value) + && lower_value >= upper_value; + default: + LOG(FATAL) << "CONDITION UNREACHABLE"; } return false; // not certain, may be untaken } @@ -660,25 +673,23 @@ bool HInductionVarAnalysis::IsFinite(InductionInfo* upper_expr, int64_t stride_value, Primitive::Type type, IfCondition cmp) { - const int64_t min = type == Primitive::kPrimInt - ? std::numeric_limits::min() - : std::numeric_limits::min(); - const int64_t max = type == Primitive::kPrimInt - ? std::numeric_limits::max() - : std::numeric_limits::max(); + const int64_t min = type == Primitive::kPrimInt ? std::numeric_limits::min() + : std::numeric_limits::min(); + const int64_t max = type == Primitive::kPrimInt ? std::numeric_limits::max() + : std::numeric_limits::max(); // Some rules under which it is certain at compile-time that the loop is finite. int64_t value; switch (cmp) { case kCondLT: return stride_value == 1 || - (IsIntAndGet(upper_expr, &value) && value <= (max - stride_value + 1)); + (IsAtMost(upper_expr, &value) && value <= (max - stride_value + 1)); case kCondLE: - return (IsIntAndGet(upper_expr, &value) && value <= (max - stride_value)); + return (IsAtMost(upper_expr, &value) && value <= (max - stride_value)); case kCondGT: return stride_value == -1 || - (IsIntAndGet(upper_expr, &value) && value >= (min - stride_value - 1)); + (IsAtLeast(upper_expr, &value) && value >= (min - stride_value - 1)); case kCondGE: - return (IsIntAndGet(upper_expr, &value) && value >= (min - stride_value)); + return (IsAtLeast(upper_expr, &value) && value >= (min - stride_value)); default: LOG(FATAL) << "CONDITION UNREACHABLE"; } @@ -733,7 +744,7 @@ HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::CreateSimplifiedInv // More exhaustive simplifications are done by later phases once induction nodes are // translated back into HIR code (e.g. by loop optimizations or BCE). int64_t value = -1; - if (IsIntAndGet(a, &value)) { + if (IsExact(a, &value)) { if (value == 0) { // Simplify 0 + b = b, 0 * b = 0. if (op == kAdd) { @@ -750,7 +761,7 @@ HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::CreateSimplifiedInv } } } - if (IsIntAndGet(b, &value)) { + if (IsExact(b, &value)) { if (value == 0) { // Simplify a + 0 = a, a - 0 = a, a * 0 = 0, -0 = 0. if (op == kAdd || op == kSub) { @@ -784,29 +795,16 @@ HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::CreateSimplifiedInv return new (graph_->GetArena()) InductionInfo(kInvariant, op, a, b, nullptr); } -bool HInductionVarAnalysis::IsIntAndGet(InductionInfo* info, int64_t* value) { - if (info != nullptr && info->induction_class == kInvariant) { - // A direct constant fetch. - if (info->operation == kFetch) { - DCHECK(info->fetch); - if (info->fetch->IsIntConstant()) { - *value = info->fetch->AsIntConstant()->GetValue(); - return true; - } else if (info->fetch->IsLongConstant()) { - *value = info->fetch->AsLongConstant()->GetValue(); - return true; - } - } - // Use range analysis to resolve compound values. - InductionVarRange range(this); - int32_t min_val = 0; - int32_t max_val = 0; - if (range.IsConstantRange(info, &min_val, &max_val) && min_val == max_val) { - *value = min_val; - return true; - } - } - return false; +bool HInductionVarAnalysis::IsExact(InductionInfo* info, int64_t* value) { + return InductionVarRange(this).IsConstant(info, InductionVarRange::kExact, value); +} + +bool HInductionVarAnalysis::IsAtMost(InductionInfo* info, int64_t* value) { + return InductionVarRange(this).IsConstant(info, InductionVarRange::kAtMost, value); +} + +bool HInductionVarAnalysis::IsAtLeast(InductionInfo* info, int64_t* value) { + return InductionVarRange(this).IsConstant(info, InductionVarRange::kAtLeast, value); } bool HInductionVarAnalysis::InductionEqual(InductionInfo* info1, diff --git a/compiler/optimizing/induction_var_analysis.h b/compiler/optimizing/induction_var_analysis.h index 84d5d82568..94d2646aec 100644 --- a/compiler/optimizing/induction_var_analysis.h +++ b/compiler/optimizing/induction_var_analysis.h @@ -189,7 +189,9 @@ class HInductionVarAnalysis : public HOptimization { InductionInfo* CreateSimplifiedInvariant(InductionOp op, InductionInfo* a, InductionInfo* b); // Constants. - bool IsIntAndGet(InductionInfo* info, int64_t* value); + bool IsExact(InductionInfo* info, /*out*/ int64_t* value); + bool IsAtMost(InductionInfo* info, /*out*/ int64_t* value); + bool IsAtLeast(InductionInfo* info, /*out*/ int64_t* value); // Helpers. static bool InductionEqual(InductionInfo* info1, InductionInfo* info2); diff --git a/compiler/optimizing/induction_var_range.cc b/compiler/optimizing/induction_var_range.cc index 9566c29adf..b162696a42 100644 --- a/compiler/optimizing/induction_var_range.cc +++ b/compiler/optimizing/induction_var_range.cc @@ -45,17 +45,14 @@ static bool IsSafeDiv(int32_t c1, int32_t c2) { return c2 != 0 && CanLongValueFitIntoInt(static_cast(c1) / static_cast(c2)); } -/** Returns true for 32/64-bit integral constant. */ -static bool IsIntAndGet(HInstruction* instruction, int32_t* value) { +/** Returns true for 32/64-bit constant instruction. */ +static bool IsIntAndGet(HInstruction* instruction, int64_t* value) { if (instruction->IsIntConstant()) { *value = instruction->AsIntConstant()->GetValue(); return true; } else if (instruction->IsLongConstant()) { - const int64_t c = instruction->AsLongConstant()->GetValue(); - if (CanLongValueFitIntoInt(c)) { - *value = static_cast(c); - return true; - } + *value = instruction->AsLongConstant()->GetValue(); + return true; } return false; } @@ -65,8 +62,9 @@ static bool IsIntAndGet(HInstruction* instruction, int32_t* value) { * because length >= 0 is true. This makes it more likely the bound is useful to clients. */ static InductionVarRange::Value SimplifyMax(InductionVarRange::Value v) { - int32_t value; - if (v.a_constant > 1 && + int64_t value; + if (v.is_known && + v.a_constant > 1 && v.instruction->IsDiv() && v.instruction->InputAt(0)->IsArrayLength() && IsIntAndGet(v.instruction->InputAt(1), &value) && v.a_constant == value) { @@ -75,6 +73,16 @@ static InductionVarRange::Value SimplifyMax(InductionVarRange::Value v) { return v; } +/** Helper method to test for a constant value. */ +static bool IsConstantValue(InductionVarRange::Value v) { + return v.is_known && v.a_constant == 0; +} + +/** Helper method to test for same constant value. */ +static bool IsSameConstantValue(InductionVarRange::Value v1, InductionVarRange::Value v2) { + return IsConstantValue(v1) && IsConstantValue(v2) && v1.b_constant == v2.b_constant; +} + /** Helper method to insert an instruction. */ static HInstruction* Insert(HBasicBlock* block, HInstruction* instruction) { DCHECK(block != nullptr); @@ -99,29 +107,45 @@ bool InductionVarRange::GetInductionRange(HInstruction* context, /*out*/Value* max_val, /*out*/bool* needs_finite_test) { HLoopInformation* loop = context->GetBlock()->GetLoopInformation(); // closest enveloping loop - if (loop != nullptr) { - // Set up loop information. - HBasicBlock* header = loop->GetHeader(); - bool in_body = context->GetBlock() != header; - HInductionVarAnalysis::InductionInfo* info = - induction_analysis_->LookupInfo(loop, instruction); - HInductionVarAnalysis::InductionInfo* trip = - induction_analysis_->LookupInfo(loop, header->GetLastInstruction()); - // Find range. - *min_val = GetVal(info, trip, in_body, /* is_min */ true); - *max_val = SimplifyMax(GetVal(info, trip, in_body, /* is_min */ false)); - *needs_finite_test = NeedsTripCount(info) && IsUnsafeTripCount(trip); - return true; + if (loop == nullptr) { + return false; // no loop + } + HInductionVarAnalysis::InductionInfo* info = induction_analysis_->LookupInfo(loop, instruction); + if (info == nullptr) { + return false; // no induction information } - return false; // Nothing known + // Set up loop information. + HBasicBlock* header = loop->GetHeader(); + bool in_body = context->GetBlock() != header; + HInductionVarAnalysis::InductionInfo* trip = + induction_analysis_->LookupInfo(loop, header->GetLastInstruction()); + // Find range. + *min_val = GetVal(info, trip, in_body, /* is_min */ true); + *max_val = SimplifyMax(GetVal(info, trip, in_body, /* is_min */ false)); + *needs_finite_test = NeedsTripCount(info) && IsUnsafeTripCount(trip); + return true; } -bool InductionVarRange::RefineOuter(/*in-out*/Value* min_val, /*in-out*/Value* max_val) const { - Value v1 = RefineOuter(*min_val, /* is_min */ true); - Value v2 = RefineOuter(*max_val, /* is_min */ false); - if (v1.instruction != min_val->instruction || v2.instruction != max_val->instruction) { - *min_val = v1; - *max_val = v2; +bool InductionVarRange::RefineOuter(/*in-out*/ Value* min_val, + /*in-out*/ Value* max_val) const { + Value v1_min = RefineOuter(*min_val, /* is_min */ true); + Value v2_max = RefineOuter(*max_val, /* is_min */ false); + // The refined range is safe if both sides refine the same instruction. Otherwise, since two + // different ranges are combined, the new refined range is safe to pass back to the client if + // the extremes of the computed ranges ensure no arithmetic wrap-around anomalies occur. + if (min_val->instruction != max_val->instruction) { + Value v1_max = RefineOuter(*min_val, /* is_min */ false); + Value v2_min = RefineOuter(*max_val, /* is_min */ true); + if (!IsConstantValue(v1_max) || + !IsConstantValue(v2_min) || + v1_max.b_constant > v2_min.b_constant) { + return false; + } + } + // Did something change? + if (v1_min.instruction != min_val->instruction || v2_max.instruction != max_val->instruction) { + *min_val = v1_min; + *max_val = v2_max; return true; } return false; @@ -164,6 +188,38 @@ void InductionVarRange::GenerateTakenTest(HInstruction* context, // Private class methods. // +bool InductionVarRange::IsConstant(HInductionVarAnalysis::InductionInfo* info, + ConstantRequest request, + /*out*/ int64_t *value) const { + if (info != nullptr) { + // A direct 32-bit or 64-bit constant fetch. This immediately satisfies + // any of the three requests (kExact, kAtMost, and KAtLeast). + if (info->induction_class == HInductionVarAnalysis::kInvariant && + info->operation == HInductionVarAnalysis::kFetch) { + if (IsIntAndGet(info->fetch, value)) { + return true; + } + } + // Try range analysis while traversing outward on loops. + bool in_body = true; // no known trip count + Value v_min = GetVal(info, nullptr, in_body, /* is_min */ true); + Value v_max = GetVal(info, nullptr, in_body, /* is_min */ false); + do { + // Make sure *both* extremes are known to avoid arithmetic wrap-around anomalies. + if (IsConstantValue(v_min) && IsConstantValue(v_max) && v_min.b_constant <= v_max.b_constant) { + if ((request == kExact && v_min.b_constant == v_max.b_constant) || request == kAtMost) { + *value = v_max.b_constant; + return true; + } else if (request == kAtLeast) { + *value = v_min.b_constant; + return true; + } + } + } while (RefineOuter(&v_min, &v_max)); + } + return false; +} + bool InductionVarRange::NeedsTripCount(HInductionVarAnalysis::InductionInfo* info) const { if (info != nullptr) { if (info->induction_class == HInductionVarAnalysis::kLinear) { @@ -206,12 +262,10 @@ InductionVarRange::Value InductionVarRange::GetLinear(HInductionVarAnalysis::Ind if (trip != nullptr) { HInductionVarAnalysis::InductionInfo* trip_expr = trip->op_a; if (trip_expr->operation == HInductionVarAnalysis::kSub) { - int32_t min_value = 0; - int32_t stride_value = 0; - if (IsConstantRange(info->op_a, &min_value, &stride_value) && min_value == stride_value) { + int64_t stride_value = 0; + if (IsConstant(info->op_a, kExact, &stride_value)) { if (!is_min && stride_value == 1) { - // Test original trip's negative operand (trip_expr->op_b) against - // the offset of the linear induction. + // Test original trip's negative operand (trip_expr->op_b) against offset of induction. if (HInductionVarAnalysis::InductionEqual(trip_expr->op_b, info->op_b)) { // Analyze cancelled trip with just the positive operand (trip_expr->op_a). HInductionVarAnalysis::InductionInfo cancelled_trip( @@ -219,8 +273,7 @@ InductionVarRange::Value InductionVarRange::GetLinear(HInductionVarAnalysis::Ind return GetVal(&cancelled_trip, trip, in_body, is_min); } } else if (is_min && stride_value == -1) { - // Test original trip's positive operand (trip_expr->op_a) against - // the offset of the linear induction. + // Test original trip's positive operand (trip_expr->op_a) against offset of induction. if (HInductionVarAnalysis::InductionEqual(trip_expr->op_a, info->op_b)) { // Analyze cancelled trip with just the negative operand (trip_expr->op_b). HInductionVarAnalysis::InductionInfo neg( @@ -248,14 +301,16 @@ InductionVarRange::Value InductionVarRange::GetFetch(HInstruction* instruction, bool is_min) const { // Detect constants and chase the fetch a bit deeper into the HIR tree, so that it becomes // more likely range analysis will compare the same instructions as terminal nodes. - int32_t value; - if (IsIntAndGet(instruction, &value)) { - return Value(value); + int64_t value; + if (IsIntAndGet(instruction, &value) && CanLongValueFitIntoInt(value)) { + return Value(static_cast(value)); } else if (instruction->IsAdd()) { - if (IsIntAndGet(instruction->InputAt(0), &value)) { - return AddValue(Value(value), GetFetch(instruction->InputAt(1), trip, in_body, is_min)); - } else if (IsIntAndGet(instruction->InputAt(1), &value)) { - return AddValue(GetFetch(instruction->InputAt(0), trip, in_body, is_min), Value(value)); + if (IsIntAndGet(instruction->InputAt(0), &value) && CanLongValueFitIntoInt(value)) { + return AddValue(Value(static_cast(value)), + GetFetch(instruction->InputAt(1), trip, in_body, is_min)); + } else if (IsIntAndGet(instruction->InputAt(1), &value) && CanLongValueFitIntoInt(value)) { + return AddValue(GetFetch(instruction->InputAt(0), trip, in_body, is_min), + Value(static_cast(value))); } } else if (instruction->IsArrayLength() && instruction->InputAt(0)->IsNewArray()) { return GetFetch(instruction->InputAt(0)->InputAt(0), trip, in_body, is_min); @@ -331,29 +386,30 @@ InductionVarRange::Value InductionVarRange::GetMul(HInductionVarAnalysis::Induct Value v1_max = GetVal(info1, trip, in_body, /* is_min */ false); Value v2_min = GetVal(info2, trip, in_body, /* is_min */ true); Value v2_max = GetVal(info2, trip, in_body, /* is_min */ false); - // Try to refine certain failure. - if (v1_min.a_constant && v1_max.a_constant) { - v1_min = RefineOuter(v1_min, /* is_min */ true); - v1_max = RefineOuter(v1_max, /* is_min */ false); - } - // Positive or negative range? - if (v1_min.is_known && v1_min.a_constant == 0 && v1_min.b_constant >= 0) { - // Positive range vs. positive or negative range. - if (v2_min.is_known && v2_min.a_constant == 0 && v2_min.b_constant >= 0) { - return is_min ? MulValue(v1_min, v2_min) - : MulValue(v1_max, v2_max); - } else if (v2_max.is_known && v2_max.a_constant == 0 && v2_max.b_constant <= 0) { - return is_min ? MulValue(v1_max, v2_min) - : MulValue(v1_min, v2_max); + // Try to refine first operand. + if (!IsConstantValue(v1_min) && !IsConstantValue(v1_max)) { + RefineOuter(&v1_min, &v1_max); + } + // Constant times range. + if (IsSameConstantValue(v1_min, v1_max)) { + return MulRangeAndConstant(v2_min, v2_max, v1_min, is_min); + } else if (IsSameConstantValue(v2_min, v2_max)) { + return MulRangeAndConstant(v1_min, v1_max, v2_min, is_min); + } + // Positive range vs. positive or negative range. + if (IsConstantValue(v1_min) && v1_min.b_constant >= 0) { + if (IsConstantValue(v2_min) && v2_min.b_constant >= 0) { + return is_min ? MulValue(v1_min, v2_min) : MulValue(v1_max, v2_max); + } else if (IsConstantValue(v2_max) && v2_max.b_constant <= 0) { + return is_min ? MulValue(v1_max, v2_min) : MulValue(v1_min, v2_max); } - } else if (v1_max.is_known && v1_max.a_constant == 0 && v1_max.b_constant <= 0) { - // Negative range vs. positive or negative range. - if (v2_min.is_known && v2_min.a_constant == 0 && v2_min.b_constant >= 0) { - return is_min ? MulValue(v1_min, v2_max) - : MulValue(v1_max, v2_min); - } else if (v2_max.is_known && v2_max.a_constant == 0 && v2_max.b_constant <= 0) { - return is_min ? MulValue(v1_max, v2_max) - : MulValue(v1_min, v2_min); + } + // Negative range vs. positive or negative range. + if (IsConstantValue(v1_max) && v1_max.b_constant <= 0) { + if (IsConstantValue(v2_min) && v2_min.b_constant >= 0) { + return is_min ? MulValue(v1_min, v2_max) : MulValue(v1_max, v2_min); + } else if (IsConstantValue(v2_max) && v2_max.b_constant <= 0) { + return is_min ? MulValue(v1_max, v2_max) : MulValue(v1_min, v2_min); } } return Value(); @@ -368,43 +424,41 @@ InductionVarRange::Value InductionVarRange::GetDiv(HInductionVarAnalysis::Induct Value v1_max = GetVal(info1, trip, in_body, /* is_min */ false); Value v2_min = GetVal(info2, trip, in_body, /* is_min */ true); Value v2_max = GetVal(info2, trip, in_body, /* is_min */ false); - // Positive or negative range? - if (v1_min.is_known && v1_min.a_constant == 0 && v1_min.b_constant >= 0) { - // Positive range vs. positive or negative range. - if (v2_min.is_known && v2_min.a_constant == 0 && v2_min.b_constant >= 0) { - return is_min ? DivValue(v1_min, v2_max) - : DivValue(v1_max, v2_min); - } else if (v2_max.is_known && v2_max.a_constant == 0 && v2_max.b_constant <= 0) { - return is_min ? DivValue(v1_max, v2_max) - : DivValue(v1_min, v2_min); + // Range divided by constant. + if (IsSameConstantValue(v2_min, v2_max)) { + return DivRangeAndConstant(v1_min, v1_max, v2_min, is_min); + } + // Positive range vs. positive or negative range. + if (IsConstantValue(v1_min) && v1_min.b_constant >= 0) { + if (IsConstantValue(v2_min) && v2_min.b_constant >= 0) { + return is_min ? DivValue(v1_min, v2_max) : DivValue(v1_max, v2_min); + } else if (IsConstantValue(v2_max) && v2_max.b_constant <= 0) { + return is_min ? DivValue(v1_max, v2_max) : DivValue(v1_min, v2_min); } - } else if (v1_max.is_known && v1_max.a_constant == 0 && v1_max.b_constant <= 0) { - // Negative range vs. positive or negative range. - if (v2_min.is_known && v2_min.a_constant == 0 && v2_min.b_constant >= 0) { - return is_min ? DivValue(v1_min, v2_min) - : DivValue(v1_max, v2_max); - } else if (v2_max.is_known && v2_max.a_constant == 0 && v2_max.b_constant <= 0) { - return is_min ? DivValue(v1_max, v2_min) - : DivValue(v1_min, v2_max); + } + // Negative range vs. positive or negative range. + if (IsConstantValue(v1_max) && v1_max.b_constant <= 0) { + if (IsConstantValue(v2_min) && v2_min.b_constant >= 0) { + return is_min ? DivValue(v1_min, v2_min) : DivValue(v1_max, v2_max); + } else if (IsConstantValue(v2_max) && v2_max.b_constant <= 0) { + return is_min ? DivValue(v1_max, v2_min) : DivValue(v1_min, v2_max); } } return Value(); } -bool InductionVarRange::IsConstantRange(HInductionVarAnalysis::InductionInfo* info, - int32_t *min_value, - int32_t *max_value) const { - bool in_body = true; // no known trip count - Value v_min = GetVal(info, nullptr, in_body, /* is_min */ true); - Value v_max = GetVal(info, nullptr, in_body, /* is_min */ false); - do { - if (v_min.is_known && v_min.a_constant == 0 && v_max.is_known && v_max.a_constant == 0) { - *min_value = v_min.b_constant; - *max_value = v_max.b_constant; - return true; - } - } while (RefineOuter(&v_min, &v_max)); - return false; +InductionVarRange::Value InductionVarRange::MulRangeAndConstant(Value v_min, + Value v_max, + Value c, + bool is_min) const { + return is_min == (c.b_constant >= 0) ? MulValue(v_min, c) : MulValue(v_max, c); +} + +InductionVarRange::Value InductionVarRange::DivRangeAndConstant(Value v_min, + Value v_max, + Value c, + bool is_min) const { + return is_min == (c.b_constant >= 0) ? DivValue(v_min, c) : DivValue(v_max, c); } InductionVarRange::Value InductionVarRange::AddValue(Value v1, Value v2) const { @@ -471,22 +525,25 @@ InductionVarRange::Value InductionVarRange::MergeVal(Value v1, Value v2, bool is } InductionVarRange::Value InductionVarRange::RefineOuter(Value v, bool is_min) const { - if (v.instruction != nullptr) { - HLoopInformation* loop = - v.instruction->GetBlock()->GetLoopInformation(); // closest enveloping loop - if (loop != nullptr) { - // Set up loop information. - bool in_body = true; // use is always in body of outer loop - HInductionVarAnalysis::InductionInfo* info = - induction_analysis_->LookupInfo(loop, v.instruction); - HInductionVarAnalysis::InductionInfo* trip = - induction_analysis_->LookupInfo(loop, loop->GetHeader()->GetLastInstruction()); - // Try to refine "a x instruction + b" with outer loop range information on instruction. - return AddValue(MulValue(Value(v.a_constant), GetVal(info, trip, in_body, is_min)), - Value(v.b_constant)); - } + if (v.instruction == nullptr) { + return v; // nothing to refine } - return v; + HLoopInformation* loop = + v.instruction->GetBlock()->GetLoopInformation(); // closest enveloping loop + if (loop == nullptr) { + return v; // no loop + } + HInductionVarAnalysis::InductionInfo* info = induction_analysis_->LookupInfo(loop, v.instruction); + if (info == nullptr) { + return v; // no induction information + } + // Set up loop information. + HBasicBlock* header = loop->GetHeader(); + bool in_body = true; // inner always in more outer + HInductionVarAnalysis::InductionInfo* trip = + induction_analysis_->LookupInfo(loop, header->GetLastInstruction()); + // Try to refine "a x instruction + b" with outer loop range information on instruction. + return AddValue(MulValue(Value(v.a_constant), GetVal(info, trip, in_body, is_min)), Value(v.b_constant)); } bool InductionVarRange::GenerateCode(HInstruction* context, @@ -499,44 +556,45 @@ bool InductionVarRange::GenerateCode(HInstruction* context, /*out*/bool* needs_finite_test, /*out*/bool* needs_taken_test) const { HLoopInformation* loop = context->GetBlock()->GetLoopInformation(); // closest enveloping loop - if (loop != nullptr) { - // Set up loop information. - HBasicBlock* header = loop->GetHeader(); - bool in_body = context->GetBlock() != header; - HInductionVarAnalysis::InductionInfo* info = - induction_analysis_->LookupInfo(loop, instruction); - if (info == nullptr) { - return false; // nothing to analyze - } - HInductionVarAnalysis::InductionInfo* trip = - induction_analysis_->LookupInfo(loop, header->GetLastInstruction()); - // Determine what tests are needed. A finite test is needed if the evaluation code uses the - // trip-count and the loop maybe unsafe (because in such cases, the index could "overshoot" - // the computed range). A taken test is needed for any unknown trip-count, even if evaluation - // code does not use the trip-count explicitly (since there could be an implicit relation - // between e.g. an invariant subscript and a not-taken condition). - *needs_finite_test = NeedsTripCount(info) && IsUnsafeTripCount(trip); - *needs_taken_test = IsBodyTripCount(trip); - // Code generation for taken test: generate the code when requested or otherwise analyze - // if code generation is feasible when taken test is needed. - if (taken_test != nullptr) { - return GenerateCode( - trip->op_b, nullptr, graph, block, taken_test, in_body, /* is_min */ false); - } else if (*needs_taken_test) { - if (!GenerateCode( - trip->op_b, nullptr, nullptr, nullptr, nullptr, in_body, /* is_min */ false)) { - return false; - } + if (loop == nullptr) { + return false; // no loop + } + HInductionVarAnalysis::InductionInfo* info = induction_analysis_->LookupInfo(loop, instruction); + if (info == nullptr) { + return false; // no induction information + } + // Set up loop information. + HBasicBlock* header = loop->GetHeader(); + bool in_body = context->GetBlock() != header; + HInductionVarAnalysis::InductionInfo* trip = + induction_analysis_->LookupInfo(loop, header->GetLastInstruction()); + if (trip == nullptr) { + return false; // codegen relies on trip count + } + // Determine what tests are needed. A finite test is needed if the evaluation code uses the + // trip-count and the loop maybe unsafe (because in such cases, the index could "overshoot" + // the computed range). A taken test is needed for any unknown trip-count, even if evaluation + // code does not use the trip-count explicitly (since there could be an implicit relation + // between e.g. an invariant subscript and a not-taken condition). + *needs_finite_test = NeedsTripCount(info) && IsUnsafeTripCount(trip); + *needs_taken_test = IsBodyTripCount(trip); + // Code generation for taken test: generate the code when requested or otherwise analyze + // if code generation is feasible when taken test is needed. + if (taken_test != nullptr) { + return GenerateCode(trip->op_b, nullptr, graph, block, taken_test, in_body, /* is_min */ false); + } else if (*needs_taken_test) { + if (!GenerateCode( + trip->op_b, nullptr, nullptr, nullptr, nullptr, in_body, /* is_min */ false)) { + return false; } - // Code generation for lower and upper. - return - // Success on lower if invariant (not set), or code can be generated. - ((info->induction_class == HInductionVarAnalysis::kInvariant) || - GenerateCode(info, trip, graph, block, lower, in_body, /* is_min */ true)) && - // And success on upper. - GenerateCode(info, trip, graph, block, upper, in_body, /* is_min */ false); } - return false; + // Code generation for lower and upper. + return + // Success on lower if invariant (not set), or code can be generated. + ((info->induction_class == HInductionVarAnalysis::kInvariant) || + GenerateCode(info, trip, graph, block, lower, in_body, /* is_min */ true)) && + // And success on upper. + GenerateCode(info, trip, graph, block, upper, in_body, /* is_min */ false); } bool InductionVarRange::GenerateCode(HInductionVarAnalysis::InductionInfo* info, @@ -639,9 +697,8 @@ bool InductionVarRange::GenerateCode(HInductionVarAnalysis::InductionInfo* info, case HInductionVarAnalysis::kLinear: { // Linear induction a * i + b, for normalized 0 <= i < TC. Restrict to unit stride only // to avoid arithmetic wrap-around situations that are hard to guard against. - int32_t min_value = 0; - int32_t stride_value = 0; - if (IsConstantRange(info->op_a, &min_value, &stride_value) && min_value == stride_value) { + int64_t stride_value = 0; + if (IsConstant(info->op_a, kExact, &stride_value)) { if (stride_value == 1 || stride_value == -1) { const bool is_min_a = stride_value == 1 ? is_min : !is_min; if (GenerateCode(trip, trip, graph, block, &opa, in_body, is_min_a) && @@ -666,7 +723,7 @@ bool InductionVarRange::GenerateCode(HInductionVarAnalysis::InductionInfo* info, // Wrap-around and periodic inductions are restricted to constants only, so that extreme // values are easy to test at runtime without complications of arithmetic wrap-around. Value extreme = GetVal(info, trip, in_body, is_min); - if (extreme.is_known && extreme.a_constant == 0) { + if (IsConstantValue(extreme)) { if (graph != nullptr) { *result = graph->GetIntConstant(extreme.b_constant); } diff --git a/compiler/optimizing/induction_var_range.h b/compiler/optimizing/induction_var_range.h index 3cb7b4bfd5..0af41560ff 100644 --- a/compiler/optimizing/induction_var_range.h +++ b/compiler/optimizing/induction_var_range.h @@ -69,7 +69,8 @@ class InductionVarRange { /*out*/ bool* needs_finite_test); /** Refines the values with induction of next outer loop. Returns true on change. */ - bool RefineOuter(/*in-out*/Value* min_val, /*in-out*/Value* max_val) const; + bool RefineOuter(/*in-out*/ Value* min_val, + /*in-out*/ Value* max_val) const; /** * Returns true if range analysis is able to generate code for the lower and upper @@ -116,6 +117,23 @@ class InductionVarRange { /*out*/ HInstruction** taken_test); private: + /* + * Enum used in IsConstant() request. + */ + enum ConstantRequest { + kExact, + kAtMost, + kAtLeast + }; + + /** + * Returns true if exact or upper/lower bound on the given induction + * information is known as a 64-bit constant, which is returned in value. + */ + bool IsConstant(HInductionVarAnalysis::InductionInfo* info, + ConstantRequest request, + /*out*/ int64_t *value) const; + bool NeedsTripCount(HInductionVarAnalysis::InductionInfo* info) const; bool IsBodyTripCount(HInductionVarAnalysis::InductionInfo* trip) const; bool IsUnsafeTripCount(HInductionVarAnalysis::InductionInfo* trip) const; @@ -143,9 +161,8 @@ class InductionVarRange { bool in_body, bool is_min) const; - bool IsConstantRange(HInductionVarAnalysis::InductionInfo* info, - int32_t *min_value, - int32_t *max_value) const; + Value MulRangeAndConstant(Value v1, Value v2, Value c, bool is_min) const; + Value DivRangeAndConstant(Value v1, Value v2, Value c, bool is_min) const; Value AddValue(Value v1, Value v2) const; Value SubValue(Value v1, Value v2) const; diff --git a/compiler/optimizing/induction_var_range_test.cc b/compiler/optimizing/induction_var_range_test.cc index 55a654e301..c5c33bd9bc 100644 --- a/compiler/optimizing/induction_var_range_test.cc +++ b/compiler/optimizing/induction_var_range_test.cc @@ -215,10 +215,16 @@ class InductionVarRangeTest : public CommonCompilerTest { return range_.GetDiv(info1, info2, nullptr, /* in_body */ true, is_min); } - bool IsConstantRange(HInductionVarAnalysis::InductionInfo* info, - int32_t* min_value, - int32_t* max_value) { - return range_.IsConstantRange(info, min_value, max_value); + bool IsExact(HInductionVarAnalysis::InductionInfo* info, int64_t* value) { + return range_.IsConstant(info, InductionVarRange::kExact, value); + } + + bool IsAtMost(HInductionVarAnalysis::InductionInfo* info, int64_t* value) { + return range_.IsConstant(info, InductionVarRange::kAtMost, value); + } + + bool IsAtLeast(HInductionVarAnalysis::InductionInfo* info, int64_t* value) { + return range_.IsConstant(info, InductionVarRange::kAtLeast, value); } Value AddValue(Value v1, Value v2) { return range_.AddValue(v1, v2); } @@ -249,6 +255,34 @@ class InductionVarRangeTest : public CommonCompilerTest { // Tests on private methods. // +TEST_F(InductionVarRangeTest, IsConstant) { + int64_t value; + // Constant. + EXPECT_TRUE(IsExact(CreateConst(12345), &value)); + EXPECT_EQ(12345, value); + EXPECT_TRUE(IsAtMost(CreateConst(12345), &value)); + EXPECT_EQ(12345, value); + EXPECT_TRUE(IsAtLeast(CreateConst(12345), &value)); + EXPECT_EQ(12345, value); + // Constant trivial range. + EXPECT_TRUE(IsExact(CreateRange(111, 111), &value)); + EXPECT_EQ(111, value); + EXPECT_TRUE(IsAtMost(CreateRange(111, 111), &value)); + EXPECT_EQ(111, value); + EXPECT_TRUE(IsAtLeast(CreateRange(111, 111), &value)); + EXPECT_EQ(111, value); + // Constant non-trivial range. + EXPECT_FALSE(IsExact(CreateRange(11, 22), &value)); + EXPECT_TRUE(IsAtMost(CreateRange(11, 22), &value)); + EXPECT_EQ(22, value); + EXPECT_TRUE(IsAtLeast(CreateRange(11, 22), &value)); + EXPECT_EQ(11, value); + // Symbolic. + EXPECT_FALSE(IsExact(CreateFetch(x_), &value)); + EXPECT_FALSE(IsAtMost(CreateFetch(x_), &value)); + EXPECT_FALSE(IsAtLeast(CreateFetch(x_), &value)); +} + TEST_F(InductionVarRangeTest, TripCountProperties) { EXPECT_FALSE(NeedsTripCount(nullptr)); EXPECT_FALSE(NeedsTripCount(CreateConst(1))); @@ -367,6 +401,10 @@ TEST_F(InductionVarRangeTest, GetMinMaxPeriodic) { } TEST_F(InductionVarRangeTest, GetMulMin) { + ExpectEqual(Value(-14), GetMul(CreateConst(2), CreateRange(-7, 8), true)); + ExpectEqual(Value(-16), GetMul(CreateConst(-2), CreateRange(-7, 8), true)); + ExpectEqual(Value(-14), GetMul(CreateRange(-7, 8), CreateConst(2), true)); + ExpectEqual(Value(-16), GetMul(CreateRange(-7, 8), CreateConst(-2), true)); ExpectEqual(Value(6), GetMul(CreateRange(2, 10), CreateRange(3, 5), true)); ExpectEqual(Value(-50), GetMul(CreateRange(2, 10), CreateRange(-5, -3), true)); ExpectEqual(Value(), GetMul(CreateRange(2, 10), CreateRange(-1, 1), true)); @@ -379,6 +417,10 @@ TEST_F(InductionVarRangeTest, GetMulMin) { } TEST_F(InductionVarRangeTest, GetMulMax) { + ExpectEqual(Value(16), GetMul(CreateConst(2), CreateRange(-7, 8), false)); + ExpectEqual(Value(14), GetMul(CreateConst(-2), CreateRange(-7, 8), false)); + ExpectEqual(Value(16), GetMul(CreateRange(-7, 8), CreateConst(2), false)); + ExpectEqual(Value(14), GetMul(CreateRange(-7, 8), CreateConst(-2), false)); ExpectEqual(Value(50), GetMul(CreateRange(2, 10), CreateRange(3, 5), false)); ExpectEqual(Value(-6), GetMul(CreateRange(2, 10), CreateRange(-5, -3), false)); ExpectEqual(Value(), GetMul(CreateRange(2, 10), CreateRange(-1, 1), false)); @@ -391,6 +433,8 @@ TEST_F(InductionVarRangeTest, GetMulMax) { } TEST_F(InductionVarRangeTest, GetDivMin) { + ExpectEqual(Value(-5), GetDiv(CreateRange(-10, 20), CreateConst(2), true)); + ExpectEqual(Value(-10), GetDiv(CreateRange(-10, 20), CreateConst(-2), true)); ExpectEqual(Value(10), GetDiv(CreateRange(40, 1000), CreateRange(2, 4), true)); ExpectEqual(Value(-500), GetDiv(CreateRange(40, 1000), CreateRange(-4, -2), true)); ExpectEqual(Value(), GetDiv(CreateRange(40, 1000), CreateRange(-1, 1), true)); @@ -403,6 +447,8 @@ TEST_F(InductionVarRangeTest, GetDivMin) { } TEST_F(InductionVarRangeTest, GetDivMax) { + ExpectEqual(Value(10), GetDiv(CreateRange(-10, 20), CreateConst(2), false)); + ExpectEqual(Value(5), GetDiv(CreateRange(-10, 20), CreateConst(-2), false)); ExpectEqual(Value(500), GetDiv(CreateRange(40, 1000), CreateRange(2, 4), false)); ExpectEqual(Value(-10), GetDiv(CreateRange(40, 1000), CreateRange(-4, -2), false)); ExpectEqual(Value(), GetDiv(CreateRange(40, 1000), CreateRange(-1, 1), false)); @@ -414,18 +460,6 @@ TEST_F(InductionVarRangeTest, GetDivMax) { ExpectEqual(Value(), GetDiv(CreateRange(-1, 1), CreateRange(-1, 1), false)); } -TEST_F(InductionVarRangeTest, IsConstantRange) { - int32_t min_value; - int32_t max_value; - ASSERT_TRUE(IsConstantRange(CreateConst(12345), &min_value, &max_value)); - EXPECT_EQ(12345, min_value); - EXPECT_EQ(12345, max_value); - ASSERT_TRUE(IsConstantRange(CreateRange(1, 2), &min_value, &max_value)); - EXPECT_EQ(1, min_value); - EXPECT_EQ(2, max_value); - EXPECT_FALSE(IsConstantRange(CreateFetch(x_), &min_value, &max_value)); -} - TEST_F(InductionVarRangeTest, AddValue) { ExpectEqual(Value(110), AddValue(Value(10), Value(100))); ExpectEqual(Value(-5), AddValue(Value(x_, 1, -4), Value(x_, -1, -1))); @@ -459,6 +493,24 @@ TEST_F(InductionVarRangeTest, MulValue) { ExpectEqual(Value(), MulValue(Value(90000), Value(-90000))); // unsafe } +TEST_F(InductionVarRangeTest, MulValueSpecial) { + const int32_t min_value = std::numeric_limits::min(); + const int32_t max_value = std::numeric_limits::max(); + + // Unsafe. + ExpectEqual(Value(), MulValue(Value(min_value), Value(min_value))); + ExpectEqual(Value(), MulValue(Value(min_value), Value(-1))); + ExpectEqual(Value(), MulValue(Value(min_value), Value(max_value))); + ExpectEqual(Value(), MulValue(Value(max_value), Value(max_value))); + + // Safe. + ExpectEqual(Value(min_value), MulValue(Value(min_value), Value(1))); + ExpectEqual(Value(max_value), MulValue(Value(max_value), Value(1))); + ExpectEqual(Value(-max_value), MulValue(Value(max_value), Value(-1))); + ExpectEqual(Value(-1), MulValue(Value(1), Value(-1))); + ExpectEqual(Value(1), MulValue(Value(-1), Value(-1))); +} + TEST_F(InductionVarRangeTest, DivValue) { ExpectEqual(Value(25), DivValue(Value(100), Value(4))); ExpectEqual(Value(), DivValue(Value(x_, 1, -4), Value(x_, 1, -1))); @@ -468,6 +520,23 @@ TEST_F(InductionVarRangeTest, DivValue) { ExpectEqual(Value(), DivValue(Value(1), Value(0))); // unsafe } +TEST_F(InductionVarRangeTest, DivValueSpecial) { + const int32_t min_value = std::numeric_limits::min(); + const int32_t max_value = std::numeric_limits::max(); + + // Unsafe. + ExpectEqual(Value(), DivValue(Value(min_value), Value(-1))); + + // Safe. + ExpectEqual(Value(1), DivValue(Value(min_value), Value(min_value))); + ExpectEqual(Value(1), DivValue(Value(max_value), Value(max_value))); + ExpectEqual(Value(min_value), DivValue(Value(min_value), Value(1))); + ExpectEqual(Value(max_value), DivValue(Value(max_value), Value(1))); + ExpectEqual(Value(-max_value), DivValue(Value(max_value), Value(-1))); + ExpectEqual(Value(-1), DivValue(Value(1), Value(-1))); + ExpectEqual(Value(1), DivValue(Value(-1), Value(-1))); +} + TEST_F(InductionVarRangeTest, MinValue) { ExpectEqual(Value(10), MinValue(Value(10), Value(100))); ExpectEqual(Value(x_, 1, -4), MinValue(Value(x_, 1, -4), Value(x_, 1, -1))); diff --git a/test/530-checker-loops/src/Main.java b/test/530-checker-loops/src/Main.java index deff279f77..9f344dda11 100644 --- a/test/530-checker-loops/src/Main.java +++ b/test/530-checker-loops/src/Main.java @@ -471,7 +471,7 @@ public class Main { // /// CHECK-START: void Main.linearTriangularOnTwoArrayLengths(int) BCE (after) /// CHECK-NOT: BoundsCheck - /// CHECK-NOT: Deoptimize + // TODO: also CHECK-NOT: Deoptimize, see b/27151190 private static void linearTriangularOnTwoArrayLengths(int n) { int[] a = new int[n]; for (int i = 0; i < a.length; i++) { @@ -513,7 +513,7 @@ public class Main { // /// CHECK-START: void Main.linearTriangularOnParameter(int) BCE (after) /// CHECK-NOT: BoundsCheck - /// CHECK-NOT: Deoptimize + // TODO: also CHECK-NOT: Deoptimize, see b/27151190 private static void linearTriangularOnParameter(int n) { int[] a = new int[n]; for (int i = 0; i < n; i++) { @@ -528,22 +528,22 @@ public class Main { } } - /// CHECK-START: void Main.linearTriangularVariations(int) BCE (before) + /// CHECK-START: void Main.linearTriangularVariationsInnerStrict(int) BCE (before) /// CHECK-DAG: BoundsCheck /// CHECK-DAG: BoundsCheck /// CHECK-DAG: BoundsCheck /// CHECK-DAG: BoundsCheck // - /// CHECK-START: void Main.linearTriangularVariations(int) BCE (after) + /// CHECK-START: void Main.linearTriangularVariationsInnerStrict(int) BCE (after) /// CHECK-NOT: BoundsCheck - /// CHECK-NOT: Deoptimize - private static void linearTriangularVariations(int n) { + // TODO: also CHECK-NOT: Deoptimize, see b/27151190 + private static void linearTriangularVariationsInnerStrict(int n) { int[] a = new int[n]; for (int i = 0; i < n; i++) { for (int j = 0; j < i; j++) { a[j] += 1; } - for (int j = i - 1; j >= 0; j--) { + for (int j = i - 1; j > -1; j--) { a[j] += 1; } for (int j = i; j < n; j++) { @@ -556,6 +556,34 @@ public class Main { verifyTriangular(a); } + /// CHECK-START: void Main.linearTriangularVariationsInnerNonStrict(int) BCE (before) + /// CHECK-DAG: BoundsCheck + /// CHECK-DAG: BoundsCheck + /// CHECK-DAG: BoundsCheck + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: void Main.linearTriangularVariationsInnerNonStrict(int) BCE (after) + /// CHECK-NOT: BoundsCheck + // TODO: also CHECK-NOT: Deoptimize, see b/27151190 + private static void linearTriangularVariationsInnerNonStrict(int n) { + int[] a = new int[n]; + for (int i = 0; i < n; i++) { + for (int j = 0; j <= i - 1; j++) { + a[j] += 1; + } + for (int j = i - 1; j >= 0; j--) { + a[j] += 1; + } + for (int j = i; j <= n - 1; j++) { + a[j] += 1; + } + for (int j = n - 1; j >= i; j--) { + a[j] += 1; + } + } + verifyTriangular(a); + } + // Verifier for triangular loops. private static void verifyTriangular(int[] a, int[] b, int m, int n) { expectEquals(n, a.length); @@ -583,7 +611,7 @@ public class Main { // /// CHECK-START: void Main.bubble(int[]) BCE (after) /// CHECK-NOT: BoundsCheck - /// CHECK-NOT: Deoptimize + // TODO: also CHECK-NOT: Deoptimize, see b/27151190 private static void bubble(int[] a) { for (int i = a.length; --i >= 0;) { for (int j = 0; j < i; j++) { @@ -853,6 +881,104 @@ public class Main { } while (-1 <= i); } + /// CHECK-START: void Main.hiddenOOB1(int) BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: void Main.hiddenOOB1(int) BCE (after) + /// CHECK-DAG: Deoptimize + // + /// CHECK-START: void Main.hiddenOOB1(int) BCE (after) + /// CHECK-NOT: BoundsCheck + private static void hiddenOOB1(int lo) { + int[] a = { 1 } ; + for (int i = lo; i <= 10; i++) { + // Dangerous loop where careless static range analysis would yield strict upper bound + // on index j of 5. When, for instance, lo and thus i = -2147483648, the upper bound + // becomes really positive due to arithmetic wrap-around, causing OOB. + // Dynamic BCE is feasible though, since it checks the range. + for (int j = 4; j < i - 5; j++) { + sResult += a[j - 4]; + } + } + } + + /// CHECK-START: void Main.hiddenOOB2(int) BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: void Main.hiddenOOB2(int) BCE (after) + /// CHECK-DAG: Deoptimize + // + /// CHECK-START: void Main.hiddenOOB2(int) BCE (after) + /// CHECK-NOT: BoundsCheck + private static void hiddenOOB2(int hi) { + int[] a = { 1 } ; + for (int i = 0; i < hi; i++) { + // Dangerous loop where careless static range analysis would yield strict lower bound + // on index j of 5. When, for instance, lo and thus i = 2147483647, the upper bound + // becomes really negative due to arithmetic wrap-around, causing OOB. + // Dynamic BCE is feasible though, since it checks the range. + for (int j = 6; j > i + 5; j--) { + sResult += a[j - 6]; + } + } + } + + /// CHECK-START: void Main.hiddenInfiniteOOB() BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: void Main.hiddenInfiniteOOB() BCE (after) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: void Main.hiddenInfiniteOOB() BCE (after) + /// CHECK-NOT: Deoptimize + private static void hiddenInfiniteOOB() { + int[] a = { 11 } ; + for (int i = -1; i <= 0; i++) { + // Dangerous loop where careless static range analysis would yield a safe upper bound + // of -3. In reality, due to arithmetic wrap-around (when i = -1, j <= 2147483647; + // whereas when i = 0, j <= -3), this is an infinite loop that goes OOB. + for (int j = -3; j <= 2147483646 * i - 3; j++) { + sResult += a[j + 3]; + } + } + } + + /// CHECK-START: void Main.hiddenFiniteOOB() BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: void Main.hiddenFiniteOOB() BCE (after) + /// CHECK-DAG: Deoptimize + // + /// CHECK-START: void Main.hiddenFiniteOOB() BCE (after) + /// CHECK-NOT: BoundsCheck + private static void hiddenFiniteOOB() { + int[] a = { 111 } ; + for (int i = -1; i <= 0; i++) { + // Dangerous loop similar as above where the loop is now finite, but the + // loop still goes out of bounds for i = -1 due to the large upper bound. + // Dynamic BCE is feasible though, since it checks the range. + for (int j = -4; j < 2147483646 * i - 3; j++) { + sResult += a[j + 4]; + } + } + } + + /// CHECK-START: int[] Main.add() BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: int[] Main.add() BCE (after) + /// CHECK-NOT: BoundsCheck + /// CHECK-NOT: Deoptimize + private static int[] add() { + int[] a = new int[10]; + for (int i = 0; i <= 3; i++) { + for (int j = 0; j <= 6; j++) { + a[i + j] += 1; + } + } + return a; + } + /// CHECK-START: int[] Main.multiply1() BCE (before) /// CHECK-DAG: BoundsCheck // @@ -1053,6 +1179,37 @@ public class Main { return result; } + /// CHECK-START: int Main.dynamicBCEConstantRange(int[]) BCE (before) + /// CHECK-DAG: BoundsCheck loop:<> + /// CHECK-DAG: ArrayGet loop:<> + /// CHECK-DAG: If loop:<> + /// CHECK-DAG: If loop:<> + /// CHECK-EVAL: "<>" != "<>" + // + /// CHECK-START: int Main.dynamicBCEConstantRange(int[]) BCE (after) + /// CHECK-DAG: ArrayGet loop:<> + /// CHECK-DAG: Deoptimize loop:<> + /// CHECK-EVAL: "<>" != "<>" + // + /// CHECK-START: int Main.dynamicBCEConstantRange(int[]) BCE (after) + /// CHECK-NOT: BoundsCheck + // + // No additional top tests were introduced. + /// CHECK-START: int Main.dynamicBCEConstantRange(int[]) BCE (after) + /// CHECK-DAG: If + /// CHECK-DAG: If + /// CHECK-NOT: If + static int dynamicBCEConstantRange(int[] x) { + int result = 0; + for (int i = 2; i <= 6; i++) { + // Range analysis sees that innermost loop is finite and always taken. + for (int j = i - 2; j <= i + 2; j++) { + result += x[j]; + } + } + return result; + } + /// CHECK-START: int Main.dynamicBCEAndConstantIndices(int[], int[][], int, int) BCE (before) /// CHECK-DAG: {{l\d+}} ArrayGet loop:<> /// CHECK-DAG: {{l\d+}} ArrayGet loop:<> @@ -1239,7 +1396,8 @@ public class Main { linearTriangularOnTwoArrayLengths(10); linearTriangularOnOneArrayLength(10); linearTriangularOnParameter(10); - linearTriangularVariations(10); + linearTriangularVariationsInnerStrict(10); + linearTriangularVariationsInnerNonStrict(10); // Sorting. int[] sort = { 5, 4, 1, 9, 10, 2, 7, 6, 3, 8 }; @@ -1330,6 +1488,59 @@ public class Main { } expectEquals(1055, sResult); + // Hidden OOB. + sResult = 0; + try { + hiddenOOB1(10); // no OOB + } catch (ArrayIndexOutOfBoundsException e) { + sResult += 1000; + } + expectEquals(1, sResult); + sResult = 0; + try { + hiddenOOB1(-2147483648); // OOB + } catch (ArrayIndexOutOfBoundsException e) { + sResult += 1000; + } + expectEquals(1001, sResult); + sResult = 0; + try { + hiddenOOB2(1); // no OOB + } catch (ArrayIndexOutOfBoundsException e) { + sResult += 1000; + } + expectEquals(1, sResult); + sResult = 0; + try { + hiddenOOB2(2147483647); // OOB + } catch (ArrayIndexOutOfBoundsException e) { + sResult += 1000; + } + expectEquals(1002, sResult); + sResult = 0; + try { + hiddenInfiniteOOB(); + } catch (ArrayIndexOutOfBoundsException e) { + sResult += 1000; + } + expectEquals(1011, sResult); + sResult = 0; + try { + hiddenFiniteOOB(); + } catch (ArrayIndexOutOfBoundsException e) { + sResult += 1000; + } + expectEquals(1111, sResult); + + // Addition. + { + int[] e1 ={ 1, 2, 3, 4, 4, 4, 4, 3, 2, 1 }; + int[] a1 = add(); + for (int i = 0; i < 10; i++) { + expectEquals(a1[i], e1[i]); + } + } + // Multiplication. { int[] e1 = { 7, 1, 2, 2, 1, 0, 2, 0, 0, 1 }; @@ -1388,6 +1599,7 @@ public class Main { expectEquals(55, dynamicBCEPossiblyInfiniteLoop(x, 0, 9)); expectEquals(55, noDynamicBCEPossiblyInfiniteLoop(x, 0, 9)); expectEquals(55, noDynamicBCEMixedInductionTypes(x, 0, 10)); + expectEquals(125, dynamicBCEConstantRange(x)); // Dynamic BCE combined with constant indices. int[][] a; -- GitLab From 0ccfe2c039092ebefcbfdb7d523e20fc611e3e5f Mon Sep 17 00:00:00 2001 From: Igor Murashkin Date: Fri, 19 Feb 2016 16:41:44 -0800 Subject: [PATCH 031/204] image: Allow methods with code in another oat file to work correctly This fixes an issue when classes with default methods are in the boot image and are used by an app image. (cherry picked from commit 6e2237d6615c8f7b09c99d196e5effcfd087943b) Bug: 27315287 Change-Id: Iaa66848fc07a0c779bc6e047bd154e2a4b87e1c8 --- compiler/image_writer.cc | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc index aeb89f413a..5eff8f37ec 100644 --- a/compiler/image_writer.cc +++ b/compiler/image_writer.cc @@ -1951,9 +1951,19 @@ const uint8_t* ImageWriter::GetQuickCode(ArtMethod* method, // trampoline. // Quick entrypoint: - uint32_t quick_oat_code_offset = PointerToLowMemUInt32( - method->GetEntryPointFromQuickCompiledCodePtrSize(target_ptr_size_)); - const uint8_t* quick_code = GetOatAddressForOffset(quick_oat_code_offset, image_info); + const void* quick_oat_entry_point = + method->GetEntryPointFromQuickCompiledCodePtrSize(target_ptr_size_); + const uint8_t* quick_code; + + if (UNLIKELY(IsInBootImage(method->GetDeclaringClass()))) { + DCHECK(method->IsCopied()); + // If the code is not in the oat file corresponding to this image (e.g. default methods) + quick_code = reinterpret_cast(quick_oat_entry_point); + } else { + uint32_t quick_oat_code_offset = PointerToLowMemUInt32(quick_oat_entry_point); + quick_code = GetOatAddressForOffset(quick_oat_code_offset, image_info); + } + *quick_is_interpreted = false; if (quick_code != nullptr && (!method->IsStatic() || method->IsConstructor() || method->GetDeclaringClass()->IsInitialized())) { -- GitLab From 4429b110c7b4d9bda2c31b4df257e840831d01f6 Mon Sep 17 00:00:00 2001 From: buzbee Date: Wed, 24 Feb 2016 15:16:46 -0800 Subject: [PATCH 032/204] ART: Support interpreter switching in x86 mterp Because mterp only supports a subset of special modes and instrumentation, it needs to recognize when new instrumentation is added and bail out to the reference interpreter if needed. The arm and arm64 mterp targets do this. This CL adds the functionality to x86 mterp. Change-Id: I72783577e6f26b2695677b07d8fa57fb887a36c8 --- runtime/interpreter/mterp/out/mterp_x86.S | 42 ++++++++++++++++++++++- runtime/interpreter/mterp/x86/footer.S | 6 +++- runtime/interpreter/mterp/x86/invoke.S | 3 ++ 3 files changed, 49 insertions(+), 2 deletions(-) diff --git a/runtime/interpreter/mterp/out/mterp_x86.S b/runtime/interpreter/mterp/out/mterp_x86.S index b05360b6ae..589639bdbc 100644 --- a/runtime/interpreter/mterp/out/mterp_x86.S +++ b/runtime/interpreter/mterp/out/mterp_x86.S @@ -2989,6 +2989,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop call SYMBOL(MterpInvokeVirtual) testb %al, %al jz MterpException + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback RESTORE_IBASE ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 @@ -3022,6 +3025,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop call SYMBOL(MterpInvokeSuper) testb %al, %al jz MterpException + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback RESTORE_IBASE ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 @@ -3055,6 +3061,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop call SYMBOL(MterpInvokeDirect) testb %al, %al jz MterpException + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback RESTORE_IBASE ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 @@ -3081,6 +3090,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop call SYMBOL(MterpInvokeStatic) testb %al, %al jz MterpException + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback RESTORE_IBASE ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 @@ -3108,6 +3120,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop call SYMBOL(MterpInvokeInterface) testb %al, %al jz MterpException + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback RESTORE_IBASE ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 @@ -3155,6 +3170,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop call SYMBOL(MterpInvokeVirtualRange) testb %al, %al jz MterpException + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback RESTORE_IBASE ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 @@ -3181,6 +3199,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop call SYMBOL(MterpInvokeSuperRange) testb %al, %al jz MterpException + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback RESTORE_IBASE ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 @@ -3207,6 +3228,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop call SYMBOL(MterpInvokeDirectRange) testb %al, %al jz MterpException + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback RESTORE_IBASE ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 @@ -3233,6 +3257,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop call SYMBOL(MterpInvokeStaticRange) testb %al, %al jz MterpException + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback RESTORE_IBASE ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 @@ -3259,6 +3286,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop call SYMBOL(MterpInvokeInterfaceRange) testb %al, %al jz MterpException + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback RESTORE_IBASE ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 @@ -6002,6 +6032,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop call SYMBOL(MterpInvokeVirtualQuick) testb %al, %al jz MterpException + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback RESTORE_IBASE ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 @@ -6028,6 +6061,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop call SYMBOL(MterpInvokeVirtualQuickRange) testb %al, %al jz MterpException + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback RESTORE_IBASE ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 @@ -12851,13 +12887,17 @@ MterpException: call SYMBOL(MterpHandleException) testb %al, %al jz MterpExceptionReturn - REFRESH_IBASE movl OFF_FP_CODE_ITEM(rFP), %eax movl OFF_FP_DEX_PC(rFP), %ecx lea CODEITEM_INSNS_OFFSET(%eax), rPC lea (rPC, %ecx, 2), rPC movl rPC, OFF_FP_DEX_PC_PTR(rFP) + /* Do we need to switch interpreters? */ + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback /* resume execution at catch block */ + REFRESH_IBASE FETCH_INST GOTO_NEXT /* NOTE: no fallthrough */ diff --git a/runtime/interpreter/mterp/x86/footer.S b/runtime/interpreter/mterp/x86/footer.S index c67491e577..64d72d7709 100644 --- a/runtime/interpreter/mterp/x86/footer.S +++ b/runtime/interpreter/mterp/x86/footer.S @@ -115,13 +115,17 @@ MterpException: call SYMBOL(MterpHandleException) testb %al, %al jz MterpExceptionReturn - REFRESH_IBASE movl OFF_FP_CODE_ITEM(rFP), %eax movl OFF_FP_DEX_PC(rFP), %ecx lea CODEITEM_INSNS_OFFSET(%eax), rPC lea (rPC, %ecx, 2), rPC movl rPC, OFF_FP_DEX_PC_PTR(rFP) + /* Do we need to switch interpreters? */ + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback /* resume execution at catch block */ + REFRESH_IBASE FETCH_INST GOTO_NEXT /* NOTE: no fallthrough */ diff --git a/runtime/interpreter/mterp/x86/invoke.S b/runtime/interpreter/mterp/x86/invoke.S index bbd88cf40b..cb74a04478 100644 --- a/runtime/interpreter/mterp/x86/invoke.S +++ b/runtime/interpreter/mterp/x86/invoke.S @@ -16,5 +16,8 @@ call SYMBOL($helper) testb %al, %al jz MterpException + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback RESTORE_IBASE ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 -- GitLab From 6ab903c1ada868d9e7368107385f0de0b17ae646 Mon Sep 17 00:00:00 2001 From: Aart Bik Date: Wed, 24 Feb 2016 15:18:55 -0800 Subject: [PATCH 033/204] Avoid single expensive test in 530-checker-loops. Rationale: All tests run in "no time", except one instance that iterates over full iteration space before going OOB. Protect that instance with a HEAVY boolean to avoid timing out in interpretation mode. Impact on interpreted runtime: 1m6.936s -> 0m0.031s Change-Id: I413939b683c76b7be0f1259da8533d4b069eac6d --- test/530-checker-loops/src/Main.java | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/test/530-checker-loops/src/Main.java b/test/530-checker-loops/src/Main.java index 9f344dda11..0c32491e4e 100644 --- a/test/530-checker-loops/src/Main.java +++ b/test/530-checker-loops/src/Main.java @@ -914,7 +914,7 @@ public class Main { int[] a = { 1 } ; for (int i = 0; i < hi; i++) { // Dangerous loop where careless static range analysis would yield strict lower bound - // on index j of 5. When, for instance, lo and thus i = 2147483647, the upper bound + // on index j of 5. When, for instance, hi and thus i = 2147483647, the upper bound // becomes really negative due to arithmetic wrap-around, causing OOB. // Dynamic BCE is feasible though, since it checks the range. for (int j = 6; j > i + 5; j--) { @@ -1325,6 +1325,9 @@ public class Main { // public static void main(String[] args) { + // Set to run expensive tests for correctness too. + boolean HEAVY = false; + int[] empty = { }; int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; @@ -1510,13 +1513,15 @@ public class Main { sResult += 1000; } expectEquals(1, sResult); - sResult = 0; - try { - hiddenOOB2(2147483647); // OOB - } catch (ArrayIndexOutOfBoundsException e) { - sResult += 1000; + if (HEAVY) { + sResult = 0; + try { + hiddenOOB2(2147483647); // OOB + } catch (ArrayIndexOutOfBoundsException e) { + sResult += 1000; + } + expectEquals(1002, sResult); } - expectEquals(1002, sResult); sResult = 0; try { hiddenInfiniteOOB(); -- GitLab From ea9fc5bd31781fb380ace74ccddf2137c7dcc5d9 Mon Sep 17 00:00:00 2001 From: Dimitry Ivanov Date: Mon, 22 Feb 2016 14:26:48 -0800 Subject: [PATCH 034/204] Preload public libraries on Runtime::Start() Preloading public libraries on Runtime::Start() instead of doing it in native_loader c-tor ensures that the libraries are loaded only for binaries that are using Android Runtime, and not just linking against libandroid_runtime or libart. Loading public libraries on Runtime::Start() also helps some apps to save startup time and space by ensuring that all NDK libraries are loaded and ready to use. Bug: http://b/27245894 Change-Id: I15679a72450f3d18a7dd503d0ae977cebcfd7f52 (cherry picked from commit 7bca74e53d3665bcd243d4e0277f6561c0c80271) --- runtime/runtime.cc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/runtime/runtime.cc b/runtime/runtime.cc index eb5455a4cd..9f7fabad40 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -25,6 +25,7 @@ #define ATRACE_TAG ATRACE_TAG_DALVIK #include +#include #include #include #include "base/memory_tool.h" @@ -551,6 +552,11 @@ bool Runtime::Start() { } #endif + // This line makes sure that all public native libraries + // are loaded prior to runtime start; saves app load times + // and memory. + android::PreloadPublicNativeLibraries(); + // Restore main thread state to kNative as expected by native code. Thread* self = Thread::Current(); -- GitLab From 45bdb25a38489809458f5000bb08a37ab9621b2d Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Thu, 25 Feb 2016 08:41:31 +0000 Subject: [PATCH 035/204] Revert "Disable test due to toybox update." Bug: 26395656 This reverts commit 3e9be9904a7a3dfce076ba64a5f27e87c38e027d. Change-Id: Ia994d695fdcebf00d6663aec3943539ab1347f87 --- tools/libcore_failures.txt | 7 ------- 1 file changed, 7 deletions(-) diff --git a/tools/libcore_failures.txt b/tools/libcore_failures.txt index e6394a999f..46100ae15c 100644 --- a/tools/libcore_failures.txt +++ b/tools/libcore_failures.txt @@ -207,13 +207,6 @@ "org.apache.harmony.crypto.tests.javax.crypto.spec.SecretKeySpecTest#testGetFormat", "org.apache.harmony.tests.java.util.TimerTaskTest#test_scheduledExecutionTime"] }, -{ - description: "'cat -' does not work anymore", - result: EXEC_FAILED, - bug: 26395656, - modes: [device], - names: ["org.apache.harmony.tests.java.lang.ProcessTest#test_getOutputStream"] -}, { description: "Missing resource in classpath", result: EXEC_FAILED, -- GitLab From c66577ece35b73250f2078d8d59ac661a95a63d0 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Thu, 25 Feb 2016 08:50:50 +0000 Subject: [PATCH 036/204] Revert "Preload public libraries on Runtime::Start()" Breaks anyone not working on up-to-date devices/trees. I'll work on a revert of the revert. Bug: http://b/27245894 This reverts commit ea9fc5bd31781fb380ace74ccddf2137c7dcc5d9. Change-Id: Ie8fc286a64de4db2c905502a528005022803ed94 --- runtime/runtime.cc | 6 ------ 1 file changed, 6 deletions(-) diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 9f7fabad40..eb5455a4cd 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -25,7 +25,6 @@ #define ATRACE_TAG ATRACE_TAG_DALVIK #include -#include #include #include #include "base/memory_tool.h" @@ -552,11 +551,6 @@ bool Runtime::Start() { } #endif - // This line makes sure that all public native libraries - // are loaded prior to runtime start; saves app load times - // and memory. - android::PreloadPublicNativeLibraries(); - // Restore main thread state to kNative as expected by native code. Thread* self = Thread::Current(); -- GitLab From 4a0dad67867f389e01a5a6c0fe381d210f687c0d Mon Sep 17 00:00:00 2001 From: Artem Udovichenko Date: Tue, 26 Jan 2016 12:28:31 +0300 Subject: [PATCH 037/204] Revert "Revert "ARM/ARM64: Extend support of instruction combining."" This reverts commit 6b5afdd144d2bb3bf994240797834b5666b2cf98. Change-Id: Ic27a10f02e21109503edd64e6d73d1bb0c6a8ac6 --- compiler/Android.mk | 2 + compiler/optimizing/code_generator_arm.cc | 27 +++ compiler/optimizing/code_generator_arm.h | 2 + compiler/optimizing/code_generator_arm64.cc | 33 ++- compiler/optimizing/code_generator_arm64.h | 2 + compiler/optimizing/graph_visualizer.cc | 10 +- .../optimizing/instruction_simplifier_arm.cc | 30 +++ .../optimizing/instruction_simplifier_arm.h | 58 +++++ .../instruction_simplifier_arm64.cc | 133 +---------- .../optimizing/instruction_simplifier_arm64.h | 4 - .../instruction_simplifier_shared.cc | 189 +++++++++++++++ .../instruction_simplifier_shared.h | 28 +++ compiler/optimizing/nodes.h | 17 +- compiler/optimizing/nodes_arm64.h | 34 --- compiler/optimizing/nodes_shared.h | 58 +++++ compiler/optimizing/optimizing_compiler.cc | 4 + .../src/Main.java | 215 +++++++++++++++++- 17 files changed, 656 insertions(+), 190 deletions(-) create mode 100644 compiler/optimizing/instruction_simplifier_arm.cc create mode 100644 compiler/optimizing/instruction_simplifier_arm.h create mode 100644 compiler/optimizing/instruction_simplifier_shared.cc create mode 100644 compiler/optimizing/instruction_simplifier_shared.h create mode 100644 compiler/optimizing/nodes_shared.h diff --git a/compiler/Android.mk b/compiler/Android.mk index 2cbafea573..7a257b649f 100644 --- a/compiler/Android.mk +++ b/compiler/Android.mk @@ -142,7 +142,9 @@ LIBART_COMPILER_SRC_FILES_arm64 := \ jni/quick/arm64/calling_convention_arm64.cc \ linker/arm64/relative_patcher_arm64.cc \ optimizing/code_generator_arm64.cc \ + optimizing/instruction_simplifier_arm.cc \ optimizing/instruction_simplifier_arm64.cc \ + optimizing/instruction_simplifier_shared.cc \ optimizing/intrinsics_arm64.cc \ utils/arm64/assembler_arm64.cc \ utils/arm64/managed_register_arm64.cc \ diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index cdbb9c31aa..10d3426a58 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -6413,6 +6413,33 @@ Literal* CodeGeneratorARM::DeduplicateMethodCodeLiteral(MethodReference target_m return DeduplicateMethodLiteral(target_method, &call_patches_); } +void LocationsBuilderARM::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(instr, LocationSummary::kNoCall); + locations->SetInAt(HMultiplyAccumulate::kInputAccumulatorIndex, + Location::RequiresRegister()); + locations->SetInAt(HMultiplyAccumulate::kInputMulLeftIndex, Location::RequiresRegister()); + locations->SetInAt(HMultiplyAccumulate::kInputMulRightIndex, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); +} + +void InstructionCodeGeneratorARM::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) { + LocationSummary* locations = instr->GetLocations(); + Register res = locations->Out().AsRegister(); + Register accumulator = + locations->InAt(HMultiplyAccumulate::kInputAccumulatorIndex).AsRegister(); + Register mul_left = + locations->InAt(HMultiplyAccumulate::kInputMulLeftIndex).AsRegister(); + Register mul_right = + locations->InAt(HMultiplyAccumulate::kInputMulRightIndex).AsRegister(); + + if (instr->GetOpKind() == HInstruction::kAdd) { + __ mla(res, mul_left, mul_right, accumulator); + } else { + __ mls(res, mul_left, mul_right, accumulator); + } +} + void LocationsBuilderARM::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) { // Nothing to do, this should be removed during prepare for register allocator. LOG(FATAL) << "Unreachable"; diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h index 2e4dc1e014..06e7c0015c 100644 --- a/compiler/optimizing/code_generator_arm.h +++ b/compiler/optimizing/code_generator_arm.h @@ -159,6 +159,7 @@ class LocationsBuilderARM : public HGraphVisitor { FOR_EACH_CONCRETE_INSTRUCTION_COMMON(DECLARE_VISIT_INSTRUCTION) FOR_EACH_CONCRETE_INSTRUCTION_ARM(DECLARE_VISIT_INSTRUCTION) + FOR_EACH_CONCRETE_INSTRUCTION_SHARED(DECLARE_VISIT_INSTRUCTION) #undef DECLARE_VISIT_INSTRUCTION @@ -197,6 +198,7 @@ class InstructionCodeGeneratorARM : public InstructionCodeGenerator { FOR_EACH_CONCRETE_INSTRUCTION_COMMON(DECLARE_VISIT_INSTRUCTION) FOR_EACH_CONCRETE_INSTRUCTION_ARM(DECLARE_VISIT_INSTRUCTION) + FOR_EACH_CONCRETE_INSTRUCTION_SHARED(DECLARE_VISIT_INSTRUCTION) #undef DECLARE_VISIT_INSTRUCTION diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index 814f8b4d51..beb75f0afc 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -1959,21 +1959,27 @@ void InstructionCodeGeneratorARM64::VisitArm64IntermediateAddress( Operand(InputOperandAt(instruction, 1))); } -void LocationsBuilderARM64::VisitArm64MultiplyAccumulate(HArm64MultiplyAccumulate* instr) { +void LocationsBuilderARM64::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instr, LocationSummary::kNoCall); - locations->SetInAt(HArm64MultiplyAccumulate::kInputAccumulatorIndex, - Location::RequiresRegister()); - locations->SetInAt(HArm64MultiplyAccumulate::kInputMulLeftIndex, Location::RequiresRegister()); - locations->SetInAt(HArm64MultiplyAccumulate::kInputMulRightIndex, Location::RequiresRegister()); + HInstruction* accumulator = instr->InputAt(HMultiplyAccumulate::kInputAccumulatorIndex); + if (instr->GetOpKind() == HInstruction::kSub && + accumulator->IsConstant() && + accumulator->AsConstant()->IsZero()) { + // Don't allocate register for Mneg instruction. + } else { + locations->SetInAt(HMultiplyAccumulate::kInputAccumulatorIndex, + Location::RequiresRegister()); + } + locations->SetInAt(HMultiplyAccumulate::kInputMulLeftIndex, Location::RequiresRegister()); + locations->SetInAt(HMultiplyAccumulate::kInputMulRightIndex, Location::RequiresRegister()); locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); } -void InstructionCodeGeneratorARM64::VisitArm64MultiplyAccumulate(HArm64MultiplyAccumulate* instr) { +void InstructionCodeGeneratorARM64::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) { Register res = OutputRegister(instr); - Register accumulator = InputRegisterAt(instr, HArm64MultiplyAccumulate::kInputAccumulatorIndex); - Register mul_left = InputRegisterAt(instr, HArm64MultiplyAccumulate::kInputMulLeftIndex); - Register mul_right = InputRegisterAt(instr, HArm64MultiplyAccumulate::kInputMulRightIndex); + Register mul_left = InputRegisterAt(instr, HMultiplyAccumulate::kInputMulLeftIndex); + Register mul_right = InputRegisterAt(instr, HMultiplyAccumulate::kInputMulRightIndex); // Avoid emitting code that could trigger Cortex A53's erratum 835769. // This fixup should be carried out for all multiply-accumulate instructions: @@ -1993,10 +1999,17 @@ void InstructionCodeGeneratorARM64::VisitArm64MultiplyAccumulate(HArm64MultiplyA } if (instr->GetOpKind() == HInstruction::kAdd) { + Register accumulator = InputRegisterAt(instr, HMultiplyAccumulate::kInputAccumulatorIndex); __ Madd(res, mul_left, mul_right, accumulator); } else { DCHECK(instr->GetOpKind() == HInstruction::kSub); - __ Msub(res, mul_left, mul_right, accumulator); + HInstruction* accum_instr = instr->InputAt(HMultiplyAccumulate::kInputAccumulatorIndex); + if (accum_instr->IsConstant() && accum_instr->AsConstant()->IsZero()) { + __ Mneg(res, mul_left, mul_right); + } else { + Register accumulator = InputRegisterAt(instr, HMultiplyAccumulate::kInputAccumulatorIndex); + __ Msub(res, mul_left, mul_right, accumulator); + } } } diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h index 3527261835..10f1e7f008 100644 --- a/compiler/optimizing/code_generator_arm64.h +++ b/compiler/optimizing/code_generator_arm64.h @@ -196,6 +196,7 @@ class InstructionCodeGeneratorARM64 : public InstructionCodeGenerator { FOR_EACH_CONCRETE_INSTRUCTION_COMMON(DECLARE_VISIT_INSTRUCTION) FOR_EACH_CONCRETE_INSTRUCTION_ARM64(DECLARE_VISIT_INSTRUCTION) + FOR_EACH_CONCRETE_INSTRUCTION_SHARED(DECLARE_VISIT_INSTRUCTION) #undef DECLARE_VISIT_INSTRUCTION @@ -285,6 +286,7 @@ class LocationsBuilderARM64 : public HGraphVisitor { FOR_EACH_CONCRETE_INSTRUCTION_COMMON(DECLARE_VISIT_INSTRUCTION) FOR_EACH_CONCRETE_INSTRUCTION_ARM64(DECLARE_VISIT_INSTRUCTION) + FOR_EACH_CONCRETE_INSTRUCTION_SHARED(DECLARE_VISIT_INSTRUCTION) #undef DECLARE_VISIT_INSTRUCTION diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc index c0263e4e5b..a3d6bcf450 100644 --- a/compiler/optimizing/graph_visualizer.cc +++ b/compiler/optimizing/graph_visualizer.cc @@ -436,6 +436,12 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor { StartAttributeStream("kind") << (try_boundary->IsEntry() ? "entry" : "exit"); } +#if defined(ART_ENABLE_CODEGEN_arm) || defined(ART_ENABLE_CODEGEN_arm64) + void VisitMultiplyAccumulate(HMultiplyAccumulate* instruction) OVERRIDE { + StartAttributeStream("kind") << instruction->GetOpKind(); + } +#endif + #ifdef ART_ENABLE_CODEGEN_arm64 void VisitArm64DataProcWithShifterOp(HArm64DataProcWithShifterOp* instruction) OVERRIDE { StartAttributeStream("kind") << instruction->GetInstrKind() << "+" << instruction->GetOpKind(); @@ -443,10 +449,6 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor { StartAttributeStream("shift") << instruction->GetShiftAmount(); } } - - void VisitArm64MultiplyAccumulate(HArm64MultiplyAccumulate* instruction) OVERRIDE { - StartAttributeStream("kind") << instruction->GetOpKind(); - } #endif bool IsPass(const char* name) { diff --git a/compiler/optimizing/instruction_simplifier_arm.cc b/compiler/optimizing/instruction_simplifier_arm.cc new file mode 100644 index 0000000000..db1f9a79aa --- /dev/null +++ b/compiler/optimizing/instruction_simplifier_arm.cc @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "instruction_simplifier_arm.h" +#include "instruction_simplifier_shared.h" + +namespace art { +namespace arm { + +void InstructionSimplifierArmVisitor::VisitMul(HMul* instruction) { + if (TryCombineMultiplyAccumulate(instruction, kArm)) { + RecordSimplification(); + } +} + +} // namespace arm +} // namespace art diff --git a/compiler/optimizing/instruction_simplifier_arm.h b/compiler/optimizing/instruction_simplifier_arm.h new file mode 100644 index 0000000000..379b95d6ae --- /dev/null +++ b/compiler/optimizing/instruction_simplifier_arm.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_ARM_H_ +#define ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_ARM_H_ + +#include "nodes.h" +#include "optimization.h" + +namespace art { +namespace arm { + +class InstructionSimplifierArmVisitor : public HGraphVisitor { + public: + InstructionSimplifierArmVisitor(HGraph* graph, OptimizingCompilerStats* stats) + : HGraphVisitor(graph), stats_(stats) {} + + private: + void RecordSimplification() { + if (stats_ != nullptr) { + stats_->RecordStat(kInstructionSimplificationsArch); + } + } + + void VisitMul(HMul* instruction) OVERRIDE; + + OptimizingCompilerStats* stats_; +}; + + +class InstructionSimplifierArm : public HOptimization { + public: + InstructionSimplifierArm(HGraph* graph, OptimizingCompilerStats* stats) + : HOptimization(graph, "instruction_simplifier_arm", stats) {} + + void Run() OVERRIDE { + InstructionSimplifierArmVisitor visitor(graph_, stats_); + visitor.VisitReversePostOrder(); + } +}; + +} // namespace arm +} // namespace art + +#endif // ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_ARM_H_ diff --git a/compiler/optimizing/instruction_simplifier_arm64.cc b/compiler/optimizing/instruction_simplifier_arm64.cc index 4bcfc54791..83126a5c4d 100644 --- a/compiler/optimizing/instruction_simplifier_arm64.cc +++ b/compiler/optimizing/instruction_simplifier_arm64.cc @@ -17,6 +17,7 @@ #include "instruction_simplifier_arm64.h" #include "common_arm64.h" +#include "instruction_simplifier_shared.h" #include "mirror/array-inl.h" namespace art { @@ -179,67 +180,6 @@ bool InstructionSimplifierArm64Visitor::TryMergeIntoUsersShifterOperand(HInstruc return true; } -bool InstructionSimplifierArm64Visitor::TrySimpleMultiplyAccumulatePatterns( - HMul* mul, HBinaryOperation* input_binop, HInstruction* input_other) { - DCHECK(Primitive::IsIntOrLongType(mul->GetType())); - DCHECK(input_binop->IsAdd() || input_binop->IsSub()); - DCHECK_NE(input_binop, input_other); - if (!input_binop->HasOnlyOneNonEnvironmentUse()) { - return false; - } - - // Try to interpret patterns like - // a * (b <+/-> 1) - // as - // (a * b) <+/-> a - HInstruction* input_a = input_other; - HInstruction* input_b = nullptr; // Set to a non-null value if we found a pattern to optimize. - HInstruction::InstructionKind op_kind; - - if (input_binop->IsAdd()) { - if ((input_binop->GetConstantRight() != nullptr) && input_binop->GetConstantRight()->IsOne()) { - // Interpret - // a * (b + 1) - // as - // (a * b) + a - input_b = input_binop->GetLeastConstantLeft(); - op_kind = HInstruction::kAdd; - } - } else { - DCHECK(input_binop->IsSub()); - if (input_binop->GetRight()->IsConstant() && - input_binop->GetRight()->AsConstant()->IsMinusOne()) { - // Interpret - // a * (b - (-1)) - // as - // a + (a * b) - input_b = input_binop->GetLeft(); - op_kind = HInstruction::kAdd; - } else if (input_binop->GetLeft()->IsConstant() && - input_binop->GetLeft()->AsConstant()->IsOne()) { - // Interpret - // a * (1 - b) - // as - // a - (a * b) - input_b = input_binop->GetRight(); - op_kind = HInstruction::kSub; - } - } - - if (input_b == nullptr) { - // We did not find a pattern we can optimize. - return false; - } - - HArm64MultiplyAccumulate* mulacc = new(GetGraph()->GetArena()) HArm64MultiplyAccumulate( - mul->GetType(), op_kind, input_a, input_a, input_b, mul->GetDexPc()); - - mul->GetBlock()->ReplaceAndRemoveInstructionWith(mul, mulacc); - input_binop->GetBlock()->RemoveInstruction(input_binop); - - return false; -} - void InstructionSimplifierArm64Visitor::VisitArrayGet(HArrayGet* instruction) { TryExtractArrayAccessAddress(instruction, instruction->GetArray(), @@ -255,75 +195,8 @@ void InstructionSimplifierArm64Visitor::VisitArraySet(HArraySet* instruction) { } void InstructionSimplifierArm64Visitor::VisitMul(HMul* instruction) { - Primitive::Type type = instruction->GetType(); - if (!Primitive::IsIntOrLongType(type)) { - return; - } - - HInstruction* use = instruction->HasNonEnvironmentUses() - ? instruction->GetUses().GetFirst()->GetUser() - : nullptr; - - if (instruction->HasOnlyOneNonEnvironmentUse() && (use->IsAdd() || use->IsSub())) { - // Replace code looking like - // MUL tmp, x, y - // SUB dst, acc, tmp - // with - // MULSUB dst, acc, x, y - // Note that we do not want to (unconditionally) perform the merge when the - // multiplication has multiple uses and it can be merged in all of them. - // Multiple uses could happen on the same control-flow path, and we would - // then increase the amount of work. In the future we could try to evaluate - // whether all uses are on different control-flow paths (using dominance and - // reverse-dominance information) and only perform the merge when they are. - HInstruction* accumulator = nullptr; - HBinaryOperation* binop = use->AsBinaryOperation(); - HInstruction* binop_left = binop->GetLeft(); - HInstruction* binop_right = binop->GetRight(); - // Be careful after GVN. This should not happen since the `HMul` has only - // one use. - DCHECK_NE(binop_left, binop_right); - if (binop_right == instruction) { - accumulator = binop_left; - } else if (use->IsAdd()) { - DCHECK_EQ(binop_left, instruction); - accumulator = binop_right; - } - - if (accumulator != nullptr) { - HArm64MultiplyAccumulate* mulacc = - new (GetGraph()->GetArena()) HArm64MultiplyAccumulate(type, - binop->GetKind(), - accumulator, - instruction->GetLeft(), - instruction->GetRight()); - - binop->GetBlock()->ReplaceAndRemoveInstructionWith(binop, mulacc); - DCHECK(!instruction->HasUses()); - instruction->GetBlock()->RemoveInstruction(instruction); - RecordSimplification(); - return; - } - } - - // Use multiply accumulate instruction for a few simple patterns. - // We prefer not applying the following transformations if the left and - // right inputs perform the same operation. - // We rely on GVN having squashed the inputs if appropriate. However the - // results are still correct even if that did not happen. - if (instruction->GetLeft() == instruction->GetRight()) { - return; - } - - HInstruction* left = instruction->GetLeft(); - HInstruction* right = instruction->GetRight(); - if ((right->IsAdd() || right->IsSub()) && - TrySimpleMultiplyAccumulatePatterns(instruction, right->AsBinaryOperation(), left)) { - return; - } - if ((left->IsAdd() || left->IsSub()) && - TrySimpleMultiplyAccumulatePatterns(instruction, left->AsBinaryOperation(), right)) { - return; + if (TryCombineMultiplyAccumulate(instruction, kArm64)) { + RecordSimplification(); } } diff --git a/compiler/optimizing/instruction_simplifier_arm64.h b/compiler/optimizing/instruction_simplifier_arm64.h index b7f490bb8c..37a34c0373 100644 --- a/compiler/optimizing/instruction_simplifier_arm64.h +++ b/compiler/optimizing/instruction_simplifier_arm64.h @@ -51,10 +51,6 @@ class InstructionSimplifierArm64Visitor : public HGraphVisitor { return TryMergeIntoShifterOperand(use, bitfield_op, true); } - bool TrySimpleMultiplyAccumulatePatterns(HMul* mul, - HBinaryOperation* input_binop, - HInstruction* input_other); - // HInstruction visitors, sorted alphabetically. void VisitArrayGet(HArrayGet* instruction) OVERRIDE; void VisitArraySet(HArraySet* instruction) OVERRIDE; diff --git a/compiler/optimizing/instruction_simplifier_shared.cc b/compiler/optimizing/instruction_simplifier_shared.cc new file mode 100644 index 0000000000..45d196fa6d --- /dev/null +++ b/compiler/optimizing/instruction_simplifier_shared.cc @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "instruction_simplifier_shared.h" + +namespace art { + +namespace { + +bool TrySimpleMultiplyAccumulatePatterns(HMul* mul, + HBinaryOperation* input_binop, + HInstruction* input_other) { + DCHECK(Primitive::IsIntOrLongType(mul->GetType())); + DCHECK(input_binop->IsAdd() || input_binop->IsSub()); + DCHECK_NE(input_binop, input_other); + if (!input_binop->HasOnlyOneNonEnvironmentUse()) { + return false; + } + + // Try to interpret patterns like + // a * (b <+/-> 1) + // as + // (a * b) <+/-> a + HInstruction* input_a = input_other; + HInstruction* input_b = nullptr; // Set to a non-null value if we found a pattern to optimize. + HInstruction::InstructionKind op_kind; + + if (input_binop->IsAdd()) { + if ((input_binop->GetConstantRight() != nullptr) && input_binop->GetConstantRight()->IsOne()) { + // Interpret + // a * (b + 1) + // as + // (a * b) + a + input_b = input_binop->GetLeastConstantLeft(); + op_kind = HInstruction::kAdd; + } + } else { + DCHECK(input_binop->IsSub()); + if (input_binop->GetRight()->IsConstant() && + input_binop->GetRight()->AsConstant()->IsMinusOne()) { + // Interpret + // a * (b - (-1)) + // as + // a + (a * b) + input_b = input_binop->GetLeft(); + op_kind = HInstruction::kAdd; + } else if (input_binop->GetLeft()->IsConstant() && + input_binop->GetLeft()->AsConstant()->IsOne()) { + // Interpret + // a * (1 - b) + // as + // a - (a * b) + input_b = input_binop->GetRight(); + op_kind = HInstruction::kSub; + } + } + + if (input_b == nullptr) { + // We did not find a pattern we can optimize. + return false; + } + + ArenaAllocator* arena = mul->GetBlock()->GetGraph()->GetArena(); + HMultiplyAccumulate* mulacc = new(arena) HMultiplyAccumulate( + mul->GetType(), op_kind, input_a, input_a, input_b, mul->GetDexPc()); + + mul->GetBlock()->ReplaceAndRemoveInstructionWith(mul, mulacc); + input_binop->GetBlock()->RemoveInstruction(input_binop); + + return true; +} + +} // namespace + +bool TryCombineMultiplyAccumulate(HMul* mul, InstructionSet isa) { + Primitive::Type type = mul->GetType(); + switch (isa) { + case kArm: + case kThumb2: + if (type != Primitive::kPrimInt) { + return false; + } + break; + case kArm64: + if (!Primitive::IsIntOrLongType(type)) { + return false; + } + break; + default: + return false; + } + + HInstruction* use = mul->HasNonEnvironmentUses() + ? mul->GetUses().GetFirst()->GetUser() + : nullptr; + + ArenaAllocator* arena = mul->GetBlock()->GetGraph()->GetArena(); + + if (mul->HasOnlyOneNonEnvironmentUse()) { + if (use->IsAdd() || use->IsSub()) { + // Replace code looking like + // MUL tmp, x, y + // SUB dst, acc, tmp + // with + // MULSUB dst, acc, x, y + // Note that we do not want to (unconditionally) perform the merge when the + // multiplication has multiple uses and it can be merged in all of them. + // Multiple uses could happen on the same control-flow path, and we would + // then increase the amount of work. In the future we could try to evaluate + // whether all uses are on different control-flow paths (using dominance and + // reverse-dominance information) and only perform the merge when they are. + HInstruction* accumulator = nullptr; + HBinaryOperation* binop = use->AsBinaryOperation(); + HInstruction* binop_left = binop->GetLeft(); + HInstruction* binop_right = binop->GetRight(); + // Be careful after GVN. This should not happen since the `HMul` has only + // one use. + DCHECK_NE(binop_left, binop_right); + if (binop_right == mul) { + accumulator = binop_left; + } else if (use->IsAdd()) { + DCHECK_EQ(binop_left, mul); + accumulator = binop_right; + } + + if (accumulator != nullptr) { + HMultiplyAccumulate* mulacc = + new (arena) HMultiplyAccumulate(type, + binop->GetKind(), + accumulator, + mul->GetLeft(), + mul->GetRight()); + + binop->GetBlock()->ReplaceAndRemoveInstructionWith(binop, mulacc); + DCHECK(!mul->HasUses()); + mul->GetBlock()->RemoveInstruction(mul); + return true; + } + } else if (use->IsNeg() && isa != kArm) { + HMultiplyAccumulate* mulacc = + new (arena) HMultiplyAccumulate(type, + HInstruction::kSub, + mul->GetBlock()->GetGraph()->GetConstant(type, 0), + mul->GetLeft(), + mul->GetRight()); + + use->GetBlock()->ReplaceAndRemoveInstructionWith(use, mulacc); + DCHECK(!mul->HasUses()); + mul->GetBlock()->RemoveInstruction(mul); + return true; + } + } + + // Use multiply accumulate instruction for a few simple patterns. + // We prefer not applying the following transformations if the left and + // right inputs perform the same operation. + // We rely on GVN having squashed the inputs if appropriate. However the + // results are still correct even if that did not happen. + if (mul->GetLeft() == mul->GetRight()) { + return false; + } + + HInstruction* left = mul->GetLeft(); + HInstruction* right = mul->GetRight(); + if ((right->IsAdd() || right->IsSub()) && + TrySimpleMultiplyAccumulatePatterns(mul, right->AsBinaryOperation(), left)) { + return true; + } + if ((left->IsAdd() || left->IsSub()) && + TrySimpleMultiplyAccumulatePatterns(mul, left->AsBinaryOperation(), right)) { + return true; + } + return false; +} + +} // namespace art diff --git a/compiler/optimizing/instruction_simplifier_shared.h b/compiler/optimizing/instruction_simplifier_shared.h new file mode 100644 index 0000000000..9832ecc058 --- /dev/null +++ b/compiler/optimizing/instruction_simplifier_shared.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_SHARED_H_ +#define ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_SHARED_H_ + +#include "nodes.h" + +namespace art { + +bool TryCombineMultiplyAccumulate(HMul* mul, InstructionSet isa); + +} // namespace art + +#endif // ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_SHARED_H_ diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 399afabea6..2b7d2dec8a 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -1247,6 +1247,16 @@ class HLoopInformationOutwardIterator : public ValueObject { M(UShr, BinaryOperation) \ M(Xor, BinaryOperation) \ +/* + * Instructions, shared across several (not all) architectures. + */ +#if !defined(ART_ENABLE_CODEGEN_arm) && !defined(ART_ENABLE_CODEGEN_arm64) +#define FOR_EACH_CONCRETE_INSTRUCTION_SHARED(M) +#else +#define FOR_EACH_CONCRETE_INSTRUCTION_SHARED(M) \ + M(MultiplyAccumulate, Instruction) +#endif + #ifndef ART_ENABLE_CODEGEN_arm #define FOR_EACH_CONCRETE_INSTRUCTION_ARM(M) #else @@ -1259,8 +1269,7 @@ class HLoopInformationOutwardIterator : public ValueObject { #else #define FOR_EACH_CONCRETE_INSTRUCTION_ARM64(M) \ M(Arm64DataProcWithShifterOp, Instruction) \ - M(Arm64IntermediateAddress, Instruction) \ - M(Arm64MultiplyAccumulate, Instruction) + M(Arm64IntermediateAddress, Instruction) #endif #define FOR_EACH_CONCRETE_INSTRUCTION_MIPS(M) @@ -1281,6 +1290,7 @@ class HLoopInformationOutwardIterator : public ValueObject { #define FOR_EACH_CONCRETE_INSTRUCTION(M) \ FOR_EACH_CONCRETE_INSTRUCTION_COMMON(M) \ + FOR_EACH_CONCRETE_INSTRUCTION_SHARED(M) \ FOR_EACH_CONCRETE_INSTRUCTION_ARM(M) \ FOR_EACH_CONCRETE_INSTRUCTION_ARM64(M) \ FOR_EACH_CONCRETE_INSTRUCTION_MIPS(M) \ @@ -6060,6 +6070,9 @@ class HParallelMove : public HTemplateInstruction<0> { } // namespace art +#if defined(ART_ENABLE_CODEGEN_arm) || defined(ART_ENABLE_CODEGEN_arm64) +#include "nodes_shared.h" +#endif #ifdef ART_ENABLE_CODEGEN_arm #include "nodes_arm.h" #endif diff --git a/compiler/optimizing/nodes_arm64.h b/compiler/optimizing/nodes_arm64.h index 445cdab191..173852a55d 100644 --- a/compiler/optimizing/nodes_arm64.h +++ b/compiler/optimizing/nodes_arm64.h @@ -118,40 +118,6 @@ class HArm64IntermediateAddress : public HExpression<2> { DISALLOW_COPY_AND_ASSIGN(HArm64IntermediateAddress); }; -class HArm64MultiplyAccumulate : public HExpression<3> { - public: - HArm64MultiplyAccumulate(Primitive::Type type, - InstructionKind op, - HInstruction* accumulator, - HInstruction* mul_left, - HInstruction* mul_right, - uint32_t dex_pc = kNoDexPc) - : HExpression(type, SideEffects::None(), dex_pc), op_kind_(op) { - SetRawInputAt(kInputAccumulatorIndex, accumulator); - SetRawInputAt(kInputMulLeftIndex, mul_left); - SetRawInputAt(kInputMulRightIndex, mul_right); - } - - static constexpr int kInputAccumulatorIndex = 0; - static constexpr int kInputMulLeftIndex = 1; - static constexpr int kInputMulRightIndex = 2; - - bool CanBeMoved() const OVERRIDE { return true; } - bool InstructionDataEquals(HInstruction* other) const OVERRIDE { - return op_kind_ == other->AsArm64MultiplyAccumulate()->op_kind_; - } - - InstructionKind GetOpKind() const { return op_kind_; } - - DECLARE_INSTRUCTION(Arm64MultiplyAccumulate); - - private: - // Indicates if this is a MADD or MSUB. - InstructionKind op_kind_; - - DISALLOW_COPY_AND_ASSIGN(HArm64MultiplyAccumulate); -}; - } // namespace art #endif // ART_COMPILER_OPTIMIZING_NODES_ARM64_H_ diff --git a/compiler/optimizing/nodes_shared.h b/compiler/optimizing/nodes_shared.h new file mode 100644 index 0000000000..b04b622838 --- /dev/null +++ b/compiler/optimizing/nodes_shared.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_COMPILER_OPTIMIZING_NODES_SHARED_H_ +#define ART_COMPILER_OPTIMIZING_NODES_SHARED_H_ + +namespace art { + +class HMultiplyAccumulate : public HExpression<3> { + public: + HMultiplyAccumulate(Primitive::Type type, + InstructionKind op, + HInstruction* accumulator, + HInstruction* mul_left, + HInstruction* mul_right, + uint32_t dex_pc = kNoDexPc) + : HExpression(type, SideEffects::None(), dex_pc), op_kind_(op) { + SetRawInputAt(kInputAccumulatorIndex, accumulator); + SetRawInputAt(kInputMulLeftIndex, mul_left); + SetRawInputAt(kInputMulRightIndex, mul_right); + } + + static constexpr int kInputAccumulatorIndex = 0; + static constexpr int kInputMulLeftIndex = 1; + static constexpr int kInputMulRightIndex = 2; + + bool CanBeMoved() const OVERRIDE { return true; } + bool InstructionDataEquals(HInstruction* other) const OVERRIDE { + return op_kind_ == other->AsMultiplyAccumulate()->op_kind_; + } + + InstructionKind GetOpKind() const { return op_kind_; } + + DECLARE_INSTRUCTION(MultiplyAccumulate); + + private: + // Indicates if this is a MADD or MSUB. + const InstructionKind op_kind_; + + DISALLOW_COPY_AND_ASSIGN(HMultiplyAccumulate); +}; + +} // namespace art + +#endif // ART_COMPILER_OPTIMIZING_NODES_SHARED_H_ diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index b1891c979e..5a9f2583fd 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -60,6 +60,7 @@ #include "induction_var_analysis.h" #include "inliner.h" #include "instruction_simplifier.h" +#include "instruction_simplifier_arm.h" #include "intrinsics.h" #include "jit/debugger_interface.h" #include "jit/jit_code_cache.h" @@ -438,7 +439,10 @@ static void RunArchOptimizations(InstructionSet instruction_set, case kThumb2: case kArm: { arm::DexCacheArrayFixups* fixups = new (arena) arm::DexCacheArrayFixups(graph, stats); + arm::InstructionSimplifierArm* simplifier = + new (arena) arm::InstructionSimplifierArm(graph, stats); HOptimization* arm_optimizations[] = { + simplifier, fixups }; RunOptimizations(arm_optimizations, arraysize(arm_optimizations), pass_observer); diff --git a/test/550-checker-multiply-accumulate/src/Main.java b/test/550-checker-multiply-accumulate/src/Main.java index 2d0688d57e..24a4613724 100644 --- a/test/550-checker-multiply-accumulate/src/Main.java +++ b/test/550-checker-multiply-accumulate/src/Main.java @@ -47,7 +47,7 @@ public class Main { /// CHECK: <> ParameterValue /// CHECK: <> ParameterValue /// CHECK: <> ParameterValue - /// CHECK: <> Arm64MultiplyAccumulate [<>,<>,<>] kind:Add + /// CHECK: <> MultiplyAccumulate [<>,<>,<>] kind:Add /// CHECK: Return [<>] /// CHECK-START-ARM64: int Main.$opt$noinline$mulAdd(int, int, int) instruction_simplifier_arm64 (after) @@ -57,6 +57,28 @@ public class Main { /// CHECK-START-ARM64: int Main.$opt$noinline$mulAdd(int, int, int) disassembly (after) /// CHECK: madd w{{\d+}}, w{{\d+}}, w{{\d+}}, w{{\d+}} + /// CHECK-START-ARM: int Main.$opt$noinline$mulAdd(int, int, int) instruction_simplifier_arm (before) + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> Mul [<>,<>] + /// CHECK: <> Add [<>,<>] + /// CHECK: Return [<>] + + /// CHECK-START-ARM: int Main.$opt$noinline$mulAdd(int, int, int) instruction_simplifier_arm (after) + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> MultiplyAccumulate [<>,<>,<>] kind:Add + /// CHECK: Return [<>] + + /// CHECK-START-ARM: int Main.$opt$noinline$mulAdd(int, int, int) instruction_simplifier_arm (after) + /// CHECK-NOT: Mul + /// CHECK-NOT: Add + + /// CHECK-START-ARM: int Main.$opt$noinline$mulAdd(int, int, int) disassembly (after) + /// CHECK: mla r{{\d+}}, r{{\d+}}, r{{\d+}}, r{{\d+}} + public static int $opt$noinline$mulAdd(int acc, int left, int right) { if (doThrow) throw new Error(); return acc + left * right; @@ -78,7 +100,7 @@ public class Main { /// CHECK: <> ParameterValue /// CHECK: <> ParameterValue /// CHECK: <> ParameterValue - /// CHECK: <> Arm64MultiplyAccumulate [<>,<>,<>] kind:Sub + /// CHECK: <> MultiplyAccumulate [<>,<>,<>] kind:Sub /// CHECK: Return [<>] /// CHECK-START-ARM64: long Main.$opt$noinline$mulSub(long, long, long) instruction_simplifier_arm64 (after) @@ -88,6 +110,17 @@ public class Main { /// CHECK-START-ARM64: long Main.$opt$noinline$mulSub(long, long, long) disassembly (after) /// CHECK: msub x{{\d+}}, x{{\d+}}, x{{\d+}}, x{{\d+}} + /// CHECK-START-ARM: long Main.$opt$noinline$mulSub(long, long, long) instruction_simplifier_arm (before) + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> Mul [<>,<>] + /// CHECK: <> Sub [<>,<>] + /// CHECK: Return [<>] + + /// CHECK-START-ARM: long Main.$opt$noinline$mulSub(long, long, long) instruction_simplifier_arm (after) + /// CHECK-NOT: MultiplyAccumulate + public static long $opt$noinline$mulSub(long acc, long left, long right) { if (doThrow) throw new Error(); return acc - left * right; @@ -117,7 +150,28 @@ public class Main { /// CHECK: Return [<>] /// CHECK-START-ARM64: int Main.$opt$noinline$multipleUses1(int, int, int) instruction_simplifier_arm64 (after) - /// CHECK-NOT: Arm64MultiplyAccumulate + /// CHECK-NOT: MultiplyAccumulate + + /// CHECK-START-ARM: int Main.$opt$noinline$multipleUses1(int, int, int) instruction_simplifier_arm (before) + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> Mul [<>,<>] + /// CHECK: <> Add [<>,<>] + /// CHECK: <> Or [<>,<>] + /// CHECK: Return [<>] + + /// CHECK-START-ARM: int Main.$opt$noinline$multipleUses1(int, int, int) instruction_simplifier_arm (after) + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> Mul [<>,<>] + /// CHECK: <> Add [<>,<>] + /// CHECK: <> Or [<>,<>] + /// CHECK: Return [<>] + + /// CHECK-START-ARM: int Main.$opt$noinline$multipleUses1(int, int, int) instruction_simplifier_arm (after) + /// CHECK-NOT: MultiplyAccumulate public static int $opt$noinline$multipleUses1(int acc, int left, int right) { if (doThrow) throw new Error(); @@ -151,7 +205,30 @@ public class Main { /// CHECK: Return [<>] /// CHECK-START-ARM64: long Main.$opt$noinline$multipleUses2(long, long, long) instruction_simplifier_arm64 (after) - /// CHECK-NOT: Arm64MultiplyAccumulate + /// CHECK-NOT: MultiplyAccumulate + + /// CHECK-START-ARM: long Main.$opt$noinline$multipleUses2(long, long, long) instruction_simplifier_arm (before) + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> Mul [<>,<>] + /// CHECK: <> Add [<>,<>] + /// CHECK: <> Sub [<>,<>] + /// CHECK: <> Add [<>,<>] + /// CHECK: Return [<>] + + /// CHECK-START-ARM: long Main.$opt$noinline$multipleUses2(long, long, long) instruction_simplifier_arm (after) + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> Mul [<>,<>] + /// CHECK: <> Add [<>,<>] + /// CHECK: <> Sub [<>,<>] + /// CHECK: <> Add [<>,<>] + /// CHECK: Return [<>] + + /// CHECK-START-ARM: long Main.$opt$noinline$multipleUses2(long, long, long) instruction_simplifier_arm (after) + /// CHECK-NOT: MultiplyAccumulate public static long $opt$noinline$multipleUses2(long acc, long left, long right) { @@ -176,7 +253,7 @@ public class Main { /// CHECK-START-ARM64: int Main.$opt$noinline$mulPlusOne(int, int) instruction_simplifier_arm64 (after) /// CHECK: <> ParameterValue /// CHECK: <> ParameterValue - /// CHECK: <> Arm64MultiplyAccumulate [<>,<>,<>] kind:Add + /// CHECK: <> MultiplyAccumulate [<>,<>,<>] kind:Add /// CHECK: Return [<>] /// CHECK-START-ARM64: int Main.$opt$noinline$mulPlusOne(int, int) instruction_simplifier_arm64 (after) @@ -186,6 +263,27 @@ public class Main { /// CHECK-START-ARM64: int Main.$opt$noinline$mulPlusOne(int, int) disassembly (after) /// CHECK: madd w{{\d+}}, w{{\d+}}, w{{\d+}}, w{{\d+}} + /// CHECK-START-ARM: int Main.$opt$noinline$mulPlusOne(int, int) instruction_simplifier_arm (before) + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> IntConstant 1 + /// CHECK: <> Add [<>,<>] + /// CHECK: <> Mul [<>,<>] + /// CHECK: Return [<>] + + /// CHECK-START-ARM: int Main.$opt$noinline$mulPlusOne(int, int) instruction_simplifier_arm (after) + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> MultiplyAccumulate [<>,<>,<>] kind:Add + /// CHECK: Return [<>] + + /// CHECK-START-ARM: int Main.$opt$noinline$mulPlusOne(int, int) instruction_simplifier_arm (after) + /// CHECK-NOT: Mul + /// CHECK-NOT: Add + + /// CHECK-START-ARM: int Main.$opt$noinline$mulPlusOne(int, int) disassembly (after) + /// CHECK: mla r{{\d+}}, r{{\d+}}, r{{\d+}}, r{{\d+}} + public static int $opt$noinline$mulPlusOne(int acc, int var) { if (doThrow) throw new Error(); return acc * (var + 1); @@ -207,7 +305,7 @@ public class Main { /// CHECK-START-ARM64: long Main.$opt$noinline$mulMinusOne(long, long) instruction_simplifier_arm64 (after) /// CHECK: <> ParameterValue /// CHECK: <> ParameterValue - /// CHECK: <> Arm64MultiplyAccumulate [<>,<>,<>] kind:Sub + /// CHECK: <> MultiplyAccumulate [<>,<>,<>] kind:Sub /// CHECK: Return [<>] /// CHECK-START-ARM64: long Main.$opt$noinline$mulMinusOne(long, long) instruction_simplifier_arm64 (after) @@ -217,11 +315,114 @@ public class Main { /// CHECK-START-ARM64: long Main.$opt$noinline$mulMinusOne(long, long) disassembly (after) /// CHECK: msub x{{\d+}}, x{{\d+}}, x{{\d+}}, x{{\d+}} + /// CHECK-START-ARM: long Main.$opt$noinline$mulMinusOne(long, long) instruction_simplifier_arm (before) + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> LongConstant 1 + /// CHECK: <> Sub [<>,<>] + /// CHECK: <> Mul [<>,<>] + /// CHECK: Return [<>] + + /// CHECK-START-ARM: long Main.$opt$noinline$mulMinusOne(long, long) instruction_simplifier_arm (after) + /// CHECK-NOT: MultiplyAccumulate public static long $opt$noinline$mulMinusOne(long acc, long var) { if (doThrow) throw new Error(); return acc * (1 - var); } + /** + * Test basic merging of `MUL+NEG` into `MULNEG`. + */ + + /// CHECK-START-ARM64: int Main.$opt$noinline$mulNeg(int, int) instruction_simplifier_arm64 (before) + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> Mul [<>,<>] + /// CHECK: <> Neg [<>] + /// CHECK: Return [<>] + + /// CHECK-START-ARM64: int Main.$opt$noinline$mulNeg(int, int) instruction_simplifier_arm64 (after) + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> IntConstant 0 + /// CHECK: <> MultiplyAccumulate [<>,<>,<>] kind:Sub + /// CHECK: Return [<>] + + /// CHECK-START-ARM64: int Main.$opt$noinline$mulNeg(int, int) instruction_simplifier_arm64 (after) + /// CHECK-NOT: Mul + /// CHECK-NOT: Neg + + /// CHECK-START-ARM64: int Main.$opt$noinline$mulNeg(int, int) disassembly (after) + /// CHECK: mneg w{{\d+}}, w{{\d+}}, w{{\d+}} + + /// CHECK-START-ARM: int Main.$opt$noinline$mulNeg(int, int) instruction_simplifier_arm (before) + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> Mul [<>,<>] + /// CHECK: <> Neg [<>] + /// CHECK: Return [<>] + + /// CHECK-START-ARM: int Main.$opt$noinline$mulNeg(int, int) instruction_simplifier_arm (after) + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> Mul [<>,<>] + /// CHECK: <> Neg [<>] + /// CHECK: Return [<>] + + /// CHECK-START-ARM: int Main.$opt$noinline$mulNeg(int, int) instruction_simplifier_arm (after) + /// CHECK-NOT: MultiplyAccumulate + + public static int $opt$noinline$mulNeg(int left, int right) { + if (doThrow) throw new Error(); + return - (left * right); + } + + /** + * Test basic merging of `MUL+NEG` into `MULNEG`. + */ + + /// CHECK-START-ARM64: long Main.$opt$noinline$mulNeg(long, long) instruction_simplifier_arm64 (before) + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> Mul [<>,<>] + /// CHECK: <> Neg [<>] + /// CHECK: Return [<>] + + /// CHECK-START-ARM64: long Main.$opt$noinline$mulNeg(long, long) instruction_simplifier_arm64 (after) + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> LongConstant 0 + /// CHECK: <> MultiplyAccumulate [<>,<>,<>] kind:Sub + /// CHECK: Return [<>] + + /// CHECK-START-ARM64: long Main.$opt$noinline$mulNeg(long, long) instruction_simplifier_arm64 (after) + /// CHECK-NOT: Mul + /// CHECK-NOT: Neg + + /// CHECK-START-ARM64: long Main.$opt$noinline$mulNeg(long, long) disassembly (after) + /// CHECK: mneg x{{\d+}}, x{{\d+}}, x{{\d+}} + + /// CHECK-START-ARM: long Main.$opt$noinline$mulNeg(long, long) instruction_simplifier_arm (before) + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> Mul [<>,<>] + /// CHECK: <> Neg [<>] + /// CHECK: Return [<>] + + /// CHECK-START-ARM: long Main.$opt$noinline$mulNeg(long, long) instruction_simplifier_arm (after) + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> Mul [<>,<>] + /// CHECK: <> Neg [<>] + /// CHECK: Return [<>] + + /// CHECK-START-ARM: long Main.$opt$noinline$mulNeg(long, long) instruction_simplifier_arm (after) + /// CHECK-NOT: MultiplyAccumulate + + public static long $opt$noinline$mulNeg(long left, long right) { + if (doThrow) throw new Error(); + return - (left * right); + } public static void main(String[] args) { assertIntEquals(7, $opt$noinline$mulAdd(1, 2, 3)); @@ -230,5 +431,7 @@ public class Main { assertLongEquals(20, $opt$noinline$multipleUses2(10, 11, 12)); assertIntEquals(195, $opt$noinline$mulPlusOne(13, 14)); assertLongEquals(-225, $opt$noinline$mulMinusOne(15, 16)); + assertIntEquals(-306, $opt$noinline$mulNeg(17, 18)); + assertLongEquals(-380, $opt$noinline$mulNeg(19, 20)); } } -- GitLab From bef5eba8e2c6fe0ca753dfe4431de1aa4a658f7a Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Thu, 25 Feb 2016 12:40:27 +0000 Subject: [PATCH 038/204] Split 530-checker-loops to please our run-test file size limit. Change-Id: Ib03303435918b333691625f3999b937072f8ec31 --- test/530-checker-loops/src/Main.java | 962 ------------------------- test/530-checker-loops2/expected.txt | 0 test/530-checker-loops2/info.txt | 1 + test/530-checker-loops2/src/Main.java | 999 ++++++++++++++++++++++++++ 4 files changed, 1000 insertions(+), 962 deletions(-) create mode 100644 test/530-checker-loops2/expected.txt create mode 100644 test/530-checker-loops2/info.txt create mode 100644 test/530-checker-loops2/src/Main.java diff --git a/test/530-checker-loops/src/Main.java b/test/530-checker-loops/src/Main.java index 0c32491e4e..8633745a8a 100644 --- a/test/530-checker-loops/src/Main.java +++ b/test/530-checker-loops/src/Main.java @@ -605,729 +605,7 @@ public class Main { } } - /// CHECK-START: void Main.bubble(int[]) BCE (before) - /// CHECK-DAG: BoundsCheck - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: void Main.bubble(int[]) BCE (after) - /// CHECK-NOT: BoundsCheck - // TODO: also CHECK-NOT: Deoptimize, see b/27151190 - private static void bubble(int[] a) { - for (int i = a.length; --i >= 0;) { - for (int j = 0; j < i; j++) { - if (a[j] > a[j+1]) { - int tmp = a[j]; - a[j] = a[j+1]; - a[j+1] = tmp; - } - } - } - } - - /// CHECK-START: int Main.periodicIdiom(int) BCE (before) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: int Main.periodicIdiom(int) BCE (after) - /// CHECK-NOT: BoundsCheck - /// CHECK-NOT: Deoptimize - private static int periodicIdiom(int tc) { - int[] x = { 1, 3 }; - // Loop with periodic sequence (0, 1). - int k = 0; - int result = 0; - for (int i = 0; i < tc; i++) { - result += x[k]; - k = 1 - k; - } - return result; - } - - /// CHECK-START: int Main.periodicSequence2(int) BCE (before) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: int Main.periodicSequence2(int) BCE (after) - /// CHECK-NOT: BoundsCheck - /// CHECK-NOT: Deoptimize - private static int periodicSequence2(int tc) { - int[] x = { 1, 3 }; - // Loop with periodic sequence (0, 1). - int k = 0; - int l = 1; - int result = 0; - for (int i = 0; i < tc; i++) { - result += x[k]; - int t = l; - l = k; - k = t; - } - return result; - } - - /// CHECK-START: int Main.periodicSequence4(int) BCE (before) - /// CHECK-DAG: BoundsCheck - /// CHECK-DAG: BoundsCheck - /// CHECK-DAG: BoundsCheck - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: int Main.periodicSequence4(int) BCE (after) - /// CHECK-NOT: BoundsCheck - /// CHECK-NOT: Deoptimize - private static int periodicSequence4(int tc) { - int[] x = { 1, 3, 5, 7 }; - // Loop with periodic sequence (0, 1, 2, 3). - int k = 0; - int l = 1; - int m = 2; - int n = 3; - int result = 0; - for (int i = 0; i < tc; i++) { - result += x[k] + x[l] + x[m] + x[n]; // all used at once - int t = n; - n = k; - k = l; - l = m; - m = t; - } - return result; - } - - /// CHECK-START: int Main.justRightUp1() BCE (before) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: int Main.justRightUp1() BCE (after) - /// CHECK-NOT: BoundsCheck - /// CHECK-NOT: Deoptimize - private static int justRightUp1() { - int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; - int result = 0; - for (int i = Integer.MAX_VALUE - 10, k = 0; i < Integer.MAX_VALUE; i++) { - result += x[k++]; - } - return result; - } - - /// CHECK-START: int Main.justRightUp2() BCE (before) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: int Main.justRightUp2() BCE (after) - /// CHECK-NOT: BoundsCheck - /// CHECK-NOT: Deoptimize - private static int justRightUp2() { - int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; - int result = 0; - for (int i = Integer.MAX_VALUE - 10; i < Integer.MAX_VALUE; i++) { - result += x[i - Integer.MAX_VALUE + 10]; - } - return result; - } - - /// CHECK-START: int Main.justRightUp3() BCE (before) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: int Main.justRightUp3() BCE (after) - /// CHECK-NOT: BoundsCheck - /// CHECK-NOT: Deoptimize - private static int justRightUp3() { - int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; - int result = 0; - for (int i = Integer.MAX_VALUE - 10, k = 0; i <= Integer.MAX_VALUE - 1; i++) { - result += x[k++]; - } - return result; - } - - /// CHECK-START: int Main.justOOBUp() BCE (before) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: int Main.justOOBUp() BCE (after) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: int Main.justOOBUp() BCE (after) - /// CHECK-NOT: Deoptimize - private static int justOOBUp() { - int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; - int result = 0; - // Infinite loop! - for (int i = Integer.MAX_VALUE - 9, k = 0; i <= Integer.MAX_VALUE; i++) { - result += x[k++]; - } - return result; - } - - /// CHECK-START: int Main.justRightDown1() BCE (before) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: int Main.justRightDown1() BCE (after) - /// CHECK-NOT: BoundsCheck - /// CHECK-NOT: Deoptimize - private static int justRightDown1() { - int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; - int result = 0; - for (int i = Integer.MIN_VALUE + 10, k = 0; i > Integer.MIN_VALUE; i--) { - result += x[k++]; - } - return result; - } - - /// CHECK-START: int Main.justRightDown2() BCE (before) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: int Main.justRightDown2() BCE (after) - /// CHECK-NOT: BoundsCheck - /// CHECK-NOT: Deoptimize - private static int justRightDown2() { - int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; - int result = 0; - for (int i = Integer.MIN_VALUE + 10; i > Integer.MIN_VALUE; i--) { - result += x[Integer.MAX_VALUE + i]; - } - return result; - } - - /// CHECK-START: int Main.justRightDown3() BCE (before) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: int Main.justRightDown3() BCE (after) - /// CHECK-NOT: BoundsCheck - /// CHECK-NOT: Deoptimize - private static int justRightDown3() { - int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; - int result = 0; - for (int i = Integer.MIN_VALUE + 10, k = 0; i >= Integer.MIN_VALUE + 1; i--) { - result += x[k++]; - } - return result; - } - - /// CHECK-START: int Main.justOOBDown() BCE (before) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: int Main.justOOBDown() BCE (after) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: int Main.justOOBDown() BCE (after) - /// CHECK-NOT: Deoptimize - private static int justOOBDown() { - int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; - int result = 0; - // Infinite loop! - for (int i = Integer.MIN_VALUE + 9, k = 0; i >= Integer.MIN_VALUE; i--) { - result += x[k++]; - } - return result; - } - - /// CHECK-START: void Main.lowerOOB(int[]) BCE (before) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: void Main.lowerOOB(int[]) BCE (after) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: void Main.lowerOOB(int[]) BCE (after) - /// CHECK-NOT: Deoptimize - private static void lowerOOB(int[] x) { - // OOB! - for (int i = -1; i < x.length; i++) { - sResult += x[i]; - } - } - - /// CHECK-START: void Main.upperOOB(int[]) BCE (before) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: void Main.upperOOB(int[]) BCE (after) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: void Main.upperOOB(int[]) BCE (after) - /// CHECK-NOT: Deoptimize - private static void upperOOB(int[] x) { - // OOB! - for (int i = 0; i <= x.length; i++) { - sResult += x[i]; - } - } - - /// CHECK-START: void Main.doWhileUpOOB() BCE (before) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: void Main.doWhileUpOOB() BCE (after) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: void Main.doWhileUpOOB() BCE (after) - /// CHECK-NOT: Deoptimize - private static void doWhileUpOOB() { - int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; - int i = 0; - // OOB! - do { - sResult += x[i++]; - } while (i <= x.length); - } - - /// CHECK-START: void Main.doWhileDownOOB() BCE (before) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: void Main.doWhileDownOOB() BCE (after) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: void Main.doWhileDownOOB() BCE (after) - /// CHECK-NOT: Deoptimize - private static void doWhileDownOOB() { - int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; - int i = x.length - 1; - // OOB! - do { - sResult += x[i--]; - } while (-1 <= i); - } - - /// CHECK-START: void Main.hiddenOOB1(int) BCE (before) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: void Main.hiddenOOB1(int) BCE (after) - /// CHECK-DAG: Deoptimize - // - /// CHECK-START: void Main.hiddenOOB1(int) BCE (after) - /// CHECK-NOT: BoundsCheck - private static void hiddenOOB1(int lo) { - int[] a = { 1 } ; - for (int i = lo; i <= 10; i++) { - // Dangerous loop where careless static range analysis would yield strict upper bound - // on index j of 5. When, for instance, lo and thus i = -2147483648, the upper bound - // becomes really positive due to arithmetic wrap-around, causing OOB. - // Dynamic BCE is feasible though, since it checks the range. - for (int j = 4; j < i - 5; j++) { - sResult += a[j - 4]; - } - } - } - - /// CHECK-START: void Main.hiddenOOB2(int) BCE (before) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: void Main.hiddenOOB2(int) BCE (after) - /// CHECK-DAG: Deoptimize - // - /// CHECK-START: void Main.hiddenOOB2(int) BCE (after) - /// CHECK-NOT: BoundsCheck - private static void hiddenOOB2(int hi) { - int[] a = { 1 } ; - for (int i = 0; i < hi; i++) { - // Dangerous loop where careless static range analysis would yield strict lower bound - // on index j of 5. When, for instance, hi and thus i = 2147483647, the upper bound - // becomes really negative due to arithmetic wrap-around, causing OOB. - // Dynamic BCE is feasible though, since it checks the range. - for (int j = 6; j > i + 5; j--) { - sResult += a[j - 6]; - } - } - } - - /// CHECK-START: void Main.hiddenInfiniteOOB() BCE (before) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: void Main.hiddenInfiniteOOB() BCE (after) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: void Main.hiddenInfiniteOOB() BCE (after) - /// CHECK-NOT: Deoptimize - private static void hiddenInfiniteOOB() { - int[] a = { 11 } ; - for (int i = -1; i <= 0; i++) { - // Dangerous loop where careless static range analysis would yield a safe upper bound - // of -3. In reality, due to arithmetic wrap-around (when i = -1, j <= 2147483647; - // whereas when i = 0, j <= -3), this is an infinite loop that goes OOB. - for (int j = -3; j <= 2147483646 * i - 3; j++) { - sResult += a[j + 3]; - } - } - } - - /// CHECK-START: void Main.hiddenFiniteOOB() BCE (before) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: void Main.hiddenFiniteOOB() BCE (after) - /// CHECK-DAG: Deoptimize - // - /// CHECK-START: void Main.hiddenFiniteOOB() BCE (after) - /// CHECK-NOT: BoundsCheck - private static void hiddenFiniteOOB() { - int[] a = { 111 } ; - for (int i = -1; i <= 0; i++) { - // Dangerous loop similar as above where the loop is now finite, but the - // loop still goes out of bounds for i = -1 due to the large upper bound. - // Dynamic BCE is feasible though, since it checks the range. - for (int j = -4; j < 2147483646 * i - 3; j++) { - sResult += a[j + 4]; - } - } - } - - /// CHECK-START: int[] Main.add() BCE (before) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: int[] Main.add() BCE (after) - /// CHECK-NOT: BoundsCheck - /// CHECK-NOT: Deoptimize - private static int[] add() { - int[] a = new int[10]; - for (int i = 0; i <= 3; i++) { - for (int j = 0; j <= 6; j++) { - a[i + j] += 1; - } - } - return a; - } - - /// CHECK-START: int[] Main.multiply1() BCE (before) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: int[] Main.multiply1() BCE (after) - /// CHECK-NOT: BoundsCheck - /// CHECK-NOT: Deoptimize - private static int[] multiply1() { - int[] a = new int[10]; - try { - for (int i = 0; i <= 3; i++) { - for (int j = 0; j <= 3; j++) { - // Range [0,9]: safe. - a[i * j] += 1; - } - } - } catch (Exception e) { - a[0] += 1000; - } - return a; - } - - /// CHECK-START: int[] Main.multiply2() BCE (before) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: int[] Main.multiply2() BCE (after) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: int[] Main.multiply2() BCE (after) - /// CHECK-NOT: Deoptimize - static int[] multiply2() { - int[] a = new int[10]; - try { - for (int i = -3; i <= 3; i++) { - for (int j = -3; j <= 3; j++) { - // Range [-9,9]: unsafe. - a[i * j] += 1; - } - } - } catch (Exception e) { - a[0] += 1000; - } - return a; - } - - /// CHECK-START: int Main.linearDynamicBCE1(int[], int, int) BCE (before) - /// CHECK-DAG: ArrayGet loop:<> - /// CHECK-DAG: NullCheck loop:<> - /// CHECK-DAG: ArrayLength loop:<> - /// CHECK-DAG: BoundsCheck loop:<> - // - /// CHECK-START: int Main.linearDynamicBCE1(int[], int, int) BCE (after) - /// CHECK-DAG: ArrayGet loop:{{B\d+}} - /// CHECK-DAG: Deoptimize loop:none - // - /// CHECK-START: int Main.linearDynamicBCE1(int[], int, int) BCE (after) - /// CHECK-NOT: NullCheck loop:{{B\d+}} - /// CHECK-NOT: ArrayLength loop:{{B\d+}} - /// CHECK-NOT: BoundsCheck loop:{{B\d+}} - private static int linearDynamicBCE1(int[] x, int lo, int hi) { - int result = 0; - for (int i = lo; i < hi; i++) { - sResult += x[i]; - } - return result; - } - - /// CHECK-START: int Main.linearDynamicBCE2(int[], int, int, int) BCE (before) - /// CHECK-DAG: ArrayGet loop:<> - /// CHECK-DAG: NullCheck loop:<> - /// CHECK-DAG: ArrayLength loop:<> - /// CHECK-DAG: BoundsCheck loop:<> - // - /// CHECK-START: int Main.linearDynamicBCE2(int[], int, int, int) BCE (after) - /// CHECK-DAG: ArrayGet loop:{{B\d+}} - /// CHECK-DAG: Deoptimize loop:none - // - /// CHECK-START: int Main.linearDynamicBCE2(int[], int, int, int) BCE (after) - /// CHECK-NOT: NullCheck loop:{{B\d+}} - /// CHECK-NOT: ArrayLength loop:{{B\d+}} - /// CHECK-NOT: BoundsCheck loop:{{B\d+}} - private static int linearDynamicBCE2(int[] x, int lo, int hi, int offset) { - int result = 0; - for (int i = lo; i < hi; i++) { - sResult += x[offset + i]; - } - return result; - } - - /// CHECK-START: int Main.wrapAroundDynamicBCE(int[]) BCE (before) - /// CHECK-DAG: ArrayGet loop:<> - /// CHECK-DAG: NullCheck loop:<> - /// CHECK-DAG: ArrayLength loop:<> - /// CHECK-DAG: BoundsCheck loop:<> - // - /// CHECK-START: int Main.wrapAroundDynamicBCE(int[]) BCE (after) - /// CHECK-DAG: ArrayGet loop:{{B\d+}} - /// CHECK-DAG: Deoptimize loop:none - // - /// CHECK-START: int Main.wrapAroundDynamicBCE(int[]) BCE (after) - /// CHECK-NOT: NullCheck loop:{{B\d+}} - /// CHECK-NOT: ArrayLength loop:{{B\d+}} - /// CHECK-NOT: BoundsCheck loop:{{B\d+}} - private static int wrapAroundDynamicBCE(int[] x) { - int w = 9; - int result = 0; - for (int i = 0; i < 10; i++) { - result += x[w]; - w = i; - } - return result; - } - - /// CHECK-START: int Main.periodicDynamicBCE(int[]) BCE (before) - /// CHECK-DAG: ArrayGet loop:<> - /// CHECK-DAG: NullCheck loop:<> - /// CHECK-DAG: ArrayLength loop:<> - /// CHECK-DAG: BoundsCheck loop:<> - // - /// CHECK-START: int Main.periodicDynamicBCE(int[]) BCE (after) - /// CHECK-DAG: ArrayGet loop:{{B\d+}} - /// CHECK-DAG: Deoptimize loop:none - // - /// CHECK-START: int Main.periodicDynamicBCE(int[]) BCE (after) - /// CHECK-NOT: NullCheck loop:{{B\d+}} - /// CHECK-NOT: ArrayLength loop:{{B\d+}} - /// CHECK-NOT: BoundsCheck loop:{{B\d+}} - private static int periodicDynamicBCE(int[] x) { - int k = 0; - int result = 0; - for (int i = 0; i < 10; i++) { - result += x[k]; - k = 1 - k; - } - return result; - } - - /// CHECK-START: int Main.dynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (before) - /// CHECK-DAG: ArrayGet loop:<> - /// CHECK-DAG: NullCheck loop:<> - /// CHECK-DAG: ArrayLength loop:<> - /// CHECK-DAG: BoundsCheck loop:<> - // - /// CHECK-START: int Main.dynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (after) - /// CHECK-DAG: ArrayGet loop:{{B\d+}} - /// CHECK-DAG: Deoptimize loop:none - // - /// CHECK-START: int Main.dynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (after) - /// CHECK-NOT: NullCheck loop:{{B\d+}} - /// CHECK-NOT: ArrayLength loop:{{B\d+}} - /// CHECK-NOT: BoundsCheck loop:{{B\d+}} - static int dynamicBCEPossiblyInfiniteLoop(int[] x, int lo, int hi) { - // This loop could be infinite for hi = max int. Since i is also used - // as subscript, however, dynamic bce can proceed. - int result = 0; - for (int i = lo; i <= hi; i++) { - result += x[i]; - } - return result; - } - - /// CHECK-START: int Main.noDynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (before) - /// CHECK-DAG: ArrayGet loop:<> - /// CHECK-DAG: BoundsCheck loop:<> - // - /// CHECK-START: int Main.noDynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (after) - /// CHECK-DAG: ArrayGet loop:<> - /// CHECK-DAG: BoundsCheck loop:<> - // - /// CHECK-START: int Main.noDynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (after) - /// CHECK-NOT: Deoptimize - static int noDynamicBCEPossiblyInfiniteLoop(int[] x, int lo, int hi) { - // As above, but now the index is not used as subscript, - // and dynamic bce is not applied. - int result = 0; - for (int k = 0, i = lo; i <= hi; i++) { - result += x[k++]; - } - return result; - } - - /// CHECK-START: int Main.noDynamicBCEMixedInductionTypes(int[], long, long) BCE (before) - /// CHECK-DAG: ArrayGet loop:<> - /// CHECK-DAG: BoundsCheck loop:<> - // - /// CHECK-START: int Main.noDynamicBCEMixedInductionTypes(int[], long, long) BCE (after) - /// CHECK-DAG: ArrayGet loop:<> - /// CHECK-DAG: BoundsCheck loop:<> - // - /// CHECK-START: int Main.noDynamicBCEMixedInductionTypes(int[], long, long) BCE (after) - /// CHECK-NOT: Deoptimize - static int noDynamicBCEMixedInductionTypes(int[] x, long lo, long hi) { - int result = 0; - // Mix of int and long induction. - int k = 0; - for (long i = lo; i < hi; i++) { - result += x[k++]; - } - return result; - } - - /// CHECK-START: int Main.dynamicBCEConstantRange(int[]) BCE (before) - /// CHECK-DAG: BoundsCheck loop:<> - /// CHECK-DAG: ArrayGet loop:<> - /// CHECK-DAG: If loop:<> - /// CHECK-DAG: If loop:<> - /// CHECK-EVAL: "<>" != "<>" - // - /// CHECK-START: int Main.dynamicBCEConstantRange(int[]) BCE (after) - /// CHECK-DAG: ArrayGet loop:<> - /// CHECK-DAG: Deoptimize loop:<> - /// CHECK-EVAL: "<>" != "<>" - // - /// CHECK-START: int Main.dynamicBCEConstantRange(int[]) BCE (after) - /// CHECK-NOT: BoundsCheck - // - // No additional top tests were introduced. - /// CHECK-START: int Main.dynamicBCEConstantRange(int[]) BCE (after) - /// CHECK-DAG: If - /// CHECK-DAG: If - /// CHECK-NOT: If - static int dynamicBCEConstantRange(int[] x) { - int result = 0; - for (int i = 2; i <= 6; i++) { - // Range analysis sees that innermost loop is finite and always taken. - for (int j = i - 2; j <= i + 2; j++) { - result += x[j]; - } - } - return result; - } - - /// CHECK-START: int Main.dynamicBCEAndConstantIndices(int[], int[][], int, int) BCE (before) - /// CHECK-DAG: {{l\d+}} ArrayGet loop:<> - /// CHECK-DAG: {{l\d+}} ArrayGet loop:<> - /// CHECK-DAG: {{l\d+}} ArrayGet loop:<> - // - /// CHECK-START: int Main.dynamicBCEAndConstantIndices(int[], int[][], int, int) BCE (after) - // Order matters: - /// CHECK: Deoptimize loop:<> - // CHECK-NOT: Goto loop:<> - /// CHECK-DAG: {{l\d+}} ArrayGet loop:<> - /// CHECK-DAG: {{l\d+}} ArrayGet loop:<> - /// CHECK-DAG: {{l\d+}} ArrayGet loop:<> - /// CHECK: Goto loop:<> - // - /// CHECK-START: int Main.dynamicBCEAndConstantIndices(int[], int[][], int, int) BCE (after) - /// CHECK-DAG: Deoptimize loop:none - static int dynamicBCEAndConstantIndices(int[] x, int[][] a, int lo, int hi) { - // Deliberately test array length on a before the loop so that only bounds checks - // on constant subscripts remain, making them a viable candidate for hoisting. - if (a.length == 0) { - return -1; - } - // Loop that allows BCE on x[i]. - int result = 0; - for (int i = lo; i < hi; i++) { - result += x[i]; - if ((i % 10) != 0) { - // None of the subscripts inside a conditional are removed by dynamic bce, - // making them a candidate for deoptimization based on constant indices. - // Compiler should ensure the array loads are not subsequently hoisted - // "above" the deoptimization "barrier" on the bounds. - a[0][i] = 1; - a[1][i] = 2; - a[99][i] = 3; - } - } - return result; - } - - /// CHECK-START: int Main.dynamicBCEAndConstantIndicesAllPrimTypes(int[], boolean[], byte[], char[], short[], int[], long[], float[], double[], int, int) BCE (before) - /// CHECK-DAG: ArrayGet loop:<> - /// CHECK-DAG: ArrayGet loop:<> - /// CHECK-DAG: ArrayGet loop:<> - /// CHECK-DAG: ArrayGet loop:<> - /// CHECK-DAG: ArrayGet loop:<> - /// CHECK-DAG: ArrayGet loop:<> - /// CHECK-DAG: ArrayGet loop:<> - /// CHECK-DAG: ArrayGet loop:<> - /// CHECK-DAG: ArrayGet loop:<> - // For brevity, just test occurrence of at least one of each in the loop: - /// CHECK-DAG: NullCheck loop:<> - /// CHECK-DAG: ArrayLength loop:<> - /// CHECK-DAG: BoundsCheck loop:<> - // - /// CHECK-START: int Main.dynamicBCEAndConstantIndicesAllPrimTypes(int[], boolean[], byte[], char[], short[], int[], long[], float[], double[], int, int) BCE (after) - /// CHECK-DAG: ArrayGet loop:<> - /// CHECK-NOT: ArrayGet loop:<> - // - /// CHECK-START: int Main.dynamicBCEAndConstantIndicesAllPrimTypes(int[], boolean[], byte[], char[], short[], int[], long[], float[], double[], int, int) BCE (after) - /// CHECK-NOT: NullCheck loop:{{B\d+}} - /// CHECK-NOT: ArrayLength loop:{{B\d+}} - /// CHECK-NOT: BoundsCheck loop:{{B\d+}} - // - /// CHECK-START: int Main.dynamicBCEAndConstantIndicesAllPrimTypes(int[], boolean[], byte[], char[], short[], int[], long[], float[], double[], int, int) BCE (after) - /// CHECK-DAG: Deoptimize loop:none - static int dynamicBCEAndConstantIndicesAllPrimTypes(int[] q, - boolean[] r, - byte[] s, - char[] t, - short[] u, - int[] v, - long[] w, - float[] x, - double[] y, int lo, int hi) { - int result = 0; - for (int i = lo; i < hi; i++) { - // All constant index array references can be hoisted out of the loop during BCE on q[i]. - result += q[i] + (r[0] ? 1 : 0) + (int) s[0] + (int) t[0] + (int) u[0] + (int) v[0] + - (int) w[0] + (int) x[0] + (int) y[0]; - } - return result; - } - - /// CHECK-START: int Main.dynamicBCEAndConstantIndexRefType(int[], java.lang.Integer[], int, int) BCE (before) - /// CHECK-DAG: ArrayGet loop:<> - /// CHECK-DAG: NullCheck loop:<> - /// CHECK-DAG: ArrayLength loop:<> - /// CHECK-DAG: BoundsCheck loop:<> - /// CHECK-DAG: ArrayGet loop:<> - /// CHECK-DAG: NullCheck loop:<> - /// CHECK-DAG: ArrayLength loop:<> - /// CHECK-DAG: BoundsCheck loop:<> - // - /// CHECK-START: int Main.dynamicBCEAndConstantIndexRefType(int[], java.lang.Integer[], int, int) BCE (after) - /// CHECK-DAG: ArrayGet loop:<> - /// CHECK-DAG: Deoptimize loop:none - // - /// CHECK-START: int Main.dynamicBCEAndConstantIndexRefType(int[], java.lang.Integer[], int, int) BCE (after) - /// CHECK-NOT: ArrayLength loop:{{B\d+}} - /// CHECK-NOT: BoundsCheck loop:{{B\d+}} - static int dynamicBCEAndConstantIndexRefType(int[] q, Integer[] z, int lo, int hi) { - int result = 0; - for (int i = lo; i < hi; i++) { - // Similar to above, but now implicit call to intValue() may prevent hoisting - // z[0] itself during BCE on q[i]. Therefore, we just check BCE on q[i]. - result += q[i] + z[0]; - } - return result; - } - - // - // Verifier. - // - public static void main(String[] args) { - // Set to run expensive tests for correctness too. - boolean HEAVY = false; - int[] empty = { }; int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; @@ -1401,246 +679,6 @@ public class Main { linearTriangularOnParameter(10); linearTriangularVariationsInnerStrict(10); linearTriangularVariationsInnerNonStrict(10); - - // Sorting. - int[] sort = { 5, 4, 1, 9, 10, 2, 7, 6, 3, 8 }; - bubble(sort); - for (int i = 0; i < 10; i++) { - expectEquals(sort[i], x[i]); - } - - // Periodic adds (1, 3), one at the time. - expectEquals(0, periodicIdiom(-1)); - for (int tc = 0; tc < 32; tc++) { - int expected = (tc >> 1) << 2; - if ((tc & 1) != 0) - expected += 1; - expectEquals(expected, periodicIdiom(tc)); - } - - // Periodic adds (1, 3), one at the time. - expectEquals(0, periodicSequence2(-1)); - for (int tc = 0; tc < 32; tc++) { - int expected = (tc >> 1) << 2; - if ((tc & 1) != 0) - expected += 1; - expectEquals(expected, periodicSequence2(tc)); - } - - // Periodic adds (1, 3, 5, 7), all at once. - expectEquals(0, periodicSequence4(-1)); - for (int tc = 0; tc < 32; tc++) { - expectEquals(tc * 16, periodicSequence4(tc)); - } - - // Large bounds. - expectEquals(55, justRightUp1()); - expectEquals(55, justRightUp2()); - expectEquals(55, justRightUp3()); - expectEquals(55, justRightDown1()); - expectEquals(55, justRightDown2()); - expectEquals(55, justRightDown3()); - sResult = 0; - try { - justOOBUp(); - } catch (ArrayIndexOutOfBoundsException e) { - sResult = 1; - } - expectEquals(1, sResult); - sResult = 0; - try { - justOOBDown(); - } catch (ArrayIndexOutOfBoundsException e) { - sResult = 1; - } - expectEquals(1, sResult); - - // Lower bound goes OOB. - sResult = 0; - try { - lowerOOB(x); - } catch (ArrayIndexOutOfBoundsException e) { - sResult += 1000; - } - expectEquals(1000, sResult); - - // Upper bound goes OOB. - sResult = 0; - try { - upperOOB(x); - } catch (ArrayIndexOutOfBoundsException e) { - sResult += 1000; - } - expectEquals(1055, sResult); - - // Do while up goes OOB. - sResult = 0; - try { - doWhileUpOOB(); - } catch (ArrayIndexOutOfBoundsException e) { - sResult += 1000; - } - expectEquals(1055, sResult); - - // Do while down goes OOB. - sResult = 0; - try { - doWhileDownOOB(); - } catch (ArrayIndexOutOfBoundsException e) { - sResult += 1000; - } - expectEquals(1055, sResult); - - // Hidden OOB. - sResult = 0; - try { - hiddenOOB1(10); // no OOB - } catch (ArrayIndexOutOfBoundsException e) { - sResult += 1000; - } - expectEquals(1, sResult); - sResult = 0; - try { - hiddenOOB1(-2147483648); // OOB - } catch (ArrayIndexOutOfBoundsException e) { - sResult += 1000; - } - expectEquals(1001, sResult); - sResult = 0; - try { - hiddenOOB2(1); // no OOB - } catch (ArrayIndexOutOfBoundsException e) { - sResult += 1000; - } - expectEquals(1, sResult); - if (HEAVY) { - sResult = 0; - try { - hiddenOOB2(2147483647); // OOB - } catch (ArrayIndexOutOfBoundsException e) { - sResult += 1000; - } - expectEquals(1002, sResult); - } - sResult = 0; - try { - hiddenInfiniteOOB(); - } catch (ArrayIndexOutOfBoundsException e) { - sResult += 1000; - } - expectEquals(1011, sResult); - sResult = 0; - try { - hiddenFiniteOOB(); - } catch (ArrayIndexOutOfBoundsException e) { - sResult += 1000; - } - expectEquals(1111, sResult); - - // Addition. - { - int[] e1 ={ 1, 2, 3, 4, 4, 4, 4, 3, 2, 1 }; - int[] a1 = add(); - for (int i = 0; i < 10; i++) { - expectEquals(a1[i], e1[i]); - } - } - - // Multiplication. - { - int[] e1 = { 7, 1, 2, 2, 1, 0, 2, 0, 0, 1 }; - int[] a1 = multiply1(); - for (int i = 0; i < 10; i++) { - expectEquals(a1[i], e1[i]); - } - int[] e2 = { 1001, 0, 0, 1, 0, 0, 1, 0, 0, 1 }; - int[] a2 = multiply2(); - for (int i = 0; i < 10; i++) { - expectEquals(a2[i], e2[i]); - } - } - - // Dynamic BCE. - sResult = 0; - try { - linearDynamicBCE1(x, -1, x.length); - } catch (ArrayIndexOutOfBoundsException e) { - sResult += 1000; - } - expectEquals(1000, sResult); - sResult = 0; - linearDynamicBCE1(x, 0, x.length); - expectEquals(55, sResult); - sResult = 0; - try { - linearDynamicBCE1(x, 0, x.length + 1); - } catch (ArrayIndexOutOfBoundsException e) { - sResult += 1000; - } - expectEquals(1055, sResult); - - // Dynamic BCE with offset. - sResult = 0; - try { - linearDynamicBCE2(x, 0, x.length, -1); - } catch (ArrayIndexOutOfBoundsException e) { - sResult += 1000; - } - expectEquals(1000, sResult); - sResult = 0; - linearDynamicBCE2(x, 0, x.length, 0); - expectEquals(55, sResult); - sResult = 0; - try { - linearDynamicBCE2(x, 0, x.length, 1); - } catch (ArrayIndexOutOfBoundsException e) { - sResult += 1000; - } - expectEquals(1054, sResult); - - // Dynamic BCE candidates. - expectEquals(55, wrapAroundDynamicBCE(x)); - expectEquals(15, periodicDynamicBCE(x)); - expectEquals(55, dynamicBCEPossiblyInfiniteLoop(x, 0, 9)); - expectEquals(55, noDynamicBCEPossiblyInfiniteLoop(x, 0, 9)); - expectEquals(55, noDynamicBCEMixedInductionTypes(x, 0, 10)); - expectEquals(125, dynamicBCEConstantRange(x)); - - // Dynamic BCE combined with constant indices. - int[][] a; - a = new int[0][0]; - expectEquals(-1, dynamicBCEAndConstantIndices(x, a, 0, 10)); - a = new int[100][10]; - expectEquals(55, dynamicBCEAndConstantIndices(x, a, 0, 10)); - for (int i = 0; i < 10; i++) { - expectEquals((i % 10) != 0 ? 1 : 0, a[0][i]); - expectEquals((i % 10) != 0 ? 2 : 0, a[1][i]); - expectEquals((i % 10) != 0 ? 3 : 0, a[99][i]); - } - a = new int[2][10]; - sResult = 0; - try { - expectEquals(55, dynamicBCEAndConstantIndices(x, a, 0, 10)); - } catch (ArrayIndexOutOfBoundsException e) { - sResult = 1; - } - expectEquals(1, sResult); - expectEquals(a[0][1], 1); - expectEquals(a[1][1], 2); - - // Dynamic BCE combined with constant indices of all types. - boolean[] x1 = { true }; - byte[] x2 = { 2 }; - char[] x3 = { 3 }; - short[] x4 = { 4 }; - int[] x5 = { 5 }; - long[] x6 = { 6 }; - float[] x7 = { 7 }; - double[] x8 = { 8 }; - expectEquals(415, - dynamicBCEAndConstantIndicesAllPrimTypes(x, x1, x2, x3, x4, x5, x6, x7, x8, 0, 10)); - Integer[] x9 = { 9 }; - expectEquals(145, dynamicBCEAndConstantIndexRefType(x, x9, 0, 10)); } private static void expectEquals(int expected, int result) { diff --git a/test/530-checker-loops2/expected.txt b/test/530-checker-loops2/expected.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/530-checker-loops2/info.txt b/test/530-checker-loops2/info.txt new file mode 100644 index 0000000000..f5d334d011 --- /dev/null +++ b/test/530-checker-loops2/info.txt @@ -0,0 +1 @@ +Test on loop optimizations. diff --git a/test/530-checker-loops2/src/Main.java b/test/530-checker-loops2/src/Main.java new file mode 100644 index 0000000000..64be1a2be4 --- /dev/null +++ b/test/530-checker-loops2/src/Main.java @@ -0,0 +1,999 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// Test on loop optimizations. +// +public class Main { + + static int sResult; + + // + // Various sequence variables used in bound checks. + // + + /// CHECK-START: void Main.bubble(int[]) BCE (before) + /// CHECK-DAG: BoundsCheck + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: void Main.bubble(int[]) BCE (after) + /// CHECK-NOT: BoundsCheck + // TODO: also CHECK-NOT: Deoptimize, see b/27151190 + private static void bubble(int[] a) { + for (int i = a.length; --i >= 0;) { + for (int j = 0; j < i; j++) { + if (a[j] > a[j+1]) { + int tmp = a[j]; + a[j] = a[j+1]; + a[j+1] = tmp; + } + } + } + } + + /// CHECK-START: int Main.periodicIdiom(int) BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: int Main.periodicIdiom(int) BCE (after) + /// CHECK-NOT: BoundsCheck + /// CHECK-NOT: Deoptimize + private static int periodicIdiom(int tc) { + int[] x = { 1, 3 }; + // Loop with periodic sequence (0, 1). + int k = 0; + int result = 0; + for (int i = 0; i < tc; i++) { + result += x[k]; + k = 1 - k; + } + return result; + } + + /// CHECK-START: int Main.periodicSequence2(int) BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: int Main.periodicSequence2(int) BCE (after) + /// CHECK-NOT: BoundsCheck + /// CHECK-NOT: Deoptimize + private static int periodicSequence2(int tc) { + int[] x = { 1, 3 }; + // Loop with periodic sequence (0, 1). + int k = 0; + int l = 1; + int result = 0; + for (int i = 0; i < tc; i++) { + result += x[k]; + int t = l; + l = k; + k = t; + } + return result; + } + + /// CHECK-START: int Main.periodicSequence4(int) BCE (before) + /// CHECK-DAG: BoundsCheck + /// CHECK-DAG: BoundsCheck + /// CHECK-DAG: BoundsCheck + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: int Main.periodicSequence4(int) BCE (after) + /// CHECK-NOT: BoundsCheck + /// CHECK-NOT: Deoptimize + private static int periodicSequence4(int tc) { + int[] x = { 1, 3, 5, 7 }; + // Loop with periodic sequence (0, 1, 2, 3). + int k = 0; + int l = 1; + int m = 2; + int n = 3; + int result = 0; + for (int i = 0; i < tc; i++) { + result += x[k] + x[l] + x[m] + x[n]; // all used at once + int t = n; + n = k; + k = l; + l = m; + m = t; + } + return result; + } + + /// CHECK-START: int Main.justRightUp1() BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: int Main.justRightUp1() BCE (after) + /// CHECK-NOT: BoundsCheck + /// CHECK-NOT: Deoptimize + private static int justRightUp1() { + int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + int result = 0; + for (int i = Integer.MAX_VALUE - 10, k = 0; i < Integer.MAX_VALUE; i++) { + result += x[k++]; + } + return result; + } + + /// CHECK-START: int Main.justRightUp2() BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: int Main.justRightUp2() BCE (after) + /// CHECK-NOT: BoundsCheck + /// CHECK-NOT: Deoptimize + private static int justRightUp2() { + int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + int result = 0; + for (int i = Integer.MAX_VALUE - 10; i < Integer.MAX_VALUE; i++) { + result += x[i - Integer.MAX_VALUE + 10]; + } + return result; + } + + /// CHECK-START: int Main.justRightUp3() BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: int Main.justRightUp3() BCE (after) + /// CHECK-NOT: BoundsCheck + /// CHECK-NOT: Deoptimize + private static int justRightUp3() { + int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + int result = 0; + for (int i = Integer.MAX_VALUE - 10, k = 0; i <= Integer.MAX_VALUE - 1; i++) { + result += x[k++]; + } + return result; + } + + /// CHECK-START: int Main.justOOBUp() BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: int Main.justOOBUp() BCE (after) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: int Main.justOOBUp() BCE (after) + /// CHECK-NOT: Deoptimize + private static int justOOBUp() { + int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + int result = 0; + // Infinite loop! + for (int i = Integer.MAX_VALUE - 9, k = 0; i <= Integer.MAX_VALUE; i++) { + result += x[k++]; + } + return result; + } + + /// CHECK-START: int Main.justRightDown1() BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: int Main.justRightDown1() BCE (after) + /// CHECK-NOT: BoundsCheck + /// CHECK-NOT: Deoptimize + private static int justRightDown1() { + int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + int result = 0; + for (int i = Integer.MIN_VALUE + 10, k = 0; i > Integer.MIN_VALUE; i--) { + result += x[k++]; + } + return result; + } + + /// CHECK-START: int Main.justRightDown2() BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: int Main.justRightDown2() BCE (after) + /// CHECK-NOT: BoundsCheck + /// CHECK-NOT: Deoptimize + private static int justRightDown2() { + int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + int result = 0; + for (int i = Integer.MIN_VALUE + 10; i > Integer.MIN_VALUE; i--) { + result += x[Integer.MAX_VALUE + i]; + } + return result; + } + + /// CHECK-START: int Main.justRightDown3() BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: int Main.justRightDown3() BCE (after) + /// CHECK-NOT: BoundsCheck + /// CHECK-NOT: Deoptimize + private static int justRightDown3() { + int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + int result = 0; + for (int i = Integer.MIN_VALUE + 10, k = 0; i >= Integer.MIN_VALUE + 1; i--) { + result += x[k++]; + } + return result; + } + + /// CHECK-START: int Main.justOOBDown() BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: int Main.justOOBDown() BCE (after) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: int Main.justOOBDown() BCE (after) + /// CHECK-NOT: Deoptimize + private static int justOOBDown() { + int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + int result = 0; + // Infinite loop! + for (int i = Integer.MIN_VALUE + 9, k = 0; i >= Integer.MIN_VALUE; i--) { + result += x[k++]; + } + return result; + } + + /// CHECK-START: void Main.lowerOOB(int[]) BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: void Main.lowerOOB(int[]) BCE (after) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: void Main.lowerOOB(int[]) BCE (after) + /// CHECK-NOT: Deoptimize + private static void lowerOOB(int[] x) { + // OOB! + for (int i = -1; i < x.length; i++) { + sResult += x[i]; + } + } + + /// CHECK-START: void Main.upperOOB(int[]) BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: void Main.upperOOB(int[]) BCE (after) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: void Main.upperOOB(int[]) BCE (after) + /// CHECK-NOT: Deoptimize + private static void upperOOB(int[] x) { + // OOB! + for (int i = 0; i <= x.length; i++) { + sResult += x[i]; + } + } + + /// CHECK-START: void Main.doWhileUpOOB() BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: void Main.doWhileUpOOB() BCE (after) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: void Main.doWhileUpOOB() BCE (after) + /// CHECK-NOT: Deoptimize + private static void doWhileUpOOB() { + int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + int i = 0; + // OOB! + do { + sResult += x[i++]; + } while (i <= x.length); + } + + /// CHECK-START: void Main.doWhileDownOOB() BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: void Main.doWhileDownOOB() BCE (after) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: void Main.doWhileDownOOB() BCE (after) + /// CHECK-NOT: Deoptimize + private static void doWhileDownOOB() { + int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + int i = x.length - 1; + // OOB! + do { + sResult += x[i--]; + } while (-1 <= i); + } + + /// CHECK-START: void Main.hiddenOOB1(int) BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: void Main.hiddenOOB1(int) BCE (after) + /// CHECK-DAG: Deoptimize + // + /// CHECK-START: void Main.hiddenOOB1(int) BCE (after) + /// CHECK-NOT: BoundsCheck + private static void hiddenOOB1(int lo) { + int[] a = { 1 } ; + for (int i = lo; i <= 10; i++) { + // Dangerous loop where careless static range analysis would yield strict upper bound + // on index j of 5. When, for instance, lo and thus i = -2147483648, the upper bound + // becomes really positive due to arithmetic wrap-around, causing OOB. + // Dynamic BCE is feasible though, since it checks the range. + for (int j = 4; j < i - 5; j++) { + sResult += a[j - 4]; + } + } + } + + /// CHECK-START: void Main.hiddenOOB2(int) BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: void Main.hiddenOOB2(int) BCE (after) + /// CHECK-DAG: Deoptimize + // + /// CHECK-START: void Main.hiddenOOB2(int) BCE (after) + /// CHECK-NOT: BoundsCheck + private static void hiddenOOB2(int hi) { + int[] a = { 1 } ; + for (int i = 0; i < hi; i++) { + // Dangerous loop where careless static range analysis would yield strict lower bound + // on index j of 5. When, for instance, hi and thus i = 2147483647, the upper bound + // becomes really negative due to arithmetic wrap-around, causing OOB. + // Dynamic BCE is feasible though, since it checks the range. + for (int j = 6; j > i + 5; j--) { + sResult += a[j - 6]; + } + } + } + + /// CHECK-START: void Main.hiddenInfiniteOOB() BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: void Main.hiddenInfiniteOOB() BCE (after) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: void Main.hiddenInfiniteOOB() BCE (after) + /// CHECK-NOT: Deoptimize + private static void hiddenInfiniteOOB() { + int[] a = { 11 } ; + for (int i = -1; i <= 0; i++) { + // Dangerous loop where careless static range analysis would yield a safe upper bound + // of -3. In reality, due to arithmetic wrap-around (when i = -1, j <= 2147483647; + // whereas when i = 0, j <= -3), this is an infinite loop that goes OOB. + for (int j = -3; j <= 2147483646 * i - 3; j++) { + sResult += a[j + 3]; + } + } + } + + /// CHECK-START: void Main.hiddenFiniteOOB() BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: void Main.hiddenFiniteOOB() BCE (after) + /// CHECK-DAG: Deoptimize + // + /// CHECK-START: void Main.hiddenFiniteOOB() BCE (after) + /// CHECK-NOT: BoundsCheck + private static void hiddenFiniteOOB() { + int[] a = { 111 } ; + for (int i = -1; i <= 0; i++) { + // Dangerous loop similar as above where the loop is now finite, but the + // loop still goes out of bounds for i = -1 due to the large upper bound. + // Dynamic BCE is feasible though, since it checks the range. + for (int j = -4; j < 2147483646 * i - 3; j++) { + sResult += a[j + 4]; + } + } + } + + /// CHECK-START: int[] Main.add() BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: int[] Main.add() BCE (after) + /// CHECK-NOT: BoundsCheck + /// CHECK-NOT: Deoptimize + private static int[] add() { + int[] a = new int[10]; + for (int i = 0; i <= 3; i++) { + for (int j = 0; j <= 6; j++) { + a[i + j] += 1; + } + } + return a; + } + + /// CHECK-START: int[] Main.multiply1() BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: int[] Main.multiply1() BCE (after) + /// CHECK-NOT: BoundsCheck + /// CHECK-NOT: Deoptimize + private static int[] multiply1() { + int[] a = new int[10]; + try { + for (int i = 0; i <= 3; i++) { + for (int j = 0; j <= 3; j++) { + // Range [0,9]: safe. + a[i * j] += 1; + } + } + } catch (Exception e) { + a[0] += 1000; + } + return a; + } + + /// CHECK-START: int[] Main.multiply2() BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: int[] Main.multiply2() BCE (after) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: int[] Main.multiply2() BCE (after) + /// CHECK-NOT: Deoptimize + static int[] multiply2() { + int[] a = new int[10]; + try { + for (int i = -3; i <= 3; i++) { + for (int j = -3; j <= 3; j++) { + // Range [-9,9]: unsafe. + a[i * j] += 1; + } + } + } catch (Exception e) { + a[0] += 1000; + } + return a; + } + + /// CHECK-START: int Main.linearDynamicBCE1(int[], int, int) BCE (before) + /// CHECK-DAG: ArrayGet loop:<> + /// CHECK-DAG: NullCheck loop:<> + /// CHECK-DAG: ArrayLength loop:<> + /// CHECK-DAG: BoundsCheck loop:<> + // + /// CHECK-START: int Main.linearDynamicBCE1(int[], int, int) BCE (after) + /// CHECK-DAG: ArrayGet loop:{{B\d+}} + /// CHECK-DAG: Deoptimize loop:none + // + /// CHECK-START: int Main.linearDynamicBCE1(int[], int, int) BCE (after) + /// CHECK-NOT: NullCheck loop:{{B\d+}} + /// CHECK-NOT: ArrayLength loop:{{B\d+}} + /// CHECK-NOT: BoundsCheck loop:{{B\d+}} + private static int linearDynamicBCE1(int[] x, int lo, int hi) { + int result = 0; + for (int i = lo; i < hi; i++) { + sResult += x[i]; + } + return result; + } + + /// CHECK-START: int Main.linearDynamicBCE2(int[], int, int, int) BCE (before) + /// CHECK-DAG: ArrayGet loop:<> + /// CHECK-DAG: NullCheck loop:<> + /// CHECK-DAG: ArrayLength loop:<> + /// CHECK-DAG: BoundsCheck loop:<> + // + /// CHECK-START: int Main.linearDynamicBCE2(int[], int, int, int) BCE (after) + /// CHECK-DAG: ArrayGet loop:{{B\d+}} + /// CHECK-DAG: Deoptimize loop:none + // + /// CHECK-START: int Main.linearDynamicBCE2(int[], int, int, int) BCE (after) + /// CHECK-NOT: NullCheck loop:{{B\d+}} + /// CHECK-NOT: ArrayLength loop:{{B\d+}} + /// CHECK-NOT: BoundsCheck loop:{{B\d+}} + private static int linearDynamicBCE2(int[] x, int lo, int hi, int offset) { + int result = 0; + for (int i = lo; i < hi; i++) { + sResult += x[offset + i]; + } + return result; + } + + /// CHECK-START: int Main.wrapAroundDynamicBCE(int[]) BCE (before) + /// CHECK-DAG: ArrayGet loop:<> + /// CHECK-DAG: NullCheck loop:<> + /// CHECK-DAG: ArrayLength loop:<> + /// CHECK-DAG: BoundsCheck loop:<> + // + /// CHECK-START: int Main.wrapAroundDynamicBCE(int[]) BCE (after) + /// CHECK-DAG: ArrayGet loop:{{B\d+}} + /// CHECK-DAG: Deoptimize loop:none + // + /// CHECK-START: int Main.wrapAroundDynamicBCE(int[]) BCE (after) + /// CHECK-NOT: NullCheck loop:{{B\d+}} + /// CHECK-NOT: ArrayLength loop:{{B\d+}} + /// CHECK-NOT: BoundsCheck loop:{{B\d+}} + private static int wrapAroundDynamicBCE(int[] x) { + int w = 9; + int result = 0; + for (int i = 0; i < 10; i++) { + result += x[w]; + w = i; + } + return result; + } + + /// CHECK-START: int Main.periodicDynamicBCE(int[]) BCE (before) + /// CHECK-DAG: ArrayGet loop:<> + /// CHECK-DAG: NullCheck loop:<> + /// CHECK-DAG: ArrayLength loop:<> + /// CHECK-DAG: BoundsCheck loop:<> + // + /// CHECK-START: int Main.periodicDynamicBCE(int[]) BCE (after) + /// CHECK-DAG: ArrayGet loop:{{B\d+}} + /// CHECK-DAG: Deoptimize loop:none + // + /// CHECK-START: int Main.periodicDynamicBCE(int[]) BCE (after) + /// CHECK-NOT: NullCheck loop:{{B\d+}} + /// CHECK-NOT: ArrayLength loop:{{B\d+}} + /// CHECK-NOT: BoundsCheck loop:{{B\d+}} + private static int periodicDynamicBCE(int[] x) { + int k = 0; + int result = 0; + for (int i = 0; i < 10; i++) { + result += x[k]; + k = 1 - k; + } + return result; + } + + /// CHECK-START: int Main.dynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (before) + /// CHECK-DAG: ArrayGet loop:<> + /// CHECK-DAG: NullCheck loop:<> + /// CHECK-DAG: ArrayLength loop:<> + /// CHECK-DAG: BoundsCheck loop:<> + // + /// CHECK-START: int Main.dynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (after) + /// CHECK-DAG: ArrayGet loop:{{B\d+}} + /// CHECK-DAG: Deoptimize loop:none + // + /// CHECK-START: int Main.dynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (after) + /// CHECK-NOT: NullCheck loop:{{B\d+}} + /// CHECK-NOT: ArrayLength loop:{{B\d+}} + /// CHECK-NOT: BoundsCheck loop:{{B\d+}} + static int dynamicBCEPossiblyInfiniteLoop(int[] x, int lo, int hi) { + // This loop could be infinite for hi = max int. Since i is also used + // as subscript, however, dynamic bce can proceed. + int result = 0; + for (int i = lo; i <= hi; i++) { + result += x[i]; + } + return result; + } + + /// CHECK-START: int Main.noDynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (before) + /// CHECK-DAG: ArrayGet loop:<> + /// CHECK-DAG: BoundsCheck loop:<> + // + /// CHECK-START: int Main.noDynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (after) + /// CHECK-DAG: ArrayGet loop:<> + /// CHECK-DAG: BoundsCheck loop:<> + // + /// CHECK-START: int Main.noDynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (after) + /// CHECK-NOT: Deoptimize + static int noDynamicBCEPossiblyInfiniteLoop(int[] x, int lo, int hi) { + // As above, but now the index is not used as subscript, + // and dynamic bce is not applied. + int result = 0; + for (int k = 0, i = lo; i <= hi; i++) { + result += x[k++]; + } + return result; + } + + /// CHECK-START: int Main.noDynamicBCEMixedInductionTypes(int[], long, long) BCE (before) + /// CHECK-DAG: ArrayGet loop:<> + /// CHECK-DAG: BoundsCheck loop:<> + // + /// CHECK-START: int Main.noDynamicBCEMixedInductionTypes(int[], long, long) BCE (after) + /// CHECK-DAG: ArrayGet loop:<> + /// CHECK-DAG: BoundsCheck loop:<> + // + /// CHECK-START: int Main.noDynamicBCEMixedInductionTypes(int[], long, long) BCE (after) + /// CHECK-NOT: Deoptimize + static int noDynamicBCEMixedInductionTypes(int[] x, long lo, long hi) { + int result = 0; + // Mix of int and long induction. + int k = 0; + for (long i = lo; i < hi; i++) { + result += x[k++]; + } + return result; + } + + /// CHECK-START: int Main.dynamicBCEConstantRange(int[]) BCE (before) + /// CHECK-DAG: BoundsCheck loop:<> + /// CHECK-DAG: ArrayGet loop:<> + /// CHECK-DAG: If loop:<> + /// CHECK-DAG: If loop:<> + /// CHECK-EVAL: "<>" != "<>" + // + /// CHECK-START: int Main.dynamicBCEConstantRange(int[]) BCE (after) + /// CHECK-DAG: ArrayGet loop:<> + /// CHECK-DAG: Deoptimize loop:<> + /// CHECK-EVAL: "<>" != "<>" + // + /// CHECK-START: int Main.dynamicBCEConstantRange(int[]) BCE (after) + /// CHECK-NOT: BoundsCheck + // + // No additional top tests were introduced. + /// CHECK-START: int Main.dynamicBCEConstantRange(int[]) BCE (after) + /// CHECK-DAG: If + /// CHECK-DAG: If + /// CHECK-NOT: If + static int dynamicBCEConstantRange(int[] x) { + int result = 0; + for (int i = 2; i <= 6; i++) { + // Range analysis sees that innermost loop is finite and always taken. + for (int j = i - 2; j <= i + 2; j++) { + result += x[j]; + } + } + return result; + } + + /// CHECK-START: int Main.dynamicBCEAndConstantIndices(int[], int[][], int, int) BCE (before) + /// CHECK-DAG: {{l\d+}} ArrayGet loop:<> + /// CHECK-DAG: {{l\d+}} ArrayGet loop:<> + /// CHECK-DAG: {{l\d+}} ArrayGet loop:<> + // + /// CHECK-START: int Main.dynamicBCEAndConstantIndices(int[], int[][], int, int) BCE (after) + // Order matters: + /// CHECK: Deoptimize loop:<> + // CHECK-NOT: Goto loop:<> + /// CHECK-DAG: {{l\d+}} ArrayGet loop:<> + /// CHECK-DAG: {{l\d+}} ArrayGet loop:<> + /// CHECK-DAG: {{l\d+}} ArrayGet loop:<> + /// CHECK: Goto loop:<> + // + /// CHECK-START: int Main.dynamicBCEAndConstantIndices(int[], int[][], int, int) BCE (after) + /// CHECK-DAG: Deoptimize loop:none + static int dynamicBCEAndConstantIndices(int[] x, int[][] a, int lo, int hi) { + // Deliberately test array length on a before the loop so that only bounds checks + // on constant subscripts remain, making them a viable candidate for hoisting. + if (a.length == 0) { + return -1; + } + // Loop that allows BCE on x[i]. + int result = 0; + for (int i = lo; i < hi; i++) { + result += x[i]; + if ((i % 10) != 0) { + // None of the subscripts inside a conditional are removed by dynamic bce, + // making them a candidate for deoptimization based on constant indices. + // Compiler should ensure the array loads are not subsequently hoisted + // "above" the deoptimization "barrier" on the bounds. + a[0][i] = 1; + a[1][i] = 2; + a[99][i] = 3; + } + } + return result; + } + + /// CHECK-START: int Main.dynamicBCEAndConstantIndicesAllPrimTypes(int[], boolean[], byte[], char[], short[], int[], long[], float[], double[], int, int) BCE (before) + /// CHECK-DAG: ArrayGet loop:<> + /// CHECK-DAG: ArrayGet loop:<> + /// CHECK-DAG: ArrayGet loop:<> + /// CHECK-DAG: ArrayGet loop:<> + /// CHECK-DAG: ArrayGet loop:<> + /// CHECK-DAG: ArrayGet loop:<> + /// CHECK-DAG: ArrayGet loop:<> + /// CHECK-DAG: ArrayGet loop:<> + /// CHECK-DAG: ArrayGet loop:<> + // For brevity, just test occurrence of at least one of each in the loop: + /// CHECK-DAG: NullCheck loop:<> + /// CHECK-DAG: ArrayLength loop:<> + /// CHECK-DAG: BoundsCheck loop:<> + // + /// CHECK-START: int Main.dynamicBCEAndConstantIndicesAllPrimTypes(int[], boolean[], byte[], char[], short[], int[], long[], float[], double[], int, int) BCE (after) + /// CHECK-DAG: ArrayGet loop:<> + /// CHECK-NOT: ArrayGet loop:<> + // + /// CHECK-START: int Main.dynamicBCEAndConstantIndicesAllPrimTypes(int[], boolean[], byte[], char[], short[], int[], long[], float[], double[], int, int) BCE (after) + /// CHECK-NOT: NullCheck loop:{{B\d+}} + /// CHECK-NOT: ArrayLength loop:{{B\d+}} + /// CHECK-NOT: BoundsCheck loop:{{B\d+}} + // + /// CHECK-START: int Main.dynamicBCEAndConstantIndicesAllPrimTypes(int[], boolean[], byte[], char[], short[], int[], long[], float[], double[], int, int) BCE (after) + /// CHECK-DAG: Deoptimize loop:none + static int dynamicBCEAndConstantIndicesAllPrimTypes(int[] q, + boolean[] r, + byte[] s, + char[] t, + short[] u, + int[] v, + long[] w, + float[] x, + double[] y, int lo, int hi) { + int result = 0; + for (int i = lo; i < hi; i++) { + // All constant index array references can be hoisted out of the loop during BCE on q[i]. + result += q[i] + (r[0] ? 1 : 0) + (int) s[0] + (int) t[0] + (int) u[0] + (int) v[0] + + (int) w[0] + (int) x[0] + (int) y[0]; + } + return result; + } + + /// CHECK-START: int Main.dynamicBCEAndConstantIndexRefType(int[], java.lang.Integer[], int, int) BCE (before) + /// CHECK-DAG: ArrayGet loop:<> + /// CHECK-DAG: NullCheck loop:<> + /// CHECK-DAG: ArrayLength loop:<> + /// CHECK-DAG: BoundsCheck loop:<> + /// CHECK-DAG: ArrayGet loop:<> + /// CHECK-DAG: NullCheck loop:<> + /// CHECK-DAG: ArrayLength loop:<> + /// CHECK-DAG: BoundsCheck loop:<> + // + /// CHECK-START: int Main.dynamicBCEAndConstantIndexRefType(int[], java.lang.Integer[], int, int) BCE (after) + /// CHECK-DAG: ArrayGet loop:<> + /// CHECK-DAG: Deoptimize loop:none + // + /// CHECK-START: int Main.dynamicBCEAndConstantIndexRefType(int[], java.lang.Integer[], int, int) BCE (after) + /// CHECK-NOT: ArrayLength loop:{{B\d+}} + /// CHECK-NOT: BoundsCheck loop:{{B\d+}} + static int dynamicBCEAndConstantIndexRefType(int[] q, Integer[] z, int lo, int hi) { + int result = 0; + for (int i = lo; i < hi; i++) { + // Similar to above, but now implicit call to intValue() may prevent hoisting + // z[0] itself during BCE on q[i]. Therefore, we just check BCE on q[i]. + result += q[i] + z[0]; + } + return result; + } + + // + // Verifier. + // + + public static void main(String[] args) { + // Set to run expensive tests for correctness too. + boolean HEAVY = false; + + int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + + // Sorting. + int[] sort = { 5, 4, 1, 9, 10, 2, 7, 6, 3, 8 }; + bubble(sort); + for (int i = 0; i < 10; i++) { + expectEquals(sort[i], x[i]); + } + + // Periodic adds (1, 3), one at the time. + expectEquals(0, periodicIdiom(-1)); + for (int tc = 0; tc < 32; tc++) { + int expected = (tc >> 1) << 2; + if ((tc & 1) != 0) + expected += 1; + expectEquals(expected, periodicIdiom(tc)); + } + + // Periodic adds (1, 3), one at the time. + expectEquals(0, periodicSequence2(-1)); + for (int tc = 0; tc < 32; tc++) { + int expected = (tc >> 1) << 2; + if ((tc & 1) != 0) + expected += 1; + expectEquals(expected, periodicSequence2(tc)); + } + + // Periodic adds (1, 3, 5, 7), all at once. + expectEquals(0, periodicSequence4(-1)); + for (int tc = 0; tc < 32; tc++) { + expectEquals(tc * 16, periodicSequence4(tc)); + } + + // Large bounds. + expectEquals(55, justRightUp1()); + expectEquals(55, justRightUp2()); + expectEquals(55, justRightUp3()); + expectEquals(55, justRightDown1()); + expectEquals(55, justRightDown2()); + expectEquals(55, justRightDown3()); + sResult = 0; + try { + justOOBUp(); + } catch (ArrayIndexOutOfBoundsException e) { + sResult = 1; + } + expectEquals(1, sResult); + sResult = 0; + try { + justOOBDown(); + } catch (ArrayIndexOutOfBoundsException e) { + sResult = 1; + } + expectEquals(1, sResult); + + // Lower bound goes OOB. + sResult = 0; + try { + lowerOOB(x); + } catch (ArrayIndexOutOfBoundsException e) { + sResult += 1000; + } + expectEquals(1000, sResult); + + // Upper bound goes OOB. + sResult = 0; + try { + upperOOB(x); + } catch (ArrayIndexOutOfBoundsException e) { + sResult += 1000; + } + expectEquals(1055, sResult); + + // Do while up goes OOB. + sResult = 0; + try { + doWhileUpOOB(); + } catch (ArrayIndexOutOfBoundsException e) { + sResult += 1000; + } + expectEquals(1055, sResult); + + // Do while down goes OOB. + sResult = 0; + try { + doWhileDownOOB(); + } catch (ArrayIndexOutOfBoundsException e) { + sResult += 1000; + } + expectEquals(1055, sResult); + + // Hidden OOB. + sResult = 0; + try { + hiddenOOB1(10); // no OOB + } catch (ArrayIndexOutOfBoundsException e) { + sResult += 1000; + } + expectEquals(1, sResult); + sResult = 0; + try { + hiddenOOB1(-2147483648); // OOB + } catch (ArrayIndexOutOfBoundsException e) { + sResult += 1000; + } + expectEquals(1001, sResult); + sResult = 0; + try { + hiddenOOB2(1); // no OOB + } catch (ArrayIndexOutOfBoundsException e) { + sResult += 1000; + } + expectEquals(1, sResult); + if (HEAVY) { + sResult = 0; + try { + hiddenOOB2(2147483647); // OOB + } catch (ArrayIndexOutOfBoundsException e) { + sResult += 1000; + } + expectEquals(1002, sResult); + } + sResult = 0; + try { + hiddenInfiniteOOB(); + } catch (ArrayIndexOutOfBoundsException e) { + sResult += 1000; + } + expectEquals(1011, sResult); + sResult = 0; + try { + hiddenFiniteOOB(); + } catch (ArrayIndexOutOfBoundsException e) { + sResult += 1000; + } + expectEquals(1111, sResult); + + // Addition. + { + int[] e1 ={ 1, 2, 3, 4, 4, 4, 4, 3, 2, 1 }; + int[] a1 = add(); + for (int i = 0; i < 10; i++) { + expectEquals(a1[i], e1[i]); + } + } + + // Multiplication. + { + int[] e1 = { 7, 1, 2, 2, 1, 0, 2, 0, 0, 1 }; + int[] a1 = multiply1(); + for (int i = 0; i < 10; i++) { + expectEquals(a1[i], e1[i]); + } + int[] e2 = { 1001, 0, 0, 1, 0, 0, 1, 0, 0, 1 }; + int[] a2 = multiply2(); + for (int i = 0; i < 10; i++) { + expectEquals(a2[i], e2[i]); + } + } + + // Dynamic BCE. + sResult = 0; + try { + linearDynamicBCE1(x, -1, x.length); + } catch (ArrayIndexOutOfBoundsException e) { + sResult += 1000; + } + expectEquals(1000, sResult); + sResult = 0; + linearDynamicBCE1(x, 0, x.length); + expectEquals(55, sResult); + sResult = 0; + try { + linearDynamicBCE1(x, 0, x.length + 1); + } catch (ArrayIndexOutOfBoundsException e) { + sResult += 1000; + } + expectEquals(1055, sResult); + + // Dynamic BCE with offset. + sResult = 0; + try { + linearDynamicBCE2(x, 0, x.length, -1); + } catch (ArrayIndexOutOfBoundsException e) { + sResult += 1000; + } + expectEquals(1000, sResult); + sResult = 0; + linearDynamicBCE2(x, 0, x.length, 0); + expectEquals(55, sResult); + sResult = 0; + try { + linearDynamicBCE2(x, 0, x.length, 1); + } catch (ArrayIndexOutOfBoundsException e) { + sResult += 1000; + } + expectEquals(1054, sResult); + + // Dynamic BCE candidates. + expectEquals(55, wrapAroundDynamicBCE(x)); + expectEquals(15, periodicDynamicBCE(x)); + expectEquals(55, dynamicBCEPossiblyInfiniteLoop(x, 0, 9)); + expectEquals(55, noDynamicBCEPossiblyInfiniteLoop(x, 0, 9)); + expectEquals(55, noDynamicBCEMixedInductionTypes(x, 0, 10)); + expectEquals(125, dynamicBCEConstantRange(x)); + + // Dynamic BCE combined with constant indices. + int[][] a; + a = new int[0][0]; + expectEquals(-1, dynamicBCEAndConstantIndices(x, a, 0, 10)); + a = new int[100][10]; + expectEquals(55, dynamicBCEAndConstantIndices(x, a, 0, 10)); + for (int i = 0; i < 10; i++) { + expectEquals((i % 10) != 0 ? 1 : 0, a[0][i]); + expectEquals((i % 10) != 0 ? 2 : 0, a[1][i]); + expectEquals((i % 10) != 0 ? 3 : 0, a[99][i]); + } + a = new int[2][10]; + sResult = 0; + try { + expectEquals(55, dynamicBCEAndConstantIndices(x, a, 0, 10)); + } catch (ArrayIndexOutOfBoundsException e) { + sResult = 1; + } + expectEquals(1, sResult); + expectEquals(a[0][1], 1); + expectEquals(a[1][1], 2); + + // Dynamic BCE combined with constant indices of all types. + boolean[] x1 = { true }; + byte[] x2 = { 2 }; + char[] x3 = { 3 }; + short[] x4 = { 4 }; + int[] x5 = { 5 }; + long[] x6 = { 6 }; + float[] x7 = { 7 }; + double[] x8 = { 8 }; + expectEquals(415, + dynamicBCEAndConstantIndicesAllPrimTypes(x, x1, x2, x3, x4, x5, x6, x7, x8, 0, 10)); + Integer[] x9 = { 9 }; + expectEquals(145, dynamicBCEAndConstantIndexRefType(x, x9, 0, 10)); + } + + private static void expectEquals(int expected, int result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } +} -- GitLab From fcdd72941810f03460a8efca0c6255439be80c35 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Thu, 25 Feb 2016 13:27:47 +0000 Subject: [PATCH 039/204] Dump the number of OSR compiled code. Change-Id: I20efc80e8556da8220dab92c3a7947f883d48cf8 --- runtime/jit/jit.cc | 3 ++- runtime/jit/jit_code_cache.cc | 9 ++++++++- runtime/jit/jit_code_cache.h | 2 ++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc index bdc7ee2428..cdf09bf51c 100644 --- a/runtime/jit/jit.cc +++ b/runtime/jit/jit.cc @@ -63,7 +63,8 @@ void Jit::DumpInfo(std::ostream& os) { << "JIT data cache size=" << PrettySize(code_cache_->DataCacheSize()) << "\n" << "JIT current capacity=" << PrettySize(code_cache_->GetCurrentCapacity()) << "\n" << "JIT number of compiled code=" << code_cache_->NumberOfCompiledCode() << "\n" - << "JIT total number of compilations=" << code_cache_->NumberOfCompilations() << "\n"; + << "JIT total number of compilations=" << code_cache_->NumberOfCompilations() << "\n" + << "JIT total number of osr compilations=" << code_cache_->NumberOfOsrCompilations() << "\n"; cumulative_timings_.Dump(os); } diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index 478b164597..8858b486f9 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -128,7 +128,8 @@ JitCodeCache::JitCodeCache(MemMap* code_map, garbage_collect_code_(garbage_collect_code), used_memory_for_data_(0), used_memory_for_code_(0), - number_of_compilations_(0) { + number_of_compilations_(0), + number_of_osr_compilations_(0) { DCHECK_GE(max_capacity, initial_code_capacity + initial_data_capacity); code_mspace_ = create_mspace_with_base(code_map_->Begin(), code_end_, false /*locked*/); @@ -338,6 +339,7 @@ uint8_t* JitCodeCache::CommitCodeInternal(Thread* self, MutexLock mu(self, lock_); method_code_map_.Put(code_ptr, method); if (osr) { + number_of_osr_compilations_++; osr_code_map_.Put(method, code_ptr); } else { Runtime::Current()->GetInstrumentation()->UpdateMethodsCode( @@ -366,6 +368,11 @@ size_t JitCodeCache::NumberOfCompilations() { return number_of_compilations_; } +size_t JitCodeCache::NumberOfOsrCompilations() { + MutexLock mu(Thread::Current(), lock_); + return number_of_osr_compilations_; +} + size_t JitCodeCache::CodeCacheSize() { MutexLock mu(Thread::Current(), lock_); return CodeCacheSizeLocked(); diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h index e5b8e6ca17..4574edfb46 100644 --- a/runtime/jit/jit_code_cache.h +++ b/runtime/jit/jit_code_cache.h @@ -73,6 +73,7 @@ class JitCodeCache { // Number of compilations done throughout the lifetime of the JIT. size_t NumberOfCompilations() REQUIRES(!lock_); + size_t NumberOfOsrCompilations() REQUIRES(!lock_); bool NotifyCompilationOf(ArtMethod* method, Thread* self, bool osr) SHARED_REQUIRES(Locks::mutator_lock_) @@ -304,6 +305,7 @@ class JitCodeCache { // Number of compilations done throughout the lifetime of the JIT. size_t number_of_compilations_ GUARDED_BY(lock_); + size_t number_of_osr_compilations_ GUARDED_BY(lock_); DISALLOW_IMPLICIT_CONSTRUCTORS(JitCodeCache); }; -- GitLab From a14bf4460268064e0adc4bb210d237209be33eaf Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Thu, 25 Feb 2016 13:34:18 +0000 Subject: [PATCH 040/204] Revert "ART: Support interpreter switching in x86 mterp" Fails: test-art-host-run-test-ndebug-prebuild-interpreter-relocate-ntrace-cms-checkjni-image-npictest-ndebuggable-304-method-tracing32 test-art-host-run-test-ndebug-prebuild-interpreter-relocate-ntrace-cms-checkjni-image-npictest-ndebuggable-545-tracing-and-jit32 test-art-host-run-test-ndebug-prebuild-interpreter-relocate-ntrace-cms-checkjni-image-npictest-ndebuggable-570-checker-osr32 test-art-host-run-test-ndebug-prebuild-interpreter-relocate-ntrace-cms-checkjni-image-npictest-ndebuggable-802-deoptimization32 This reverts commit 4429b110c7b4d9bda2c31b4df257e840831d01f6. Change-Id: I30c42e22b44bb9c9bbd6efaedbc97516d575bf11 --- runtime/interpreter/mterp/out/mterp_x86.S | 42 +---------------------- runtime/interpreter/mterp/x86/footer.S | 6 +--- runtime/interpreter/mterp/x86/invoke.S | 3 -- 3 files changed, 2 insertions(+), 49 deletions(-) diff --git a/runtime/interpreter/mterp/out/mterp_x86.S b/runtime/interpreter/mterp/out/mterp_x86.S index 589639bdbc..b05360b6ae 100644 --- a/runtime/interpreter/mterp/out/mterp_x86.S +++ b/runtime/interpreter/mterp/out/mterp_x86.S @@ -2989,9 +2989,6 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop call SYMBOL(MterpInvokeVirtual) testb %al, %al jz MterpException - call SYMBOL(MterpShouldSwitchInterpreters) - testb %al, %al - jnz MterpFallback RESTORE_IBASE ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 @@ -3025,9 +3022,6 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop call SYMBOL(MterpInvokeSuper) testb %al, %al jz MterpException - call SYMBOL(MterpShouldSwitchInterpreters) - testb %al, %al - jnz MterpFallback RESTORE_IBASE ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 @@ -3061,9 +3055,6 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop call SYMBOL(MterpInvokeDirect) testb %al, %al jz MterpException - call SYMBOL(MterpShouldSwitchInterpreters) - testb %al, %al - jnz MterpFallback RESTORE_IBASE ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 @@ -3090,9 +3081,6 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop call SYMBOL(MterpInvokeStatic) testb %al, %al jz MterpException - call SYMBOL(MterpShouldSwitchInterpreters) - testb %al, %al - jnz MterpFallback RESTORE_IBASE ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 @@ -3120,9 +3108,6 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop call SYMBOL(MterpInvokeInterface) testb %al, %al jz MterpException - call SYMBOL(MterpShouldSwitchInterpreters) - testb %al, %al - jnz MterpFallback RESTORE_IBASE ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 @@ -3170,9 +3155,6 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop call SYMBOL(MterpInvokeVirtualRange) testb %al, %al jz MterpException - call SYMBOL(MterpShouldSwitchInterpreters) - testb %al, %al - jnz MterpFallback RESTORE_IBASE ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 @@ -3199,9 +3181,6 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop call SYMBOL(MterpInvokeSuperRange) testb %al, %al jz MterpException - call SYMBOL(MterpShouldSwitchInterpreters) - testb %al, %al - jnz MterpFallback RESTORE_IBASE ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 @@ -3228,9 +3207,6 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop call SYMBOL(MterpInvokeDirectRange) testb %al, %al jz MterpException - call SYMBOL(MterpShouldSwitchInterpreters) - testb %al, %al - jnz MterpFallback RESTORE_IBASE ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 @@ -3257,9 +3233,6 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop call SYMBOL(MterpInvokeStaticRange) testb %al, %al jz MterpException - call SYMBOL(MterpShouldSwitchInterpreters) - testb %al, %al - jnz MterpFallback RESTORE_IBASE ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 @@ -3286,9 +3259,6 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop call SYMBOL(MterpInvokeInterfaceRange) testb %al, %al jz MterpException - call SYMBOL(MterpShouldSwitchInterpreters) - testb %al, %al - jnz MterpFallback RESTORE_IBASE ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 @@ -6032,9 +6002,6 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop call SYMBOL(MterpInvokeVirtualQuick) testb %al, %al jz MterpException - call SYMBOL(MterpShouldSwitchInterpreters) - testb %al, %al - jnz MterpFallback RESTORE_IBASE ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 @@ -6061,9 +6028,6 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop call SYMBOL(MterpInvokeVirtualQuickRange) testb %al, %al jz MterpException - call SYMBOL(MterpShouldSwitchInterpreters) - testb %al, %al - jnz MterpFallback RESTORE_IBASE ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 @@ -12887,17 +12851,13 @@ MterpException: call SYMBOL(MterpHandleException) testb %al, %al jz MterpExceptionReturn + REFRESH_IBASE movl OFF_FP_CODE_ITEM(rFP), %eax movl OFF_FP_DEX_PC(rFP), %ecx lea CODEITEM_INSNS_OFFSET(%eax), rPC lea (rPC, %ecx, 2), rPC movl rPC, OFF_FP_DEX_PC_PTR(rFP) - /* Do we need to switch interpreters? */ - call SYMBOL(MterpShouldSwitchInterpreters) - testb %al, %al - jnz MterpFallback /* resume execution at catch block */ - REFRESH_IBASE FETCH_INST GOTO_NEXT /* NOTE: no fallthrough */ diff --git a/runtime/interpreter/mterp/x86/footer.S b/runtime/interpreter/mterp/x86/footer.S index 64d72d7709..c67491e577 100644 --- a/runtime/interpreter/mterp/x86/footer.S +++ b/runtime/interpreter/mterp/x86/footer.S @@ -115,17 +115,13 @@ MterpException: call SYMBOL(MterpHandleException) testb %al, %al jz MterpExceptionReturn + REFRESH_IBASE movl OFF_FP_CODE_ITEM(rFP), %eax movl OFF_FP_DEX_PC(rFP), %ecx lea CODEITEM_INSNS_OFFSET(%eax), rPC lea (rPC, %ecx, 2), rPC movl rPC, OFF_FP_DEX_PC_PTR(rFP) - /* Do we need to switch interpreters? */ - call SYMBOL(MterpShouldSwitchInterpreters) - testb %al, %al - jnz MterpFallback /* resume execution at catch block */ - REFRESH_IBASE FETCH_INST GOTO_NEXT /* NOTE: no fallthrough */ diff --git a/runtime/interpreter/mterp/x86/invoke.S b/runtime/interpreter/mterp/x86/invoke.S index cb74a04478..bbd88cf40b 100644 --- a/runtime/interpreter/mterp/x86/invoke.S +++ b/runtime/interpreter/mterp/x86/invoke.S @@ -16,8 +16,5 @@ call SYMBOL($helper) testb %al, %al jz MterpException - call SYMBOL(MterpShouldSwitchInterpreters) - testb %al, %al - jnz MterpFallback RESTORE_IBASE ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 -- GitLab From f1dcaccfac4a9e022ea49752a774552b2f791489 Mon Sep 17 00:00:00 2001 From: buzbee Date: Wed, 24 Feb 2016 14:24:24 -0800 Subject: [PATCH 041/204] ART: Profile all branches for on-stack replacement Change the switch, goto and mterp interpreters to profile not-taken as well as taken branches. This allows for on-stack replacement when the cfg has been rearranged such that the loop header was originally the fallthrough of a Dalvik byte-code branch. Note that this increases the already-heavy cost of branch profiling. Measuring on a Nexus 6 using a very branchy benchmark (logic subtest from Caffeinemark), we see: No profiling Taken only Taken & not-taken mterp 9728 3434 2384 C++ goto 3914 2422 2037 C++ switch 2986 2411 2112 As measured, the cost of branch profiling is dominating execution time. This will be addressed in follow-up CLs. Change-Id: Ibc858f317398dd991ed8e4f3c3d72bd4c9a60594 --- .../interpreter_goto_table_impl.cc | 12 + .../interpreter/interpreter_switch_impl.cc | 12 + runtime/interpreter/mterp/arm/bincmp.S | 25 +- runtime/interpreter/mterp/arm/zcmp.S | 23 +- runtime/interpreter/mterp/arm64/bincmp.S | 29 +- runtime/interpreter/mterp/arm64/zcmp.S | 27 +- runtime/interpreter/mterp/out/mterp_arm.S | 288 +++------------ runtime/interpreter/mterp/out/mterp_arm64.S | 336 ++++-------------- 8 files changed, 164 insertions(+), 588 deletions(-) diff --git a/runtime/interpreter/interpreter_goto_table_impl.cc b/runtime/interpreter/interpreter_goto_table_impl.cc index ca8598e5e6..12d6fdc00d 100644 --- a/runtime/interpreter/interpreter_goto_table_impl.cc +++ b/runtime/interpreter/interpreter_goto_table_impl.cc @@ -792,6 +792,7 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF } ADVANCE(offset); } else { + BRANCH_INSTRUMENTATION(2); ADVANCE(2); } } @@ -810,6 +811,7 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF } ADVANCE(offset); } else { + BRANCH_INSTRUMENTATION(2); ADVANCE(2); } } @@ -828,6 +830,7 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF } ADVANCE(offset); } else { + BRANCH_INSTRUMENTATION(2); ADVANCE(2); } } @@ -846,6 +849,7 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF } ADVANCE(offset); } else { + BRANCH_INSTRUMENTATION(2); ADVANCE(2); } } @@ -864,6 +868,7 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF } ADVANCE(offset); } else { + BRANCH_INSTRUMENTATION(2); ADVANCE(2); } } @@ -882,6 +887,7 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF } ADVANCE(offset); } else { + BRANCH_INSTRUMENTATION(2); ADVANCE(2); } } @@ -899,6 +905,7 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF } ADVANCE(offset); } else { + BRANCH_INSTRUMENTATION(2); ADVANCE(2); } } @@ -916,6 +923,7 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF } ADVANCE(offset); } else { + BRANCH_INSTRUMENTATION(2); ADVANCE(2); } } @@ -933,6 +941,7 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF } ADVANCE(offset); } else { + BRANCH_INSTRUMENTATION(2); ADVANCE(2); } } @@ -950,6 +959,7 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF } ADVANCE(offset); } else { + BRANCH_INSTRUMENTATION(2); ADVANCE(2); } } @@ -967,6 +977,7 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF } ADVANCE(offset); } else { + BRANCH_INSTRUMENTATION(2); ADVANCE(2); } } @@ -984,6 +995,7 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF } ADVANCE(offset); } else { + BRANCH_INSTRUMENTATION(2); ADVANCE(2); } } diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc index 25dbab2494..0488dbf028 100644 --- a/runtime/interpreter/interpreter_switch_impl.cc +++ b/runtime/interpreter/interpreter_switch_impl.cc @@ -712,6 +712,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, } inst = inst->RelativeAt(offset); } else { + BRANCH_INSTRUMENTATION(2); inst = inst->Next_2xx(); } break; @@ -727,6 +728,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, } inst = inst->RelativeAt(offset); } else { + BRANCH_INSTRUMENTATION(2); inst = inst->Next_2xx(); } break; @@ -742,6 +744,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, } inst = inst->RelativeAt(offset); } else { + BRANCH_INSTRUMENTATION(2); inst = inst->Next_2xx(); } break; @@ -757,6 +760,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, } inst = inst->RelativeAt(offset); } else { + BRANCH_INSTRUMENTATION(2); inst = inst->Next_2xx(); } break; @@ -772,6 +776,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, } inst = inst->RelativeAt(offset); } else { + BRANCH_INSTRUMENTATION(2); inst = inst->Next_2xx(); } break; @@ -787,6 +792,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, } inst = inst->RelativeAt(offset); } else { + BRANCH_INSTRUMENTATION(2); inst = inst->Next_2xx(); } break; @@ -801,6 +807,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, } inst = inst->RelativeAt(offset); } else { + BRANCH_INSTRUMENTATION(2); inst = inst->Next_2xx(); } break; @@ -815,6 +822,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, } inst = inst->RelativeAt(offset); } else { + BRANCH_INSTRUMENTATION(2); inst = inst->Next_2xx(); } break; @@ -829,6 +837,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, } inst = inst->RelativeAt(offset); } else { + BRANCH_INSTRUMENTATION(2); inst = inst->Next_2xx(); } break; @@ -843,6 +852,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, } inst = inst->RelativeAt(offset); } else { + BRANCH_INSTRUMENTATION(2); inst = inst->Next_2xx(); } break; @@ -857,6 +867,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, } inst = inst->RelativeAt(offset); } else { + BRANCH_INSTRUMENTATION(2); inst = inst->Next_2xx(); } break; @@ -871,6 +882,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, } inst = inst->RelativeAt(offset); } else { + BRANCH_INSTRUMENTATION(2); inst = inst->Next_2xx(); } break; diff --git a/runtime/interpreter/mterp/arm/bincmp.S b/runtime/interpreter/mterp/arm/bincmp.S index 774e1676b7..cfad7147e2 100644 --- a/runtime/interpreter/mterp/arm/bincmp.S +++ b/runtime/interpreter/mterp/arm/bincmp.S @@ -6,14 +6,15 @@ * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le */ /* if-cmp vA, vB, +CCCC */ -#if MTERP_PROFILE_BRANCHES mov r1, rINST, lsr #12 @ r1<- B ubfx r0, rINST, #8, #4 @ r0<- A GET_VREG r3, r1 @ r3<- vB GET_VREG r2, r0 @ r2<- vA FETCH_S rINST, 1 @ rINST<- branch offset, in code units cmp r2, r3 @ compare (vA, vB) - b${revcmp} .L_${opcode}_not_taken + mov${revcmp} rINST, #2 +#if MTERP_PROFILE_BRANCHES + @ TUNING: once measurements are complete, remove #if and hand-schedule. EXPORT_PC mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME @@ -21,28 +22,10 @@ bl MterpProfileBranch @ (self, shadow_frame, offset) cmp r0, #0 bne MterpOnStackReplacement @ Note: offset must be in rINST +#endif adds r2, rINST, rINST @ convert to bytes, check sign ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST bmi MterpCheckSuspendAndContinue GET_INST_OPCODE ip @ extract opcode from rINST GOTO_OPCODE ip @ jump to next instruction -.L_${opcode}_not_taken: - FETCH_ADVANCE_INST 2 @ update rPC, load rINST - GET_INST_OPCODE ip @ extract opcode from rINST - GOTO_OPCODE ip @ jump to next instruction -#else - mov r1, rINST, lsr #12 @ r1<- B - ubfx r0, rINST, #8, #4 @ r0<- A - GET_VREG r3, r1 @ r3<- vB - GET_VREG r2, r0 @ r2<- vA - ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] - FETCH_S rINST, 1 @ rINST<- branch offset, in code units - cmp r2, r3 @ compare (vA, vB) - mov${revcmp} rINST, #2 @ rINST<- BYTE branch dist for not-taken - adds r2, rINST, rINST @ convert to bytes, check sign - FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST - bmi MterpCheckSuspendAndContinue - GET_INST_OPCODE ip @ extract opcode from rINST - GOTO_OPCODE ip @ jump to next instruction -#endif diff --git a/runtime/interpreter/mterp/arm/zcmp.S b/runtime/interpreter/mterp/arm/zcmp.S index 800804d95e..3d7dec006d 100644 --- a/runtime/interpreter/mterp/arm/zcmp.S +++ b/runtime/interpreter/mterp/arm/zcmp.S @@ -6,13 +6,14 @@ * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez */ /* if-cmp vAA, +BBBB */ -#if MTERP_PROFILE_BRANCHES mov r0, rINST, lsr #8 @ r0<- AA GET_VREG r2, r0 @ r2<- vAA FETCH_S rINST, 1 @ rINST<- branch offset, in code units ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] cmp r2, #0 @ compare (vA, 0) - b${revcmp} .L_${opcode}_not_taken + mov${revcmp} rINST, #2 +#if MTERP_PROFILE_BRANCHES + @ TUNING: once measurements are complete, remove #if and hand-schedule. EXPORT_PC mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME @@ -20,25 +21,9 @@ bl MterpProfileBranch @ (self, shadow_frame, offset) cmp r0, #0 bne MterpOnStackReplacement @ Note: offset must be in rINST +#endif adds r1, rINST, rINST @ convert to bytes & set flags FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST bmi MterpCheckSuspendAndContinue GET_INST_OPCODE ip @ extract opcode from rINST GOTO_OPCODE ip @ jump to next instruction -.L_${opcode}_not_taken: - FETCH_ADVANCE_INST 2 @ update rPC, load rINST - GET_INST_OPCODE ip @ extract opcode from rINST - GOTO_OPCODE ip @ jump to next instruction -#else - mov r0, rINST, lsr #8 @ r0<- AA - GET_VREG r2, r0 @ r2<- vAA - FETCH_S rINST, 1 @ rINST<- branch offset, in code units - ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] - cmp r2, #0 @ compare (vA, 0) - mov${revcmp} rINST, #2 @ rINST<- inst branch dist for not-taken - adds r1, rINST, rINST @ convert to bytes & set flags - FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST - bmi MterpCheckSuspendAndContinue - GET_INST_OPCODE ip @ extract opcode from rINST - GOTO_OPCODE ip @ jump to next instruction -#endif diff --git a/runtime/interpreter/mterp/arm64/bincmp.S b/runtime/interpreter/mterp/arm64/bincmp.S index ed850fc49d..2356ecbb89 100644 --- a/runtime/interpreter/mterp/arm64/bincmp.S +++ b/runtime/interpreter/mterp/arm64/bincmp.S @@ -6,43 +6,26 @@ * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le */ /* if-cmp vA, vB, +CCCC */ -#if MTERP_PROFILE_BRANCHES lsr w1, wINST, #12 // w1<- B ubfx w0, wINST, #8, #4 // w0<- A GET_VREG w3, w1 // w3<- vB GET_VREG w2, w0 // w2<- vA - FETCH_S wINST, 1 // wINST<- branch offset, in code units + FETCH_S w1, 1 // w1<- branch offset, in code units + mov w0, #2 // Offset if branch not taken cmp w2, w3 // compare (vA, vB) - b.${condition} .L_${opcode}_taken - FETCH_ADVANCE_INST 2 // update rPC, load wINST - GET_INST_OPCODE ip // extract opcode from wINST - GOTO_OPCODE ip // jump to next instruction -.L_${opcode}_taken: + csel wINST, w1, w0, ${condition} // Branch if true, stashing result in callee save reg. +#if MTERP_PROFILE_BRANCHES + // TUINING: once measurements are complete, remove #if and hand-schedule. EXPORT_PC mov x0, xSELF add x1, xFP, #OFF_FP_SHADOWFRAME sbfm x2, xINST, 0, 31 // Sign extend branch offset bl MterpProfileBranch // (self, shadow_frame, offset) cbnz w0, MterpOnStackReplacement // Note: offset must be in xINST +#endif ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] adds w2, wINST, wINST // convert to bytes, check sign FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST b.mi MterpCheckSuspendAndContinue GET_INST_OPCODE ip // extract opcode from wINST GOTO_OPCODE ip // jump to next instruction -#else - lsr w1, wINST, #12 // w1<- B - ubfx w0, wINST, #8, #4 // w0<- A - GET_VREG w3, w1 // w3<- vB - GET_VREG w2, w0 // w2<- vA - FETCH_S w1, 1 // w1<- branch offset, in code units - mov w0, #2 // Offset if branch not taken - cmp w2, w3 // compare (vA, vB) - csel wINST, w1, w0, ${condition} // Branch if true, stashing result in callee save reg. - ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] - adds w2, wINST, wINST // convert to bytes, check sign - FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST - b.mi MterpCheckSuspendAndContinue - GET_INST_OPCODE ip // extract opcode from wINST - GOTO_OPCODE ip // jump to next instruction -#endif diff --git a/runtime/interpreter/mterp/arm64/zcmp.S b/runtime/interpreter/mterp/arm64/zcmp.S index e528d9f030..3f1e1b180f 100644 --- a/runtime/interpreter/mterp/arm64/zcmp.S +++ b/runtime/interpreter/mterp/arm64/zcmp.S @@ -6,39 +6,24 @@ * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez */ /* if-cmp vAA, +BBBB */ -#if MTERP_PROFILE_BRANCHES lsr w0, wINST, #8 // w0<- AA GET_VREG w2, w0 // w2<- vAA - FETCH_S wINST, 1 // w1<- branch offset, in code units + FETCH_S w1, 1 // w1<- branch offset, in code units + mov w0, #2 // Branch offset if not taken cmp w2, #0 // compare (vA, 0) - b.${condition} .L_${opcode}_taken - FETCH_ADVANCE_INST 2 // update rPC, load wINST - GET_INST_OPCODE ip // extract opcode from wINST - GOTO_OPCODE ip // jump to next instruction -.L_${opcode}_taken: + csel wINST, w1, w0, ${condition} // Branch if true, stashing result in callee save reg +#if MTERP_PROFILE_BRANCHES + // TUNING: once measurements are complete, remove #if and hand-schedule. EXPORT_PC mov x0, xSELF add x1, xFP, #OFF_FP_SHADOWFRAME sbfm x2, xINST, 0, 31 bl MterpProfileBranch // (self, shadow_frame, offset) cbnz w0, MterpOnStackReplacement // Note: offset must be in wINST +#endif ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] adds w2, wINST, wINST // convert to bytes & set flags FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST b.mi MterpCheckSuspendAndContinue GET_INST_OPCODE ip // extract opcode from wINST GOTO_OPCODE ip // jump to next instruction -#else - lsr w0, wINST, #8 // w0<- AA - GET_VREG w2, w0 // w2<- vAA - FETCH_S w1, 1 // w1<- branch offset, in code units - mov w0, #2 // Branch offset if not taken - cmp w2, #0 // compare (vA, 0) - csel wINST, w1, w0, ${condition} // Branch if true, stashing result in callee save reg - ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] - adds w2, wINST, wINST // convert to bytes & set flags - FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST - b.mi MterpCheckSuspendAndContinue - GET_INST_OPCODE ip // extract opcode from wINST - GOTO_OPCODE ip // jump to next instruction -#endif diff --git a/runtime/interpreter/mterp/out/mterp_arm.S b/runtime/interpreter/mterp/out/mterp_arm.S index 94cbd2d10e..2b74d4c86e 100644 --- a/runtime/interpreter/mterp/out/mterp_arm.S +++ b/runtime/interpreter/mterp/out/mterp_arm.S @@ -1534,14 +1534,15 @@ artMterpAsmInstructionStart = .L_op_nop * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le */ /* if-cmp vA, vB, +CCCC */ -#if MTERP_PROFILE_BRANCHES mov r1, rINST, lsr #12 @ r1<- B ubfx r0, rINST, #8, #4 @ r0<- A GET_VREG r3, r1 @ r3<- vB GET_VREG r2, r0 @ r2<- vA FETCH_S rINST, 1 @ rINST<- branch offset, in code units cmp r2, r3 @ compare (vA, vB) - bne .L_op_if_eq_not_taken + movne rINST, #2 +#if MTERP_PROFILE_BRANCHES + @ TUNING: once measurements are complete, remove #if and hand-schedule. EXPORT_PC mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME @@ -1549,31 +1550,13 @@ artMterpAsmInstructionStart = .L_op_nop bl MterpProfileBranch @ (self, shadow_frame, offset) cmp r0, #0 bne MterpOnStackReplacement @ Note: offset must be in rINST +#endif adds r2, rINST, rINST @ convert to bytes, check sign ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST bmi MterpCheckSuspendAndContinue GET_INST_OPCODE ip @ extract opcode from rINST GOTO_OPCODE ip @ jump to next instruction -.L_op_if_eq_not_taken: - FETCH_ADVANCE_INST 2 @ update rPC, load rINST - GET_INST_OPCODE ip @ extract opcode from rINST - GOTO_OPCODE ip @ jump to next instruction -#else - mov r1, rINST, lsr #12 @ r1<- B - ubfx r0, rINST, #8, #4 @ r0<- A - GET_VREG r3, r1 @ r3<- vB - GET_VREG r2, r0 @ r2<- vA - ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] - FETCH_S rINST, 1 @ rINST<- branch offset, in code units - cmp r2, r3 @ compare (vA, vB) - movne rINST, #2 @ rINST<- BYTE branch dist for not-taken - adds r2, rINST, rINST @ convert to bytes, check sign - FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST - bmi MterpCheckSuspendAndContinue - GET_INST_OPCODE ip @ extract opcode from rINST - GOTO_OPCODE ip @ jump to next instruction -#endif /* ------------------------------ */ @@ -1589,14 +1572,15 @@ artMterpAsmInstructionStart = .L_op_nop * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le */ /* if-cmp vA, vB, +CCCC */ -#if MTERP_PROFILE_BRANCHES mov r1, rINST, lsr #12 @ r1<- B ubfx r0, rINST, #8, #4 @ r0<- A GET_VREG r3, r1 @ r3<- vB GET_VREG r2, r0 @ r2<- vA FETCH_S rINST, 1 @ rINST<- branch offset, in code units cmp r2, r3 @ compare (vA, vB) - beq .L_op_if_ne_not_taken + moveq rINST, #2 +#if MTERP_PROFILE_BRANCHES + @ TUNING: once measurements are complete, remove #if and hand-schedule. EXPORT_PC mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME @@ -1604,31 +1588,13 @@ artMterpAsmInstructionStart = .L_op_nop bl MterpProfileBranch @ (self, shadow_frame, offset) cmp r0, #0 bne MterpOnStackReplacement @ Note: offset must be in rINST +#endif adds r2, rINST, rINST @ convert to bytes, check sign ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST bmi MterpCheckSuspendAndContinue GET_INST_OPCODE ip @ extract opcode from rINST GOTO_OPCODE ip @ jump to next instruction -.L_op_if_ne_not_taken: - FETCH_ADVANCE_INST 2 @ update rPC, load rINST - GET_INST_OPCODE ip @ extract opcode from rINST - GOTO_OPCODE ip @ jump to next instruction -#else - mov r1, rINST, lsr #12 @ r1<- B - ubfx r0, rINST, #8, #4 @ r0<- A - GET_VREG r3, r1 @ r3<- vB - GET_VREG r2, r0 @ r2<- vA - ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] - FETCH_S rINST, 1 @ rINST<- branch offset, in code units - cmp r2, r3 @ compare (vA, vB) - moveq rINST, #2 @ rINST<- BYTE branch dist for not-taken - adds r2, rINST, rINST @ convert to bytes, check sign - FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST - bmi MterpCheckSuspendAndContinue - GET_INST_OPCODE ip @ extract opcode from rINST - GOTO_OPCODE ip @ jump to next instruction -#endif /* ------------------------------ */ @@ -1644,14 +1610,15 @@ artMterpAsmInstructionStart = .L_op_nop * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le */ /* if-cmp vA, vB, +CCCC */ -#if MTERP_PROFILE_BRANCHES mov r1, rINST, lsr #12 @ r1<- B ubfx r0, rINST, #8, #4 @ r0<- A GET_VREG r3, r1 @ r3<- vB GET_VREG r2, r0 @ r2<- vA FETCH_S rINST, 1 @ rINST<- branch offset, in code units cmp r2, r3 @ compare (vA, vB) - bge .L_op_if_lt_not_taken + movge rINST, #2 +#if MTERP_PROFILE_BRANCHES + @ TUNING: once measurements are complete, remove #if and hand-schedule. EXPORT_PC mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME @@ -1659,31 +1626,13 @@ artMterpAsmInstructionStart = .L_op_nop bl MterpProfileBranch @ (self, shadow_frame, offset) cmp r0, #0 bne MterpOnStackReplacement @ Note: offset must be in rINST +#endif adds r2, rINST, rINST @ convert to bytes, check sign ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST bmi MterpCheckSuspendAndContinue GET_INST_OPCODE ip @ extract opcode from rINST GOTO_OPCODE ip @ jump to next instruction -.L_op_if_lt_not_taken: - FETCH_ADVANCE_INST 2 @ update rPC, load rINST - GET_INST_OPCODE ip @ extract opcode from rINST - GOTO_OPCODE ip @ jump to next instruction -#else - mov r1, rINST, lsr #12 @ r1<- B - ubfx r0, rINST, #8, #4 @ r0<- A - GET_VREG r3, r1 @ r3<- vB - GET_VREG r2, r0 @ r2<- vA - ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] - FETCH_S rINST, 1 @ rINST<- branch offset, in code units - cmp r2, r3 @ compare (vA, vB) - movge rINST, #2 @ rINST<- BYTE branch dist for not-taken - adds r2, rINST, rINST @ convert to bytes, check sign - FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST - bmi MterpCheckSuspendAndContinue - GET_INST_OPCODE ip @ extract opcode from rINST - GOTO_OPCODE ip @ jump to next instruction -#endif /* ------------------------------ */ @@ -1699,14 +1648,15 @@ artMterpAsmInstructionStart = .L_op_nop * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le */ /* if-cmp vA, vB, +CCCC */ -#if MTERP_PROFILE_BRANCHES mov r1, rINST, lsr #12 @ r1<- B ubfx r0, rINST, #8, #4 @ r0<- A GET_VREG r3, r1 @ r3<- vB GET_VREG r2, r0 @ r2<- vA FETCH_S rINST, 1 @ rINST<- branch offset, in code units cmp r2, r3 @ compare (vA, vB) - blt .L_op_if_ge_not_taken + movlt rINST, #2 +#if MTERP_PROFILE_BRANCHES + @ TUNING: once measurements are complete, remove #if and hand-schedule. EXPORT_PC mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME @@ -1714,31 +1664,13 @@ artMterpAsmInstructionStart = .L_op_nop bl MterpProfileBranch @ (self, shadow_frame, offset) cmp r0, #0 bne MterpOnStackReplacement @ Note: offset must be in rINST +#endif adds r2, rINST, rINST @ convert to bytes, check sign ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST bmi MterpCheckSuspendAndContinue GET_INST_OPCODE ip @ extract opcode from rINST GOTO_OPCODE ip @ jump to next instruction -.L_op_if_ge_not_taken: - FETCH_ADVANCE_INST 2 @ update rPC, load rINST - GET_INST_OPCODE ip @ extract opcode from rINST - GOTO_OPCODE ip @ jump to next instruction -#else - mov r1, rINST, lsr #12 @ r1<- B - ubfx r0, rINST, #8, #4 @ r0<- A - GET_VREG r3, r1 @ r3<- vB - GET_VREG r2, r0 @ r2<- vA - ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] - FETCH_S rINST, 1 @ rINST<- branch offset, in code units - cmp r2, r3 @ compare (vA, vB) - movlt rINST, #2 @ rINST<- BYTE branch dist for not-taken - adds r2, rINST, rINST @ convert to bytes, check sign - FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST - bmi MterpCheckSuspendAndContinue - GET_INST_OPCODE ip @ extract opcode from rINST - GOTO_OPCODE ip @ jump to next instruction -#endif /* ------------------------------ */ @@ -1754,14 +1686,15 @@ artMterpAsmInstructionStart = .L_op_nop * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le */ /* if-cmp vA, vB, +CCCC */ -#if MTERP_PROFILE_BRANCHES mov r1, rINST, lsr #12 @ r1<- B ubfx r0, rINST, #8, #4 @ r0<- A GET_VREG r3, r1 @ r3<- vB GET_VREG r2, r0 @ r2<- vA FETCH_S rINST, 1 @ rINST<- branch offset, in code units cmp r2, r3 @ compare (vA, vB) - ble .L_op_if_gt_not_taken + movle rINST, #2 +#if MTERP_PROFILE_BRANCHES + @ TUNING: once measurements are complete, remove #if and hand-schedule. EXPORT_PC mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME @@ -1769,31 +1702,13 @@ artMterpAsmInstructionStart = .L_op_nop bl MterpProfileBranch @ (self, shadow_frame, offset) cmp r0, #0 bne MterpOnStackReplacement @ Note: offset must be in rINST +#endif adds r2, rINST, rINST @ convert to bytes, check sign ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST bmi MterpCheckSuspendAndContinue GET_INST_OPCODE ip @ extract opcode from rINST GOTO_OPCODE ip @ jump to next instruction -.L_op_if_gt_not_taken: - FETCH_ADVANCE_INST 2 @ update rPC, load rINST - GET_INST_OPCODE ip @ extract opcode from rINST - GOTO_OPCODE ip @ jump to next instruction -#else - mov r1, rINST, lsr #12 @ r1<- B - ubfx r0, rINST, #8, #4 @ r0<- A - GET_VREG r3, r1 @ r3<- vB - GET_VREG r2, r0 @ r2<- vA - ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] - FETCH_S rINST, 1 @ rINST<- branch offset, in code units - cmp r2, r3 @ compare (vA, vB) - movle rINST, #2 @ rINST<- BYTE branch dist for not-taken - adds r2, rINST, rINST @ convert to bytes, check sign - FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST - bmi MterpCheckSuspendAndContinue - GET_INST_OPCODE ip @ extract opcode from rINST - GOTO_OPCODE ip @ jump to next instruction -#endif /* ------------------------------ */ @@ -1809,14 +1724,15 @@ artMterpAsmInstructionStart = .L_op_nop * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le */ /* if-cmp vA, vB, +CCCC */ -#if MTERP_PROFILE_BRANCHES mov r1, rINST, lsr #12 @ r1<- B ubfx r0, rINST, #8, #4 @ r0<- A GET_VREG r3, r1 @ r3<- vB GET_VREG r2, r0 @ r2<- vA FETCH_S rINST, 1 @ rINST<- branch offset, in code units cmp r2, r3 @ compare (vA, vB) - bgt .L_op_if_le_not_taken + movgt rINST, #2 +#if MTERP_PROFILE_BRANCHES + @ TUNING: once measurements are complete, remove #if and hand-schedule. EXPORT_PC mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME @@ -1824,31 +1740,13 @@ artMterpAsmInstructionStart = .L_op_nop bl MterpProfileBranch @ (self, shadow_frame, offset) cmp r0, #0 bne MterpOnStackReplacement @ Note: offset must be in rINST +#endif adds r2, rINST, rINST @ convert to bytes, check sign ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST bmi MterpCheckSuspendAndContinue GET_INST_OPCODE ip @ extract opcode from rINST GOTO_OPCODE ip @ jump to next instruction -.L_op_if_le_not_taken: - FETCH_ADVANCE_INST 2 @ update rPC, load rINST - GET_INST_OPCODE ip @ extract opcode from rINST - GOTO_OPCODE ip @ jump to next instruction -#else - mov r1, rINST, lsr #12 @ r1<- B - ubfx r0, rINST, #8, #4 @ r0<- A - GET_VREG r3, r1 @ r3<- vB - GET_VREG r2, r0 @ r2<- vA - ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] - FETCH_S rINST, 1 @ rINST<- branch offset, in code units - cmp r2, r3 @ compare (vA, vB) - movgt rINST, #2 @ rINST<- BYTE branch dist for not-taken - adds r2, rINST, rINST @ convert to bytes, check sign - FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST - bmi MterpCheckSuspendAndContinue - GET_INST_OPCODE ip @ extract opcode from rINST - GOTO_OPCODE ip @ jump to next instruction -#endif /* ------------------------------ */ @@ -1864,13 +1762,14 @@ artMterpAsmInstructionStart = .L_op_nop * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez */ /* if-cmp vAA, +BBBB */ -#if MTERP_PROFILE_BRANCHES mov r0, rINST, lsr #8 @ r0<- AA GET_VREG r2, r0 @ r2<- vAA FETCH_S rINST, 1 @ rINST<- branch offset, in code units ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] cmp r2, #0 @ compare (vA, 0) - bne .L_op_if_eqz_not_taken + movne rINST, #2 +#if MTERP_PROFILE_BRANCHES + @ TUNING: once measurements are complete, remove #if and hand-schedule. EXPORT_PC mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME @@ -1878,28 +1777,12 @@ artMterpAsmInstructionStart = .L_op_nop bl MterpProfileBranch @ (self, shadow_frame, offset) cmp r0, #0 bne MterpOnStackReplacement @ Note: offset must be in rINST +#endif adds r1, rINST, rINST @ convert to bytes & set flags FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST bmi MterpCheckSuspendAndContinue GET_INST_OPCODE ip @ extract opcode from rINST GOTO_OPCODE ip @ jump to next instruction -.L_op_if_eqz_not_taken: - FETCH_ADVANCE_INST 2 @ update rPC, load rINST - GET_INST_OPCODE ip @ extract opcode from rINST - GOTO_OPCODE ip @ jump to next instruction -#else - mov r0, rINST, lsr #8 @ r0<- AA - GET_VREG r2, r0 @ r2<- vAA - FETCH_S rINST, 1 @ rINST<- branch offset, in code units - ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] - cmp r2, #0 @ compare (vA, 0) - movne rINST, #2 @ rINST<- inst branch dist for not-taken - adds r1, rINST, rINST @ convert to bytes & set flags - FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST - bmi MterpCheckSuspendAndContinue - GET_INST_OPCODE ip @ extract opcode from rINST - GOTO_OPCODE ip @ jump to next instruction -#endif /* ------------------------------ */ @@ -1915,13 +1798,14 @@ artMterpAsmInstructionStart = .L_op_nop * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez */ /* if-cmp vAA, +BBBB */ -#if MTERP_PROFILE_BRANCHES mov r0, rINST, lsr #8 @ r0<- AA GET_VREG r2, r0 @ r2<- vAA FETCH_S rINST, 1 @ rINST<- branch offset, in code units ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] cmp r2, #0 @ compare (vA, 0) - beq .L_op_if_nez_not_taken + moveq rINST, #2 +#if MTERP_PROFILE_BRANCHES + @ TUNING: once measurements are complete, remove #if and hand-schedule. EXPORT_PC mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME @@ -1929,28 +1813,12 @@ artMterpAsmInstructionStart = .L_op_nop bl MterpProfileBranch @ (self, shadow_frame, offset) cmp r0, #0 bne MterpOnStackReplacement @ Note: offset must be in rINST +#endif adds r1, rINST, rINST @ convert to bytes & set flags FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST bmi MterpCheckSuspendAndContinue GET_INST_OPCODE ip @ extract opcode from rINST GOTO_OPCODE ip @ jump to next instruction -.L_op_if_nez_not_taken: - FETCH_ADVANCE_INST 2 @ update rPC, load rINST - GET_INST_OPCODE ip @ extract opcode from rINST - GOTO_OPCODE ip @ jump to next instruction -#else - mov r0, rINST, lsr #8 @ r0<- AA - GET_VREG r2, r0 @ r2<- vAA - FETCH_S rINST, 1 @ rINST<- branch offset, in code units - ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] - cmp r2, #0 @ compare (vA, 0) - moveq rINST, #2 @ rINST<- inst branch dist for not-taken - adds r1, rINST, rINST @ convert to bytes & set flags - FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST - bmi MterpCheckSuspendAndContinue - GET_INST_OPCODE ip @ extract opcode from rINST - GOTO_OPCODE ip @ jump to next instruction -#endif /* ------------------------------ */ @@ -1966,13 +1834,14 @@ artMterpAsmInstructionStart = .L_op_nop * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez */ /* if-cmp vAA, +BBBB */ -#if MTERP_PROFILE_BRANCHES mov r0, rINST, lsr #8 @ r0<- AA GET_VREG r2, r0 @ r2<- vAA FETCH_S rINST, 1 @ rINST<- branch offset, in code units ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] cmp r2, #0 @ compare (vA, 0) - bge .L_op_if_ltz_not_taken + movge rINST, #2 +#if MTERP_PROFILE_BRANCHES + @ TUNING: once measurements are complete, remove #if and hand-schedule. EXPORT_PC mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME @@ -1980,28 +1849,12 @@ artMterpAsmInstructionStart = .L_op_nop bl MterpProfileBranch @ (self, shadow_frame, offset) cmp r0, #0 bne MterpOnStackReplacement @ Note: offset must be in rINST +#endif adds r1, rINST, rINST @ convert to bytes & set flags FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST bmi MterpCheckSuspendAndContinue GET_INST_OPCODE ip @ extract opcode from rINST GOTO_OPCODE ip @ jump to next instruction -.L_op_if_ltz_not_taken: - FETCH_ADVANCE_INST 2 @ update rPC, load rINST - GET_INST_OPCODE ip @ extract opcode from rINST - GOTO_OPCODE ip @ jump to next instruction -#else - mov r0, rINST, lsr #8 @ r0<- AA - GET_VREG r2, r0 @ r2<- vAA - FETCH_S rINST, 1 @ rINST<- branch offset, in code units - ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] - cmp r2, #0 @ compare (vA, 0) - movge rINST, #2 @ rINST<- inst branch dist for not-taken - adds r1, rINST, rINST @ convert to bytes & set flags - FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST - bmi MterpCheckSuspendAndContinue - GET_INST_OPCODE ip @ extract opcode from rINST - GOTO_OPCODE ip @ jump to next instruction -#endif /* ------------------------------ */ @@ -2017,13 +1870,14 @@ artMterpAsmInstructionStart = .L_op_nop * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez */ /* if-cmp vAA, +BBBB */ -#if MTERP_PROFILE_BRANCHES mov r0, rINST, lsr #8 @ r0<- AA GET_VREG r2, r0 @ r2<- vAA FETCH_S rINST, 1 @ rINST<- branch offset, in code units ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] cmp r2, #0 @ compare (vA, 0) - blt .L_op_if_gez_not_taken + movlt rINST, #2 +#if MTERP_PROFILE_BRANCHES + @ TUNING: once measurements are complete, remove #if and hand-schedule. EXPORT_PC mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME @@ -2031,28 +1885,12 @@ artMterpAsmInstructionStart = .L_op_nop bl MterpProfileBranch @ (self, shadow_frame, offset) cmp r0, #0 bne MterpOnStackReplacement @ Note: offset must be in rINST +#endif adds r1, rINST, rINST @ convert to bytes & set flags FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST bmi MterpCheckSuspendAndContinue GET_INST_OPCODE ip @ extract opcode from rINST GOTO_OPCODE ip @ jump to next instruction -.L_op_if_gez_not_taken: - FETCH_ADVANCE_INST 2 @ update rPC, load rINST - GET_INST_OPCODE ip @ extract opcode from rINST - GOTO_OPCODE ip @ jump to next instruction -#else - mov r0, rINST, lsr #8 @ r0<- AA - GET_VREG r2, r0 @ r2<- vAA - FETCH_S rINST, 1 @ rINST<- branch offset, in code units - ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] - cmp r2, #0 @ compare (vA, 0) - movlt rINST, #2 @ rINST<- inst branch dist for not-taken - adds r1, rINST, rINST @ convert to bytes & set flags - FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST - bmi MterpCheckSuspendAndContinue - GET_INST_OPCODE ip @ extract opcode from rINST - GOTO_OPCODE ip @ jump to next instruction -#endif /* ------------------------------ */ @@ -2068,13 +1906,14 @@ artMterpAsmInstructionStart = .L_op_nop * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez */ /* if-cmp vAA, +BBBB */ -#if MTERP_PROFILE_BRANCHES mov r0, rINST, lsr #8 @ r0<- AA GET_VREG r2, r0 @ r2<- vAA FETCH_S rINST, 1 @ rINST<- branch offset, in code units ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] cmp r2, #0 @ compare (vA, 0) - ble .L_op_if_gtz_not_taken + movle rINST, #2 +#if MTERP_PROFILE_BRANCHES + @ TUNING: once measurements are complete, remove #if and hand-schedule. EXPORT_PC mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME @@ -2082,28 +1921,12 @@ artMterpAsmInstructionStart = .L_op_nop bl MterpProfileBranch @ (self, shadow_frame, offset) cmp r0, #0 bne MterpOnStackReplacement @ Note: offset must be in rINST +#endif adds r1, rINST, rINST @ convert to bytes & set flags FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST bmi MterpCheckSuspendAndContinue GET_INST_OPCODE ip @ extract opcode from rINST GOTO_OPCODE ip @ jump to next instruction -.L_op_if_gtz_not_taken: - FETCH_ADVANCE_INST 2 @ update rPC, load rINST - GET_INST_OPCODE ip @ extract opcode from rINST - GOTO_OPCODE ip @ jump to next instruction -#else - mov r0, rINST, lsr #8 @ r0<- AA - GET_VREG r2, r0 @ r2<- vAA - FETCH_S rINST, 1 @ rINST<- branch offset, in code units - ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] - cmp r2, #0 @ compare (vA, 0) - movle rINST, #2 @ rINST<- inst branch dist for not-taken - adds r1, rINST, rINST @ convert to bytes & set flags - FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST - bmi MterpCheckSuspendAndContinue - GET_INST_OPCODE ip @ extract opcode from rINST - GOTO_OPCODE ip @ jump to next instruction -#endif /* ------------------------------ */ @@ -2119,13 +1942,14 @@ artMterpAsmInstructionStart = .L_op_nop * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez */ /* if-cmp vAA, +BBBB */ -#if MTERP_PROFILE_BRANCHES mov r0, rINST, lsr #8 @ r0<- AA GET_VREG r2, r0 @ r2<- vAA FETCH_S rINST, 1 @ rINST<- branch offset, in code units ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] cmp r2, #0 @ compare (vA, 0) - bgt .L_op_if_lez_not_taken + movgt rINST, #2 +#if MTERP_PROFILE_BRANCHES + @ TUNING: once measurements are complete, remove #if and hand-schedule. EXPORT_PC mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME @@ -2133,28 +1957,12 @@ artMterpAsmInstructionStart = .L_op_nop bl MterpProfileBranch @ (self, shadow_frame, offset) cmp r0, #0 bne MterpOnStackReplacement @ Note: offset must be in rINST +#endif adds r1, rINST, rINST @ convert to bytes & set flags FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST bmi MterpCheckSuspendAndContinue GET_INST_OPCODE ip @ extract opcode from rINST GOTO_OPCODE ip @ jump to next instruction -.L_op_if_lez_not_taken: - FETCH_ADVANCE_INST 2 @ update rPC, load rINST - GET_INST_OPCODE ip @ extract opcode from rINST - GOTO_OPCODE ip @ jump to next instruction -#else - mov r0, rINST, lsr #8 @ r0<- AA - GET_VREG r2, r0 @ r2<- vAA - FETCH_S rINST, 1 @ rINST<- branch offset, in code units - ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] - cmp r2, #0 @ compare (vA, 0) - movgt rINST, #2 @ rINST<- inst branch dist for not-taken - adds r1, rINST, rINST @ convert to bytes & set flags - FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST - bmi MterpCheckSuspendAndContinue - GET_INST_OPCODE ip @ extract opcode from rINST - GOTO_OPCODE ip @ jump to next instruction -#endif /* ------------------------------ */ diff --git a/runtime/interpreter/mterp/out/mterp_arm64.S b/runtime/interpreter/mterp/out/mterp_arm64.S index e4825f0489..c7c0fb5b79 100644 --- a/runtime/interpreter/mterp/out/mterp_arm64.S +++ b/runtime/interpreter/mterp/out/mterp_arm64.S @@ -1372,46 +1372,29 @@ artMterpAsmInstructionStart = .L_op_nop * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le */ /* if-cmp vA, vB, +CCCC */ -#if MTERP_PROFILE_BRANCHES lsr w1, wINST, #12 // w1<- B ubfx w0, wINST, #8, #4 // w0<- A GET_VREG w3, w1 // w3<- vB GET_VREG w2, w0 // w2<- vA - FETCH_S wINST, 1 // wINST<- branch offset, in code units + FETCH_S w1, 1 // w1<- branch offset, in code units + mov w0, #2 // Offset if branch not taken cmp w2, w3 // compare (vA, vB) - b.eq .L_op_if_eq_taken - FETCH_ADVANCE_INST 2 // update rPC, load wINST - GET_INST_OPCODE ip // extract opcode from wINST - GOTO_OPCODE ip // jump to next instruction -.L_op_if_eq_taken: + csel wINST, w1, w0, eq // Branch if true, stashing result in callee save reg. +#if MTERP_PROFILE_BRANCHES + // TUINING: once measurements are complete, remove #if and hand-schedule. EXPORT_PC mov x0, xSELF add x1, xFP, #OFF_FP_SHADOWFRAME sbfm x2, xINST, 0, 31 // Sign extend branch offset bl MterpProfileBranch // (self, shadow_frame, offset) cbnz w0, MterpOnStackReplacement // Note: offset must be in xINST +#endif ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] adds w2, wINST, wINST // convert to bytes, check sign FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST b.mi MterpCheckSuspendAndContinue GET_INST_OPCODE ip // extract opcode from wINST GOTO_OPCODE ip // jump to next instruction -#else - lsr w1, wINST, #12 // w1<- B - ubfx w0, wINST, #8, #4 // w0<- A - GET_VREG w3, w1 // w3<- vB - GET_VREG w2, w0 // w2<- vA - FETCH_S w1, 1 // w1<- branch offset, in code units - mov w0, #2 // Offset if branch not taken - cmp w2, w3 // compare (vA, vB) - csel wINST, w1, w0, eq // Branch if true, stashing result in callee save reg. - ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] - adds w2, wINST, wINST // convert to bytes, check sign - FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST - b.mi MterpCheckSuspendAndContinue - GET_INST_OPCODE ip // extract opcode from wINST - GOTO_OPCODE ip // jump to next instruction -#endif /* ------------------------------ */ @@ -1427,46 +1410,29 @@ artMterpAsmInstructionStart = .L_op_nop * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le */ /* if-cmp vA, vB, +CCCC */ -#if MTERP_PROFILE_BRANCHES lsr w1, wINST, #12 // w1<- B ubfx w0, wINST, #8, #4 // w0<- A GET_VREG w3, w1 // w3<- vB GET_VREG w2, w0 // w2<- vA - FETCH_S wINST, 1 // wINST<- branch offset, in code units + FETCH_S w1, 1 // w1<- branch offset, in code units + mov w0, #2 // Offset if branch not taken cmp w2, w3 // compare (vA, vB) - b.ne .L_op_if_ne_taken - FETCH_ADVANCE_INST 2 // update rPC, load wINST - GET_INST_OPCODE ip // extract opcode from wINST - GOTO_OPCODE ip // jump to next instruction -.L_op_if_ne_taken: + csel wINST, w1, w0, ne // Branch if true, stashing result in callee save reg. +#if MTERP_PROFILE_BRANCHES + // TUINING: once measurements are complete, remove #if and hand-schedule. EXPORT_PC mov x0, xSELF add x1, xFP, #OFF_FP_SHADOWFRAME sbfm x2, xINST, 0, 31 // Sign extend branch offset bl MterpProfileBranch // (self, shadow_frame, offset) cbnz w0, MterpOnStackReplacement // Note: offset must be in xINST +#endif ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] adds w2, wINST, wINST // convert to bytes, check sign FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST b.mi MterpCheckSuspendAndContinue GET_INST_OPCODE ip // extract opcode from wINST GOTO_OPCODE ip // jump to next instruction -#else - lsr w1, wINST, #12 // w1<- B - ubfx w0, wINST, #8, #4 // w0<- A - GET_VREG w3, w1 // w3<- vB - GET_VREG w2, w0 // w2<- vA - FETCH_S w1, 1 // w1<- branch offset, in code units - mov w0, #2 // Offset if branch not taken - cmp w2, w3 // compare (vA, vB) - csel wINST, w1, w0, ne // Branch if true, stashing result in callee save reg. - ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] - adds w2, wINST, wINST // convert to bytes, check sign - FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST - b.mi MterpCheckSuspendAndContinue - GET_INST_OPCODE ip // extract opcode from wINST - GOTO_OPCODE ip // jump to next instruction -#endif /* ------------------------------ */ @@ -1482,46 +1448,29 @@ artMterpAsmInstructionStart = .L_op_nop * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le */ /* if-cmp vA, vB, +CCCC */ -#if MTERP_PROFILE_BRANCHES lsr w1, wINST, #12 // w1<- B ubfx w0, wINST, #8, #4 // w0<- A GET_VREG w3, w1 // w3<- vB GET_VREG w2, w0 // w2<- vA - FETCH_S wINST, 1 // wINST<- branch offset, in code units + FETCH_S w1, 1 // w1<- branch offset, in code units + mov w0, #2 // Offset if branch not taken cmp w2, w3 // compare (vA, vB) - b.lt .L_op_if_lt_taken - FETCH_ADVANCE_INST 2 // update rPC, load wINST - GET_INST_OPCODE ip // extract opcode from wINST - GOTO_OPCODE ip // jump to next instruction -.L_op_if_lt_taken: + csel wINST, w1, w0, lt // Branch if true, stashing result in callee save reg. +#if MTERP_PROFILE_BRANCHES + // TUINING: once measurements are complete, remove #if and hand-schedule. EXPORT_PC mov x0, xSELF add x1, xFP, #OFF_FP_SHADOWFRAME sbfm x2, xINST, 0, 31 // Sign extend branch offset bl MterpProfileBranch // (self, shadow_frame, offset) cbnz w0, MterpOnStackReplacement // Note: offset must be in xINST +#endif ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] adds w2, wINST, wINST // convert to bytes, check sign FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST b.mi MterpCheckSuspendAndContinue GET_INST_OPCODE ip // extract opcode from wINST GOTO_OPCODE ip // jump to next instruction -#else - lsr w1, wINST, #12 // w1<- B - ubfx w0, wINST, #8, #4 // w0<- A - GET_VREG w3, w1 // w3<- vB - GET_VREG w2, w0 // w2<- vA - FETCH_S w1, 1 // w1<- branch offset, in code units - mov w0, #2 // Offset if branch not taken - cmp w2, w3 // compare (vA, vB) - csel wINST, w1, w0, lt // Branch if true, stashing result in callee save reg. - ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] - adds w2, wINST, wINST // convert to bytes, check sign - FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST - b.mi MterpCheckSuspendAndContinue - GET_INST_OPCODE ip // extract opcode from wINST - GOTO_OPCODE ip // jump to next instruction -#endif /* ------------------------------ */ @@ -1537,46 +1486,29 @@ artMterpAsmInstructionStart = .L_op_nop * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le */ /* if-cmp vA, vB, +CCCC */ -#if MTERP_PROFILE_BRANCHES lsr w1, wINST, #12 // w1<- B ubfx w0, wINST, #8, #4 // w0<- A GET_VREG w3, w1 // w3<- vB GET_VREG w2, w0 // w2<- vA - FETCH_S wINST, 1 // wINST<- branch offset, in code units + FETCH_S w1, 1 // w1<- branch offset, in code units + mov w0, #2 // Offset if branch not taken cmp w2, w3 // compare (vA, vB) - b.ge .L_op_if_ge_taken - FETCH_ADVANCE_INST 2 // update rPC, load wINST - GET_INST_OPCODE ip // extract opcode from wINST - GOTO_OPCODE ip // jump to next instruction -.L_op_if_ge_taken: + csel wINST, w1, w0, ge // Branch if true, stashing result in callee save reg. +#if MTERP_PROFILE_BRANCHES + // TUINING: once measurements are complete, remove #if and hand-schedule. EXPORT_PC mov x0, xSELF add x1, xFP, #OFF_FP_SHADOWFRAME sbfm x2, xINST, 0, 31 // Sign extend branch offset bl MterpProfileBranch // (self, shadow_frame, offset) cbnz w0, MterpOnStackReplacement // Note: offset must be in xINST +#endif ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] adds w2, wINST, wINST // convert to bytes, check sign FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST b.mi MterpCheckSuspendAndContinue GET_INST_OPCODE ip // extract opcode from wINST GOTO_OPCODE ip // jump to next instruction -#else - lsr w1, wINST, #12 // w1<- B - ubfx w0, wINST, #8, #4 // w0<- A - GET_VREG w3, w1 // w3<- vB - GET_VREG w2, w0 // w2<- vA - FETCH_S w1, 1 // w1<- branch offset, in code units - mov w0, #2 // Offset if branch not taken - cmp w2, w3 // compare (vA, vB) - csel wINST, w1, w0, ge // Branch if true, stashing result in callee save reg. - ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] - adds w2, wINST, wINST // convert to bytes, check sign - FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST - b.mi MterpCheckSuspendAndContinue - GET_INST_OPCODE ip // extract opcode from wINST - GOTO_OPCODE ip // jump to next instruction -#endif /* ------------------------------ */ @@ -1592,46 +1524,29 @@ artMterpAsmInstructionStart = .L_op_nop * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le */ /* if-cmp vA, vB, +CCCC */ -#if MTERP_PROFILE_BRANCHES lsr w1, wINST, #12 // w1<- B ubfx w0, wINST, #8, #4 // w0<- A GET_VREG w3, w1 // w3<- vB GET_VREG w2, w0 // w2<- vA - FETCH_S wINST, 1 // wINST<- branch offset, in code units + FETCH_S w1, 1 // w1<- branch offset, in code units + mov w0, #2 // Offset if branch not taken cmp w2, w3 // compare (vA, vB) - b.gt .L_op_if_gt_taken - FETCH_ADVANCE_INST 2 // update rPC, load wINST - GET_INST_OPCODE ip // extract opcode from wINST - GOTO_OPCODE ip // jump to next instruction -.L_op_if_gt_taken: + csel wINST, w1, w0, gt // Branch if true, stashing result in callee save reg. +#if MTERP_PROFILE_BRANCHES + // TUINING: once measurements are complete, remove #if and hand-schedule. EXPORT_PC mov x0, xSELF add x1, xFP, #OFF_FP_SHADOWFRAME sbfm x2, xINST, 0, 31 // Sign extend branch offset bl MterpProfileBranch // (self, shadow_frame, offset) cbnz w0, MterpOnStackReplacement // Note: offset must be in xINST +#endif ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] adds w2, wINST, wINST // convert to bytes, check sign FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST b.mi MterpCheckSuspendAndContinue GET_INST_OPCODE ip // extract opcode from wINST GOTO_OPCODE ip // jump to next instruction -#else - lsr w1, wINST, #12 // w1<- B - ubfx w0, wINST, #8, #4 // w0<- A - GET_VREG w3, w1 // w3<- vB - GET_VREG w2, w0 // w2<- vA - FETCH_S w1, 1 // w1<- branch offset, in code units - mov w0, #2 // Offset if branch not taken - cmp w2, w3 // compare (vA, vB) - csel wINST, w1, w0, gt // Branch if true, stashing result in callee save reg. - ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] - adds w2, wINST, wINST // convert to bytes, check sign - FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST - b.mi MterpCheckSuspendAndContinue - GET_INST_OPCODE ip // extract opcode from wINST - GOTO_OPCODE ip // jump to next instruction -#endif /* ------------------------------ */ @@ -1647,46 +1562,29 @@ artMterpAsmInstructionStart = .L_op_nop * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le */ /* if-cmp vA, vB, +CCCC */ -#if MTERP_PROFILE_BRANCHES lsr w1, wINST, #12 // w1<- B ubfx w0, wINST, #8, #4 // w0<- A GET_VREG w3, w1 // w3<- vB GET_VREG w2, w0 // w2<- vA - FETCH_S wINST, 1 // wINST<- branch offset, in code units + FETCH_S w1, 1 // w1<- branch offset, in code units + mov w0, #2 // Offset if branch not taken cmp w2, w3 // compare (vA, vB) - b.le .L_op_if_le_taken - FETCH_ADVANCE_INST 2 // update rPC, load wINST - GET_INST_OPCODE ip // extract opcode from wINST - GOTO_OPCODE ip // jump to next instruction -.L_op_if_le_taken: + csel wINST, w1, w0, le // Branch if true, stashing result in callee save reg. +#if MTERP_PROFILE_BRANCHES + // TUINING: once measurements are complete, remove #if and hand-schedule. EXPORT_PC mov x0, xSELF add x1, xFP, #OFF_FP_SHADOWFRAME sbfm x2, xINST, 0, 31 // Sign extend branch offset bl MterpProfileBranch // (self, shadow_frame, offset) cbnz w0, MterpOnStackReplacement // Note: offset must be in xINST +#endif ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] adds w2, wINST, wINST // convert to bytes, check sign FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST b.mi MterpCheckSuspendAndContinue GET_INST_OPCODE ip // extract opcode from wINST GOTO_OPCODE ip // jump to next instruction -#else - lsr w1, wINST, #12 // w1<- B - ubfx w0, wINST, #8, #4 // w0<- A - GET_VREG w3, w1 // w3<- vB - GET_VREG w2, w0 // w2<- vA - FETCH_S w1, 1 // w1<- branch offset, in code units - mov w0, #2 // Offset if branch not taken - cmp w2, w3 // compare (vA, vB) - csel wINST, w1, w0, le // Branch if true, stashing result in callee save reg. - ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] - adds w2, wINST, wINST // convert to bytes, check sign - FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST - b.mi MterpCheckSuspendAndContinue - GET_INST_OPCODE ip // extract opcode from wINST - GOTO_OPCODE ip // jump to next instruction -#endif /* ------------------------------ */ @@ -1702,42 +1600,27 @@ artMterpAsmInstructionStart = .L_op_nop * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez */ /* if-cmp vAA, +BBBB */ -#if MTERP_PROFILE_BRANCHES lsr w0, wINST, #8 // w0<- AA GET_VREG w2, w0 // w2<- vAA - FETCH_S wINST, 1 // w1<- branch offset, in code units + FETCH_S w1, 1 // w1<- branch offset, in code units + mov w0, #2 // Branch offset if not taken cmp w2, #0 // compare (vA, 0) - b.eq .L_op_if_eqz_taken - FETCH_ADVANCE_INST 2 // update rPC, load wINST - GET_INST_OPCODE ip // extract opcode from wINST - GOTO_OPCODE ip // jump to next instruction -.L_op_if_eqz_taken: + csel wINST, w1, w0, eq // Branch if true, stashing result in callee save reg +#if MTERP_PROFILE_BRANCHES + // TUNING: once measurements are complete, remove #if and hand-schedule. EXPORT_PC mov x0, xSELF add x1, xFP, #OFF_FP_SHADOWFRAME sbfm x2, xINST, 0, 31 bl MterpProfileBranch // (self, shadow_frame, offset) cbnz w0, MterpOnStackReplacement // Note: offset must be in wINST +#endif ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] adds w2, wINST, wINST // convert to bytes & set flags FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST b.mi MterpCheckSuspendAndContinue GET_INST_OPCODE ip // extract opcode from wINST GOTO_OPCODE ip // jump to next instruction -#else - lsr w0, wINST, #8 // w0<- AA - GET_VREG w2, w0 // w2<- vAA - FETCH_S w1, 1 // w1<- branch offset, in code units - mov w0, #2 // Branch offset if not taken - cmp w2, #0 // compare (vA, 0) - csel wINST, w1, w0, eq // Branch if true, stashing result in callee save reg - ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] - adds w2, wINST, wINST // convert to bytes & set flags - FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST - b.mi MterpCheckSuspendAndContinue - GET_INST_OPCODE ip // extract opcode from wINST - GOTO_OPCODE ip // jump to next instruction -#endif /* ------------------------------ */ @@ -1753,42 +1636,27 @@ artMterpAsmInstructionStart = .L_op_nop * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez */ /* if-cmp vAA, +BBBB */ -#if MTERP_PROFILE_BRANCHES lsr w0, wINST, #8 // w0<- AA GET_VREG w2, w0 // w2<- vAA - FETCH_S wINST, 1 // w1<- branch offset, in code units + FETCH_S w1, 1 // w1<- branch offset, in code units + mov w0, #2 // Branch offset if not taken cmp w2, #0 // compare (vA, 0) - b.ne .L_op_if_nez_taken - FETCH_ADVANCE_INST 2 // update rPC, load wINST - GET_INST_OPCODE ip // extract opcode from wINST - GOTO_OPCODE ip // jump to next instruction -.L_op_if_nez_taken: + csel wINST, w1, w0, ne // Branch if true, stashing result in callee save reg +#if MTERP_PROFILE_BRANCHES + // TUNING: once measurements are complete, remove #if and hand-schedule. EXPORT_PC mov x0, xSELF add x1, xFP, #OFF_FP_SHADOWFRAME sbfm x2, xINST, 0, 31 bl MterpProfileBranch // (self, shadow_frame, offset) cbnz w0, MterpOnStackReplacement // Note: offset must be in wINST +#endif ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] adds w2, wINST, wINST // convert to bytes & set flags FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST b.mi MterpCheckSuspendAndContinue GET_INST_OPCODE ip // extract opcode from wINST GOTO_OPCODE ip // jump to next instruction -#else - lsr w0, wINST, #8 // w0<- AA - GET_VREG w2, w0 // w2<- vAA - FETCH_S w1, 1 // w1<- branch offset, in code units - mov w0, #2 // Branch offset if not taken - cmp w2, #0 // compare (vA, 0) - csel wINST, w1, w0, ne // Branch if true, stashing result in callee save reg - ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] - adds w2, wINST, wINST // convert to bytes & set flags - FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST - b.mi MterpCheckSuspendAndContinue - GET_INST_OPCODE ip // extract opcode from wINST - GOTO_OPCODE ip // jump to next instruction -#endif /* ------------------------------ */ @@ -1804,42 +1672,27 @@ artMterpAsmInstructionStart = .L_op_nop * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez */ /* if-cmp vAA, +BBBB */ -#if MTERP_PROFILE_BRANCHES lsr w0, wINST, #8 // w0<- AA GET_VREG w2, w0 // w2<- vAA - FETCH_S wINST, 1 // w1<- branch offset, in code units + FETCH_S w1, 1 // w1<- branch offset, in code units + mov w0, #2 // Branch offset if not taken cmp w2, #0 // compare (vA, 0) - b.lt .L_op_if_ltz_taken - FETCH_ADVANCE_INST 2 // update rPC, load wINST - GET_INST_OPCODE ip // extract opcode from wINST - GOTO_OPCODE ip // jump to next instruction -.L_op_if_ltz_taken: + csel wINST, w1, w0, lt // Branch if true, stashing result in callee save reg +#if MTERP_PROFILE_BRANCHES + // TUNING: once measurements are complete, remove #if and hand-schedule. EXPORT_PC mov x0, xSELF add x1, xFP, #OFF_FP_SHADOWFRAME sbfm x2, xINST, 0, 31 bl MterpProfileBranch // (self, shadow_frame, offset) cbnz w0, MterpOnStackReplacement // Note: offset must be in wINST +#endif ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] adds w2, wINST, wINST // convert to bytes & set flags FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST b.mi MterpCheckSuspendAndContinue GET_INST_OPCODE ip // extract opcode from wINST GOTO_OPCODE ip // jump to next instruction -#else - lsr w0, wINST, #8 // w0<- AA - GET_VREG w2, w0 // w2<- vAA - FETCH_S w1, 1 // w1<- branch offset, in code units - mov w0, #2 // Branch offset if not taken - cmp w2, #0 // compare (vA, 0) - csel wINST, w1, w0, lt // Branch if true, stashing result in callee save reg - ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] - adds w2, wINST, wINST // convert to bytes & set flags - FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST - b.mi MterpCheckSuspendAndContinue - GET_INST_OPCODE ip // extract opcode from wINST - GOTO_OPCODE ip // jump to next instruction -#endif /* ------------------------------ */ @@ -1855,42 +1708,27 @@ artMterpAsmInstructionStart = .L_op_nop * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez */ /* if-cmp vAA, +BBBB */ -#if MTERP_PROFILE_BRANCHES lsr w0, wINST, #8 // w0<- AA GET_VREG w2, w0 // w2<- vAA - FETCH_S wINST, 1 // w1<- branch offset, in code units + FETCH_S w1, 1 // w1<- branch offset, in code units + mov w0, #2 // Branch offset if not taken cmp w2, #0 // compare (vA, 0) - b.ge .L_op_if_gez_taken - FETCH_ADVANCE_INST 2 // update rPC, load wINST - GET_INST_OPCODE ip // extract opcode from wINST - GOTO_OPCODE ip // jump to next instruction -.L_op_if_gez_taken: + csel wINST, w1, w0, ge // Branch if true, stashing result in callee save reg +#if MTERP_PROFILE_BRANCHES + // TUNING: once measurements are complete, remove #if and hand-schedule. EXPORT_PC mov x0, xSELF add x1, xFP, #OFF_FP_SHADOWFRAME sbfm x2, xINST, 0, 31 bl MterpProfileBranch // (self, shadow_frame, offset) cbnz w0, MterpOnStackReplacement // Note: offset must be in wINST +#endif ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] adds w2, wINST, wINST // convert to bytes & set flags FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST b.mi MterpCheckSuspendAndContinue GET_INST_OPCODE ip // extract opcode from wINST GOTO_OPCODE ip // jump to next instruction -#else - lsr w0, wINST, #8 // w0<- AA - GET_VREG w2, w0 // w2<- vAA - FETCH_S w1, 1 // w1<- branch offset, in code units - mov w0, #2 // Branch offset if not taken - cmp w2, #0 // compare (vA, 0) - csel wINST, w1, w0, ge // Branch if true, stashing result in callee save reg - ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] - adds w2, wINST, wINST // convert to bytes & set flags - FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST - b.mi MterpCheckSuspendAndContinue - GET_INST_OPCODE ip // extract opcode from wINST - GOTO_OPCODE ip // jump to next instruction -#endif /* ------------------------------ */ @@ -1906,42 +1744,27 @@ artMterpAsmInstructionStart = .L_op_nop * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez */ /* if-cmp vAA, +BBBB */ -#if MTERP_PROFILE_BRANCHES lsr w0, wINST, #8 // w0<- AA GET_VREG w2, w0 // w2<- vAA - FETCH_S wINST, 1 // w1<- branch offset, in code units + FETCH_S w1, 1 // w1<- branch offset, in code units + mov w0, #2 // Branch offset if not taken cmp w2, #0 // compare (vA, 0) - b.gt .L_op_if_gtz_taken - FETCH_ADVANCE_INST 2 // update rPC, load wINST - GET_INST_OPCODE ip // extract opcode from wINST - GOTO_OPCODE ip // jump to next instruction -.L_op_if_gtz_taken: + csel wINST, w1, w0, gt // Branch if true, stashing result in callee save reg +#if MTERP_PROFILE_BRANCHES + // TUNING: once measurements are complete, remove #if and hand-schedule. EXPORT_PC mov x0, xSELF add x1, xFP, #OFF_FP_SHADOWFRAME sbfm x2, xINST, 0, 31 bl MterpProfileBranch // (self, shadow_frame, offset) cbnz w0, MterpOnStackReplacement // Note: offset must be in wINST +#endif ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] adds w2, wINST, wINST // convert to bytes & set flags FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST b.mi MterpCheckSuspendAndContinue GET_INST_OPCODE ip // extract opcode from wINST GOTO_OPCODE ip // jump to next instruction -#else - lsr w0, wINST, #8 // w0<- AA - GET_VREG w2, w0 // w2<- vAA - FETCH_S w1, 1 // w1<- branch offset, in code units - mov w0, #2 // Branch offset if not taken - cmp w2, #0 // compare (vA, 0) - csel wINST, w1, w0, gt // Branch if true, stashing result in callee save reg - ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] - adds w2, wINST, wINST // convert to bytes & set flags - FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST - b.mi MterpCheckSuspendAndContinue - GET_INST_OPCODE ip // extract opcode from wINST - GOTO_OPCODE ip // jump to next instruction -#endif /* ------------------------------ */ @@ -1957,42 +1780,27 @@ artMterpAsmInstructionStart = .L_op_nop * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez */ /* if-cmp vAA, +BBBB */ -#if MTERP_PROFILE_BRANCHES lsr w0, wINST, #8 // w0<- AA GET_VREG w2, w0 // w2<- vAA - FETCH_S wINST, 1 // w1<- branch offset, in code units + FETCH_S w1, 1 // w1<- branch offset, in code units + mov w0, #2 // Branch offset if not taken cmp w2, #0 // compare (vA, 0) - b.le .L_op_if_lez_taken - FETCH_ADVANCE_INST 2 // update rPC, load wINST - GET_INST_OPCODE ip // extract opcode from wINST - GOTO_OPCODE ip // jump to next instruction -.L_op_if_lez_taken: + csel wINST, w1, w0, le // Branch if true, stashing result in callee save reg +#if MTERP_PROFILE_BRANCHES + // TUNING: once measurements are complete, remove #if and hand-schedule. EXPORT_PC mov x0, xSELF add x1, xFP, #OFF_FP_SHADOWFRAME sbfm x2, xINST, 0, 31 bl MterpProfileBranch // (self, shadow_frame, offset) cbnz w0, MterpOnStackReplacement // Note: offset must be in wINST +#endif ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] adds w2, wINST, wINST // convert to bytes & set flags FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST b.mi MterpCheckSuspendAndContinue GET_INST_OPCODE ip // extract opcode from wINST GOTO_OPCODE ip // jump to next instruction -#else - lsr w0, wINST, #8 // w0<- AA - GET_VREG w2, w0 // w2<- vAA - FETCH_S w1, 1 // w1<- branch offset, in code units - mov w0, #2 // Branch offset if not taken - cmp w2, #0 // compare (vA, 0) - csel wINST, w1, w0, le // Branch if true, stashing result in callee save reg - ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] - adds w2, wINST, wINST // convert to bytes & set flags - FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST - b.mi MterpCheckSuspendAndContinue - GET_INST_OPCODE ip // extract opcode from wINST - GOTO_OPCODE ip // jump to next instruction -#endif /* ------------------------------ */ -- GitLab From 7dc11782ff0a5dffcd8108f256f8975f0b3e8076 Mon Sep 17 00:00:00 2001 From: David Srbecky Date: Thu, 25 Feb 2016 13:23:56 +0000 Subject: [PATCH 042/204] Implement << operator for DexRegisterLocation::Kind. This makes it comparable in DCHECK_EQ and similar methods. Change-Id: I6b5b237be89325850ae6860d011fd6741189ab01 --- compiler/debug/elf_debug_loc_writer.h | 3 +- compiler/optimizing/stack_map_stream.cc | 3 +- runtime/check_reference_map_visitor.h | 3 +- runtime/jit/jit.cc | 3 +- runtime/quick_exception_handler.cc | 14 ++++------ runtime/stack.cc | 11 ++++---- runtime/stack_map.cc | 27 +++++++++++++++++- runtime/stack_map.h | 37 +++++-------------------- 8 files changed, 48 insertions(+), 53 deletions(-) diff --git a/compiler/debug/elf_debug_loc_writer.h b/compiler/debug/elf_debug_loc_writer.h index 8fd20aa428..32f624acd3 100644 --- a/compiler/debug/elf_debug_loc_writer.h +++ b/compiler/debug/elf_debug_loc_writer.h @@ -232,8 +232,7 @@ static void WriteDebugLocEntry(const MethodDebugInfo* method_info, // kInStackLargeOffset and kConstantLargeValue are hidden by GetKind(). // kInRegisterHigh and kInFpuRegisterHigh should be handled by // the special cases above and they should not occur alone. - LOG(ERROR) << "Unexpected register location kind: " - << DexRegisterLocation::PrettyDescriptor(kind); + LOG(ERROR) << "Unexpected register location kind: " << kind; break; } if (is64bitValue) { diff --git a/compiler/optimizing/stack_map_stream.cc b/compiler/optimizing/stack_map_stream.cc index 4784de1380..b44f50890d 100644 --- a/compiler/optimizing/stack_map_stream.cc +++ b/compiler/optimizing/stack_map_stream.cc @@ -63,8 +63,7 @@ void StackMapStream::EndStackMapEntry() { void StackMapStream::AddDexRegisterEntry(DexRegisterLocation::Kind kind, int32_t value) { if (kind != DexRegisterLocation::Kind::kNone) { // Ensure we only use non-compressed location kind at this stage. - DCHECK(DexRegisterLocation::IsShortLocationKind(kind)) - << DexRegisterLocation::PrettyDescriptor(kind); + DCHECK(DexRegisterLocation::IsShortLocationKind(kind)) << kind; DexRegisterLocation location(kind, value); // Look for Dex register `location` in the location catalog (using the diff --git a/runtime/check_reference_map_visitor.h b/runtime/check_reference_map_visitor.h index b9ea475149..fcf3326c81 100644 --- a/runtime/check_reference_map_visitor.h +++ b/runtime/check_reference_map_visitor.h @@ -100,8 +100,7 @@ class CheckReferenceMapVisitor : public StackVisitor { CHECK_EQ(location.GetValue(), 0); break; default: - LOG(FATAL) << "Unexpected location kind" - << DexRegisterLocation::PrettyDescriptor(location.GetInternalKind()); + LOG(FATAL) << "Unexpected location kind " << location.GetInternalKind(); } } } diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc index 8d3da37762..c6ec5b083a 100644 --- a/runtime/jit/jit.cc +++ b/runtime/jit/jit.cc @@ -378,8 +378,7 @@ bool Jit::MaybeDoOnStackReplacement(Thread* thread, continue; } - DCHECK(location == DexRegisterLocation::Kind::kInStack) - << DexRegisterLocation::PrettyDescriptor(location); + DCHECK_EQ(location, DexRegisterLocation::Kind::kInStack); int32_t vreg_value = shadow_frame->GetVReg(vreg); int32_t slot_offset = vreg_map.GetStackOffsetInBytes(vreg, diff --git a/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc index dd384c7586..fe6a529078 100644 --- a/runtime/quick_exception_handler.cc +++ b/runtime/quick_exception_handler.cc @@ -204,8 +204,7 @@ static VRegKind ToVRegKind(DexRegisterLocation::Kind kind) { return VRegKind::kDoubleHiVReg; default: - LOG(FATAL) << "Unexpected vreg location " - << DexRegisterLocation::PrettyDescriptor(kind); + LOG(FATAL) << "Unexpected vreg location " << kind; UNREACHABLE(); } } @@ -450,12 +449,11 @@ class DeoptimizeStackVisitor FINAL : public StackVisitor { } default: { LOG(FATAL) - << "Unexpected location kind" - << DexRegisterLocation::PrettyDescriptor( - vreg_map.GetLocationInternalKind(vreg, - number_of_vregs, - code_info, - encoding)); + << "Unexpected location kind " + << vreg_map.GetLocationInternalKind(vreg, + number_of_vregs, + code_info, + encoding); UNREACHABLE(); } } diff --git a/runtime/stack.cc b/runtime/stack.cc index 5faff93b97..b1f1ed61b4 100644 --- a/runtime/stack.cc +++ b/runtime/stack.cc @@ -352,12 +352,11 @@ bool StackVisitor::GetVRegFromOptimizedCode(ArtMethod* m, uint16_t vreg, VRegKin return false; default: LOG(FATAL) - << "Unexpected location kind" - << DexRegisterLocation::PrettyDescriptor( - dex_register_map.GetLocationInternalKind(vreg, - number_of_dex_registers, - code_info, - encoding)); + << "Unexpected location kind " + << dex_register_map.GetLocationInternalKind(vreg, + number_of_dex_registers, + code_info, + encoding); UNREACHABLE(); } } diff --git a/runtime/stack_map.cc b/runtime/stack_map.cc index 5544507c06..30934360ac 100644 --- a/runtime/stack_map.cc +++ b/runtime/stack_map.cc @@ -27,6 +27,31 @@ constexpr size_t DexRegisterLocationCatalog::kNoLocationEntryIndex; constexpr uint32_t StackMap::kNoDexRegisterMap; constexpr uint32_t StackMap::kNoInlineInfo; +std::ostream& operator<<(std::ostream& stream, const DexRegisterLocation::Kind& kind) { + using Kind = DexRegisterLocation::Kind; + switch (kind) { + case Kind::kNone: + return stream << "none"; + case Kind::kInStack: + return stream << "in stack"; + case Kind::kInRegister: + return stream << "in register"; + case Kind::kInRegisterHigh: + return stream << "in register high"; + case Kind::kInFpuRegister: + return stream << "in fpu register"; + case Kind::kInFpuRegisterHigh: + return stream << "in fpu register high"; + case Kind::kConstant: + return stream << "as constant"; + case Kind::kInStackLargeOffset: + return stream << "in stack (large offset)"; + case Kind::kConstantLargeValue: + return stream << "as constant (large value)"; + } + return stream << "Kind<" << static_cast(kind) << ">"; +} + DexRegisterLocation::Kind DexRegisterMap::GetLocationInternalKind( uint16_t dex_register_number, uint16_t number_of_dex_registers, @@ -97,7 +122,7 @@ static void DumpRegisterMapping(std::ostream& os, const std::string& prefix = "v", const std::string& suffix = "") { os << prefix << dex_register_num << ": " - << DexRegisterLocation::PrettyDescriptor(location.GetInternalKind()) + << location.GetInternalKind() << " (" << location.GetValue() << ")" << suffix << '\n'; } diff --git a/runtime/stack_map.h b/runtime/stack_map.h index 97eb805501..dbf23aafc3 100644 --- a/runtime/stack_map.h +++ b/runtime/stack_map.h @@ -110,30 +110,6 @@ class DexRegisterLocation { sizeof(Kind) == 1u, "art::DexRegisterLocation::Kind has a size different from one byte."); - static const char* PrettyDescriptor(Kind kind) { - switch (kind) { - case Kind::kNone: - return "none"; - case Kind::kInStack: - return "in stack"; - case Kind::kInRegister: - return "in register"; - case Kind::kInRegisterHigh: - return "in register high"; - case Kind::kInFpuRegister: - return "in fpu register"; - case Kind::kInFpuRegisterHigh: - return "in fpu register high"; - case Kind::kConstant: - return "as constant"; - case Kind::kInStackLargeOffset: - return "in stack (large offset)"; - case Kind::kConstantLargeValue: - return "as constant (large value)"; - } - UNREACHABLE(); - } - static bool IsShortLocationKind(Kind kind) { switch (kind) { case Kind::kInStack: @@ -149,7 +125,7 @@ class DexRegisterLocation { return false; case Kind::kNone: - LOG(FATAL) << "Unexpected location kind " << PrettyDescriptor(kind); + LOG(FATAL) << "Unexpected location kind"; } UNREACHABLE(); } @@ -215,6 +191,8 @@ class DexRegisterLocation { friend class DexRegisterLocationHashFn; }; +std::ostream& operator<<(std::ostream& stream, const DexRegisterLocation::Kind& kind); + /** * Store information on unique Dex register locations used in a method. * The information is of the form: @@ -349,7 +327,7 @@ class DexRegisterLocationCatalog { case DexRegisterLocation::Kind::kConstantLargeValue: case DexRegisterLocation::Kind::kInStackLargeOffset: case DexRegisterLocation::Kind::kNone: - LOG(FATAL) << "Unexpected location kind " << DexRegisterLocation::PrettyDescriptor(kind); + LOG(FATAL) << "Unexpected location kind " << kind; } UNREACHABLE(); } @@ -373,7 +351,7 @@ class DexRegisterLocationCatalog { case DexRegisterLocation::Kind::kConstantLargeValue: case DexRegisterLocation::Kind::kInStackLargeOffset: case DexRegisterLocation::Kind::kNone: - LOG(FATAL) << "Unexpected location kind " << DexRegisterLocation::PrettyDescriptor(kind); + LOG(FATAL) << "Unexpected location kind " << kind; } UNREACHABLE(); } @@ -515,8 +493,7 @@ class DexRegisterMap { const StackMapEncoding& enc) const { DexRegisterLocation location = GetDexRegisterLocation(dex_register_number, number_of_dex_registers, code_info, enc); - DCHECK(location.GetKind() == DexRegisterLocation::Kind::kConstant) - << DexRegisterLocation::PrettyDescriptor(location.GetKind()); + DCHECK_EQ(location.GetKind(), DexRegisterLocation::Kind::kConstant); return location.GetValue(); } @@ -530,7 +507,7 @@ class DexRegisterMap { location.GetInternalKind() == DexRegisterLocation::Kind::kInRegisterHigh || location.GetInternalKind() == DexRegisterLocation::Kind::kInFpuRegister || location.GetInternalKind() == DexRegisterLocation::Kind::kInFpuRegisterHigh) - << DexRegisterLocation::PrettyDescriptor(location.GetInternalKind()); + << location.GetInternalKind(); return location.GetValue(); } -- GitLab From 1bbdfd73a98b149c31f8a80888c7ee9ab2587630 Mon Sep 17 00:00:00 2001 From: David Srbecky Date: Wed, 24 Feb 2016 16:39:26 +0000 Subject: [PATCH 043/204] Verify encoded stack maps in debug builds. Read all stack map data back after we write it and DCHECK the content. Change-Id: Ia679594ac9e5805f6d4c56686030af153b45ea8b --- compiler/optimizing/stack_map_stream.cc | 95 +++++++++++++++++++++++++ compiler/optimizing/stack_map_stream.h | 7 ++ runtime/base/bit_vector.h | 5 ++ 3 files changed, 107 insertions(+) diff --git a/compiler/optimizing/stack_map_stream.cc b/compiler/optimizing/stack_map_stream.cc index b44f50890d..54cbdf8b66 100644 --- a/compiler/optimizing/stack_map_stream.cc +++ b/compiler/optimizing/stack_map_stream.cc @@ -256,6 +256,7 @@ void StackMapStream::FillIn(MemoryRegion region) { // Ensure we reached the end of the Dex registers location_catalog. DCHECK_EQ(location_catalog_offset, dex_register_location_catalog_region.size()); + ArenaBitVector empty_bitmask(allocator_, 0, /* expandable */ false); uintptr_t next_dex_register_map_offset = 0; uintptr_t next_inline_info_offset = 0; for (size_t i = 0, e = stack_maps_.size(); i < e; ++i) { @@ -267,6 +268,9 @@ void StackMapStream::FillIn(MemoryRegion region) { stack_map.SetRegisterMask(stack_map_encoding_, entry.register_mask); if (entry.sp_mask != nullptr) { stack_map.SetStackMask(stack_map_encoding_, *entry.sp_mask); + } else { + // The MemoryRegion does not have to be zeroed, so make sure we clear the bits. + stack_map.SetStackMask(stack_map_encoding_, empty_bitmask); } if (entry.num_dex_registers == 0 || (entry.live_dex_registers_mask->NumSetBits() == 0)) { @@ -343,6 +347,11 @@ void StackMapStream::FillIn(MemoryRegion region) { } } } + + // Verify all written data in debug build. + if (kIsDebugBuild) { + CheckCodeInfo(region); + } } void StackMapStream::FillInDexRegisterMap(DexRegisterMap dex_register_map, @@ -422,4 +431,90 @@ bool StackMapStream::HaveTheSameDexMaps(const StackMapEntry& a, const StackMapEn return true; } +// Helper for CheckCodeInfo - check that register map has the expected content. +void StackMapStream::CheckDexRegisterMap(const CodeInfo& code_info, + const DexRegisterMap& dex_register_map, + size_t num_dex_registers, + BitVector* live_dex_registers_mask, + size_t dex_register_locations_index) const { + StackMapEncoding encoding = code_info.ExtractEncoding(); + for (size_t reg = 0; reg < num_dex_registers; reg++) { + // Find the location we tried to encode. + DexRegisterLocation expected = DexRegisterLocation::None(); + if (live_dex_registers_mask->IsBitSet(reg)) { + size_t catalog_index = dex_register_locations_[dex_register_locations_index++]; + expected = location_catalog_entries_[catalog_index]; + } + // Compare to the seen location. + if (expected.GetKind() == DexRegisterLocation::Kind::kNone) { + DCHECK(!dex_register_map.IsValid() || !dex_register_map.IsDexRegisterLive(reg)); + } else { + DCHECK(dex_register_map.IsDexRegisterLive(reg)); + DexRegisterLocation seen = dex_register_map.GetDexRegisterLocation( + reg, num_dex_registers, code_info, encoding); + DCHECK_EQ(expected.GetKind(), seen.GetKind()); + DCHECK_EQ(expected.GetValue(), seen.GetValue()); + } + } + if (num_dex_registers == 0) { + DCHECK(!dex_register_map.IsValid()); + } +} + +// Check that all StackMapStream inputs are correctly encoded by trying to read them back. +void StackMapStream::CheckCodeInfo(MemoryRegion region) const { + CodeInfo code_info(region); + StackMapEncoding encoding = code_info.ExtractEncoding(); + DCHECK_EQ(code_info.GetNumberOfStackMaps(), stack_maps_.size()); + for (size_t s = 0; s < stack_maps_.size(); ++s) { + const StackMap stack_map = code_info.GetStackMapAt(s, encoding); + StackMapEntry entry = stack_maps_[s]; + + // Check main stack map fields. + DCHECK_EQ(stack_map.GetNativePcOffset(encoding), entry.native_pc_offset); + DCHECK_EQ(stack_map.GetDexPc(encoding), entry.dex_pc); + DCHECK_EQ(stack_map.GetRegisterMask(encoding), entry.register_mask); + MemoryRegion stack_mask = stack_map.GetStackMask(encoding); + if (entry.sp_mask != nullptr) { + DCHECK_GE(stack_mask.size_in_bits(), entry.sp_mask->GetNumberOfBits()); + for (size_t b = 0; b < stack_mask.size_in_bits(); b++) { + DCHECK_EQ(stack_mask.LoadBit(b), entry.sp_mask->IsBitSet(b)); + } + } else { + for (size_t b = 0; b < stack_mask.size_in_bits(); b++) { + DCHECK_EQ(stack_mask.LoadBit(b), 0u); + } + } + + CheckDexRegisterMap(code_info, + code_info.GetDexRegisterMapOf( + stack_map, encoding, entry.num_dex_registers), + entry.num_dex_registers, + entry.live_dex_registers_mask, + entry.dex_register_locations_start_index); + + // Check inline info. + DCHECK_EQ(stack_map.HasInlineInfo(encoding), (entry.inlining_depth != 0)); + if (entry.inlining_depth != 0) { + InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map, encoding); + DCHECK_EQ(inline_info.GetDepth(), entry.inlining_depth); + for (size_t d = 0; d < entry.inlining_depth; ++d) { + size_t inline_info_index = entry.inline_infos_start_index + d; + DCHECK_LT(inline_info_index, inline_infos_.size()); + InlineInfoEntry inline_entry = inline_infos_[inline_info_index]; + DCHECK_EQ(inline_info.GetDexPcAtDepth(d), inline_entry.dex_pc); + DCHECK_EQ(inline_info.GetMethodIndexAtDepth(d), inline_entry.method_index); + DCHECK_EQ(inline_info.GetInvokeTypeAtDepth(d), inline_entry.invoke_type); + + CheckDexRegisterMap(code_info, + code_info.GetDexRegisterMapAtDepth( + d, inline_info, encoding, inline_entry.num_dex_registers), + inline_entry.num_dex_registers, + inline_entry.live_dex_registers_mask, + inline_entry.dex_register_locations_start_index); + } + } + } +} + } // namespace art diff --git a/compiler/optimizing/stack_map_stream.h b/compiler/optimizing/stack_map_stream.h index fc27a2b446..016a911424 100644 --- a/compiler/optimizing/stack_map_stream.h +++ b/compiler/optimizing/stack_map_stream.h @@ -167,6 +167,13 @@ class StackMapStream : public ValueObject { const BitVector& live_dex_registers_mask, uint32_t start_index_in_dex_register_locations) const; + void CheckDexRegisterMap(const CodeInfo& code_info, + const DexRegisterMap& dex_register_map, + size_t num_dex_registers, + BitVector* live_dex_registers_mask, + size_t dex_register_locations_index) const; + void CheckCodeInfo(MemoryRegion region) const; + ArenaAllocator* allocator_; ArenaVector stack_maps_; diff --git a/runtime/base/bit_vector.h b/runtime/base/bit_vector.h index 9b55e708c8..424ebb70f6 100644 --- a/runtime/base/bit_vector.h +++ b/runtime/base/bit_vector.h @@ -229,6 +229,11 @@ class BitVector { */ int GetHighestBitSet() const; + // Minimum number of bits required to store this vector, 0 if none are set. + size_t GetNumberOfBits() const { + return GetHighestBitSet() + 1; + } + // Is bit set in storage. (No range check.) static bool IsBitSet(const uint32_t* storage, uint32_t idx) { return (storage[WordIndex(idx)] & BitMask(idx)) != 0; -- GitLab From 9ff0d205fd60cba6753a91f613b198ca2d67f04d Mon Sep 17 00:00:00 2001 From: Kevin Brodsky Date: Mon, 11 Jan 2016 13:43:31 +0000 Subject: [PATCH 044/204] Optimizing: ARM64 negated bitwise operations simplification Use negated instructions on ARM64 to replace [bitwise operation + not] patterns, that is: a & ~b (BIC) a | ~b (ORN) a ^ ~b (EON) The simplification only happens if the Not is only used by the bitwise operation. It does not happen if both inputs are Not's (this should be handled by a generic simplification applying De Morgan's laws). Change-Id: I0e112b23fd8b8e10f09bfeff5994508a8ff96e9c --- compiler/optimizing/code_generator_arm64.cc | 30 +++ compiler/optimizing/graph_visualizer.cc | 4 + .../instruction_simplifier_arm64.cc | 55 +++++ .../optimizing/instruction_simplifier_arm64.h | 7 + compiler/optimizing/nodes.h | 1 + compiler/optimizing/nodes_arm64.h | 60 +++++ test/564-checker-negbitwise/expected.txt | 0 test/564-checker-negbitwise/info.txt | 1 + test/564-checker-negbitwise/src/Main.java | 207 ++++++++++++++++++ 9 files changed, 365 insertions(+) create mode 100644 test/564-checker-negbitwise/expected.txt create mode 100644 test/564-checker-negbitwise/info.txt create mode 100644 test/564-checker-negbitwise/src/Main.java diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index beb75f0afc..25487d2fad 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -1862,6 +1862,36 @@ void InstructionCodeGeneratorARM64::VisitAnd(HAnd* instruction) { HandleBinaryOp(instruction); } +void LocationsBuilderARM64::VisitArm64BitwiseNegatedRight(HArm64BitwiseNegatedRight* instr) { + DCHECK(Primitive::IsIntegralType(instr->GetType())) << instr->GetType(); + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instr); + locations->SetInAt(0, Location::RequiresRegister()); + // There is no immediate variant of negated bitwise instructions in AArch64. + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); +} + +void InstructionCodeGeneratorARM64::VisitArm64BitwiseNegatedRight( + HArm64BitwiseNegatedRight* instr) { + Register dst = OutputRegister(instr); + Register lhs = InputRegisterAt(instr, 0); + Register rhs = InputRegisterAt(instr, 1); + + switch (instr->GetOpKind()) { + case HInstruction::kAnd: + __ Bic(dst, lhs, rhs); + break; + case HInstruction::kOr: + __ Orn(dst, lhs, rhs); + break; + case HInstruction::kXor: + __ Eon(dst, lhs, rhs); + break; + default: + LOG(FATAL) << "Unreachable"; + } +} + void LocationsBuilderARM64::VisitArm64DataProcWithShifterOp( HArm64DataProcWithShifterOp* instruction) { DCHECK(instruction->GetType() == Primitive::kPrimInt || diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc index a3d6bcf450..b9638f2027 100644 --- a/compiler/optimizing/graph_visualizer.cc +++ b/compiler/optimizing/graph_visualizer.cc @@ -443,6 +443,10 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor { #endif #ifdef ART_ENABLE_CODEGEN_arm64 + void VisitArm64BitwiseNegatedRight(HArm64BitwiseNegatedRight* instruction) OVERRIDE { + StartAttributeStream("kind") << instruction->GetOpKind(); + } + void VisitArm64DataProcWithShifterOp(HArm64DataProcWithShifterOp* instruction) OVERRIDE { StartAttributeStream("kind") << instruction->GetInstrKind() << "+" << instruction->GetOpKind(); if (HArm64DataProcWithShifterOp::IsShiftOp(instruction->GetOpKind())) { diff --git a/compiler/optimizing/instruction_simplifier_arm64.cc b/compiler/optimizing/instruction_simplifier_arm64.cc index 83126a5c4d..c2bbdccc29 100644 --- a/compiler/optimizing/instruction_simplifier_arm64.cc +++ b/compiler/optimizing/instruction_simplifier_arm64.cc @@ -180,6 +180,53 @@ bool InstructionSimplifierArm64Visitor::TryMergeIntoUsersShifterOperand(HInstruc return true; } +bool InstructionSimplifierArm64Visitor::TryMergeNegatedInput(HBinaryOperation* op) { + DCHECK(op->IsAnd() || op->IsOr() || op->IsXor()) << op->DebugName(); + HInstruction* left = op->GetLeft(); + HInstruction* right = op->GetRight(); + + // Only consider the case where there is exactly one Not, with 2 Not's De + // Morgan's laws should be applied instead. + if (left->IsNot() ^ right->IsNot()) { + HInstruction* hnot = (left->IsNot() ? left : right); + HInstruction* hother = (left->IsNot() ? right : left); + + // Only do the simplification if the Not has only one use and can thus be + // safely removed. Even though ARM64 negated bitwise operations do not have + // an immediate variant (only register), we still do the simplification when + // `hother` is a constant, because it removes an instruction if the constant + // cannot be encoded as an immediate: + // mov r0, #large_constant + // neg r2, r1 + // and r0, r0, r2 + // becomes: + // mov r0, #large_constant + // bic r0, r0, r1 + if (hnot->HasOnlyOneNonEnvironmentUse()) { + // Replace code looking like + // NOT tmp, mask + // AND dst, src, tmp (respectively ORR, EOR) + // with + // BIC dst, src, mask (respectively ORN, EON) + HInstruction* src = hnot->AsNot()->GetInput(); + + HArm64BitwiseNegatedRight* neg_op = new (GetGraph()->GetArena()) + HArm64BitwiseNegatedRight(op->GetType(), op->GetKind(), hother, src, op->GetDexPc()); + + op->GetBlock()->ReplaceAndRemoveInstructionWith(op, neg_op); + hnot->GetBlock()->RemoveInstruction(hnot); + RecordSimplification(); + return true; + } + } + + return false; +} + +void InstructionSimplifierArm64Visitor::VisitAnd(HAnd* instruction) { + TryMergeNegatedInput(instruction); +} + void InstructionSimplifierArm64Visitor::VisitArrayGet(HArrayGet* instruction) { TryExtractArrayAccessAddress(instruction, instruction->GetArray(), @@ -200,6 +247,10 @@ void InstructionSimplifierArm64Visitor::VisitMul(HMul* instruction) { } } +void InstructionSimplifierArm64Visitor::VisitOr(HOr* instruction) { + TryMergeNegatedInput(instruction); +} + void InstructionSimplifierArm64Visitor::VisitShl(HShl* instruction) { if (instruction->InputAt(1)->IsConstant()) { TryMergeIntoUsersShifterOperand(instruction); @@ -232,5 +283,9 @@ void InstructionSimplifierArm64Visitor::VisitUShr(HUShr* instruction) { } } +void InstructionSimplifierArm64Visitor::VisitXor(HXor* instruction) { + TryMergeNegatedInput(instruction); +} + } // namespace arm64 } // namespace art diff --git a/compiler/optimizing/instruction_simplifier_arm64.h b/compiler/optimizing/instruction_simplifier_arm64.h index 37a34c0373..cf8458713f 100644 --- a/compiler/optimizing/instruction_simplifier_arm64.h +++ b/compiler/optimizing/instruction_simplifier_arm64.h @@ -51,14 +51,21 @@ class InstructionSimplifierArm64Visitor : public HGraphVisitor { return TryMergeIntoShifterOperand(use, bitfield_op, true); } + // For bitwise operations (And/Or/Xor) with a negated input, try to use + // a negated bitwise instruction. + bool TryMergeNegatedInput(HBinaryOperation* op); + // HInstruction visitors, sorted alphabetically. + void VisitAnd(HAnd* instruction) OVERRIDE; void VisitArrayGet(HArrayGet* instruction) OVERRIDE; void VisitArraySet(HArraySet* instruction) OVERRIDE; void VisitMul(HMul* instruction) OVERRIDE; + void VisitOr(HOr* instruction) OVERRIDE; void VisitShl(HShl* instruction) OVERRIDE; void VisitShr(HShr* instruction) OVERRIDE; void VisitTypeConversion(HTypeConversion* instruction) OVERRIDE; void VisitUShr(HUShr* instruction) OVERRIDE; + void VisitXor(HXor* instruction) OVERRIDE; OptimizingCompilerStats* stats_; }; diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 4185b2f2c5..c4764ccbb4 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -1268,6 +1268,7 @@ class HLoopInformationOutwardIterator : public ValueObject { #define FOR_EACH_CONCRETE_INSTRUCTION_ARM64(M) #else #define FOR_EACH_CONCRETE_INSTRUCTION_ARM64(M) \ + M(Arm64BitwiseNegatedRight, Instruction) \ M(Arm64DataProcWithShifterOp, Instruction) \ M(Arm64IntermediateAddress, Instruction) #endif diff --git a/compiler/optimizing/nodes_arm64.h b/compiler/optimizing/nodes_arm64.h index 173852a55d..75a71e78b8 100644 --- a/compiler/optimizing/nodes_arm64.h +++ b/compiler/optimizing/nodes_arm64.h @@ -118,6 +118,66 @@ class HArm64IntermediateAddress : public HExpression<2> { DISALLOW_COPY_AND_ASSIGN(HArm64IntermediateAddress); }; +class HArm64BitwiseNegatedRight : public HBinaryOperation { + public: + HArm64BitwiseNegatedRight(Primitive::Type result_type, + InstructionKind op, + HInstruction* left, + HInstruction* right, + uint32_t dex_pc = kNoDexPc) + : HBinaryOperation(result_type, left, right, SideEffects::None(), dex_pc), + op_kind_(op) { + DCHECK(op == HInstruction::kAnd || op == HInstruction::kOr || op == HInstruction::kXor) << op; + } + + template + auto Compute(T x, U y) const -> decltype(x & ~y) { + static_assert(std::is_same::value && + std::is_same::value, + "Inconsistent negated bitwise types"); + switch (op_kind_) { + case HInstruction::kAnd: + return x & ~y; + case HInstruction::kOr: + return x | ~y; + case HInstruction::kXor: + return x ^ ~y; + default: + LOG(FATAL) << "Unreachable"; + UNREACHABLE(); + } + } + + HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetIntConstant( + Compute(x->GetValue(), y->GetValue()), GetDexPc()); + } + HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetLongConstant( + Compute(x->GetValue(), y->GetValue()), GetDexPc()); + } + HConstant* Evaluate(HFloatConstant* x ATTRIBUTE_UNUSED, + HFloatConstant* y ATTRIBUTE_UNUSED) const OVERRIDE { + LOG(FATAL) << DebugName() << " is not defined for float values"; + UNREACHABLE(); + } + HConstant* Evaluate(HDoubleConstant* x ATTRIBUTE_UNUSED, + HDoubleConstant* y ATTRIBUTE_UNUSED) const OVERRIDE { + LOG(FATAL) << DebugName() << " is not defined for double values"; + UNREACHABLE(); + } + + InstructionKind GetOpKind() const { return op_kind_; } + + DECLARE_INSTRUCTION(Arm64BitwiseNegatedRight); + + private: + // Specifies the bitwise operation, which will be then negated. + const InstructionKind op_kind_; + + DISALLOW_COPY_AND_ASSIGN(HArm64BitwiseNegatedRight); +}; + } // namespace art #endif // ART_COMPILER_OPTIMIZING_NODES_ARM64_H_ diff --git a/test/564-checker-negbitwise/expected.txt b/test/564-checker-negbitwise/expected.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/564-checker-negbitwise/info.txt b/test/564-checker-negbitwise/info.txt new file mode 100644 index 0000000000..28b9e9e832 --- /dev/null +++ b/test/564-checker-negbitwise/info.txt @@ -0,0 +1 @@ +Test negated bitwise operations simplification on ARM64. diff --git a/test/564-checker-negbitwise/src/Main.java b/test/564-checker-negbitwise/src/Main.java new file mode 100644 index 0000000000..3de7be7161 --- /dev/null +++ b/test/564-checker-negbitwise/src/Main.java @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Main { + + // A dummy value to defeat inlining of these routines. + static boolean doThrow = false; + + public static void assertIntEquals(int expected, int result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } + + public static void assertLongEquals(long expected, long result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } + + /** + * Test merging of `NOT+AND` into `BIC`. + */ + + /// CHECK-START-ARM64: int Main.$opt$noinline$notAnd(int, int) instruction_simplifier_arm64 (before) + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> Not [<>] + /// CHECK: <> And [<>,<>] + /// CHECK: Return [<>] + + /// CHECK-START-ARM64: int Main.$opt$noinline$notAnd(int, int) instruction_simplifier_arm64 (after) + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> Arm64BitwiseNegatedRight [<>,<>] kind:And + /// CHECK: Return [<>] + + /// CHECK-START-ARM64: int Main.$opt$noinline$notAnd(int, int) instruction_simplifier_arm64 (after) + /// CHECK-NOT: Not + /// CHECK-NOT: And + + /// CHECK-START-ARM64: int Main.$opt$noinline$notAnd(int, int) disassembly (after) + /// CHECK: bic w{{\d+}}, w{{\d+}}, w{{\d+}} + + public static int $opt$noinline$notAnd(int base, int mask) { + if (doThrow) throw new Error(); + return base & ~mask; + } + + /** + * Test merging of `NOT+ORR` into `ORN`. + */ + + /// CHECK-START-ARM64: long Main.$opt$noinline$notOr(long, long) instruction_simplifier_arm64 (before) + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> Not [<>] + /// CHECK: <> Or [<>,<>] + /// CHECK: Return [<>] + + /// CHECK-START-ARM64: long Main.$opt$noinline$notOr(long, long) instruction_simplifier_arm64 (after) + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> Arm64BitwiseNegatedRight [<>,<>] kind:Or + /// CHECK: Return [<>] + + /// CHECK-START-ARM64: long Main.$opt$noinline$notOr(long, long) instruction_simplifier_arm64 (after) + /// CHECK-NOT: Not + /// CHECK-NOT: Or + + /// CHECK-START-ARM64: long Main.$opt$noinline$notOr(long, long) disassembly (after) + /// CHECK: orn x{{\d+}}, x{{\d+}}, x{{\d+}} + + public static long $opt$noinline$notOr(long base, long mask) { + if (doThrow) throw new Error(); + return base | ~mask; + } + + /** + * Test merging of `NOT+EOR` into `EON`. + */ + + /// CHECK-START-ARM64: int Main.$opt$noinline$notXor(int, int) instruction_simplifier_arm64 (before) + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> Not [<>] + /// CHECK: <> Xor [<>,<>] + /// CHECK: Return [<>] + + /// CHECK-START-ARM64: int Main.$opt$noinline$notXor(int, int) instruction_simplifier_arm64 (after) + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> Arm64BitwiseNegatedRight [<>,<>] kind:Xor + /// CHECK: Return [<>] + + /// CHECK-START-ARM64: int Main.$opt$noinline$notXor(int, int) instruction_simplifier_arm64 (after) + /// CHECK-NOT: Not + /// CHECK-NOT: Xor + + /// CHECK-START-ARM64: int Main.$opt$noinline$notXor(int, int) disassembly (after) + /// CHECK: eon w{{\d+}}, w{{\d+}}, w{{\d+}} + + public static int $opt$noinline$notXor(int base, int mask) { + if (doThrow) throw new Error(); + return base ^ ~mask; + } + + /** + * Check that the transformation is also done when the base is a constant. + */ + + /// CHECK-START-ARM64: int Main.$opt$noinline$notXorConstant(int) instruction_simplifier_arm64 (before) + /// CHECK: <> ParameterValue + /// CHECK: <> IntConstant + /// CHECK: <> Not [<>] + /// CHECK: <> Xor [<>,<>] + /// CHECK: Return [<>] + + /// CHECK-START-ARM64: int Main.$opt$noinline$notXorConstant(int) instruction_simplifier_arm64 (after) + /// CHECK: <> ParameterValue + /// CHECK: <> IntConstant + /// CHECK: <> Arm64BitwiseNegatedRight [<>,<>] kind:Xor + /// CHECK: Return [<>] + + /// CHECK-START-ARM64: int Main.$opt$noinline$notXorConstant(int) instruction_simplifier_arm64 (after) + /// CHECK-NOT: Not + /// CHECK-NOT: Xor + + /// CHECK-START-ARM64: int Main.$opt$noinline$notXorConstant(int) disassembly (after) + /// CHECK: mov <>, #0xf + /// CHECK: eon w{{\d+}}, <>, w{{\d+}} + + public static int $opt$noinline$notXorConstant(int mask) { + if (doThrow) throw new Error(); + return 0xf ^ ~mask; + } + + /** + * Check that no transformation is done when Not has multiple uses. + */ + + /// CHECK-START-ARM64: int Main.$opt$noinline$notAndMultipleUses(int, int) instruction_simplifier_arm64 (before) + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> IntConstant + /// CHECK: <> Not [<>] + /// CHECK: <> And [<>,<>] + /// CHECK: <> And [<>,<>] + /// CHECK: <> Add [<>,<>] + /// CHECK: Return [<>] + + /// CHECK-START-ARM64: int Main.$opt$noinline$notAndMultipleUses(int, int) instruction_simplifier_arm64 (after) + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> IntConstant + /// CHECK: <> Not [<>] + /// CHECK: <> And [<>,<>] + /// CHECK: <> And [<>,<>] + /// CHECK: <> Add [<>,<>] + /// CHECK: Return [<>] + + /// CHECK-START-ARM64: int Main.$opt$noinline$notAndMultipleUses(int, int) instruction_simplifier_arm64 (after) + /// CHECK-NOT: Arm64BitwiseNegatedRight + + public static int $opt$noinline$notAndMultipleUses(int base, int mask) { + if (doThrow) throw new Error(); + int tmp = ~mask; + return (tmp & 0x1) + (base & tmp); + } + + /** + * Check that no transformation is done when both inputs are Not's. + */ + + // We don't check the instructions before the pass, since if De Morgan's laws + // have been applied then Not/Not/Or is replaced by And/Not. + + /// CHECK-START-ARM64: int Main.$opt$noinline$deMorganOr(int, int) instruction_simplifier_arm64 (after) + /// CHECK-NOT: Arm64BitwiseNegatedRight + + public static int $opt$noinline$deMorganOr(int a, int b) { + if (doThrow) throw new Error(); + return ~a | ~b; + } + + public static void main(String[] args) { + assertIntEquals(0xe, $opt$noinline$notAnd(0xf, 0x1)); + assertLongEquals(~0x0, $opt$noinline$notOr(0xf, 0x1)); + assertIntEquals(~0xe, $opt$noinline$notXor(0xf, 0x1)); + assertIntEquals(~0xe, $opt$noinline$notXorConstant(0x1)); + assertIntEquals(0xe, $opt$noinline$notAndMultipleUses(0xf, 0x1)); + assertIntEquals(~0x1, $opt$noinline$deMorganOr(0x3, 0x1)); + } +} -- GitLab From 358af839c60db9e178f0b0bb9d430711c071b82a Mon Sep 17 00:00:00 2001 From: Aart Bik Date: Wed, 24 Feb 2016 14:17:53 -0800 Subject: [PATCH 045/204] Recognize for (int i = 0; i != x.length; i++) loops Rationale: Idiom occurs in real-life and is very straightforwardly recognized by existing induction/range machinery. Change-Id: I965a16e9de72f3523ea5023d30ed1c4e47ed5010 --- compiler/optimizing/induction_var_analysis.cc | 8 +++-- compiler/optimizing/induction_var_range.cc | 8 +++++ test/530-checker-loops/src/Main.java | 30 +++++++++++++++++++ 3 files changed, 43 insertions(+), 3 deletions(-) diff --git a/compiler/optimizing/induction_var_analysis.cc b/compiler/optimizing/induction_var_analysis.cc index a1e1cde9df..82a898a9f1 100644 --- a/compiler/optimizing/induction_var_analysis.cc +++ b/compiler/optimizing/induction_var_analysis.cc @@ -552,9 +552,11 @@ void HInductionVarAnalysis::VisitCondition(HLoopInformation* loop, if (!IsExact(stride_expr, &stride_value)) { return; } - // Rewrite condition i != U into i < U or i > U if end condition is reached exactly. - if (cmp == kCondNE && ((stride_value == +1 && IsTaken(lower_expr, upper_expr, kCondLT)) || - (stride_value == -1 && IsTaken(lower_expr, upper_expr, kCondGT)))) { + // Rewrite condition i != U into strict end condition i < U or i > U if this end condition + // is reached exactly (tested by verifying if the loop has a unit stride and the non-strict + // condition would be always taken). + if (cmp == kCondNE && ((stride_value == +1 && IsTaken(lower_expr, upper_expr, kCondLE)) || + (stride_value == -1 && IsTaken(lower_expr, upper_expr, kCondGE)))) { cmp = stride_value > 0 ? kCondLT : kCondGT; } // Normalize a linear loop control with a nonzero stride: diff --git a/compiler/optimizing/induction_var_range.cc b/compiler/optimizing/induction_var_range.cc index b162696a42..f9b6910acd 100644 --- a/compiler/optimizing/induction_var_range.cc +++ b/compiler/optimizing/induction_var_range.cc @@ -216,6 +216,14 @@ bool InductionVarRange::IsConstant(HInductionVarAnalysis::InductionInfo* info, } } } while (RefineOuter(&v_min, &v_max)); + // Exploit array length + c >= c, with c <= 0 to avoid arithmetic wrap-around anomalies + // (e.g. array length == maxint and c == 1 would yield minint). + if (request == kAtLeast) { + if (v_min.a_constant == 1 && v_min.b_constant <= 0 && v_min.instruction->IsArrayLength()) { + *value = v_min.b_constant; + return true; + } + } } return false; } diff --git a/test/530-checker-loops/src/Main.java b/test/530-checker-loops/src/Main.java index 0c32491e4e..8db7338e40 100644 --- a/test/530-checker-loops/src/Main.java +++ b/test/530-checker-loops/src/Main.java @@ -394,6 +394,34 @@ public class Main { return result; } + /// CHECK-START: int Main.linearForNEArrayLengthUp(int[]) BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: int Main.linearForNEArrayLengthUp(int[]) BCE (after) + /// CHECK-NOT: BoundsCheck + /// CHECK-NOT: Deoptimize + private static int linearForNEArrayLengthUp(int[] x) { + int result = 0; + for (int i = 0; i != x.length; i++) { + result += x[i]; + } + return result; + } + + /// CHECK-START: int Main.linearForNEArrayLengthDown(int[]) BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: int Main.linearForNEArrayLengthDown(int[]) BCE (after) + /// CHECK-NOT: BoundsCheck + /// CHECK-NOT: Deoptimize + private static int linearForNEArrayLengthDown(int[] x) { + int result = 0; + for (int i = x.length - 1; i != -1; i--) { + result += x[i]; + } + return result; + } + /// CHECK-START: int Main.linearDoWhileUp() BCE (before) /// CHECK-DAG: BoundsCheck // @@ -1392,6 +1420,8 @@ public class Main { // Special forms. expectEquals(55, linearForNEUp()); expectEquals(55, linearForNEDown()); + expectEquals(55, linearForNEArrayLengthUp(x)); + expectEquals(55, linearForNEArrayLengthDown(x)); expectEquals(55, linearDoWhileUp()); expectEquals(55, linearDoWhileDown()); expectEquals(55, linearShort()); -- GitLab From 4a28e1e4e02aa44aa6fd20e22d50f9d73f6279cc Mon Sep 17 00:00:00 2001 From: Alex Light Date: Wed, 24 Feb 2016 16:35:59 -0800 Subject: [PATCH 046/204] Support new JDWP InterfaceType.InvokeMethod command This command is used to invoke static methods in interfaces. Bug: 27218415 Change-Id: Ie4dc1876a20567240267f309dc18f1aec2c1b4c2 --- runtime/jdwp/jdwp_handler.cc | 16 ++++++++++++++++ runtime/jdwp/jdwp_priv.h | 2 ++ 2 files changed, 18 insertions(+) diff --git a/runtime/jdwp/jdwp_handler.cc b/runtime/jdwp/jdwp_handler.cc index f1f4a03861..6278ef09de 100644 --- a/runtime/jdwp/jdwp_handler.cc +++ b/runtime/jdwp/jdwp_handler.cc @@ -689,6 +689,19 @@ static JdwpError AT_newInstance(JdwpState*, Request* request, ExpandBuf* pReply) return ERR_NONE; } +/* + * Invoke a static method on an interface. + */ +static JdwpError IT_InvokeMethod(JdwpState* state, Request* request, + ExpandBuf* pReply ATTRIBUTE_UNUSED) + SHARED_REQUIRES(Locks::mutator_lock_) { + RefTypeId class_id = request->ReadRefTypeId(); + ObjectId thread_id = request->ReadThreadId(); + MethodId method_id = request->ReadMethodId(); + + return RequestInvoke(state, request, thread_id, 0, class_id, method_id, false); +} + /* * Return line number information for the method, if present. */ @@ -1481,6 +1494,7 @@ static const JdwpHandlerMap gHandlers[] = { { 4, 1, AT_newInstance, "ArrayType.NewInstance" }, /* InterfaceType command set (5) */ + { 5, 1, IT_InvokeMethod, "InterfaceType.InvokeMethod" }, /* Method command set (6) */ { 6, 1, M_LineTable, "Method.LineTable" }, @@ -1579,6 +1593,8 @@ static bool IsInvokeCommand(uint8_t command_set, uint8_t command) { return command == kJDWPClassTypeInvokeMethodCmd || command == kJDWPClassTypeNewInstanceCmd; } else if (command_set == kJDWPObjectReferenceCmdSet) { return command == kJDWPObjectReferenceInvokeCmd; + } else if (command_set == kJDWPInterfaceTypeCmdSet) { + return command == kJDWPInterfaceTypeInvokeMethodCmd; } else { return false; } diff --git a/runtime/jdwp/jdwp_priv.h b/runtime/jdwp/jdwp_priv.h index 29314f6274..4e1bda899f 100644 --- a/runtime/jdwp/jdwp_priv.h +++ b/runtime/jdwp/jdwp_priv.h @@ -45,6 +45,8 @@ static constexpr size_t kMagicHandshakeLen = sizeof(kMagicHandshake) - 1; static constexpr uint8_t kJDWPClassTypeCmdSet = 3U; static constexpr uint8_t kJDWPClassTypeInvokeMethodCmd = 3U; static constexpr uint8_t kJDWPClassTypeNewInstanceCmd = 4U; +static constexpr uint8_t kJDWPInterfaceTypeCmdSet = 5U; +static constexpr uint8_t kJDWPInterfaceTypeInvokeMethodCmd = 1U; static constexpr uint8_t kJDWPObjectReferenceCmdSet = 9U; static constexpr uint8_t kJDWPObjectReferenceInvokeCmd = 6U; -- GitLab From 9c1c06ad57937f0f3add3779cc46e597cb76803a Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Thu, 25 Feb 2016 17:50:41 +0000 Subject: [PATCH 047/204] Add StrictMath.round tests for large integers. This is a follow-up to https://android-review.googlesource.com/203175 . Bug: 27166445 Change-Id: I850999c782017650017fad7e27c83b60b57edaf9 --- test/082-inline-execute/src/Main.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/082-inline-execute/src/Main.java b/test/082-inline-execute/src/Main.java index 5b3fa14076..93a9005fe0 100644 --- a/test/082-inline-execute/src/Main.java +++ b/test/082-inline-execute/src/Main.java @@ -1039,6 +1039,7 @@ public class Main { Assert.assertEquals(StrictMath.round(-2.9d), -3l); Assert.assertEquals(StrictMath.round(-3.0d), -3l); Assert.assertEquals(StrictMath.round(0.49999999999999994d), 0l); + Assert.assertEquals(StrictMath.round(9007199254740991.0d), 9007199254740991l); // 2^53 - 1 Assert.assertEquals(StrictMath.round(Double.NaN), (long)+0.0d); Assert.assertEquals(StrictMath.round(Long.MAX_VALUE + 1.0d), Long.MAX_VALUE); Assert.assertEquals(StrictMath.round(Long.MIN_VALUE - 1.0d), Long.MIN_VALUE); @@ -1062,6 +1063,7 @@ public class Main { Assert.assertEquals(StrictMath.round(-3.0f), -3); // 0.4999999701976776123046875 Assert.assertEquals(StrictMath.round(Float.intBitsToFloat(0x3EFFFFFF)), (int)+0.0f); + Assert.assertEquals(StrictMath.round(16777215.0f), 16777215); // 2^24 - 1 Assert.assertEquals(StrictMath.round(Float.NaN), (int)+0.0f); Assert.assertEquals(StrictMath.round(Integer.MAX_VALUE + 1.0f), Integer.MAX_VALUE); Assert.assertEquals(StrictMath.round(Integer.MIN_VALUE - 1.0f), Integer.MIN_VALUE); -- GitLab From fc07555f6b8ce07b2c0897c6919597b53faa419d Mon Sep 17 00:00:00 2001 From: Sebastien Hertz Date: Thu, 25 Feb 2016 18:50:23 +0100 Subject: [PATCH 048/204] Disable test 097-duplicate-method for investigation Broekn by latest Jack release (1.2-a19) Bug: 27358065 Change-Id: Ifd66522bde4c5b93435c6d9ab11ebb407f842df6 --- test/Android.run-test.mk | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index 364be59919..167ad859d2 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -220,6 +220,18 @@ ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES), $(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \ $(IMAGE_TYPES), $(PICTEST_TYPES), $(DEBUGGABLE_TYPES), $(ART_TEST_RUN_TEST_SKIP), $(ALL_ADDRESS_SIZES)) + +# Disable 097-duplicate-method while investigation (broken by latest Jack release, b/27358065) +TEST_ART_BROKEN_ALL_TARGET_TESTS := \ + 097-duplicate-method + +ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \ + $(COMPILER_TYPES), $(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \ + $(IMAGE_TYPES),$(PICTEST_TYPES),$(DEBUGGABLE_TYPES), $(TEST_ART_BROKEN_ALL_TARGET_TESTS), \ + $(ALL_ADDRESS_SIZES)) + +TEST_ART_BROKEN_ALL_TARGET_TESTS := + # Tests that are timing sensitive and flaky on heavily loaded systems. TEST_ART_TIMING_SENSITIVE_RUN_TESTS := \ 002-sleep \ -- GitLab From 481352dea337cb882615c940c85d1b26fd65c0ac Mon Sep 17 00:00:00 2001 From: Bill Buzbee Date: Thu, 25 Feb 2016 17:37:46 +0000 Subject: [PATCH 049/204] Revert "Revert "ART: Support interpreter switching in x86 mterp"" Fixes the invoke template. Previously, it was permitting an interpreter switch *before* advancing the Dalvik PC. This meant that on a switch, the invoke would be interpreted twice. This reverts commit a14bf4460268064e0adc4bb210d237209be33eaf. Change-Id: I285d19077c8dd58ce607a4c42ed20549942fab45 --- runtime/interpreter/mterp/out/mterp_x86.S | 90 +++++++++++++++++++---- runtime/interpreter/mterp/x86/footer.S | 6 +- runtime/interpreter/mterp/x86/invoke.S | 7 +- 3 files changed, 88 insertions(+), 15 deletions(-) diff --git a/runtime/interpreter/mterp/out/mterp_x86.S b/runtime/interpreter/mterp/out/mterp_x86.S index b05360b6ae..567550f41f 100644 --- a/runtime/interpreter/mterp/out/mterp_x86.S +++ b/runtime/interpreter/mterp/out/mterp_x86.S @@ -2989,8 +2989,13 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop call SYMBOL(MterpInvokeVirtual) testb %al, %al jz MterpException + ADVANCE_PC 3 + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback RESTORE_IBASE - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + FETCH_INST + GOTO_NEXT /* * Handle a virtual method call. @@ -3022,8 +3027,13 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop call SYMBOL(MterpInvokeSuper) testb %al, %al jz MterpException + ADVANCE_PC 3 + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback RESTORE_IBASE - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + FETCH_INST + GOTO_NEXT /* * Handle a "super" method call. @@ -3055,8 +3065,13 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop call SYMBOL(MterpInvokeDirect) testb %al, %al jz MterpException + ADVANCE_PC 3 + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback RESTORE_IBASE - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + FETCH_INST + GOTO_NEXT /* ------------------------------ */ @@ -3081,8 +3096,13 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop call SYMBOL(MterpInvokeStatic) testb %al, %al jz MterpException + ADVANCE_PC 3 + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback RESTORE_IBASE - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + FETCH_INST + GOTO_NEXT @@ -3108,8 +3128,13 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop call SYMBOL(MterpInvokeInterface) testb %al, %al jz MterpException + ADVANCE_PC 3 + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback RESTORE_IBASE - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + FETCH_INST + GOTO_NEXT /* * Handle an interface method call. @@ -3155,8 +3180,13 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop call SYMBOL(MterpInvokeVirtualRange) testb %al, %al jz MterpException + ADVANCE_PC 3 + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback RESTORE_IBASE - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + FETCH_INST + GOTO_NEXT /* ------------------------------ */ @@ -3181,8 +3211,13 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop call SYMBOL(MterpInvokeSuperRange) testb %al, %al jz MterpException + ADVANCE_PC 3 + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback RESTORE_IBASE - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + FETCH_INST + GOTO_NEXT /* ------------------------------ */ @@ -3207,8 +3242,13 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop call SYMBOL(MterpInvokeDirectRange) testb %al, %al jz MterpException + ADVANCE_PC 3 + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback RESTORE_IBASE - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + FETCH_INST + GOTO_NEXT /* ------------------------------ */ @@ -3233,8 +3273,13 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop call SYMBOL(MterpInvokeStaticRange) testb %al, %al jz MterpException + ADVANCE_PC 3 + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback RESTORE_IBASE - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + FETCH_INST + GOTO_NEXT /* ------------------------------ */ @@ -3259,8 +3304,13 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop call SYMBOL(MterpInvokeInterfaceRange) testb %al, %al jz MterpException + ADVANCE_PC 3 + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback RESTORE_IBASE - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + FETCH_INST + GOTO_NEXT /* ------------------------------ */ @@ -6002,8 +6052,13 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop call SYMBOL(MterpInvokeVirtualQuick) testb %al, %al jz MterpException + ADVANCE_PC 3 + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback RESTORE_IBASE - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + FETCH_INST + GOTO_NEXT /* ------------------------------ */ @@ -6028,8 +6083,13 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop call SYMBOL(MterpInvokeVirtualQuickRange) testb %al, %al jz MterpException + ADVANCE_PC 3 + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback RESTORE_IBASE - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + FETCH_INST + GOTO_NEXT /* ------------------------------ */ @@ -12851,13 +12911,17 @@ MterpException: call SYMBOL(MterpHandleException) testb %al, %al jz MterpExceptionReturn - REFRESH_IBASE movl OFF_FP_CODE_ITEM(rFP), %eax movl OFF_FP_DEX_PC(rFP), %ecx lea CODEITEM_INSNS_OFFSET(%eax), rPC lea (rPC, %ecx, 2), rPC movl rPC, OFF_FP_DEX_PC_PTR(rFP) + /* Do we need to switch interpreters? */ + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback /* resume execution at catch block */ + REFRESH_IBASE FETCH_INST GOTO_NEXT /* NOTE: no fallthrough */ diff --git a/runtime/interpreter/mterp/x86/footer.S b/runtime/interpreter/mterp/x86/footer.S index c67491e577..64d72d7709 100644 --- a/runtime/interpreter/mterp/x86/footer.S +++ b/runtime/interpreter/mterp/x86/footer.S @@ -115,13 +115,17 @@ MterpException: call SYMBOL(MterpHandleException) testb %al, %al jz MterpExceptionReturn - REFRESH_IBASE movl OFF_FP_CODE_ITEM(rFP), %eax movl OFF_FP_DEX_PC(rFP), %ecx lea CODEITEM_INSNS_OFFSET(%eax), rPC lea (rPC, %ecx, 2), rPC movl rPC, OFF_FP_DEX_PC_PTR(rFP) + /* Do we need to switch interpreters? */ + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback /* resume execution at catch block */ + REFRESH_IBASE FETCH_INST GOTO_NEXT /* NOTE: no fallthrough */ diff --git a/runtime/interpreter/mterp/x86/invoke.S b/runtime/interpreter/mterp/x86/invoke.S index bbd88cf40b..c23053becb 100644 --- a/runtime/interpreter/mterp/x86/invoke.S +++ b/runtime/interpreter/mterp/x86/invoke.S @@ -16,5 +16,10 @@ call SYMBOL($helper) testb %al, %al jz MterpException + ADVANCE_PC 3 + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback RESTORE_IBASE - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + FETCH_INST + GOTO_NEXT -- GitLab From 31317c319667cf1d7d0f61229ccb5e76f22606cf Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Thu, 25 Feb 2016 12:28:40 -0800 Subject: [PATCH 050/204] Add a decompression scoped timing for image loading Shows up in systrace. Bug: 22858531 Change-Id: Ic165cd0106dd019c002b600d37560b71b4e351d1 --- runtime/gc/space/image_space.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc index 894ce9af72..bc21b334d6 100644 --- a/runtime/gc/space/image_space.cc +++ b/runtime/gc/space/image_space.cc @@ -1289,6 +1289,7 @@ ImageSpace* ImageSpace::Init(const char* image_filename, } memcpy(map->Begin(), image_header, sizeof(ImageHeader)); const uint64_t start = NanoTime(); + TimingLogger::ScopedTiming timing2("LZ4 decompress image", &logger); const size_t decompressed_size = LZ4_decompress_safe( reinterpret_cast(temp_map->Begin()) + sizeof(ImageHeader), reinterpret_cast(map->Begin()) + write_offset, -- GitLab From 7e1ce285c3c2cbd8d97c744699c2f2347fa7487b Mon Sep 17 00:00:00 2001 From: Hiroshi Yamauchi Date: Fri, 11 Dec 2015 15:46:19 -0800 Subject: [PATCH 051/204] Assembly TLAB allocation fast path for arm. Speedup (GSS GC with TLAB on N5): BinaryTrees: 1872 -> 796 ms (-57%) MemAllocTest: 2522 -> 2219 ms (-12%) Bug: 9986565 Change-Id: Icb9d1259461f3abe83a4a82c8aff911937eaf57d --- runtime/arch/arm/quick_entrypoints_arm.S | 81 +++++++++++++++++++++++- runtime/asm_support.h | 18 +++--- runtime/entrypoints_order_test.cc | 4 +- runtime/thread.h | 8 ++- 4 files changed, 96 insertions(+), 15 deletions(-) diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S index c4e314b6c8..cfcef49084 100644 --- a/runtime/arch/arm/quick_entrypoints_arm.S +++ b/runtime/arch/arm/quick_entrypoints_arm.S @@ -942,7 +942,86 @@ ONE_ARG_DOWNCALL art_quick_resolve_string, artResolveStringFromCode, RETURN_IF_R // Generate the allocation entrypoints for each allocator. GENERATE_ALLOC_ENTRYPOINTS_FOR_EACH_ALLOCATOR -GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_tlab, TLAB) + +// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_tlab, TLAB). +ENTRY art_quick_alloc_object_tlab + // Fast path tlab allocation. + // r0: type_idx/return value, r1: ArtMethod*, r9: Thread::Current + // r2, r3, r12: free. +#if defined(USE_READ_BARRIER) + eor r0, r0, r0 // Read barrier not supported here. + sub r0, r0, #1 // Return -1. + bx lr +#endif + ldr r2, [r1, #ART_METHOD_DEX_CACHE_TYPES_OFFSET_32] // Load dex cache resolved types array + // Load the class (r2) + ldr r2, [r2, r0, lsl #COMPRESSED_REFERENCE_SIZE_SHIFT] + cbz r2, .Lart_quick_alloc_object_tlab_slow_path // Check null class + // Check class status. + ldr r3, [r2, #MIRROR_CLASS_STATUS_OFFSET] + cmp r3, #MIRROR_CLASS_STATUS_INITIALIZED + bne .Lart_quick_alloc_object_tlab_slow_path + // Add a fake dependence from the + // following access flag and size + // loads to the status load. + // This is to prevent those loads + // from being reordered above the + // status load and reading wrong + // values (an alternative is to use + // a load-acquire for the status). + eor r3, r3, r3 + add r2, r2, r3 + // Check access flags has + // kAccClassIsFinalizable. + ldr r3, [r2, #MIRROR_CLASS_ACCESS_FLAGS_OFFSET] + tst r3, #ACCESS_FLAGS_CLASS_IS_FINALIZABLE + bne .Lart_quick_alloc_object_tlab_slow_path + // Load thread_local_pos (r12) and + // thread_local_end (r3) with ldrd. + // Check constraints for ldrd. +#if !((THREAD_LOCAL_POS_OFFSET + 4 == THREAD_LOCAL_END_OFFSET) && (THREAD_LOCAL_POS_OFFSET % 8 == 0)) +#error "Thread::thread_local_pos/end must be consecutive and are 8 byte aligned for performance" +#endif + ldrd r12, r3, [r9, #THREAD_LOCAL_POS_OFFSET] + sub r12, r3, r12 // Compute the remaining buf size. + ldr r3, [r2, #MIRROR_CLASS_OBJECT_SIZE_OFFSET] // Load the object size (r3). + cmp r3, r12 // Check if it fits. OK to do this + // before rounding up the object size + // assuming the buf size alignment. + bhi .Lart_quick_alloc_object_tlab_slow_path + // "Point of no slow path". Won't go to the slow path from here on. OK to clobber r0 and r1. + // Round up the object size by the + // object alignment. (addr + 7) & ~7. + add r3, r3, #OBJECT_ALIGNMENT_MASK + and r3, r3, #OBJECT_ALIGNMENT_MASK_TOGGLED + // Reload old thread_local_pos (r0) + // for the return value. + ldr r0, [r9, #THREAD_LOCAL_POS_OFFSET] + add r1, r0, r3 + str r1, [r9, #THREAD_LOCAL_POS_OFFSET] // Store new thread_local_pos. + ldr r1, [r9, #THREAD_LOCAL_OBJECTS_OFFSET] // Increment thread_local_objects. + add r1, r1, #1 + str r1, [r9, #THREAD_LOCAL_OBJECTS_OFFSET] + POISON_HEAP_REF r2 + str r2, [r0, #MIRROR_OBJECT_CLASS_OFFSET] // Store the class pointer. + // Fence. This is "ish" not "ishst" so + // that the code after this allocation + // site will see the right values in + // the fields of the class. + // Alternatively we could use "ishst" + // if we use load-acquire for the + // class status load.) + dmb ish + bx lr +.Lart_quick_alloc_object_tlab_slow_path: + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r2, r3 // Save callee saves in case of GC. + mov r2, r9 // Pass Thread::Current. + bl artAllocObjectFromCodeTLAB // (uint32_t type_idx, Method* method, Thread*) + RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME + RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER +END art_quick_alloc_object_tlab + + // A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_rosalloc, RosAlloc). ENTRY art_quick_alloc_object_rosalloc // Fast path rosalloc allocation. diff --git a/runtime/asm_support.h b/runtime/asm_support.h index eb3b7f3538..879364e6d2 100644 --- a/runtime/asm_support.h +++ b/runtime/asm_support.h @@ -121,32 +121,32 @@ ADD_TEST_EQ(THREAD_TOP_QUICK_FRAME_OFFSET, ADD_TEST_EQ(THREAD_SELF_OFFSET, art::Thread::SelfOffset<__SIZEOF_POINTER__>().Int32Value()) +// Offset of field Thread::tlsPtr_.thread_local_objects. +#define THREAD_LOCAL_OBJECTS_OFFSET (THREAD_CARD_TABLE_OFFSET + 168 * __SIZEOF_POINTER__) +ADD_TEST_EQ(THREAD_LOCAL_OBJECTS_OFFSET, + art::Thread::ThreadLocalObjectsOffset<__SIZEOF_POINTER__>().Int32Value()) // Offset of field Thread::tlsPtr_.thread_local_pos. -#define THREAD_LOCAL_POS_OFFSET (THREAD_CARD_TABLE_OFFSET + 169 * __SIZEOF_POINTER__) +#define THREAD_LOCAL_POS_OFFSET (THREAD_LOCAL_OBJECTS_OFFSET + 2 * __SIZEOF_POINTER__) ADD_TEST_EQ(THREAD_LOCAL_POS_OFFSET, art::Thread::ThreadLocalPosOffset<__SIZEOF_POINTER__>().Int32Value()) // Offset of field Thread::tlsPtr_.thread_local_end. #define THREAD_LOCAL_END_OFFSET (THREAD_LOCAL_POS_OFFSET + __SIZEOF_POINTER__) ADD_TEST_EQ(THREAD_LOCAL_END_OFFSET, art::Thread::ThreadLocalEndOffset<__SIZEOF_POINTER__>().Int32Value()) -// Offset of field Thread::tlsPtr_.thread_local_objects. -#define THREAD_LOCAL_OBJECTS_OFFSET (THREAD_LOCAL_POS_OFFSET + 2 * __SIZEOF_POINTER__) -ADD_TEST_EQ(THREAD_LOCAL_OBJECTS_OFFSET, - art::Thread::ThreadLocalObjectsOffset<__SIZEOF_POINTER__>().Int32Value()) // Offset of field Thread::tlsPtr_.mterp_current_ibase. -#define THREAD_CURRENT_IBASE_OFFSET (THREAD_LOCAL_POS_OFFSET + 3 * __SIZEOF_POINTER__) +#define THREAD_CURRENT_IBASE_OFFSET (THREAD_LOCAL_POS_OFFSET + 2 * __SIZEOF_POINTER__) ADD_TEST_EQ(THREAD_CURRENT_IBASE_OFFSET, art::Thread::MterpCurrentIBaseOffset<__SIZEOF_POINTER__>().Int32Value()) // Offset of field Thread::tlsPtr_.mterp_default_ibase. -#define THREAD_DEFAULT_IBASE_OFFSET (THREAD_LOCAL_POS_OFFSET + 4 * __SIZEOF_POINTER__) +#define THREAD_DEFAULT_IBASE_OFFSET (THREAD_LOCAL_POS_OFFSET + 3 * __SIZEOF_POINTER__) ADD_TEST_EQ(THREAD_DEFAULT_IBASE_OFFSET, art::Thread::MterpDefaultIBaseOffset<__SIZEOF_POINTER__>().Int32Value()) // Offset of field Thread::tlsPtr_.mterp_alt_ibase. -#define THREAD_ALT_IBASE_OFFSET (THREAD_LOCAL_POS_OFFSET + 5 * __SIZEOF_POINTER__) +#define THREAD_ALT_IBASE_OFFSET (THREAD_LOCAL_POS_OFFSET + 4 * __SIZEOF_POINTER__) ADD_TEST_EQ(THREAD_ALT_IBASE_OFFSET, art::Thread::MterpAltIBaseOffset<__SIZEOF_POINTER__>().Int32Value()) // Offset of field Thread::tlsPtr_.rosalloc_runs. -#define THREAD_ROSALLOC_RUNS_OFFSET (THREAD_LOCAL_POS_OFFSET + 6 * __SIZEOF_POINTER__) +#define THREAD_ROSALLOC_RUNS_OFFSET (THREAD_LOCAL_POS_OFFSET + 5 * __SIZEOF_POINTER__) ADD_TEST_EQ(THREAD_ROSALLOC_RUNS_OFFSET, art::Thread::RosAllocRunsOffset<__SIZEOF_POINTER__>().Int32Value()) // Offset of field Thread::tlsPtr_.thread_local_alloc_stack_top. diff --git a/runtime/entrypoints_order_test.cc b/runtime/entrypoints_order_test.cc index e72809b297..c621672ae7 100644 --- a/runtime/entrypoints_order_test.cc +++ b/runtime/entrypoints_order_test.cc @@ -119,10 +119,10 @@ class EntrypointsOrderTest : public CommonRuntimeTest { // Skip across the entrypoints structures. + EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_objects, thread_local_start, sizeof(void*)); EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_start, thread_local_pos, sizeof(void*)); EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_pos, thread_local_end, sizeof(void*)); - EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_end, thread_local_objects, sizeof(void*)); - EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_objects, mterp_current_ibase, sizeof(void*)); + EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_end, mterp_current_ibase, sizeof(void*)); EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, mterp_current_ibase, mterp_default_ibase, sizeof(void*)); EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, mterp_default_ibase, mterp_alt_ibase, sizeof(void*)); EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, mterp_alt_ibase, rosalloc_runs, sizeof(void*)); diff --git a/runtime/thread.h b/runtime/thread.h index 97c47e1490..234750cd9e 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -1324,8 +1324,8 @@ class Thread { instrumentation_stack(nullptr), debug_invoke_req(nullptr), single_step_control(nullptr), stacked_shadow_frame_record(nullptr), deoptimization_context_stack(nullptr), frame_id_to_shadow_frame(nullptr), name(nullptr), pthread_self(0), - last_no_thread_suspension_cause(nullptr), thread_local_start(nullptr), - thread_local_pos(nullptr), thread_local_end(nullptr), thread_local_objects(0), + last_no_thread_suspension_cause(nullptr), thread_local_objects(0), + thread_local_start(nullptr), thread_local_pos(nullptr), thread_local_end(nullptr), mterp_current_ibase(nullptr), mterp_default_ibase(nullptr), mterp_alt_ibase(nullptr), thread_local_alloc_stack_top(nullptr), thread_local_alloc_stack_end(nullptr), nested_signal_state(nullptr), flip_function(nullptr), method_verifier(nullptr), @@ -1440,10 +1440,12 @@ class Thread { QuickEntryPoints quick_entrypoints; // Thread-local allocation pointer. + size_t thread_local_objects; uint8_t* thread_local_start; + // thread_local_pos and thread_local_end must be consecutive for ldrd and are 8 byte aligned for + // potentially better performance. uint8_t* thread_local_pos; uint8_t* thread_local_end; - size_t thread_local_objects; // Mterp jump table bases. void* mterp_current_ibase; -- GitLab From a6e81ed4c185b7362cd5199ebe5507d00883a9b0 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Thu, 25 Feb 2016 13:52:10 -0800 Subject: [PATCH 052/204] Add lz4hc image compression format Smaller than lz4 and decompresses at the same speed. Compression is a bit slower. Example saves on old FB APK: Uncompressed: 44748800 bytes LZ4: 12443648 bytes LZ4HC: 11055104 bytes Generating the image slows down by ~1s per 20MB of image due to slower compression. Decompression is about the same speed but there should be a slight speedup since less data needs to be read from flash. Added test. Bug: 22858531 Change-Id: Ib2704305b9bec5b0ba3b1e871f59f4eedff330b7 --- compiler/image_test.cc | 5 +++++ compiler/image_writer.cc | 23 ++++++++++++++++++++--- dex2oat/dex2oat.cc | 4 +++- runtime/gc/space/image_space.cc | 10 +++++++++- runtime/image.h | 1 + runtime/oat_file_manager.cc | 19 +++++++++++-------- 6 files changed, 49 insertions(+), 13 deletions(-) diff --git a/compiler/image_test.cc b/compiler/image_test.cc index 992af29545..5763cec43f 100644 --- a/compiler/image_test.cc +++ b/compiler/image_test.cc @@ -289,6 +289,11 @@ TEST_F(ImageTest, WriteReadLZ4) { TestWriteRead(ImageHeader::kStorageModeLZ4); } +TEST_F(ImageTest, WriteReadLZ4HC) { + TestWriteRead(ImageHeader::kStorageModeLZ4HC); +} + + TEST_F(ImageTest, ImageHeaderIsValid) { uint32_t image_begin = ART_BASE_ADDRESS; uint32_t image_size_ = 16 * KB; diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc index 5eff8f37ec..871435b85f 100644 --- a/compiler/image_writer.cc +++ b/compiler/image_writer.cc @@ -18,6 +18,7 @@ #include #include +#include #include #include @@ -224,18 +225,28 @@ bool ImageWriter::Write(int image_fd, char* image_data = reinterpret_cast(image_info.image_->Begin()) + sizeof(ImageHeader); size_t data_size; const char* image_data_to_write; + const uint64_t compress_start_time = NanoTime(); CHECK_EQ(image_header->storage_mode_, image_storage_mode_); switch (image_storage_mode_) { case ImageHeader::kStorageModeLZ4: { - size_t compressed_max_size = LZ4_compressBound(image_data_size); + const size_t compressed_max_size = LZ4_compressBound(image_data_size); compressed_data.reset(new char[compressed_max_size]); data_size = LZ4_compress( reinterpret_cast(image_info.image_->Begin()) + sizeof(ImageHeader), &compressed_data[0], image_data_size); - image_data_to_write = &compressed_data[0]; - VLOG(compiler) << "Compressed from " << image_data_size << " to " << data_size; + + break; + } + case ImageHeader::kStorageModeLZ4HC: { + // Bound is same as non HC. + const size_t compressed_max_size = LZ4_compressBound(image_data_size); + compressed_data.reset(new char[compressed_max_size]); + data_size = LZ4_compressHC( + reinterpret_cast(image_info.image_->Begin()) + sizeof(ImageHeader), + &compressed_data[0], + image_data_size); break; } case ImageHeader::kStorageModeUncompressed: { @@ -249,6 +260,12 @@ bool ImageWriter::Write(int image_fd, } } + if (compressed_data != nullptr) { + image_data_to_write = &compressed_data[0]; + VLOG(compiler) << "Compressed from " << image_data_size << " to " << data_size << " in " + << PrettyDuration(NanoTime() - compress_start_time); + } + // Write header first, as uncompressed. image_header->data_size_ = data_size; if (!image_file->WriteFully(image_info.image_->Begin(), sizeof(ImageHeader))) { diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 53331284cb..dfcb4bcaec 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -217,7 +217,7 @@ NO_RETURN static void Usage(const char* fmt, ...) { UsageError(" --image=: specifies an output image filename."); UsageError(" Example: --image=/system/framework/boot.art"); UsageError(""); - UsageError(" --image-format=(uncompressed|lz4):"); + UsageError(" --image-format=(uncompressed|lz4|lz4hc):"); UsageError(" Which format to store the image."); UsageError(" Example: --image-format=lz4"); UsageError(" Default: uncompressed"); @@ -681,6 +681,8 @@ class Dex2Oat FINAL { const StringPiece format_str = option.substr(substr.length()); if (format_str == "lz4") { image_storage_mode_ = ImageHeader::kStorageModeLZ4; + } else if (format_str == "lz4hc") { + image_storage_mode_ = ImageHeader::kStorageModeLZ4HC; } else if (format_str == "uncompressed") { image_storage_mode_ = ImageHeader::kStorageModeUncompressed; } else { diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc index bc21b334d6..4ef36a449d 100644 --- a/runtime/gc/space/image_space.cc +++ b/runtime/gc/space/image_space.cc @@ -1252,7 +1252,8 @@ ImageSpace* ImageSpace::Init(const char* image_filename, // Only care about the error message for the last address in addresses. We want to avoid the // overhead of printing the process maps if we can relocate. std::string* out_error_msg = (address == addresses.back()) ? &temp_error_msg : nullptr; - if (image_header->GetStorageMode() == ImageHeader::kStorageModeUncompressed) { + const ImageHeader::StorageMode storage_mode = image_header->GetStorageMode(); + if (storage_mode == ImageHeader::kStorageModeUncompressed) { map.reset(MemMap::MapFileAtAddress(address, image_header->GetImageSize(), PROT_READ | PROT_WRITE, @@ -1264,6 +1265,12 @@ ImageSpace* ImageSpace::Init(const char* image_filename, image_filename, /*out*/out_error_msg)); } else { + if (storage_mode != ImageHeader::kStorageModeLZ4 && + storage_mode != ImageHeader::kStorageModeLZ4HC) { + *error_msg = StringPrintf("Invalid storage mode in image header %d", + static_cast(storage_mode)); + return nullptr; + } // Reserve output and decompress into it. map.reset(MemMap::MapAnonymous(image_location, address, @@ -1289,6 +1296,7 @@ ImageSpace* ImageSpace::Init(const char* image_filename, } memcpy(map->Begin(), image_header, sizeof(ImageHeader)); const uint64_t start = NanoTime(); + // LZ4HC and LZ4 have same internal format, both use LZ4_decompress. TimingLogger::ScopedTiming timing2("LZ4 decompress image", &logger); const size_t decompressed_size = LZ4_decompress_safe( reinterpret_cast(temp_map->Begin()) + sizeof(ImageHeader), diff --git a/runtime/image.h b/runtime/image.h index 146ee00c84..8e5dbad57d 100644 --- a/runtime/image.h +++ b/runtime/image.h @@ -81,6 +81,7 @@ class PACKED(4) ImageHeader { enum StorageMode : uint32_t { kStorageModeUncompressed, kStorageModeLZ4, + kStorageModeLZ4HC, kStorageModeCount, // Number of elements in enum. }; static constexpr StorageMode kDefaultStorageMode = kStorageModeUncompressed; diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc index 18cf81aa7c..ea26d58767 100644 --- a/runtime/oat_file_manager.cc +++ b/runtime/oat_file_manager.cc @@ -16,6 +16,8 @@ #include "oat_file_manager.h" +#define ATRACE_TAG ATRACE_TAG_DALVIK +#include #include #include #include @@ -386,13 +388,15 @@ std::vector> OatFileManager::OpenDexFilesFromOat( ScopedSuspendAll ssa("Add image space"); runtime->GetHeap()->AddSpace(image_space.get()); } - added_image_space = true; - if (runtime->GetClassLinker()->AddImageSpace(image_space.get(), - h_loader, - dex_elements, - dex_location, - /*out*/&dex_files, - /*out*/&temp_error_msg)) { + ATRACE_BEGIN(StringPrintf("Adding image space for location %s", dex_location).c_str()); + added_image_space = runtime->GetClassLinker()->AddImageSpace(image_space.get(), + h_loader, + dex_elements, + dex_location, + /*out*/&dex_files, + /*out*/&temp_error_msg); + ATRACE_END(); + if (added_image_space) { // Successfully added image space to heap, release the map so that it does not get // freed. image_space.release(); @@ -407,7 +411,6 @@ std::vector> OatFileManager::OpenDexFilesFromOat( ScopedSuspendAll ssa("Remove image space"); runtime->GetHeap()->RemoveSpace(image_space.get()); } - added_image_space = false; // Non-fatal, don't update error_msg. } } -- GitLab From 25abd6c0c9f5a6abebcdeeb6f4373e85eedcfb6b Mon Sep 17 00:00:00 2001 From: Tim Zhang Date: Tue, 19 Jan 2016 23:39:24 +0800 Subject: [PATCH 053/204] Optimizing: Add ARM and ARM64 intrinsics support for StringGetCharsNoCheck This change refers to x86 implementation of StringGetCharsNoCheck and arm implementation of SystemArrayCopy. Change-Id: I1cb86854a2a8fa8736af7726b8efacd00d416f6f --- compiler/optimizing/intrinsics_arm.cc | 64 ++++++++++++++++++++++++- compiler/optimizing/intrinsics_arm64.cc | 64 ++++++++++++++++++++++++- test/020-string/expected.txt | 6 +++ test/020-string/src/Main.java | 45 +++++++++++++++++ 4 files changed, 177 insertions(+), 2 deletions(-) diff --git a/compiler/optimizing/intrinsics_arm.cc b/compiler/optimizing/intrinsics_arm.cc index 8cbdcbbcaf..8e22f8677d 100644 --- a/compiler/optimizing/intrinsics_arm.cc +++ b/compiler/optimizing/intrinsics_arm.cc @@ -1909,6 +1909,69 @@ void IntrinsicCodeGeneratorARM::VisitShortReverseBytes(HInvoke* invoke) { __ revsh(out, in); } +void IntrinsicLocationsBuilderARM::VisitStringGetCharsNoCheck(HInvoke* invoke) { + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kNoCall, + kIntrinsified); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetInAt(2, Location::RequiresRegister()); + locations->SetInAt(3, Location::RequiresRegister()); + locations->SetInAt(4, Location::RequiresRegister()); + + locations->AddTemp(Location::RequiresRegister()); + locations->AddTemp(Location::RequiresRegister()); + locations->AddTemp(Location::RequiresRegister()); + locations->AddTemp(Location::RequiresRegister()); +} + +void IntrinsicCodeGeneratorARM::VisitStringGetCharsNoCheck(HInvoke* invoke) { + ArmAssembler* assembler = GetAssembler(); + LocationSummary* locations = invoke->GetLocations(); + + // Check assumption that sizeof(Char) is 2 (used in scaling below). + const size_t char_size = Primitive::ComponentSize(Primitive::kPrimChar); + DCHECK_EQ(char_size, 2u); + + // Location of data in char array buffer. + const uint32_t data_offset = mirror::Array::DataOffset(char_size).Uint32Value(); + + // Location of char array data in string. + const uint32_t value_offset = mirror::String::ValueOffset().Uint32Value(); + + // void getCharsNoCheck(int srcBegin, int srcEnd, char[] dst, int dstBegin); + // Since getChars() calls getCharsNoCheck() - we use registers rather than constants. + Register srcObj = locations->InAt(0).AsRegister(); + Register srcBegin = locations->InAt(1).AsRegister(); + Register srcEnd = locations->InAt(2).AsRegister(); + Register dstObj = locations->InAt(3).AsRegister(); + Register dstBegin = locations->InAt(4).AsRegister(); + + Register src_ptr = locations->GetTemp(0).AsRegister(); + Register src_ptr_end = locations->GetTemp(1).AsRegister(); + Register dst_ptr = locations->GetTemp(2).AsRegister(); + Register tmp = locations->GetTemp(3).AsRegister(); + + // src range to copy. + __ add(src_ptr, srcObj, ShifterOperand(value_offset)); + __ add(src_ptr_end, src_ptr, ShifterOperand(srcEnd, LSL, 1)); + __ add(src_ptr, src_ptr, ShifterOperand(srcBegin, LSL, 1)); + + // dst to be copied. + __ add(dst_ptr, dstObj, ShifterOperand(data_offset)); + __ add(dst_ptr, dst_ptr, ShifterOperand(dstBegin, LSL, 1)); + + // Do the copy. + Label loop, done; + __ Bind(&loop); + __ cmp(src_ptr, ShifterOperand(src_ptr_end)); + __ b(&done, EQ); + __ ldrh(tmp, Address(src_ptr, char_size, Address::PostIndex)); + __ strh(tmp, Address(dst_ptr, char_size, Address::PostIndex)); + __ b(&loop); + __ Bind(&done); +} + // Unimplemented intrinsics. #define UNIMPLEMENTED_INTRINSIC(Name) \ @@ -1933,7 +1996,6 @@ UNIMPLEMENTED_INTRINSIC(MathRoundFloat) // Could be done by changing rounding UNIMPLEMENTED_INTRINSIC(UnsafeCASLong) // High register pressure. UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar) UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent) -UNIMPLEMENTED_INTRINSIC(StringGetCharsNoCheck) UNIMPLEMENTED_INTRINSIC(FloatIsInfinite) UNIMPLEMENTED_INTRINSIC(DoubleIsInfinite) diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc index b5f15fe22d..19ccb3d568 100644 --- a/compiler/optimizing/intrinsics_arm64.cc +++ b/compiler/optimizing/intrinsics_arm64.cc @@ -1602,6 +1602,69 @@ void IntrinsicCodeGeneratorARM64::VisitMathNextAfter(HInvoke* invoke) { GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickNextAfter); } +void IntrinsicLocationsBuilderARM64::VisitStringGetCharsNoCheck(HInvoke* invoke) { + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kNoCall, + kIntrinsified); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetInAt(2, Location::RequiresRegister()); + locations->SetInAt(3, Location::RequiresRegister()); + locations->SetInAt(4, Location::RequiresRegister()); + + locations->AddTemp(Location::RequiresRegister()); + locations->AddTemp(Location::RequiresRegister()); +} + +void IntrinsicCodeGeneratorARM64::VisitStringGetCharsNoCheck(HInvoke* invoke) { + vixl::MacroAssembler* masm = GetVIXLAssembler(); + LocationSummary* locations = invoke->GetLocations(); + + // Check assumption that sizeof(Char) is 2 (used in scaling below). + const size_t char_size = Primitive::ComponentSize(Primitive::kPrimChar); + DCHECK_EQ(char_size, 2u); + + // Location of data in char array buffer. + const uint32_t data_offset = mirror::Array::DataOffset(char_size).Uint32Value(); + + // Location of char array data in string. + const uint32_t value_offset = mirror::String::ValueOffset().Uint32Value(); + + // void getCharsNoCheck(int srcBegin, int srcEnd, char[] dst, int dstBegin); + // Since getChars() calls getCharsNoCheck() - we use registers rather than constants. + Register srcObj = XRegisterFrom(locations->InAt(0)); + Register srcBegin = XRegisterFrom(locations->InAt(1)); + Register srcEnd = XRegisterFrom(locations->InAt(2)); + Register dstObj = XRegisterFrom(locations->InAt(3)); + Register dstBegin = XRegisterFrom(locations->InAt(4)); + + Register src_ptr = XRegisterFrom(locations->GetTemp(0)); + Register src_ptr_end = XRegisterFrom(locations->GetTemp(1)); + + UseScratchRegisterScope temps(masm); + Register dst_ptr = temps.AcquireX(); + Register tmp = temps.AcquireW(); + + // src range to copy. + __ Add(src_ptr, srcObj, Operand(value_offset)); + __ Add(src_ptr_end, src_ptr, Operand(srcEnd, LSL, 1)); + __ Add(src_ptr, src_ptr, Operand(srcBegin, LSL, 1)); + + // dst to be copied. + __ Add(dst_ptr, dstObj, Operand(data_offset)); + __ Add(dst_ptr, dst_ptr, Operand(dstBegin, LSL, 1)); + + // Do the copy. + vixl::Label loop, done; + __ Bind(&loop); + __ Cmp(src_ptr, src_ptr_end); + __ B(&done, eq); + __ Ldrh(tmp, MemOperand(src_ptr, char_size, vixl::PostIndex)); + __ Strh(tmp, MemOperand(dst_ptr, char_size, vixl::PostIndex)); + __ B(&loop); + __ Bind(&done); +} + // Unimplemented intrinsics. #define UNIMPLEMENTED_INTRINSIC(Name) \ @@ -1615,7 +1678,6 @@ UNIMPLEMENTED_INTRINSIC(LongBitCount) UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar) UNIMPLEMENTED_INTRINSIC(SystemArrayCopy) UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent) -UNIMPLEMENTED_INTRINSIC(StringGetCharsNoCheck) UNIMPLEMENTED_INTRINSIC(FloatIsInfinite) UNIMPLEMENTED_INTRINSIC(DoubleIsInfinite) diff --git a/test/020-string/expected.txt b/test/020-string/expected.txt index 081fea3a41..76b8929bd7 100644 --- a/test/020-string/expected.txt +++ b/test/020-string/expected.txt @@ -5,3 +5,9 @@ Compare unicode: -65302 Got expected exception subStr is 'uick brown fox jumps over the lazy ' Indexes are: 0:-1:0:43:33:-1:18:13:13:-1:18:18:-1:13:-1:-1:-1 +Got expected exception +Got expected exception +Got expected exception +Got expected exception +Got expected exception +llo And diff --git a/test/020-string/src/Main.java b/test/020-string/src/Main.java index b876e6ad21..710808255c 100644 --- a/test/020-string/src/Main.java +++ b/test/020-string/src/Main.java @@ -25,6 +25,7 @@ public class Main { basicTest(); indexTest(); constructorTest(); + copyTest(); } public static void basicTest() { @@ -117,4 +118,48 @@ public class Main { String s14 = new String(codePoints, 1, 3); String s15 = new String(stringBuilder); } + + public static void copyTest() { + String src = new String("Hello Android"); + char[] dst = new char[7]; + char[] tmp = null; + + try { + src.getChars(2, 9, tmp, 0); + System.out.println("GLITCH: expected exception"); + } catch (NullPointerException npe) { + System.out.println("Got expected exception"); + } + + try { + src.getChars(-1, 9, dst, 0); + System.out.println("GLITCH: expected exception"); + } catch (StringIndexOutOfBoundsException sioobe) { + System.out.println("Got expected exception"); + } + + try { + src.getChars(2, 19, dst, 0); + System.out.println("GLITCH: expected exception"); + } catch (StringIndexOutOfBoundsException sioobe) { + System.out.println("Got expected exception"); + } + + try { + src.getChars(2, 1, dst, 0); + System.out.println("GLITCH: expected exception"); + } catch (StringIndexOutOfBoundsException sioobe) { + System.out.println("Got expected exception"); + } + + try { + src.getChars(2, 10, dst, 0); + System.out.println("GLITCH: expected exception"); + } catch (ArrayIndexOutOfBoundsException aioobe) { + System.out.println("Got expected exception"); + } + + src.getChars(2, 9, dst, 0); + System.out.println(new String(dst)); + } } -- GitLab From 200f040af3e4fe9e178cb63c90860d58d90ef665 Mon Sep 17 00:00:00 2001 From: Douglas Leung Date: Thu, 25 Feb 2016 20:05:47 -0800 Subject: [PATCH 054/204] [MIPS] Add Fast Art interpreter for Mips32. Change-Id: I6b9714dc8c01b8c9080bcba175faec1d2de08f8f --- runtime/Android.mk | 3 +- runtime/interpreter/interpreter.cc | 4 +- runtime/interpreter/mterp/config_mips | 520 +- runtime/interpreter/mterp/mips/alt_stub.S | 13 + runtime/interpreter/mterp/mips/bincmp.S | 37 + runtime/interpreter/mterp/mips/binop.S | 33 + runtime/interpreter/mterp/mips/binop2addr.S | 29 + runtime/interpreter/mterp/mips/binopLit16.S | 30 + runtime/interpreter/mterp/mips/binopLit8.S | 31 + runtime/interpreter/mterp/mips/binopWide.S | 35 + .../interpreter/mterp/mips/binopWide2addr.S | 33 + runtime/interpreter/mterp/mips/entry.S | 67 + runtime/interpreter/mterp/mips/fallback.S | 2 + runtime/interpreter/mterp/mips/fbinop.S | 19 + runtime/interpreter/mterp/mips/fbinop2addr.S | 19 + runtime/interpreter/mterp/mips/fbinopWide.S | 28 + .../interpreter/mterp/mips/fbinopWide2addr.S | 21 + runtime/interpreter/mterp/mips/footer.S | 179 + runtime/interpreter/mterp/mips/funop.S | 18 + runtime/interpreter/mterp/mips/funopWide.S | 22 + runtime/interpreter/mterp/mips/funopWider.S | 19 + runtime/interpreter/mterp/mips/header.S | 484 + runtime/interpreter/mterp/mips/invoke.S | 19 + .../interpreter/mterp/mips/op_add_double.S | 1 + .../mterp/mips/op_add_double_2addr.S | 1 + runtime/interpreter/mterp/mips/op_add_float.S | 1 + .../mterp/mips/op_add_float_2addr.S | 1 + runtime/interpreter/mterp/mips/op_add_int.S | 1 + .../interpreter/mterp/mips/op_add_int_2addr.S | 1 + .../interpreter/mterp/mips/op_add_int_lit16.S | 1 + .../interpreter/mterp/mips/op_add_int_lit8.S | 1 + runtime/interpreter/mterp/mips/op_add_long.S | 9 + .../mterp/mips/op_add_long_2addr.S | 4 + runtime/interpreter/mterp/mips/op_aget.S | 32 + .../interpreter/mterp/mips/op_aget_boolean.S | 1 + runtime/interpreter/mterp/mips/op_aget_byte.S | 1 + runtime/interpreter/mterp/mips/op_aget_char.S | 1 + .../interpreter/mterp/mips/op_aget_object.S | 20 + .../interpreter/mterp/mips/op_aget_short.S | 1 + runtime/interpreter/mterp/mips/op_aget_wide.S | 22 + runtime/interpreter/mterp/mips/op_and_int.S | 1 + .../interpreter/mterp/mips/op_and_int_2addr.S | 1 + .../interpreter/mterp/mips/op_and_int_lit16.S | 1 + .../interpreter/mterp/mips/op_and_int_lit8.S | 1 + runtime/interpreter/mterp/mips/op_and_long.S | 1 + .../mterp/mips/op_and_long_2addr.S | 1 + runtime/interpreter/mterp/mips/op_aput.S | 30 + .../interpreter/mterp/mips/op_aput_boolean.S | 1 + runtime/interpreter/mterp/mips/op_aput_byte.S | 1 + runtime/interpreter/mterp/mips/op_aput_char.S | 1 + .../interpreter/mterp/mips/op_aput_object.S | 14 + .../interpreter/mterp/mips/op_aput_short.S | 1 + runtime/interpreter/mterp/mips/op_aput_wide.S | 25 + .../interpreter/mterp/mips/op_array_length.S | 12 + .../interpreter/mterp/mips/op_check_cast.S | 16 + runtime/interpreter/mterp/mips/op_cmp_long.S | 34 + .../interpreter/mterp/mips/op_cmpg_double.S | 1 + .../interpreter/mterp/mips/op_cmpg_float.S | 1 + .../interpreter/mterp/mips/op_cmpl_double.S | 54 + .../interpreter/mterp/mips/op_cmpl_float.S | 61 + runtime/interpreter/mterp/mips/op_const.S | 9 + runtime/interpreter/mterp/mips/op_const_16.S | 6 + runtime/interpreter/mterp/mips/op_const_4.S | 8 + .../interpreter/mterp/mips/op_const_class.S | 12 + .../interpreter/mterp/mips/op_const_high16.S | 7 + .../interpreter/mterp/mips/op_const_string.S | 12 + .../mterp/mips/op_const_string_jumbo.S | 15 + .../interpreter/mterp/mips/op_const_wide.S | 14 + .../interpreter/mterp/mips/op_const_wide_16.S | 8 + .../interpreter/mterp/mips/op_const_wide_32.S | 11 + .../mterp/mips/op_const_wide_high16.S | 9 + .../interpreter/mterp/mips/op_div_double.S | 1 + .../mterp/mips/op_div_double_2addr.S | 1 + runtime/interpreter/mterp/mips/op_div_float.S | 1 + .../mterp/mips/op_div_float_2addr.S | 1 + runtime/interpreter/mterp/mips/op_div_int.S | 5 + .../interpreter/mterp/mips/op_div_int_2addr.S | 5 + .../interpreter/mterp/mips/op_div_int_lit16.S | 5 + .../interpreter/mterp/mips/op_div_int_lit8.S | 5 + runtime/interpreter/mterp/mips/op_div_long.S | 1 + .../mterp/mips/op_div_long_2addr.S | 1 + .../mterp/mips/op_double_to_float.S | 1 + .../interpreter/mterp/mips/op_double_to_int.S | 58 + .../mterp/mips/op_double_to_long.S | 56 + .../mterp/mips/op_fill_array_data.S | 14 + .../mterp/mips/op_filled_new_array.S | 18 + .../mterp/mips/op_filled_new_array_range.S | 1 + .../mterp/mips/op_float_to_double.S | 1 + .../interpreter/mterp/mips/op_float_to_int.S | 50 + .../interpreter/mterp/mips/op_float_to_long.S | 51 + runtime/interpreter/mterp/mips/op_goto.S | 38 + runtime/interpreter/mterp/mips/op_goto_16.S | 34 + runtime/interpreter/mterp/mips/op_goto_32.S | 43 + runtime/interpreter/mterp/mips/op_if_eq.S | 1 + runtime/interpreter/mterp/mips/op_if_eqz.S | 1 + runtime/interpreter/mterp/mips/op_if_ge.S | 1 + runtime/interpreter/mterp/mips/op_if_gez.S | 1 + runtime/interpreter/mterp/mips/op_if_gt.S | 1 + runtime/interpreter/mterp/mips/op_if_gtz.S | 1 + runtime/interpreter/mterp/mips/op_if_le.S | 1 + runtime/interpreter/mterp/mips/op_if_lez.S | 1 + runtime/interpreter/mterp/mips/op_if_lt.S | 1 + runtime/interpreter/mterp/mips/op_if_ltz.S | 1 + runtime/interpreter/mterp/mips/op_if_ne.S | 1 + runtime/interpreter/mterp/mips/op_if_nez.S | 1 + runtime/interpreter/mterp/mips/op_iget.S | 25 + .../interpreter/mterp/mips/op_iget_boolean.S | 1 + .../mterp/mips/op_iget_boolean_quick.S | 1 + runtime/interpreter/mterp/mips/op_iget_byte.S | 1 + .../mterp/mips/op_iget_byte_quick.S | 1 + runtime/interpreter/mterp/mips/op_iget_char.S | 1 + .../mterp/mips/op_iget_char_quick.S | 1 + .../interpreter/mterp/mips/op_iget_object.S | 1 + .../mterp/mips/op_iget_object_quick.S | 15 + .../interpreter/mterp/mips/op_iget_quick.S | 14 + .../interpreter/mterp/mips/op_iget_short.S | 1 + .../mterp/mips/op_iget_short_quick.S | 1 + runtime/interpreter/mterp/mips/op_iget_wide.S | 20 + .../mterp/mips/op_iget_wide_quick.S | 13 + .../interpreter/mterp/mips/op_instance_of.S | 21 + .../interpreter/mterp/mips/op_int_to_byte.S | 1 + .../interpreter/mterp/mips/op_int_to_char.S | 1 + .../interpreter/mterp/mips/op_int_to_double.S | 1 + .../interpreter/mterp/mips/op_int_to_float.S | 1 + .../interpreter/mterp/mips/op_int_to_long.S | 1 + .../interpreter/mterp/mips/op_int_to_short.S | 1 + .../interpreter/mterp/mips/op_invoke_direct.S | 1 + .../mterp/mips/op_invoke_direct_range.S | 1 + .../mterp/mips/op_invoke_interface.S | 1 + .../mterp/mips/op_invoke_interface_range.S | 1 + .../interpreter/mterp/mips/op_invoke_static.S | 1 + .../mterp/mips/op_invoke_static_range.S | 1 + .../interpreter/mterp/mips/op_invoke_super.S | 1 + .../mterp/mips/op_invoke_super_range.S | 1 + .../mterp/mips/op_invoke_virtual.S | 1 + .../mterp/mips/op_invoke_virtual_quick.S | 1 + .../mterp/mips/op_invoke_virtual_range.S | 1 + .../mips/op_invoke_virtual_range_quick.S | 1 + runtime/interpreter/mterp/mips/op_iput.S | 21 + .../interpreter/mterp/mips/op_iput_boolean.S | 1 + .../mterp/mips/op_iput_boolean_quick.S | 1 + runtime/interpreter/mterp/mips/op_iput_byte.S | 1 + .../mterp/mips/op_iput_byte_quick.S | 1 + runtime/interpreter/mterp/mips/op_iput_char.S | 1 + .../mterp/mips/op_iput_char_quick.S | 1 + .../interpreter/mterp/mips/op_iput_object.S | 16 + .../mterp/mips/op_iput_object_quick.S | 11 + .../interpreter/mterp/mips/op_iput_quick.S | 14 + .../interpreter/mterp/mips/op_iput_short.S | 1 + .../mterp/mips/op_iput_short_quick.S | 1 + runtime/interpreter/mterp/mips/op_iput_wide.S | 15 + .../mterp/mips/op_iput_wide_quick.S | 14 + .../mterp/mips/op_long_to_double.S | 1 + .../interpreter/mterp/mips/op_long_to_float.S | 1 + .../interpreter/mterp/mips/op_long_to_int.S | 2 + .../interpreter/mterp/mips/op_monitor_enter.S | 13 + .../interpreter/mterp/mips/op_monitor_exit.S | 17 + runtime/interpreter/mterp/mips/op_move.S | 14 + runtime/interpreter/mterp/mips/op_move_16.S | 14 + .../mterp/mips/op_move_exception.S | 8 + .../interpreter/mterp/mips/op_move_from16.S | 14 + .../interpreter/mterp/mips/op_move_object.S | 1 + .../mterp/mips/op_move_object_16.S | 1 + .../mterp/mips/op_move_object_from16.S | 1 + .../interpreter/mterp/mips/op_move_result.S | 14 + .../mterp/mips/op_move_result_object.S | 1 + .../mterp/mips/op_move_result_wide.S | 8 + runtime/interpreter/mterp/mips/op_move_wide.S | 10 + .../interpreter/mterp/mips/op_move_wide_16.S | 10 + .../mterp/mips/op_move_wide_from16.S | 10 + .../interpreter/mterp/mips/op_mul_double.S | 1 + .../mterp/mips/op_mul_double_2addr.S | 1 + runtime/interpreter/mterp/mips/op_mul_float.S | 1 + .../mterp/mips/op_mul_float_2addr.S | 1 + runtime/interpreter/mterp/mips/op_mul_int.S | 1 + .../interpreter/mterp/mips/op_mul_int_2addr.S | 1 + .../interpreter/mterp/mips/op_mul_int_lit16.S | 1 + .../interpreter/mterp/mips/op_mul_int_lit8.S | 1 + runtime/interpreter/mterp/mips/op_mul_long.S | 43 + .../mterp/mips/op_mul_long_2addr.S | 31 + .../interpreter/mterp/mips/op_neg_double.S | 1 + runtime/interpreter/mterp/mips/op_neg_float.S | 1 + runtime/interpreter/mterp/mips/op_neg_int.S | 1 + runtime/interpreter/mterp/mips/op_neg_long.S | 1 + runtime/interpreter/mterp/mips/op_new_array.S | 18 + .../interpreter/mterp/mips/op_new_instance.S | 13 + runtime/interpreter/mterp/mips/op_nop.S | 3 + runtime/interpreter/mterp/mips/op_not_int.S | 1 + runtime/interpreter/mterp/mips/op_not_long.S | 1 + runtime/interpreter/mterp/mips/op_or_int.S | 1 + .../interpreter/mterp/mips/op_or_int_2addr.S | 1 + .../interpreter/mterp/mips/op_or_int_lit16.S | 1 + .../interpreter/mterp/mips/op_or_int_lit8.S | 1 + runtime/interpreter/mterp/mips/op_or_long.S | 1 + .../interpreter/mterp/mips/op_or_long_2addr.S | 1 + .../interpreter/mterp/mips/op_packed_switch.S | 57 + .../interpreter/mterp/mips/op_rem_double.S | 1 + .../mterp/mips/op_rem_double_2addr.S | 1 + runtime/interpreter/mterp/mips/op_rem_float.S | 1 + .../mterp/mips/op_rem_float_2addr.S | 1 + runtime/interpreter/mterp/mips/op_rem_int.S | 5 + .../interpreter/mterp/mips/op_rem_int_2addr.S | 5 + .../interpreter/mterp/mips/op_rem_int_lit16.S | 5 + .../interpreter/mterp/mips/op_rem_int_lit8.S | 5 + runtime/interpreter/mterp/mips/op_rem_long.S | 1 + .../mterp/mips/op_rem_long_2addr.S | 1 + runtime/interpreter/mterp/mips/op_return.S | 18 + .../interpreter/mterp/mips/op_return_object.S | 1 + .../interpreter/mterp/mips/op_return_void.S | 11 + .../mterp/mips/op_return_void_no_barrier.S | 9 + .../interpreter/mterp/mips/op_return_wide.S | 16 + runtime/interpreter/mterp/mips/op_rsub_int.S | 2 + .../interpreter/mterp/mips/op_rsub_int_lit8.S | 1 + runtime/interpreter/mterp/mips/op_sget.S | 25 + .../interpreter/mterp/mips/op_sget_boolean.S | 1 + runtime/interpreter/mterp/mips/op_sget_byte.S | 1 + runtime/interpreter/mterp/mips/op_sget_char.S | 1 + .../interpreter/mterp/mips/op_sget_object.S | 1 + .../interpreter/mterp/mips/op_sget_short.S | 1 + runtime/interpreter/mterp/mips/op_sget_wide.S | 17 + runtime/interpreter/mterp/mips/op_shl_int.S | 1 + .../interpreter/mterp/mips/op_shl_int_2addr.S | 1 + .../interpreter/mterp/mips/op_shl_int_lit8.S | 1 + runtime/interpreter/mterp/mips/op_shl_long.S | 31 + .../mterp/mips/op_shl_long_2addr.S | 27 + runtime/interpreter/mterp/mips/op_shr_int.S | 1 + .../interpreter/mterp/mips/op_shr_int_2addr.S | 1 + .../interpreter/mterp/mips/op_shr_int_lit8.S | 1 + runtime/interpreter/mterp/mips/op_shr_long.S | 31 + .../mterp/mips/op_shr_long_2addr.S | 27 + .../interpreter/mterp/mips/op_sparse_switch.S | 1 + runtime/interpreter/mterp/mips/op_sput.S | 19 + .../interpreter/mterp/mips/op_sput_boolean.S | 1 + runtime/interpreter/mterp/mips/op_sput_byte.S | 1 + runtime/interpreter/mterp/mips/op_sput_char.S | 1 + .../interpreter/mterp/mips/op_sput_object.S | 16 + .../interpreter/mterp/mips/op_sput_short.S | 1 + runtime/interpreter/mterp/mips/op_sput_wide.S | 17 + .../interpreter/mterp/mips/op_sub_double.S | 1 + .../mterp/mips/op_sub_double_2addr.S | 1 + runtime/interpreter/mterp/mips/op_sub_float.S | 1 + .../mterp/mips/op_sub_float_2addr.S | 1 + runtime/interpreter/mterp/mips/op_sub_int.S | 1 + .../interpreter/mterp/mips/op_sub_int_2addr.S | 1 + runtime/interpreter/mterp/mips/op_sub_long.S | 8 + .../mterp/mips/op_sub_long_2addr.S | 4 + runtime/interpreter/mterp/mips/op_throw.S | 11 + runtime/interpreter/mterp/mips/op_unused_3e.S | 1 + runtime/interpreter/mterp/mips/op_unused_3f.S | 1 + runtime/interpreter/mterp/mips/op_unused_40.S | 1 + runtime/interpreter/mterp/mips/op_unused_41.S | 1 + runtime/interpreter/mterp/mips/op_unused_42.S | 1 + runtime/interpreter/mterp/mips/op_unused_43.S | 1 + runtime/interpreter/mterp/mips/op_unused_73.S | 1 + runtime/interpreter/mterp/mips/op_unused_79.S | 1 + runtime/interpreter/mterp/mips/op_unused_7a.S | 1 + runtime/interpreter/mterp/mips/op_unused_f3.S | 1 + runtime/interpreter/mterp/mips/op_unused_f4.S | 1 + runtime/interpreter/mterp/mips/op_unused_f5.S | 1 + runtime/interpreter/mterp/mips/op_unused_f6.S | 1 + runtime/interpreter/mterp/mips/op_unused_f7.S | 1 + runtime/interpreter/mterp/mips/op_unused_f8.S | 1 + runtime/interpreter/mterp/mips/op_unused_f9.S | 1 + runtime/interpreter/mterp/mips/op_unused_fa.S | 1 + runtime/interpreter/mterp/mips/op_unused_fb.S | 1 + runtime/interpreter/mterp/mips/op_unused_fc.S | 1 + runtime/interpreter/mterp/mips/op_unused_fd.S | 1 + runtime/interpreter/mterp/mips/op_unused_fe.S | 1 + runtime/interpreter/mterp/mips/op_unused_ff.S | 1 + runtime/interpreter/mterp/mips/op_ushr_int.S | 1 + .../mterp/mips/op_ushr_int_2addr.S | 1 + .../interpreter/mterp/mips/op_ushr_int_lit8.S | 1 + runtime/interpreter/mterp/mips/op_ushr_long.S | 31 + .../mterp/mips/op_ushr_long_2addr.S | 27 + runtime/interpreter/mterp/mips/op_xor_int.S | 1 + .../interpreter/mterp/mips/op_xor_int_2addr.S | 1 + .../interpreter/mterp/mips/op_xor_int_lit16.S | 1 + .../interpreter/mterp/mips/op_xor_int_lit8.S | 1 + runtime/interpreter/mterp/mips/op_xor_long.S | 1 + .../mterp/mips/op_xor_long_2addr.S | 1 + runtime/interpreter/mterp/mips/unop.S | 19 + runtime/interpreter/mterp/mips/unopNarrower.S | 24 + runtime/interpreter/mterp/mips/unopWide.S | 20 + runtime/interpreter/mterp/mips/unopWider.S | 19 + runtime/interpreter/mterp/mips/unused.S | 4 + runtime/interpreter/mterp/mips/zcmp.S | 32 + runtime/interpreter/mterp/mterp.cc | 3 +- runtime/interpreter/mterp/out/mterp_mips.S | 13157 ++++++++++++++++ runtime/interpreter/mterp/rebuild.sh | 2 +- 289 files changed, 16538 insertions(+), 265 deletions(-) create mode 100644 runtime/interpreter/mterp/mips/alt_stub.S create mode 100644 runtime/interpreter/mterp/mips/bincmp.S create mode 100644 runtime/interpreter/mterp/mips/binop.S create mode 100644 runtime/interpreter/mterp/mips/binop2addr.S create mode 100644 runtime/interpreter/mterp/mips/binopLit16.S create mode 100644 runtime/interpreter/mterp/mips/binopLit8.S create mode 100644 runtime/interpreter/mterp/mips/binopWide.S create mode 100644 runtime/interpreter/mterp/mips/binopWide2addr.S create mode 100644 runtime/interpreter/mterp/mips/entry.S create mode 100644 runtime/interpreter/mterp/mips/fallback.S create mode 100644 runtime/interpreter/mterp/mips/fbinop.S create mode 100644 runtime/interpreter/mterp/mips/fbinop2addr.S create mode 100644 runtime/interpreter/mterp/mips/fbinopWide.S create mode 100644 runtime/interpreter/mterp/mips/fbinopWide2addr.S create mode 100644 runtime/interpreter/mterp/mips/footer.S create mode 100644 runtime/interpreter/mterp/mips/funop.S create mode 100644 runtime/interpreter/mterp/mips/funopWide.S create mode 100644 runtime/interpreter/mterp/mips/funopWider.S create mode 100644 runtime/interpreter/mterp/mips/header.S create mode 100644 runtime/interpreter/mterp/mips/invoke.S create mode 100644 runtime/interpreter/mterp/mips/op_add_double.S create mode 100644 runtime/interpreter/mterp/mips/op_add_double_2addr.S create mode 100644 runtime/interpreter/mterp/mips/op_add_float.S create mode 100644 runtime/interpreter/mterp/mips/op_add_float_2addr.S create mode 100644 runtime/interpreter/mterp/mips/op_add_int.S create mode 100644 runtime/interpreter/mterp/mips/op_add_int_2addr.S create mode 100644 runtime/interpreter/mterp/mips/op_add_int_lit16.S create mode 100644 runtime/interpreter/mterp/mips/op_add_int_lit8.S create mode 100644 runtime/interpreter/mterp/mips/op_add_long.S create mode 100644 runtime/interpreter/mterp/mips/op_add_long_2addr.S create mode 100644 runtime/interpreter/mterp/mips/op_aget.S create mode 100644 runtime/interpreter/mterp/mips/op_aget_boolean.S create mode 100644 runtime/interpreter/mterp/mips/op_aget_byte.S create mode 100644 runtime/interpreter/mterp/mips/op_aget_char.S create mode 100644 runtime/interpreter/mterp/mips/op_aget_object.S create mode 100644 runtime/interpreter/mterp/mips/op_aget_short.S create mode 100644 runtime/interpreter/mterp/mips/op_aget_wide.S create mode 100644 runtime/interpreter/mterp/mips/op_and_int.S create mode 100644 runtime/interpreter/mterp/mips/op_and_int_2addr.S create mode 100644 runtime/interpreter/mterp/mips/op_and_int_lit16.S create mode 100644 runtime/interpreter/mterp/mips/op_and_int_lit8.S create mode 100644 runtime/interpreter/mterp/mips/op_and_long.S create mode 100644 runtime/interpreter/mterp/mips/op_and_long_2addr.S create mode 100644 runtime/interpreter/mterp/mips/op_aput.S create mode 100644 runtime/interpreter/mterp/mips/op_aput_boolean.S create mode 100644 runtime/interpreter/mterp/mips/op_aput_byte.S create mode 100644 runtime/interpreter/mterp/mips/op_aput_char.S create mode 100644 runtime/interpreter/mterp/mips/op_aput_object.S create mode 100644 runtime/interpreter/mterp/mips/op_aput_short.S create mode 100644 runtime/interpreter/mterp/mips/op_aput_wide.S create mode 100644 runtime/interpreter/mterp/mips/op_array_length.S create mode 100644 runtime/interpreter/mterp/mips/op_check_cast.S create mode 100644 runtime/interpreter/mterp/mips/op_cmp_long.S create mode 100644 runtime/interpreter/mterp/mips/op_cmpg_double.S create mode 100644 runtime/interpreter/mterp/mips/op_cmpg_float.S create mode 100644 runtime/interpreter/mterp/mips/op_cmpl_double.S create mode 100644 runtime/interpreter/mterp/mips/op_cmpl_float.S create mode 100644 runtime/interpreter/mterp/mips/op_const.S create mode 100644 runtime/interpreter/mterp/mips/op_const_16.S create mode 100644 runtime/interpreter/mterp/mips/op_const_4.S create mode 100644 runtime/interpreter/mterp/mips/op_const_class.S create mode 100644 runtime/interpreter/mterp/mips/op_const_high16.S create mode 100644 runtime/interpreter/mterp/mips/op_const_string.S create mode 100644 runtime/interpreter/mterp/mips/op_const_string_jumbo.S create mode 100644 runtime/interpreter/mterp/mips/op_const_wide.S create mode 100644 runtime/interpreter/mterp/mips/op_const_wide_16.S create mode 100644 runtime/interpreter/mterp/mips/op_const_wide_32.S create mode 100644 runtime/interpreter/mterp/mips/op_const_wide_high16.S create mode 100644 runtime/interpreter/mterp/mips/op_div_double.S create mode 100644 runtime/interpreter/mterp/mips/op_div_double_2addr.S create mode 100644 runtime/interpreter/mterp/mips/op_div_float.S create mode 100644 runtime/interpreter/mterp/mips/op_div_float_2addr.S create mode 100644 runtime/interpreter/mterp/mips/op_div_int.S create mode 100644 runtime/interpreter/mterp/mips/op_div_int_2addr.S create mode 100644 runtime/interpreter/mterp/mips/op_div_int_lit16.S create mode 100644 runtime/interpreter/mterp/mips/op_div_int_lit8.S create mode 100644 runtime/interpreter/mterp/mips/op_div_long.S create mode 100644 runtime/interpreter/mterp/mips/op_div_long_2addr.S create mode 100644 runtime/interpreter/mterp/mips/op_double_to_float.S create mode 100644 runtime/interpreter/mterp/mips/op_double_to_int.S create mode 100644 runtime/interpreter/mterp/mips/op_double_to_long.S create mode 100644 runtime/interpreter/mterp/mips/op_fill_array_data.S create mode 100644 runtime/interpreter/mterp/mips/op_filled_new_array.S create mode 100644 runtime/interpreter/mterp/mips/op_filled_new_array_range.S create mode 100644 runtime/interpreter/mterp/mips/op_float_to_double.S create mode 100644 runtime/interpreter/mterp/mips/op_float_to_int.S create mode 100644 runtime/interpreter/mterp/mips/op_float_to_long.S create mode 100644 runtime/interpreter/mterp/mips/op_goto.S create mode 100644 runtime/interpreter/mterp/mips/op_goto_16.S create mode 100644 runtime/interpreter/mterp/mips/op_goto_32.S create mode 100644 runtime/interpreter/mterp/mips/op_if_eq.S create mode 100644 runtime/interpreter/mterp/mips/op_if_eqz.S create mode 100644 runtime/interpreter/mterp/mips/op_if_ge.S create mode 100644 runtime/interpreter/mterp/mips/op_if_gez.S create mode 100644 runtime/interpreter/mterp/mips/op_if_gt.S create mode 100644 runtime/interpreter/mterp/mips/op_if_gtz.S create mode 100644 runtime/interpreter/mterp/mips/op_if_le.S create mode 100644 runtime/interpreter/mterp/mips/op_if_lez.S create mode 100644 runtime/interpreter/mterp/mips/op_if_lt.S create mode 100644 runtime/interpreter/mterp/mips/op_if_ltz.S create mode 100644 runtime/interpreter/mterp/mips/op_if_ne.S create mode 100644 runtime/interpreter/mterp/mips/op_if_nez.S create mode 100644 runtime/interpreter/mterp/mips/op_iget.S create mode 100644 runtime/interpreter/mterp/mips/op_iget_boolean.S create mode 100644 runtime/interpreter/mterp/mips/op_iget_boolean_quick.S create mode 100644 runtime/interpreter/mterp/mips/op_iget_byte.S create mode 100644 runtime/interpreter/mterp/mips/op_iget_byte_quick.S create mode 100644 runtime/interpreter/mterp/mips/op_iget_char.S create mode 100644 runtime/interpreter/mterp/mips/op_iget_char_quick.S create mode 100644 runtime/interpreter/mterp/mips/op_iget_object.S create mode 100644 runtime/interpreter/mterp/mips/op_iget_object_quick.S create mode 100644 runtime/interpreter/mterp/mips/op_iget_quick.S create mode 100644 runtime/interpreter/mterp/mips/op_iget_short.S create mode 100644 runtime/interpreter/mterp/mips/op_iget_short_quick.S create mode 100644 runtime/interpreter/mterp/mips/op_iget_wide.S create mode 100644 runtime/interpreter/mterp/mips/op_iget_wide_quick.S create mode 100644 runtime/interpreter/mterp/mips/op_instance_of.S create mode 100644 runtime/interpreter/mterp/mips/op_int_to_byte.S create mode 100644 runtime/interpreter/mterp/mips/op_int_to_char.S create mode 100644 runtime/interpreter/mterp/mips/op_int_to_double.S create mode 100644 runtime/interpreter/mterp/mips/op_int_to_float.S create mode 100644 runtime/interpreter/mterp/mips/op_int_to_long.S create mode 100644 runtime/interpreter/mterp/mips/op_int_to_short.S create mode 100644 runtime/interpreter/mterp/mips/op_invoke_direct.S create mode 100644 runtime/interpreter/mterp/mips/op_invoke_direct_range.S create mode 100644 runtime/interpreter/mterp/mips/op_invoke_interface.S create mode 100644 runtime/interpreter/mterp/mips/op_invoke_interface_range.S create mode 100644 runtime/interpreter/mterp/mips/op_invoke_static.S create mode 100644 runtime/interpreter/mterp/mips/op_invoke_static_range.S create mode 100644 runtime/interpreter/mterp/mips/op_invoke_super.S create mode 100644 runtime/interpreter/mterp/mips/op_invoke_super_range.S create mode 100644 runtime/interpreter/mterp/mips/op_invoke_virtual.S create mode 100644 runtime/interpreter/mterp/mips/op_invoke_virtual_quick.S create mode 100644 runtime/interpreter/mterp/mips/op_invoke_virtual_range.S create mode 100644 runtime/interpreter/mterp/mips/op_invoke_virtual_range_quick.S create mode 100644 runtime/interpreter/mterp/mips/op_iput.S create mode 100644 runtime/interpreter/mterp/mips/op_iput_boolean.S create mode 100644 runtime/interpreter/mterp/mips/op_iput_boolean_quick.S create mode 100644 runtime/interpreter/mterp/mips/op_iput_byte.S create mode 100644 runtime/interpreter/mterp/mips/op_iput_byte_quick.S create mode 100644 runtime/interpreter/mterp/mips/op_iput_char.S create mode 100644 runtime/interpreter/mterp/mips/op_iput_char_quick.S create mode 100644 runtime/interpreter/mterp/mips/op_iput_object.S create mode 100644 runtime/interpreter/mterp/mips/op_iput_object_quick.S create mode 100644 runtime/interpreter/mterp/mips/op_iput_quick.S create mode 100644 runtime/interpreter/mterp/mips/op_iput_short.S create mode 100644 runtime/interpreter/mterp/mips/op_iput_short_quick.S create mode 100644 runtime/interpreter/mterp/mips/op_iput_wide.S create mode 100644 runtime/interpreter/mterp/mips/op_iput_wide_quick.S create mode 100644 runtime/interpreter/mterp/mips/op_long_to_double.S create mode 100644 runtime/interpreter/mterp/mips/op_long_to_float.S create mode 100644 runtime/interpreter/mterp/mips/op_long_to_int.S create mode 100644 runtime/interpreter/mterp/mips/op_monitor_enter.S create mode 100644 runtime/interpreter/mterp/mips/op_monitor_exit.S create mode 100644 runtime/interpreter/mterp/mips/op_move.S create mode 100644 runtime/interpreter/mterp/mips/op_move_16.S create mode 100644 runtime/interpreter/mterp/mips/op_move_exception.S create mode 100644 runtime/interpreter/mterp/mips/op_move_from16.S create mode 100644 runtime/interpreter/mterp/mips/op_move_object.S create mode 100644 runtime/interpreter/mterp/mips/op_move_object_16.S create mode 100644 runtime/interpreter/mterp/mips/op_move_object_from16.S create mode 100644 runtime/interpreter/mterp/mips/op_move_result.S create mode 100644 runtime/interpreter/mterp/mips/op_move_result_object.S create mode 100644 runtime/interpreter/mterp/mips/op_move_result_wide.S create mode 100644 runtime/interpreter/mterp/mips/op_move_wide.S create mode 100644 runtime/interpreter/mterp/mips/op_move_wide_16.S create mode 100644 runtime/interpreter/mterp/mips/op_move_wide_from16.S create mode 100644 runtime/interpreter/mterp/mips/op_mul_double.S create mode 100644 runtime/interpreter/mterp/mips/op_mul_double_2addr.S create mode 100644 runtime/interpreter/mterp/mips/op_mul_float.S create mode 100644 runtime/interpreter/mterp/mips/op_mul_float_2addr.S create mode 100644 runtime/interpreter/mterp/mips/op_mul_int.S create mode 100644 runtime/interpreter/mterp/mips/op_mul_int_2addr.S create mode 100644 runtime/interpreter/mterp/mips/op_mul_int_lit16.S create mode 100644 runtime/interpreter/mterp/mips/op_mul_int_lit8.S create mode 100644 runtime/interpreter/mterp/mips/op_mul_long.S create mode 100644 runtime/interpreter/mterp/mips/op_mul_long_2addr.S create mode 100644 runtime/interpreter/mterp/mips/op_neg_double.S create mode 100644 runtime/interpreter/mterp/mips/op_neg_float.S create mode 100644 runtime/interpreter/mterp/mips/op_neg_int.S create mode 100644 runtime/interpreter/mterp/mips/op_neg_long.S create mode 100644 runtime/interpreter/mterp/mips/op_new_array.S create mode 100644 runtime/interpreter/mterp/mips/op_new_instance.S create mode 100644 runtime/interpreter/mterp/mips/op_nop.S create mode 100644 runtime/interpreter/mterp/mips/op_not_int.S create mode 100644 runtime/interpreter/mterp/mips/op_not_long.S create mode 100644 runtime/interpreter/mterp/mips/op_or_int.S create mode 100644 runtime/interpreter/mterp/mips/op_or_int_2addr.S create mode 100644 runtime/interpreter/mterp/mips/op_or_int_lit16.S create mode 100644 runtime/interpreter/mterp/mips/op_or_int_lit8.S create mode 100644 runtime/interpreter/mterp/mips/op_or_long.S create mode 100644 runtime/interpreter/mterp/mips/op_or_long_2addr.S create mode 100644 runtime/interpreter/mterp/mips/op_packed_switch.S create mode 100644 runtime/interpreter/mterp/mips/op_rem_double.S create mode 100644 runtime/interpreter/mterp/mips/op_rem_double_2addr.S create mode 100644 runtime/interpreter/mterp/mips/op_rem_float.S create mode 100644 runtime/interpreter/mterp/mips/op_rem_float_2addr.S create mode 100644 runtime/interpreter/mterp/mips/op_rem_int.S create mode 100644 runtime/interpreter/mterp/mips/op_rem_int_2addr.S create mode 100644 runtime/interpreter/mterp/mips/op_rem_int_lit16.S create mode 100644 runtime/interpreter/mterp/mips/op_rem_int_lit8.S create mode 100644 runtime/interpreter/mterp/mips/op_rem_long.S create mode 100644 runtime/interpreter/mterp/mips/op_rem_long_2addr.S create mode 100644 runtime/interpreter/mterp/mips/op_return.S create mode 100644 runtime/interpreter/mterp/mips/op_return_object.S create mode 100644 runtime/interpreter/mterp/mips/op_return_void.S create mode 100644 runtime/interpreter/mterp/mips/op_return_void_no_barrier.S create mode 100644 runtime/interpreter/mterp/mips/op_return_wide.S create mode 100644 runtime/interpreter/mterp/mips/op_rsub_int.S create mode 100644 runtime/interpreter/mterp/mips/op_rsub_int_lit8.S create mode 100644 runtime/interpreter/mterp/mips/op_sget.S create mode 100644 runtime/interpreter/mterp/mips/op_sget_boolean.S create mode 100644 runtime/interpreter/mterp/mips/op_sget_byte.S create mode 100644 runtime/interpreter/mterp/mips/op_sget_char.S create mode 100644 runtime/interpreter/mterp/mips/op_sget_object.S create mode 100644 runtime/interpreter/mterp/mips/op_sget_short.S create mode 100644 runtime/interpreter/mterp/mips/op_sget_wide.S create mode 100644 runtime/interpreter/mterp/mips/op_shl_int.S create mode 100644 runtime/interpreter/mterp/mips/op_shl_int_2addr.S create mode 100644 runtime/interpreter/mterp/mips/op_shl_int_lit8.S create mode 100644 runtime/interpreter/mterp/mips/op_shl_long.S create mode 100644 runtime/interpreter/mterp/mips/op_shl_long_2addr.S create mode 100644 runtime/interpreter/mterp/mips/op_shr_int.S create mode 100644 runtime/interpreter/mterp/mips/op_shr_int_2addr.S create mode 100644 runtime/interpreter/mterp/mips/op_shr_int_lit8.S create mode 100644 runtime/interpreter/mterp/mips/op_shr_long.S create mode 100644 runtime/interpreter/mterp/mips/op_shr_long_2addr.S create mode 100644 runtime/interpreter/mterp/mips/op_sparse_switch.S create mode 100644 runtime/interpreter/mterp/mips/op_sput.S create mode 100644 runtime/interpreter/mterp/mips/op_sput_boolean.S create mode 100644 runtime/interpreter/mterp/mips/op_sput_byte.S create mode 100644 runtime/interpreter/mterp/mips/op_sput_char.S create mode 100644 runtime/interpreter/mterp/mips/op_sput_object.S create mode 100644 runtime/interpreter/mterp/mips/op_sput_short.S create mode 100644 runtime/interpreter/mterp/mips/op_sput_wide.S create mode 100644 runtime/interpreter/mterp/mips/op_sub_double.S create mode 100644 runtime/interpreter/mterp/mips/op_sub_double_2addr.S create mode 100644 runtime/interpreter/mterp/mips/op_sub_float.S create mode 100644 runtime/interpreter/mterp/mips/op_sub_float_2addr.S create mode 100644 runtime/interpreter/mterp/mips/op_sub_int.S create mode 100644 runtime/interpreter/mterp/mips/op_sub_int_2addr.S create mode 100644 runtime/interpreter/mterp/mips/op_sub_long.S create mode 100644 runtime/interpreter/mterp/mips/op_sub_long_2addr.S create mode 100644 runtime/interpreter/mterp/mips/op_throw.S create mode 100644 runtime/interpreter/mterp/mips/op_unused_3e.S create mode 100644 runtime/interpreter/mterp/mips/op_unused_3f.S create mode 100644 runtime/interpreter/mterp/mips/op_unused_40.S create mode 100644 runtime/interpreter/mterp/mips/op_unused_41.S create mode 100644 runtime/interpreter/mterp/mips/op_unused_42.S create mode 100644 runtime/interpreter/mterp/mips/op_unused_43.S create mode 100644 runtime/interpreter/mterp/mips/op_unused_73.S create mode 100644 runtime/interpreter/mterp/mips/op_unused_79.S create mode 100644 runtime/interpreter/mterp/mips/op_unused_7a.S create mode 100644 runtime/interpreter/mterp/mips/op_unused_f3.S create mode 100644 runtime/interpreter/mterp/mips/op_unused_f4.S create mode 100644 runtime/interpreter/mterp/mips/op_unused_f5.S create mode 100644 runtime/interpreter/mterp/mips/op_unused_f6.S create mode 100644 runtime/interpreter/mterp/mips/op_unused_f7.S create mode 100644 runtime/interpreter/mterp/mips/op_unused_f8.S create mode 100644 runtime/interpreter/mterp/mips/op_unused_f9.S create mode 100644 runtime/interpreter/mterp/mips/op_unused_fa.S create mode 100644 runtime/interpreter/mterp/mips/op_unused_fb.S create mode 100644 runtime/interpreter/mterp/mips/op_unused_fc.S create mode 100644 runtime/interpreter/mterp/mips/op_unused_fd.S create mode 100644 runtime/interpreter/mterp/mips/op_unused_fe.S create mode 100644 runtime/interpreter/mterp/mips/op_unused_ff.S create mode 100644 runtime/interpreter/mterp/mips/op_ushr_int.S create mode 100644 runtime/interpreter/mterp/mips/op_ushr_int_2addr.S create mode 100644 runtime/interpreter/mterp/mips/op_ushr_int_lit8.S create mode 100644 runtime/interpreter/mterp/mips/op_ushr_long.S create mode 100644 runtime/interpreter/mterp/mips/op_ushr_long_2addr.S create mode 100644 runtime/interpreter/mterp/mips/op_xor_int.S create mode 100644 runtime/interpreter/mterp/mips/op_xor_int_2addr.S create mode 100644 runtime/interpreter/mterp/mips/op_xor_int_lit16.S create mode 100644 runtime/interpreter/mterp/mips/op_xor_int_lit8.S create mode 100644 runtime/interpreter/mterp/mips/op_xor_long.S create mode 100644 runtime/interpreter/mterp/mips/op_xor_long_2addr.S create mode 100644 runtime/interpreter/mterp/mips/unop.S create mode 100644 runtime/interpreter/mterp/mips/unopNarrower.S create mode 100644 runtime/interpreter/mterp/mips/unopWide.S create mode 100644 runtime/interpreter/mterp/mips/unopWider.S create mode 100644 runtime/interpreter/mterp/mips/unused.S create mode 100644 runtime/interpreter/mterp/mips/zcmp.S create mode 100644 runtime/interpreter/mterp/out/mterp_mips.S diff --git a/runtime/Android.mk b/runtime/Android.mk index 947de8a79e..deee3a641e 100644 --- a/runtime/Android.mk +++ b/runtime/Android.mk @@ -306,7 +306,8 @@ LIBART_TARGET_SRC_FILES_x86_64 := \ $(LIBART_SRC_FILES_x86_64) \ LIBART_TARGET_SRC_FILES_mips := \ - interpreter/mterp/mterp_stub.cc \ + interpreter/mterp/mterp.cc \ + interpreter/mterp/out/mterp_mips.S \ arch/mips/context_mips.cc \ arch/mips/entrypoints_init_mips.cc \ arch/mips/jni_entrypoints_mips.S \ diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc index a595d33f04..8b72d7189e 100644 --- a/runtime/interpreter/interpreter.cc +++ b/runtime/interpreter/interpreter.cc @@ -241,7 +241,7 @@ static std::ostream& operator<<(std::ostream& os, const InterpreterImplKind& rhs } #if !defined(__clang__) -#if (defined(__arm__) || defined(__i386__) || defined(__aarch64__)) +#if (defined(__arm__) || defined(__i386__) || defined(__aarch64__) || (defined(__mips__) && !defined(__LP64__))) // TODO: remove when all targets implemented. static constexpr InterpreterImplKind kInterpreterImplKind = kMterpImplKind; #else @@ -249,7 +249,7 @@ static constexpr InterpreterImplKind kInterpreterImplKind = kComputedGotoImplKin #endif #else // Clang 3.4 fails to build the goto interpreter implementation. -#if (defined(__arm__) || defined(__i386__) || defined(__aarch64__)) +#if (defined(__arm__) || defined(__i386__) || defined(__aarch64__) || (defined(__mips__) && !defined(__LP64__))) static constexpr InterpreterImplKind kInterpreterImplKind = kMterpImplKind; #else static constexpr InterpreterImplKind kInterpreterImplKind = kSwitchImplKind; diff --git a/runtime/interpreter/mterp/config_mips b/runtime/interpreter/mterp/config_mips index d1221f731b..c6292c3c37 100644 --- a/runtime/interpreter/mterp/config_mips +++ b/runtime/interpreter/mterp/config_mips @@ -1,4 +1,4 @@ -# Copyright (C) 2015 The Android Open Source Project +# Copyright (C) 2016 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -13,7 +13,7 @@ # limitations under the License. # -# Configuration for MIPS_32 +# Configuration for MIPS_32 targets. # handler-style computed-goto @@ -33,265 +33,265 @@ fallback-stub mips/fallback.S # opcode list; argument to op-start is default directory op-start mips - # (override example:) op OP_SUB_FLOAT_2ADDR arm-vfp - # (fallback example:) op OP_SUB_FLOAT_2ADDR FALLBACK + # (override example:) op op_sub_float_2addr arm-vfp + # (fallback example:) op op_sub_float_2addr FALLBACK - op op_nop FALLBACK - op op_move FALLBACK - op op_move_from16 FALLBACK - op op_move_16 FALLBACK - op op_move_wide FALLBACK - op op_move_wide_from16 FALLBACK - op op_move_wide_16 FALLBACK - op op_move_object FALLBACK - op op_move_object_from16 FALLBACK - op op_move_object_16 FALLBACK - op op_move_result FALLBACK - op op_move_result_wide FALLBACK - op op_move_result_object FALLBACK - op op_move_exception FALLBACK - op op_return_void FALLBACK - op op_return FALLBACK - op op_return_wide FALLBACK - op op_return_object FALLBACK - op op_const_4 FALLBACK - op op_const_16 FALLBACK - op op_const FALLBACK - op op_const_high16 FALLBACK - op op_const_wide_16 FALLBACK - op op_const_wide_32 FALLBACK - op op_const_wide FALLBACK - op op_const_wide_high16 FALLBACK - op op_const_string FALLBACK - op op_const_string_jumbo FALLBACK - op op_const_class FALLBACK - op op_monitor_enter FALLBACK - op op_monitor_exit FALLBACK - op op_check_cast FALLBACK - op op_instance_of FALLBACK - op op_array_length FALLBACK - op op_new_instance FALLBACK - op op_new_array FALLBACK - op op_filled_new_array FALLBACK - op op_filled_new_array_range FALLBACK - op op_fill_array_data FALLBACK - op op_throw FALLBACK - op op_goto FALLBACK - op op_goto_16 FALLBACK - op op_goto_32 FALLBACK - op op_packed_switch FALLBACK - op op_sparse_switch FALLBACK - op op_cmpl_float FALLBACK - op op_cmpg_float FALLBACK - op op_cmpl_double FALLBACK - op op_cmpg_double FALLBACK - op op_cmp_long FALLBACK - op op_if_eq FALLBACK - op op_if_ne FALLBACK - op op_if_lt FALLBACK - op op_if_ge FALLBACK - op op_if_gt FALLBACK - op op_if_le FALLBACK - op op_if_eqz FALLBACK - op op_if_nez FALLBACK - op op_if_ltz FALLBACK - op op_if_gez FALLBACK - op op_if_gtz FALLBACK - op op_if_lez FALLBACK - op_unused_3e FALLBACK - op_unused_3f FALLBACK - op_unused_40 FALLBACK - op_unused_41 FALLBACK - op_unused_42 FALLBACK - op_unused_43 FALLBACK - op op_aget FALLBACK - op op_aget_wide FALLBACK - op op_aget_object FALLBACK - op op_aget_boolean FALLBACK - op op_aget_byte FALLBACK - op op_aget_char FALLBACK - op op_aget_short FALLBACK - op op_aput FALLBACK - op op_aput_wide FALLBACK - op op_aput_object FALLBACK - op op_aput_boolean FALLBACK - op op_aput_byte FALLBACK - op op_aput_char FALLBACK - op op_aput_short FALLBACK - op op_iget FALLBACK - op op_iget_wide FALLBACK - op op_iget_object FALLBACK - op op_iget_boolean FALLBACK - op op_iget_byte FALLBACK - op op_iget_char FALLBACK - op op_iget_short FALLBACK - op op_iput FALLBACK - op op_iput_wide FALLBACK - op op_iput_object FALLBACK - op op_iput_boolean FALLBACK - op op_iput_byte FALLBACK - op op_iput_char FALLBACK - op op_iput_short FALLBACK - op op_sget FALLBACK - op op_sget_wide FALLBACK - op op_sget_object FALLBACK - op op_sget_boolean FALLBACK - op op_sget_byte FALLBACK - op op_sget_char FALLBACK - op op_sget_short FALLBACK - op op_sput FALLBACK - op op_sput_wide FALLBACK - op op_sput_object FALLBACK - op op_sput_boolean FALLBACK - op op_sput_byte FALLBACK - op op_sput_char FALLBACK - op op_sput_short FALLBACK - op op_invoke_virtual FALLBACK - op op_invoke_super FALLBACK - op op_invoke_direct FALLBACK - op op_invoke_static FALLBACK - op op_invoke_interface FALLBACK - op op_return_void_no_barrier FALLBACK - op op_invoke_virtual_range FALLBACK - op op_invoke_super_range FALLBACK - op op_invoke_direct_range FALLBACK - op op_invoke_static_range FALLBACK - op op_invoke_interface_range FALLBACK - op_unused_79 FALLBACK - op_unused_7a FALLBACK - op op_neg_int FALLBACK - op op_not_int FALLBACK - op op_neg_long FALLBACK - op op_not_long FALLBACK - op op_neg_float FALLBACK - op op_neg_double FALLBACK - op op_int_to_long FALLBACK - op op_int_to_float FALLBACK - op op_int_to_double FALLBACK - op op_long_to_int FALLBACK - op op_long_to_float FALLBACK - op op_long_to_double FALLBACK - op op_float_to_int FALLBACK - op op_float_to_long FALLBACK - op op_float_to_double FALLBACK - op op_double_to_int FALLBACK - op op_double_to_long FALLBACK - op op_double_to_float FALLBACK - op op_int_to_byte FALLBACK - op op_int_to_char FALLBACK - op op_int_to_short FALLBACK - op op_add_int FALLBACK - op op_sub_int FALLBACK - op op_mul_int FALLBACK - op op_div_int FALLBACK - op op_rem_int FALLBACK - op op_and_int FALLBACK - op op_or_int FALLBACK - op op_xor_int FALLBACK - op op_shl_int FALLBACK - op op_shr_int FALLBACK - op op_ushr_int FALLBACK - op op_add_long FALLBACK - op op_sub_long FALLBACK - op op_mul_long FALLBACK - op op_div_long FALLBACK - op op_rem_long FALLBACK - op op_and_long FALLBACK - op op_or_long FALLBACK - op op_xor_long FALLBACK - op op_shl_long FALLBACK - op op_shr_long FALLBACK - op op_ushr_long FALLBACK - op op_add_float FALLBACK - op op_sub_float FALLBACK - op op_mul_float FALLBACK - op op_div_float FALLBACK - op op_rem_float FALLBACK - op op_add_double FALLBACK - op op_sub_double FALLBACK - op op_mul_double FALLBACK - op op_div_double FALLBACK - op op_rem_double FALLBACK - op op_add_int_2addr FALLBACK - op op_sub_int_2addr FALLBACK - op op_mul_int_2addr FALLBACK - op op_div_int_2addr FALLBACK - op op_rem_int_2addr FALLBACK - op op_and_int_2addr FALLBACK - op op_or_int_2addr FALLBACK - op op_xor_int_2addr FALLBACK - op op_shl_int_2addr FALLBACK - op op_shr_int_2addr FALLBACK - op op_ushr_int_2addr FALLBACK - op op_add_long_2addr FALLBACK - op op_sub_long_2addr FALLBACK - op op_mul_long_2addr FALLBACK - op op_div_long_2addr FALLBACK - op op_rem_long_2addr FALLBACK - op op_and_long_2addr FALLBACK - op op_or_long_2addr FALLBACK - op op_xor_long_2addr FALLBACK - op op_shl_long_2addr FALLBACK - op op_shr_long_2addr FALLBACK - op op_ushr_long_2addr FALLBACK - op op_add_float_2addr FALLBACK - op op_sub_float_2addr FALLBACK - op op_mul_float_2addr FALLBACK - op op_div_float_2addr FALLBACK - op op_rem_float_2addr FALLBACK - op op_add_double_2addr FALLBACK - op op_sub_double_2addr FALLBACK - op op_mul_double_2addr FALLBACK - op op_div_double_2addr FALLBACK - op op_rem_double_2addr FALLBACK - op op_add_int_lit16 FALLBACK - op op_rsub_int FALLBACK - op op_mul_int_lit16 FALLBACK - op op_div_int_lit16 FALLBACK - op op_rem_int_lit16 FALLBACK - op op_and_int_lit16 FALLBACK - op op_or_int_lit16 FALLBACK - op op_xor_int_lit16 FALLBACK - op op_add_int_lit8 FALLBACK - op op_rsub_int_lit8 FALLBACK - op op_mul_int_lit8 FALLBACK - op op_div_int_lit8 FALLBACK - op op_rem_int_lit8 FALLBACK - op op_and_int_lit8 FALLBACK - op op_or_int_lit8 FALLBACK - op op_xor_int_lit8 FALLBACK - op op_shl_int_lit8 FALLBACK - op op_shr_int_lit8 FALLBACK - op op_ushr_int_lit8 FALLBACK - op op_iget_quick FALLBACK - op op_iget_wide_quick FALLBACK - op op_iget_object_quick FALLBACK - op op_iput_quick FALLBACK - op op_iput_wide_quick FALLBACK - op op_iput_object_quick FALLBACK - op op_invoke_virtual_quick FALLBACK - op op_invoke_virtual_range_quick FALLBACK - op op_iput_boolean_quick FALLBACK - op op_iput_byte_quick FALLBACK - op op_iput_char_quick FALLBACK - op op_iput_short_quick FALLBACK - op op_iget_boolean_quick FALLBACK - op op_iget_byte_quick FALLBACK - op op_iget_char_quick FALLBACK - op op_iget_short_quick FALLBACK - op_unused_f3 FALLBACK - op_unused_f4 FALLBACK - op_unused_f5 FALLBACK - op_unused_f6 FALLBACK - op_unused_f7 FALLBACK - op_unused_f8 FALLBACK - op_unused_f9 FALLBACK - op_unused_fa FALLBACK - op_unused_fb FALLBACK - op_unused_fc FALLBACK - op_unused_fd FALLBACK - op_unused_fe FALLBACK - op_unused_ff FALLBACK + # op op_nop FALLBACK + # op op_move FALLBACK + # op op_move_from16 FALLBACK + # op op_move_16 FALLBACK + # op op_move_wide FALLBACK + # op op_move_wide_from16 FALLBACK + # op op_move_wide_16 FALLBACK + # op op_move_object FALLBACK + # op op_move_object_from16 FALLBACK + # op op_move_object_16 FALLBACK + # op op_move_result FALLBACK + # op op_move_result_wide FALLBACK + # op op_move_result_object FALLBACK + # op op_move_exception FALLBACK + # op op_return_void FALLBACK + # op op_return FALLBACK + # op op_return_wide FALLBACK + # op op_return_object FALLBACK + # op op_const_4 FALLBACK + # op op_const_16 FALLBACK + # op op_const FALLBACK + # op op_const_high16 FALLBACK + # op op_const_wide_16 FALLBACK + # op op_const_wide_32 FALLBACK + # op op_const_wide FALLBACK + # op op_const_wide_high16 FALLBACK + # op op_const_string FALLBACK + # op op_const_string_jumbo FALLBACK + # op op_const_class FALLBACK + # op op_monitor_enter FALLBACK + # op op_monitor_exit FALLBACK + # op op_check_cast FALLBACK + # op op_instance_of FALLBACK + # op op_array_length FALLBACK + # op op_new_instance FALLBACK + # op op_new_array FALLBACK + # op op_filled_new_array FALLBACK + # op op_filled_new_array_range FALLBACK + # op op_fill_array_data FALLBACK + # op op_throw FALLBACK + # op op_goto FALLBACK + # op op_goto_16 FALLBACK + # op op_goto_32 FALLBACK + # op op_packed_switch FALLBACK + # op op_sparse_switch FALLBACK + # op op_cmpl_float FALLBACK + # op op_cmpg_float FALLBACK + # op op_cmpl_double FALLBACK + # op op_cmpg_double FALLBACK + # op op_cmp_long FALLBACK + # op op_if_eq FALLBACK + # op op_if_ne FALLBACK + # op op_if_lt FALLBACK + # op op_if_ge FALLBACK + # op op_if_gt FALLBACK + # op op_if_le FALLBACK + # op op_if_eqz FALLBACK + # op op_if_nez FALLBACK + # op op_if_ltz FALLBACK + # op op_if_gez FALLBACK + # op op_if_gtz FALLBACK + # op op_if_lez FALLBACK + # op op_unused_3e FALLBACK + # op op_unused_3f FALLBACK + # op op_unused_40 FALLBACK + # op op_unused_41 FALLBACK + # op op_unused_42 FALLBACK + # op op_unused_43 FALLBACK + # op op_aget FALLBACK + # op op_aget_wide FALLBACK + # op op_aget_object FALLBACK + # op op_aget_boolean FALLBACK + # op op_aget_byte FALLBACK + # op op_aget_char FALLBACK + # op op_aget_short FALLBACK + # op op_aput FALLBACK + # op op_aput_wide FALLBACK + # op op_aput_object FALLBACK + # op op_aput_boolean FALLBACK + # op op_aput_byte FALLBACK + # op op_aput_char FALLBACK + # op op_aput_short FALLBACK + # op op_iget FALLBACK + # op op_iget_wide FALLBACK + # op op_iget_object FALLBACK + # op op_iget_boolean FALLBACK + # op op_iget_byte FALLBACK + # op op_iget_char FALLBACK + # op op_iget_short FALLBACK + # op op_iput FALLBACK + # op op_iput_wide FALLBACK + # op op_iput_object FALLBACK + # op op_iput_boolean FALLBACK + # op op_iput_byte FALLBACK + # op op_iput_char FALLBACK + # op op_iput_short FALLBACK + # op op_sget FALLBACK + # op op_sget_wide FALLBACK + # op op_sget_object FALLBACK + # op op_sget_boolean FALLBACK + # op op_sget_byte FALLBACK + # op op_sget_char FALLBACK + # op op_sget_short FALLBACK + # op op_sput FALLBACK + # op op_sput_wide FALLBACK + # op op_sput_object FALLBACK + # op op_sput_boolean FALLBACK + # op op_sput_byte FALLBACK + # op op_sput_char FALLBACK + # op op_sput_short FALLBACK + # op op_invoke_virtual FALLBACK + # op op_invoke_super FALLBACK + # op op_invoke_direct FALLBACK + # op op_invoke_static FALLBACK + # op op_invoke_interface FALLBACK + # op op_return_void_no_barrier FALLBACK + # op op_invoke_virtual_range FALLBACK + # op op_invoke_super_range FALLBACK + # op op_invoke_direct_range FALLBACK + # op op_invoke_static_range FALLBACK + # op op_invoke_interface_range FALLBACK + # op op_unused_79 FALLBACK + # op op_unused_7a FALLBACK + # op op_neg_int FALLBACK + # op op_not_int FALLBACK + # op op_neg_long FALLBACK + # op op_not_long FALLBACK + # op op_neg_float FALLBACK + # op op_neg_double FALLBACK + # op op_int_to_long FALLBACK + # op op_int_to_float FALLBACK + # op op_int_to_double FALLBACK + # op op_long_to_int FALLBACK + # op op_long_to_float FALLBACK + # op op_long_to_double FALLBACK + # op op_float_to_int FALLBACK + # op op_float_to_long FALLBACK + # op op_float_to_double FALLBACK + # op op_double_to_int FALLBACK + # op op_double_to_long FALLBACK + # op op_double_to_float FALLBACK + # op op_int_to_byte FALLBACK + # op op_int_to_char FALLBACK + # op op_int_to_short FALLBACK + # op op_add_int FALLBACK + # op op_sub_int FALLBACK + # op op_mul_int FALLBACK + # op op_div_int FALLBACK + # op op_rem_int FALLBACK + # op op_and_int FALLBACK + # op op_or_int FALLBACK + # op op_xor_int FALLBACK + # op op_shl_int FALLBACK + # op op_shr_int FALLBACK + # op op_ushr_int FALLBACK + # op op_add_long FALLBACK + # op op_sub_long FALLBACK + # op op_mul_long FALLBACK + # op op_div_long FALLBACK + # op op_rem_long FALLBACK + # op op_and_long FALLBACK + # op op_or_long FALLBACK + # op op_xor_long FALLBACK + # op op_shl_long FALLBACK + # op op_shr_long FALLBACK + # op op_ushr_long FALLBACK + # op op_add_float FALLBACK + # op op_sub_float FALLBACK + # op op_mul_float FALLBACK + # op op_div_float FALLBACK + # op op_rem_float FALLBACK + # op op_add_double FALLBACK + # op op_sub_double FALLBACK + # op op_mul_double FALLBACK + # op op_div_double FALLBACK + # op op_rem_double FALLBACK + # op op_add_int_2addr FALLBACK + # op op_sub_int_2addr FALLBACK + # op op_mul_int_2addr FALLBACK + # op op_div_int_2addr FALLBACK + # op op_rem_int_2addr FALLBACK + # op op_and_int_2addr FALLBACK + # op op_or_int_2addr FALLBACK + # op op_xor_int_2addr FALLBACK + # op op_shl_int_2addr FALLBACK + # op op_shr_int_2addr FALLBACK + # op op_ushr_int_2addr FALLBACK + # op op_add_long_2addr FALLBACK + # op op_sub_long_2addr FALLBACK + # op op_mul_long_2addr FALLBACK + # op op_div_long_2addr FALLBACK + # op op_rem_long_2addr FALLBACK + # op op_and_long_2addr FALLBACK + # op op_or_long_2addr FALLBACK + # op op_xor_long_2addr FALLBACK + # op op_shl_long_2addr FALLBACK + # op op_shr_long_2addr FALLBACK + # op op_ushr_long_2addr FALLBACK + # op op_add_float_2addr FALLBACK + # op op_sub_float_2addr FALLBACK + # op op_mul_float_2addr FALLBACK + # op op_div_float_2addr FALLBACK + # op op_rem_float_2addr FALLBACK + # op op_add_double_2addr FALLBACK + # op op_sub_double_2addr FALLBACK + # op op_mul_double_2addr FALLBACK + # op op_div_double_2addr FALLBACK + # op op_rem_double_2addr FALLBACK + # op op_add_int_lit16 FALLBACK + # op op_rsub_int FALLBACK + # op op_mul_int_lit16 FALLBACK + # op op_div_int_lit16 FALLBACK + # op op_rem_int_lit16 FALLBACK + # op op_and_int_lit16 FALLBACK + # op op_or_int_lit16 FALLBACK + # op op_xor_int_lit16 FALLBACK + # op op_add_int_lit8 FALLBACK + # op op_rsub_int_lit8 FALLBACK + # op op_mul_int_lit8 FALLBACK + # op op_div_int_lit8 FALLBACK + # op op_rem_int_lit8 FALLBACK + # op op_and_int_lit8 FALLBACK + # op op_or_int_lit8 FALLBACK + # op op_xor_int_lit8 FALLBACK + # op op_shl_int_lit8 FALLBACK + # op op_shr_int_lit8 FALLBACK + # op op_ushr_int_lit8 FALLBACK + # op op_iget_quick FALLBACK + # op op_iget_wide_quick FALLBACK + # op op_iget_object_quick FALLBACK + # op op_iput_quick FALLBACK + # op op_iput_wide_quick FALLBACK + # op op_iput_object_quick FALLBACK + # op op_invoke_virtual_quick FALLBACK + # op op_invoke_virtual_range_quick FALLBACK + # op op_iput_boolean_quick FALLBACK + # op op_iput_byte_quick FALLBACK + # op op_iput_char_quick FALLBACK + # op op_iput_short_quick FALLBACK + # op op_iget_boolean_quick FALLBACK + # op op_iget_byte_quick FALLBACK + # op op_iget_char_quick FALLBACK + # op op_iget_short_quick FALLBACK + op op_invoke_lambda FALLBACK + # op op_unused_f4 FALLBACK + op op_capture_variable FALLBACK + op op_create_lambda FALLBACK + op op_liberate_variable FALLBACK + op op_box_lambda FALLBACK + op op_unbox_lambda FALLBACK + # op op_unused_fa FALLBACK + # op op_unused_fb FALLBACK + # op op_unused_fc FALLBACK + # op op_unused_fd FALLBACK + # op op_unused_fe FALLBACK + # op op_unused_ff FALLBACK op-end # common subroutines for asm diff --git a/runtime/interpreter/mterp/mips/alt_stub.S b/runtime/interpreter/mterp/mips/alt_stub.S new file mode 100644 index 0000000000..45980610b8 --- /dev/null +++ b/runtime/interpreter/mterp/mips/alt_stub.S @@ -0,0 +1,13 @@ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (${opnum} * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) diff --git a/runtime/interpreter/mterp/mips/bincmp.S b/runtime/interpreter/mterp/mips/bincmp.S new file mode 100644 index 0000000000..70057f6792 --- /dev/null +++ b/runtime/interpreter/mterp/mips/bincmp.S @@ -0,0 +1,37 @@ + /* + * Generic two-operand compare-and-branch operation. Provide a "revcmp" + * fragment that specifies the *reverse* comparison to perform, e.g. + * for "if-le" you would use "gt". + * + * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le + */ + /* if-cmp vA, vB, +CCCC */ + GET_OPA4(a0) # a0 <- A+ + GET_OPB(a1) # a1 <- B + GET_VREG(a3, a1) # a3 <- vB + GET_VREG(a2, a0) # a2 <- vA + b${revcmp} a2, a3, 1f # branch to 1 if comparison failed + FETCH_S(rINST, 1) # rINST<- branch offset, in code units + b 2f +1: + li rINST, 2 # rINST- BYTE branch dist for not-taken +2: +#if MTERP_PROFILE_BRANCHES + EXPORT_PC() + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + JAL(MterpProfileBranch) # (self, shadow_frame, offset) + bnez v0, MterpOnStackReplacement # Note: offset must be in rINST +#endif + addu a2, rINST, rINST # convert to bytes + FETCH_ADVANCE_INST_RB(a2) # update rPC, load rINST + bgez a2, .L_${opcode}_finish + lw ra, THREAD_FLAGS_OFFSET(rSELF) + b MterpCheckSuspendAndContinue + +%break + +.L_${opcode}_finish: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/binop.S b/runtime/interpreter/mterp/mips/binop.S new file mode 100644 index 0000000000..ce09da453a --- /dev/null +++ b/runtime/interpreter/mterp/mips/binop.S @@ -0,0 +1,33 @@ +%default {"preinstr":"", "result":"a0", "chkzero":"0"} + /* + * Generic 32-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the ARM math lib + * handles it correctly. + * + * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int, + * xor-int, shl-int, shr-int, ushr-int + */ + /* binop vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # rOBJ <- AA + srl a3, a0, 8 # a3 <- CC + and a2, a0, 255 # a2 <- BB + GET_VREG(a1, a3) # a1 <- vCC + GET_VREG(a0, a2) # a0 <- vBB + .if $chkzero + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + $preinstr # optional op + $instr # $result <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO($result, rOBJ, t0) # vAA <- $result + /* 11-14 instructions */ diff --git a/runtime/interpreter/mterp/mips/binop2addr.S b/runtime/interpreter/mterp/mips/binop2addr.S new file mode 100644 index 0000000000..548cbcb088 --- /dev/null +++ b/runtime/interpreter/mterp/mips/binop2addr.S @@ -0,0 +1,29 @@ +%default {"preinstr":"", "result":"a0", "chkzero":"0"} + /* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, + * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, + * shl-int/2addr, shr-int/2addr, ushr-int/2addr + */ + /* binop/2addr vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a3) # a3 <- B + GET_VREG(a0, rOBJ) # a0 <- vA + GET_VREG(a1, a3) # a1 <- vB + .if $chkzero + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + + $preinstr # optional op + $instr # $result <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO($result, rOBJ, t0) # vAA <- $result + /* 10-13 instructions */ diff --git a/runtime/interpreter/mterp/mips/binopLit16.S b/runtime/interpreter/mterp/mips/binopLit16.S new file mode 100644 index 0000000000..fc0c9ff630 --- /dev/null +++ b/runtime/interpreter/mterp/mips/binopLit16.S @@ -0,0 +1,30 @@ +%default {"preinstr":"", "result":"a0", "chkzero":"0"} + /* + * Generic 32-bit "lit16" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16, + * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16 + */ + # binop/lit16 vA, vB, /* +CCCC */ + FETCH_S(a1, 1) # a1 <- ssssCCCC (sign-extended) + GET_OPB(a2) # a2 <- B + GET_OPA(rOBJ) # rOBJ <- A+ + GET_VREG(a0, a2) # a0 <- vB + and rOBJ, rOBJ, 15 + .if $chkzero + # cmp a1, 0; is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + + $preinstr # optional op + $instr # $result <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO($result, rOBJ, t0) # vAA <- $result + /* 10-13 instructions */ diff --git a/runtime/interpreter/mterp/mips/binopLit8.S b/runtime/interpreter/mterp/mips/binopLit8.S new file mode 100644 index 0000000000..a591408ef5 --- /dev/null +++ b/runtime/interpreter/mterp/mips/binopLit8.S @@ -0,0 +1,31 @@ +%default {"preinstr":"", "result":"a0", "chkzero":"0"} + /* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8, + * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + # binop/lit8 vAA, vBB, /* +CC */ + FETCH_S(a3, 1) # a3 <- ssssCCBB (sign-extended for CC) + GET_OPA(rOBJ) # rOBJ <- AA + and a2, a3, 255 # a2 <- BB + GET_VREG(a0, a2) # a0 <- vBB + sra a1, a3, 8 # a1 <- ssssssCC (sign extended) + .if $chkzero + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + + $preinstr # optional op + $instr # $result <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO($result, rOBJ, t0) # vAA <- $result + /* 10-12 instructions */ diff --git a/runtime/interpreter/mterp/mips/binopWide.S b/runtime/interpreter/mterp/mips/binopWide.S new file mode 100644 index 0000000000..608525bccd --- /dev/null +++ b/runtime/interpreter/mterp/mips/binopWide.S @@ -0,0 +1,35 @@ +%default {"preinstr":"", "result0":"a0", "result1":"a1", "chkzero":"0", "arg0":"a0", "arg1":"a1", "arg2":"a2", "arg3":"a3"} + /* + * Generic 64-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0-a1 op a2-a3". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * for: add-long, sub-long, div-long, rem-long, and-long, or-long, + * xor-long + * + * IMPORTANT: you may specify "chkzero" or "preinstr" but not both. + */ + /* binop vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # rOBJ <- AA + and a2, a0, 255 # a2 <- BB + srl a3, a0, 8 # a3 <- CC + EAS2(a2, rFP, a2) # a2 <- &fp[BB] + EAS2(t1, rFP, a3) # a3 <- &fp[CC] + LOAD64($arg0, $arg1, a2) # a0/a1 <- vBB/vBB+1 + LOAD64($arg2, $arg3, t1) # a2/a3 <- vCC/vCC+1 + .if $chkzero + or t0, $arg2, $arg3 # second arg (a2-a3) is zero? + beqz t0, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + + $preinstr # optional op + $instr # result <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG64_GOTO($result0, $result1, rOBJ, t0) # vAA/vAA+1 <- $result0/$result1 + /* 14-17 instructions */ diff --git a/runtime/interpreter/mterp/mips/binopWide2addr.S b/runtime/interpreter/mterp/mips/binopWide2addr.S new file mode 100644 index 0000000000..cc92149039 --- /dev/null +++ b/runtime/interpreter/mterp/mips/binopWide2addr.S @@ -0,0 +1,33 @@ +%default {"preinstr":"", "result0":"a0", "result1":"a1", "chkzero":"0", "arg0":"a0", "arg1":"a1", "arg2":"a2", "arg3":"a3"} + /* + * Generic 64-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0-a1 op a2-a3". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr, + * and-long/2addr, or-long/2addr, xor-long/2addr + * rem-double/2addr + */ + /* binop/2addr vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a1) # a1 <- B + EAS2(a1, rFP, a1) # a1 <- &fp[B] + EAS2(t0, rFP, rOBJ) # t0 <- &fp[A] + LOAD64($arg2, $arg3, a1) # a2/a3 <- vBB/vBB+1 + LOAD64($arg0, $arg1, t0) # a0/a1 <- vAA/vAA+1 + .if $chkzero + or t0, $arg2, $arg3 # second arg (a2-a3) is zero? + beqz t0, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + + $preinstr # optional op + $instr # result <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG64($result0, $result1, rOBJ) # vAA/vAA+1 <- $result0/$result1 + GOTO_OPCODE(t0) # jump to next instruction + /* 12-15 instructions */ diff --git a/runtime/interpreter/mterp/mips/entry.S b/runtime/interpreter/mterp/mips/entry.S new file mode 100644 index 0000000000..cef08feaa0 --- /dev/null +++ b/runtime/interpreter/mterp/mips/entry.S @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * Interpreter entry point. + */ + + .text + .align 2 + .global ExecuteMterpImpl + .ent ExecuteMterpImpl + .frame sp, STACK_SIZE, ra +/* + * On entry: + * a0 Thread* self + * a1 code_item + * a2 ShadowFrame + * a3 JValue* result_register + * + */ + +ExecuteMterpImpl: + .set noreorder + .cpload t9 + .set reorder +/* Save to the stack. Frame size = STACK_SIZE */ + STACK_STORE_FULL() +/* This directive will make sure all subsequent jal restore gp at a known offset */ + .cprestore STACK_OFFSET_GP + + /* Remember the return register */ + sw a3, SHADOWFRAME_RESULT_REGISTER_OFFSET(a2) + + /* Remember the code_item */ + sw a1, SHADOWFRAME_CODE_ITEM_OFFSET(a2) + + /* set up "named" registers */ + move rSELF, a0 + lw a0, SHADOWFRAME_NUMBER_OF_VREGS_OFFSET(a2) + addu rFP, a2, SHADOWFRAME_VREGS_OFFSET # point to insns[] (i.e. - the dalivk byte code). + EAS2(rREFS, rFP, a0) # point to reference array in shadow frame + lw a0, SHADOWFRAME_DEX_PC_OFFSET(a2) # Get starting dex_pc + addu rPC, a1, CODEITEM_INSNS_OFFSET # Point to base of insns[] + EAS1(rPC, rPC, a0) # Create direct pointer to 1st dex opcode + + EXPORT_PC() + + /* Starting ibase */ + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) + + /* start executing the instruction at rPC */ + FETCH_INST() # load rINST from rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + /* NOTE: no fallthrough */ diff --git a/runtime/interpreter/mterp/mips/fallback.S b/runtime/interpreter/mterp/mips/fallback.S new file mode 100644 index 0000000000..82cbc63480 --- /dev/null +++ b/runtime/interpreter/mterp/mips/fallback.S @@ -0,0 +1,2 @@ +/* Transfer stub to alternate interpreter */ + b MterpFallback diff --git a/runtime/interpreter/mterp/mips/fbinop.S b/runtime/interpreter/mterp/mips/fbinop.S new file mode 100644 index 0000000000..d0d39aeffe --- /dev/null +++ b/runtime/interpreter/mterp/mips/fbinop.S @@ -0,0 +1,19 @@ + /* + * Generic 32-bit binary float operation. + * + * For: add-fp, sub-fp, mul-fp, div-fp, rem-fp + */ + + /* binop vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # s5 <- AA + srl a3, a0, 8 # a3 <- CC + and a2, a0, 255 # a2 <- BB + GET_VREG_F(fa1, a3) # a1 <- vCC + GET_VREG_F(fa0, a2) # a0 <- vBB + + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + $instr # f0 = result + SET_VREG_F(fv0, rOBJ) # vAA <- fv0 + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/fbinop2addr.S b/runtime/interpreter/mterp/mips/fbinop2addr.S new file mode 100644 index 0000000000..ccb67b1b90 --- /dev/null +++ b/runtime/interpreter/mterp/mips/fbinop2addr.S @@ -0,0 +1,19 @@ + /* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. + * + * For: add-float/2addr, sub-float/2addr, mul-float/2addr, + * div-float/2addr, rem-float/2addr + */ + /* binop/2addr vA, vB */ + GET_OPA4(rOBJ) # t1 <- A+ + GET_OPB(a3) # a3 <- B + GET_VREG_F(fa0, rOBJ) + GET_VREG_F(fa1, a3) + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + + $instr + SET_VREG_F(fv0, rOBJ) # vAA <- result + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/fbinopWide.S b/runtime/interpreter/mterp/mips/fbinopWide.S new file mode 100644 index 0000000000..3be9325f7c --- /dev/null +++ b/runtime/interpreter/mterp/mips/fbinopWide.S @@ -0,0 +1,28 @@ + /* + * Generic 64-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0-a1 op a2-a3". + * This could be an MIPS instruction or a function call. + * + * for: add-double, sub-double, mul-double, div-double, + * rem-double + * + */ + /* binop vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # s5 <- AA + and a2, a0, 255 # a2 <- BB + srl a3, a0, 8 # a3 <- CC + EAS2(a2, rFP, a2) # a2 <- &fp[BB] + EAS2(t1, rFP, a3) # a3 <- &fp[CC] + LOAD64_F(fa0, fa0f, a2) + LOAD64_F(fa1, fa1f, t1) + + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + $instr + SET_VREG64_F(fv0, fv0f, rOBJ) + b .L${opcode}_finish +%break + +.L${opcode}_finish: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/fbinopWide2addr.S b/runtime/interpreter/mterp/mips/fbinopWide2addr.S new file mode 100644 index 0000000000..8541f119dd --- /dev/null +++ b/runtime/interpreter/mterp/mips/fbinopWide2addr.S @@ -0,0 +1,21 @@ + /* + * Generic 64-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0-a1 op a2-a3". + * This could be an MIPS instruction or a function call. + * + * For: add-double/2addr, sub-double/2addr, mul-double/2addr, + * div-double/2addr, rem-double/2addr + */ + /* binop/2addr vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a1) # a1 <- B + EAS2(a1, rFP, a1) # a1 <- &fp[B] + EAS2(t0, rFP, rOBJ) # t0 <- &fp[A] + LOAD64_F(fa0, fa0f, t0) + LOAD64_F(fa1, fa1f, a1) + + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + $instr + SET_VREG64_F(fv0, fv0f, rOBJ) + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/footer.S b/runtime/interpreter/mterp/mips/footer.S new file mode 100644 index 0000000000..083dc15205 --- /dev/null +++ b/runtime/interpreter/mterp/mips/footer.S @@ -0,0 +1,179 @@ +/* + * =========================================================================== + * Common subroutines and data + * =========================================================================== + */ + + .text + .align 2 + +/* + * We've detected a condition that will result in an exception, but the exception + * has not yet been thrown. Just bail out to the reference interpreter to deal with it. + * TUNING: for consistency, we may want to just go ahead and handle these here. + */ +common_errDivideByZero: + EXPORT_PC() +#if MTERP_LOGGING + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + JAL(MterpLogDivideByZeroException) +#endif + b MterpCommonFallback + +common_errArrayIndex: + EXPORT_PC() +#if MTERP_LOGGING + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + JAL(MterpLogArrayIndexException) +#endif + b MterpCommonFallback + +common_errNegativeArraySize: + EXPORT_PC() +#if MTERP_LOGGING + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + JAL(MterpLogNegativeArraySizeException) +#endif + b MterpCommonFallback + +common_errNoSuchMethod: + EXPORT_PC() +#if MTERP_LOGGING + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + JAL(MterpLogNoSuchMethodException) +#endif + b MterpCommonFallback + +common_errNullObject: + EXPORT_PC() +#if MTERP_LOGGING + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + JAL(MterpLogNullObjectException) +#endif + b MterpCommonFallback + +common_exceptionThrown: + EXPORT_PC() +#if MTERP_LOGGING + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + JAL(MterpLogExceptionThrownException) +#endif + b MterpCommonFallback + +MterpSuspendFallback: + EXPORT_PC() +#if MTERP_LOGGING + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + lw a2, THREAD_FLAGS_OFFSET(rSELF) + JAL(MterpLogSuspendFallback) +#endif + b MterpCommonFallback + +/* + * If we're here, something is out of the ordinary. If there is a pending + * exception, handle it. Otherwise, roll back and retry with the reference + * interpreter. + */ +MterpPossibleException: + lw a0, THREAD_EXCEPTION_OFFSET(rSELF) + beqz a0, MterpFallback # If exception, fall back to reference interpreter. + /* intentional fallthrough - handle pending exception. */ +/* + * On return from a runtime helper routine, we've found a pending exception. + * Can we handle it here - or need to bail out to caller? + * + */ +MterpException: + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + JAL(MterpHandleException) # (self, shadow_frame) + beqz v0, MterpExceptionReturn # no local catch, back to caller. + lw a0, OFF_FP_CODE_ITEM(rFP) + lw a1, OFF_FP_DEX_PC(rFP) + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) + addu rPC, a0, CODEITEM_INSNS_OFFSET + sll a1, a1, 1 + addu rPC, rPC, a1 # generate new dex_pc_ptr + /* Do we need to switch interpreters? */ + JAL(MterpShouldSwitchInterpreters) + bnez v0, MterpFallback + /* resume execution at catch block */ + EXPORT_PC() + FETCH_INST() + GET_INST_OPCODE(t0) + GOTO_OPCODE(t0) + /* NOTE: no fallthrough */ + +/* + * Check for suspend check request. Assumes rINST already loaded, rPC advanced and + * still needs to get the opcode and branch to it, and flags are in lr. + */ +MterpCheckSuspendAndContinue: + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh rIBASE + and ra, (THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + bnez ra, 1f + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction +1: + EXPORT_PC() + move a0, rSELF + JAL(MterpSuspendCheck) # (self) + bnez v0, MterpFallback + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* + * On-stack replacement has happened, and now we've returned from the compiled method. + */ +MterpOnStackReplacement: +#if MTERP_LOGGING + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + JAL(MterpLogOSR) +#endif + li v0, 1 # Signal normal return + b MterpDone + +/* + * Bail out to reference interpreter. + */ +MterpFallback: + EXPORT_PC() +#if MTERP_LOGGING + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + JAL(MterpLogFallback) +#endif +MterpCommonFallback: + move v0, zero # signal retry with reference interpreter. + b MterpDone +/* + * We pushed some registers on the stack in ExecuteMterpImpl, then saved + * SP and LR. Here we restore SP, restore the registers, and then restore + * LR to PC. + * + * On entry: + * uint32_t* rFP (should still be live, pointer to base of vregs) + */ +MterpExceptionReturn: + li v0, 1 # signal return to caller. + b MterpDone +MterpReturn: + lw a2, OFF_FP_RESULT_REGISTER(rFP) + sw v0, 0(a2) + sw v1, 4(a2) + li v0, 1 # signal return to caller. +MterpDone: +/* Restore from the stack and return. Frame size = STACK_SIZE */ + STACK_LOAD_FULL() + jalr zero, ra + + .end ExecuteMterpImpl diff --git a/runtime/interpreter/mterp/mips/funop.S b/runtime/interpreter/mterp/mips/funop.S new file mode 100644 index 0000000000..bfb93469f6 --- /dev/null +++ b/runtime/interpreter/mterp/mips/funop.S @@ -0,0 +1,18 @@ + /* + * Generic 32-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "result = op a0". + * This could be a MIPS instruction or a function call. + * + * for: int-to-float, float-to-int + */ + /* unop vA, vB */ + GET_OPB(a3) # a3 <- B + GET_OPA4(rOBJ) # t0 <- A+ + GET_VREG_F(fa0, a3) + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + $instr + +.L${opcode}_set_vreg_f: + SET_VREG_F(fv0, rOBJ) + GET_INST_OPCODE(t1) # extract opcode from rINST + GOTO_OPCODE(t1) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/funopWide.S b/runtime/interpreter/mterp/mips/funopWide.S new file mode 100644 index 0000000000..3d4cf2216e --- /dev/null +++ b/runtime/interpreter/mterp/mips/funopWide.S @@ -0,0 +1,22 @@ +%default {"preinstr":"", "ld_arg":"LOAD64_F(fa0, fa0f, a3)", "st_result":"SET_VREG64_F(fv0, fv0f, rOBJ)"} + /* + * Generic 64-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "result = op a0/a1". + * This could be a MIPS instruction or a function call. + * + * long-to-double, double-to-long + */ + /* unop vA, vB */ + GET_OPA4(rOBJ) # t1 <- A+ + GET_OPB(a3) # a3 <- B + EAS2(a3, rFP, a3) # a3 <- &fp[B] + $ld_arg + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + $preinstr # optional op + $instr # a0/a1 <- op, a2-a3 changed + +.L${opcode}_set_vreg: + $st_result # vAA <- a0/a1 + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + /* 12-13 instructions */ diff --git a/runtime/interpreter/mterp/mips/funopWider.S b/runtime/interpreter/mterp/mips/funopWider.S new file mode 100644 index 0000000000..efb85f3ca9 --- /dev/null +++ b/runtime/interpreter/mterp/mips/funopWider.S @@ -0,0 +1,19 @@ +%default {"st_result":"SET_VREG64_F(fv0, fv0f, rOBJ)"} + /* + * Generic 32bit-to-64bit unary operation. Provide an "instr" line + * that specifies an instruction that performs "result = op a0", where + * "result" is a 64-bit quantity in a0/a1. + * + * For: int-to-double, float-to-long, float-to-double + */ + /* unop vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a3) # a3 <- B + GET_VREG_F(fa0, a3) + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + $instr + +.L${opcode}_set_vreg: + $st_result # vA/vA+1 <- a0/a1 + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/header.S b/runtime/interpreter/mterp/mips/header.S new file mode 100644 index 0000000000..37ab21de5c --- /dev/null +++ b/runtime/interpreter/mterp/mips/header.S @@ -0,0 +1,484 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + Art assembly interpreter notes: + + First validate assembly code by implementing ExecuteXXXImpl() style body (doesn't + handle invoke, allows higher-level code to create frame & shadow frame. + + Once that's working, support direct entry code & eliminate shadow frame (and + excess locals allocation. + + Some (hopefully) temporary ugliness. We'll treat rFP as pointing to the + base of the vreg array within the shadow frame. Access the other fields, + dex_pc_, method_ and number_of_vregs_ via negative offsets. For now, we'll continue + the shadow frame mechanism of double-storing object references - via rFP & + number_of_vregs_. + + */ + +#include "asm_support.h" + +#if (__mips==32) && (__mips_isa_rev>=2) +#define MIPS32REVGE2 /* mips32r2 and greater */ +#if (__mips==32) && (__mips_isa_rev>=5) +#define FPU64 /* 64 bit FPU */ +#if (__mips==32) && (__mips_isa_rev>=6) +#define MIPS32REVGE6 /* mips32r6 and greater */ +#endif +#endif +#endif + +/* MIPS definitions and declarations + + reg nick purpose + s0 rPC interpreted program counter, used for fetching instructions + s1 rFP interpreted frame pointer, used for accessing locals and args + s2 rSELF self (Thread) pointer + s3 rIBASE interpreted instruction base pointer, used for computed goto + s4 rINST first 16-bit code unit of current instruction + s6 rREFS base of object references in shadow frame (ideally, we'll get rid of this later). +*/ + +/* single-purpose registers, given names for clarity */ +#define rPC s0 +#define rFP s1 +#define rSELF s2 +#define rIBASE s3 +#define rINST s4 +#define rOBJ s5 +#define rREFS s6 +#define rTEMP s7 + +#define rARG0 a0 +#define rARG1 a1 +#define rARG2 a2 +#define rARG3 a3 +#define rRESULT0 v0 +#define rRESULT1 v1 + +/* GP register definitions */ +#define zero $$0 /* always zero */ +#define AT $$at /* assembler temp */ +#define v0 $$2 /* return value */ +#define v1 $$3 +#define a0 $$4 /* argument registers */ +#define a1 $$5 +#define a2 $$6 +#define a3 $$7 +#define t0 $$8 /* temp registers (not saved across subroutine calls) */ +#define t1 $$9 +#define t2 $$10 +#define t3 $$11 +#define t4 $$12 +#define t5 $$13 +#define t6 $$14 +#define t7 $$15 +#define ta0 $$12 /* alias */ +#define ta1 $$13 +#define ta2 $$14 +#define ta3 $$15 +#define s0 $$16 /* saved across subroutine calls (callee saved) */ +#define s1 $$17 +#define s2 $$18 +#define s3 $$19 +#define s4 $$20 +#define s5 $$21 +#define s6 $$22 +#define s7 $$23 +#define t8 $$24 /* two more temp registers */ +#define t9 $$25 +#define k0 $$26 /* kernel temporary */ +#define k1 $$27 +#define gp $$28 /* global pointer */ +#define sp $$29 /* stack pointer */ +#define s8 $$30 /* one more callee saved */ +#define ra $$31 /* return address */ + +/* FP register definitions */ +#define fv0 $$f0 +#define fv0f $$f1 +#define fv1 $$f2 +#define fv1f $$f3 +#define fa0 $$f12 +#define fa0f $$f13 +#define fa1 $$f14 +#define fa1f $$f15 +#define ft0 $$f4 +#define ft0f $$f5 +#define ft1 $$f6 +#define ft1f $$f7 +#define ft2 $$f8 +#define ft2f $$f9 +#define ft3 $$f10 +#define ft3f $$f11 +#define ft4 $$f16 +#define ft4f $$f17 +#define ft5 $$f18 +#define ft5f $$f19 +#define fs0 $$f20 +#define fs0f $$f21 +#define fs1 $$f22 +#define fs1f $$f23 +#define fs2 $$f24 +#define fs2f $$f25 +#define fs3 $$f26 +#define fs3f $$f27 +#define fs4 $$f28 +#define fs4f $$f29 +#define fs5 $$f30 +#define fs5f $$f31 + +#ifndef MIPS32REVGE6 +#define fcc0 $$fcc0 +#define fcc1 $$fcc1 +#endif + +/* + * Instead of holding a pointer to the shadow frame, we keep rFP at the base of the vregs. So, + * to access other shadow frame fields, we need to use a backwards offset. Define those here. + */ +#define OFF_FP(a) (a - SHADOWFRAME_VREGS_OFFSET) +#define OFF_FP_NUMBER_OF_VREGS OFF_FP(SHADOWFRAME_NUMBER_OF_VREGS_OFFSET) +#define OFF_FP_DEX_PC OFF_FP(SHADOWFRAME_DEX_PC_OFFSET) +#define OFF_FP_LINK OFF_FP(SHADOWFRAME_LINK_OFFSET) +#define OFF_FP_METHOD OFF_FP(SHADOWFRAME_METHOD_OFFSET) +#define OFF_FP_RESULT_REGISTER OFF_FP(SHADOWFRAME_RESULT_REGISTER_OFFSET) +#define OFF_FP_DEX_PC_PTR OFF_FP(SHADOWFRAME_DEX_PC_PTR_OFFSET) +#define OFF_FP_CODE_ITEM OFF_FP(SHADOWFRAME_CODE_ITEM_OFFSET) +#define OFF_FP_SHADOWFRAME (-SHADOWFRAME_VREGS_OFFSET) + +#define MTERP_PROFILE_BRANCHES 1 +#define MTERP_LOGGING 0 + +/* + * "export" the PC to dex_pc field in the shadow frame, f/b/o future exception objects. Must + * be done *before* something throws. + * + * It's okay to do this more than once. + * + * NOTE: the fast interpreter keeps track of dex pc as a direct pointer to the mapped + * dex byte codes. However, the rest of the runtime expects dex pc to be an instruction + * offset into the code_items_[] array. For effiency, we will "export" the + * current dex pc as a direct pointer using the EXPORT_PC macro, and rely on GetDexPC + * to convert to a dex pc when needed. + */ +#define EXPORT_PC() \ + sw rPC, OFF_FP_DEX_PC_PTR(rFP) + +#define EXPORT_DEX_PC(tmp) \ + lw tmp, OFF_FP_CODE_ITEM(rFP) \ + sw rPC, OFF_FP_DEX_PC_PTR(rFP) \ + addu tmp, CODEITEM_INSNS_OFFSET \ + subu tmp, rPC, tmp \ + sra tmp, tmp, 1 \ + sw tmp, OFF_FP_DEX_PC(rFP) + +/* + * Fetch the next instruction from rPC into rINST. Does not advance rPC. + */ +#define FETCH_INST() lhu rINST, (rPC) + +/* + * Fetch the next instruction from the specified offset. Advances rPC + * to point to the next instruction. "_count" is in 16-bit code units. + * + * This must come AFTER anything that can throw an exception, or the + * exception catch may miss. (This also implies that it must come after + * EXPORT_PC().) + */ +#define FETCH_ADVANCE_INST(_count) lhu rINST, ((_count)*2)(rPC); \ + addu rPC, rPC, ((_count) * 2) + +/* + * The operation performed here is similar to FETCH_ADVANCE_INST, except the + * src and dest registers are parameterized (not hard-wired to rPC and rINST). + */ +#define PREFETCH_ADVANCE_INST(_dreg, _sreg, _count) \ + lhu _dreg, ((_count)*2)(_sreg) ; \ + addu _sreg, _sreg, (_count)*2 + +/* + * Similar to FETCH_ADVANCE_INST, but does not update rPC. Used to load + * rINST ahead of possible exception point. Be sure to manually advance rPC + * later. + */ +#define PREFETCH_INST(_count) lhu rINST, ((_count)*2)(rPC) + +/* Advance rPC by some number of code units. */ +#define ADVANCE(_count) addu rPC, rPC, ((_count) * 2) + +/* + * Fetch the next instruction from an offset specified by rd. Updates + * rPC to point to the next instruction. "rd" must specify the distance + * in bytes, *not* 16-bit code units, and may be a signed value. + */ +#define FETCH_ADVANCE_INST_RB(rd) addu rPC, rPC, rd; \ + lhu rINST, (rPC) + +/* + * Fetch a half-word code unit from an offset past the current PC. The + * "_count" value is in 16-bit code units. Does not advance rPC. + * + * The "_S" variant works the same but treats the value as signed. + */ +#define FETCH(rd, _count) lhu rd, ((_count) * 2)(rPC) +#define FETCH_S(rd, _count) lh rd, ((_count) * 2)(rPC) + +/* + * Fetch one byte from an offset past the current PC. Pass in the same + * "_count" as you would for FETCH, and an additional 0/1 indicating which + * byte of the halfword you want (lo/hi). + */ +#define FETCH_B(rd, _count, _byte) lbu rd, ((_count) * 2 + _byte)(rPC) + +/* + * Put the instruction's opcode field into the specified register. + */ +#define GET_INST_OPCODE(rd) and rd, rINST, 0xFF + +/* + * Put the prefetched instruction's opcode field into the specified register. + */ +#define GET_PREFETCHED_OPCODE(dreg, sreg) andi dreg, sreg, 255 + +/* + * Begin executing the opcode in rd. + */ +#define GOTO_OPCODE(rd) sll rd, rd, ${handler_size_bits}; \ + addu rd, rIBASE, rd; \ + jalr zero, rd + +#define GOTO_OPCODE_BASE(_base, rd) sll rd, rd, ${handler_size_bits}; \ + addu rd, _base, rd; \ + jalr zero, rd + +/* + * Get/set the 32-bit value from a Dalvik register. + */ +#define GET_VREG(rd, rix) LOAD_eas2(rd, rFP, rix) + +#define GET_VREG_F(rd, rix) EAS2(AT, rFP, rix); \ + .set noat; l.s rd, (AT); .set at + +#define SET_VREG(rd, rix) .set noat; \ + sll AT, rix, 2; \ + addu t8, rFP, AT; \ + sw rd, 0(t8); \ + addu t8, rREFS, AT; \ + .set at; \ + sw zero, 0(t8) + +#define SET_VREG64(rlo, rhi, rix) .set noat; \ + sll AT, rix, 2; \ + addu t8, rFP, AT; \ + sw rlo, 0(t8); \ + sw rhi, 4(t8); \ + addu t8, rREFS, AT; \ + .set at; \ + sw zero, 0(t8); \ + sw zero, 4(t8) + +#ifdef FPU64 +#define SET_VREG64_F(rlo, rhi, rix) .set noat; \ + sll AT, rix, 2; \ + addu t8, rREFS, AT; \ + sw zero, 0(t8); \ + sw zero, 4(t8); \ + addu t8, rFP, AT; \ + mfhc1 AT, rlo; \ + sw AT, 4(t8); \ + .set at; \ + s.s rlo, 0(t8) +#else +#define SET_VREG64_F(rlo, rhi, rix) .set noat; \ + sll AT, rix, 2; \ + addu t8, rFP, AT; \ + s.s rlo, 0(t8); \ + s.s rhi, 4(t8); \ + addu t8, rREFS, AT; \ + .set at; \ + sw zero, 0(t8); \ + sw zero, 4(t8) +#endif + +#define SET_VREG_OBJECT(rd, rix) .set noat; \ + sll AT, rix, 2; \ + addu t8, rFP, AT; \ + sw rd, 0(t8); \ + addu t8, rREFS, AT; \ + .set at; \ + sw rd, 0(t8) + +/* Combination of the SET_VREG and GOTO_OPCODE functions to save 1 instruction */ +#define SET_VREG_GOTO(rd, rix, dst) .set noreorder; \ + sll dst, dst, ${handler_size_bits}; \ + addu dst, rIBASE, dst; \ + .set noat; \ + sll AT, rix, 2; \ + addu t8, rFP, AT; \ + sw rd, 0(t8); \ + addu t8, rREFS, AT; \ + .set at; \ + jalr zero, dst; \ + sw zero, 0(t8); \ + .set reorder + +/* Combination of the SET_VREG64 and GOTO_OPCODE functions to save 1 instruction */ +#define SET_VREG64_GOTO(rlo, rhi, rix, dst) .set noreorder; \ + sll dst, dst, ${handler_size_bits}; \ + addu dst, rIBASE, dst; \ + .set noat; \ + sll AT, rix, 2; \ + addu t8, rFP, AT; \ + sw rlo, 0(t8); \ + sw rhi, 4(t8); \ + addu t8, rREFS, AT; \ + .set at; \ + sw zero, 0(t8); \ + jalr zero, dst; \ + sw zero, 4(t8); \ + .set reorder + +#define SET_VREG_F(rd, rix) .set noat; \ + sll AT, rix, 2; \ + addu t8, rFP, AT; \ + s.s rd, 0(t8); \ + addu t8, rREFS, AT; \ + .set at; \ + sw zero, 0(t8) + +#define GET_OPA(rd) srl rd, rINST, 8 +#ifdef MIPS32REVGE2 +#define GET_OPA4(rd) ext rd, rINST, 8, 4 +#else +#define GET_OPA4(rd) GET_OPA(rd); and rd, 0xf +#endif +#define GET_OPB(rd) srl rd, rINST, 12 + +/* + * Form an Effective Address rd = rbase + roff<>n; + * Uses reg AT + */ +#define ESRN(rd, rbase, roff, rshift) .set noat; \ + srl AT, roff, rshift; \ + addu rd, rbase, AT; \ + .set at + +#define LOAD_eas2(rd, rbase, roff) EAS2(AT, rbase, roff); \ + .set noat; lw rd, 0(AT); .set at + +#define STORE_eas2(rd, rbase, roff) EAS2(AT, rbase, roff); \ + .set noat; sw rd, 0(AT); .set at + +#define LOAD_RB_OFF(rd, rbase, off) lw rd, off(rbase) +#define STORE_RB_OFF(rd, rbase, off) sw rd, off(rbase) + +#define STORE64_off(rlo, rhi, rbase, off) sw rlo, off(rbase); \ + sw rhi, (off+4)(rbase) +#define LOAD64_off(rlo, rhi, rbase, off) lw rlo, off(rbase); \ + lw rhi, (off+4)(rbase) + +#define STORE64(rlo, rhi, rbase) STORE64_off(rlo, rhi, rbase, 0) +#define LOAD64(rlo, rhi, rbase) LOAD64_off(rlo, rhi, rbase, 0) + +#ifdef FPU64 +#define STORE64_off_F(rlo, rhi, rbase, off) s.s rlo, off(rbase); \ + .set noat; \ + mfhc1 AT, rlo; \ + sw AT, (off+4)(rbase); \ + .set at +#define LOAD64_off_F(rlo, rhi, rbase, off) l.s rlo, off(rbase); \ + .set noat; \ + lw AT, (off+4)(rbase); \ + mthc1 AT, rlo; \ + .set at +#else +#define STORE64_off_F(rlo, rhi, rbase, off) s.s rlo, off(rbase); \ + s.s rhi, (off+4)(rbase) +#define LOAD64_off_F(rlo, rhi, rbase, off) l.s rlo, off(rbase); \ + l.s rhi, (off+4)(rbase) +#endif + +#define STORE64_F(rlo, rhi, rbase) STORE64_off_F(rlo, rhi, rbase, 0) +#define LOAD64_F(rlo, rhi, rbase) LOAD64_off_F(rlo, rhi, rbase, 0) + + +#define LOAD_base_offMirrorArray_length(rd, rbase) LOAD_RB_OFF(rd, rbase, MIRROR_ARRAY_LENGTH_OFFSET) + +#define STACK_STORE(rd, off) sw rd, off(sp) +#define STACK_LOAD(rd, off) lw rd, off(sp) +#define CREATE_STACK(n) subu sp, sp, n +#define DELETE_STACK(n) addu sp, sp, n + +#define LOAD_ADDR(dest, addr) la dest, addr +#define LOAD_IMM(dest, imm) li dest, imm +#define MOVE_REG(dest, src) move dest, src +#define STACK_SIZE 128 + +#define STACK_OFFSET_ARG04 16 +#define STACK_OFFSET_ARG05 20 +#define STACK_OFFSET_ARG06 24 +#define STACK_OFFSET_ARG07 28 +#define STACK_OFFSET_GP 84 + +#define JAL(n) jal n +#define BAL(n) bal n + +/* + * FP register usage restrictions: + * 1) We don't use the callee save FP registers so we don't have to save them. + * 2) We don't use the odd FP registers so we can share code with mips32r6. + */ +#define STACK_STORE_FULL() CREATE_STACK(STACK_SIZE); \ + STACK_STORE(ra, 124); \ + STACK_STORE(s8, 120); \ + STACK_STORE(s0, 116); \ + STACK_STORE(s1, 112); \ + STACK_STORE(s2, 108); \ + STACK_STORE(s3, 104); \ + STACK_STORE(s4, 100); \ + STACK_STORE(s5, 96); \ + STACK_STORE(s6, 92); \ + STACK_STORE(s7, 88); + +#define STACK_LOAD_FULL() STACK_LOAD(gp, STACK_OFFSET_GP); \ + STACK_LOAD(s7, 88); \ + STACK_LOAD(s6, 92); \ + STACK_LOAD(s5, 96); \ + STACK_LOAD(s4, 100); \ + STACK_LOAD(s3, 104); \ + STACK_LOAD(s2, 108); \ + STACK_LOAD(s1, 112); \ + STACK_LOAD(s0, 116); \ + STACK_LOAD(s8, 120); \ + STACK_LOAD(ra, 124); \ + DELETE_STACK(STACK_SIZE) diff --git a/runtime/interpreter/mterp/mips/invoke.S b/runtime/interpreter/mterp/mips/invoke.S new file mode 100644 index 0000000000..bcd3a57a71 --- /dev/null +++ b/runtime/interpreter/mterp/mips/invoke.S @@ -0,0 +1,19 @@ +%default { "helper":"UndefinedInvokeHandler" } + /* + * Generic invoke handler wrapper. + */ + # op vB, {vD, vE, vF, vG, vA}, class /* CCCC */ + # op {vCCCC..v(CCCC+AA-1)}, meth /* BBBB */ + .extern $helper + EXPORT_PC() + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rPC + move a3, rINST + JAL($helper) + beqz v0, MterpException + FETCH_ADVANCE_INST(3) + JAL(MterpShouldSwitchInterpreters) + bnez v0, MterpFallback + GET_INST_OPCODE(t0) + GOTO_OPCODE(t0) diff --git a/runtime/interpreter/mterp/mips/op_add_double.S b/runtime/interpreter/mterp/mips/op_add_double.S new file mode 100644 index 0000000000..12ef0cf3cd --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_add_double.S @@ -0,0 +1 @@ +%include "mips/fbinopWide.S" {"instr":"add.d fv0, fa0, fa1"} diff --git a/runtime/interpreter/mterp/mips/op_add_double_2addr.S b/runtime/interpreter/mterp/mips/op_add_double_2addr.S new file mode 100644 index 0000000000..c57add572c --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_add_double_2addr.S @@ -0,0 +1 @@ +%include "mips/fbinopWide2addr.S" {"instr":"add.d fv0, fa0, fa1"} diff --git a/runtime/interpreter/mterp/mips/op_add_float.S b/runtime/interpreter/mterp/mips/op_add_float.S new file mode 100644 index 0000000000..6a46cf0ab9 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_add_float.S @@ -0,0 +1 @@ +%include "mips/fbinop.S" {"instr":"add.s fv0, fa0, fa1"} diff --git a/runtime/interpreter/mterp/mips/op_add_float_2addr.S b/runtime/interpreter/mterp/mips/op_add_float_2addr.S new file mode 100644 index 0000000000..6ab5cc1730 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_add_float_2addr.S @@ -0,0 +1 @@ +%include "mips/fbinop2addr.S" {"instr":"add.s fv0, fa0, fa1"} diff --git a/runtime/interpreter/mterp/mips/op_add_int.S b/runtime/interpreter/mterp/mips/op_add_int.S new file mode 100644 index 0000000000..53a0cb128f --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_add_int.S @@ -0,0 +1 @@ +%include "mips/binop.S" {"instr":"addu a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips/op_add_int_2addr.S b/runtime/interpreter/mterp/mips/op_add_int_2addr.S new file mode 100644 index 0000000000..ddd92145c3 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_add_int_2addr.S @@ -0,0 +1 @@ +%include "mips/binop2addr.S" {"instr":"addu a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips/op_add_int_lit16.S b/runtime/interpreter/mterp/mips/op_add_int_lit16.S new file mode 100644 index 0000000000..05535c15dc --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_add_int_lit16.S @@ -0,0 +1 @@ +%include "mips/binopLit16.S" {"instr":"addu a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips/op_add_int_lit8.S b/runtime/interpreter/mterp/mips/op_add_int_lit8.S new file mode 100644 index 0000000000..fd021b31a9 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_add_int_lit8.S @@ -0,0 +1 @@ +%include "mips/binopLit8.S" {"instr":"addu a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips/op_add_long.S b/runtime/interpreter/mterp/mips/op_add_long.S new file mode 100644 index 0000000000..faacc6a3cc --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_add_long.S @@ -0,0 +1,9 @@ +/* + * The compiler generates the following sequence for + * [v1 v0] = [a1 a0] + [a3 a2]; + * addu v0,a2,a0 + * addu a1,a3,a1 + * sltu v1,v0,a2 + * addu v1,v1,a1 + */ +%include "mips/binopWide.S" { "result0":"v0", "result1":"v1", "preinstr":"addu v0, a2, a0", "instr":"addu a1, a3, a1; sltu v1, v0, a2; addu v1, v1, a1" } diff --git a/runtime/interpreter/mterp/mips/op_add_long_2addr.S b/runtime/interpreter/mterp/mips/op_add_long_2addr.S new file mode 100644 index 0000000000..bf827c10d4 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_add_long_2addr.S @@ -0,0 +1,4 @@ +/* + * See op_add_long.S for details + */ +%include "mips/binopWide2addr.S" { "result0":"v0", "result1":"v1", "preinstr":"addu v0, a2, a0", "instr":"addu a1, a3, a1; sltu v1, v0, a2; addu v1, v1, a1" } diff --git a/runtime/interpreter/mterp/mips/op_aget.S b/runtime/interpreter/mterp/mips/op_aget.S new file mode 100644 index 0000000000..8aa8992472 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_aget.S @@ -0,0 +1,32 @@ +%default { "load":"lw", "shift":"2", "data_offset":"MIRROR_INT_ARRAY_DATA_OFFSET" } + /* + * Array get, 32 bits or less. vAA <- vBB[vCC]. + * + * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17 + * instructions. We use a pair of FETCH_Bs instead. + * + * for: aget, aget-boolean, aget-byte, aget-char, aget-short + * + * NOTE: assumes data offset for arrays is the same for all non-wide types. + * If this changes, specialize. + */ + /* op vAA, vBB, vCC */ + FETCH_B(a2, 1, 0) # a2 <- BB + GET_OPA(rOBJ) # rOBJ <- AA + FETCH_B(a3, 1, 1) # a3 <- CC + GET_VREG(a0, a2) # a0 <- vBB (array object) + GET_VREG(a1, a3) # a1 <- vCC (requested index) + # null array object? + beqz a0, common_errNullObject # yes, bail + LOAD_base_offMirrorArray_length(a3, a0) # a3 <- arrayObj->length + .if $shift + EASN(a0, a0, a1, $shift) # a0 <- arrayObj + index*width + .else + addu a0, a0, a1 + .endif + # a1 >= a3; compare unsigned index + bgeu a1, a3, common_errArrayIndex # index >= length, bail + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + $load a2, $data_offset(a0) # a2 <- vBB[vCC] + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a2, rOBJ, t0) # vAA <- a2 diff --git a/runtime/interpreter/mterp/mips/op_aget_boolean.S b/runtime/interpreter/mterp/mips/op_aget_boolean.S new file mode 100644 index 0000000000..59f7f82a84 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_aget_boolean.S @@ -0,0 +1 @@ +%include "mips/op_aget.S" { "load":"lbu", "shift":"0", "data_offset":"MIRROR_BOOLEAN_ARRAY_DATA_OFFSET" } diff --git a/runtime/interpreter/mterp/mips/op_aget_byte.S b/runtime/interpreter/mterp/mips/op_aget_byte.S new file mode 100644 index 0000000000..11038fa7e1 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_aget_byte.S @@ -0,0 +1 @@ +%include "mips/op_aget.S" { "load":"lb", "shift":"0", "data_offset":"MIRROR_BYTE_ARRAY_DATA_OFFSET" } diff --git a/runtime/interpreter/mterp/mips/op_aget_char.S b/runtime/interpreter/mterp/mips/op_aget_char.S new file mode 100644 index 0000000000..96f2ab65dc --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_aget_char.S @@ -0,0 +1 @@ +%include "mips/op_aget.S" { "load":"lhu", "shift":"1", "data_offset":"MIRROR_CHAR_ARRAY_DATA_OFFSET" } diff --git a/runtime/interpreter/mterp/mips/op_aget_object.S b/runtime/interpreter/mterp/mips/op_aget_object.S new file mode 100644 index 0000000000..e3ab9d8462 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_aget_object.S @@ -0,0 +1,20 @@ + /* + * Array object get. vAA <- vBB[vCC]. + * + * for: aget-object + */ + /* op vAA, vBB, vCC */ + FETCH_B(a2, 1, 0) # a2 <- BB + GET_OPA(rOBJ) # rOBJ <- AA + FETCH_B(a3, 1, 1) # a3 <- CC + EXPORT_PC() + GET_VREG(a0, a2) # a0 <- vBB (array object) + GET_VREG(a1, a3) # a1 <- vCC (requested index) + JAL(artAGetObjectFromMterp) # v0 <- GetObj(array, index) + lw a1, THREAD_EXCEPTION_OFFSET(rSELF) + PREFETCH_INST(2) # load rINST + bnez a1, MterpException + SET_VREG_OBJECT(v0, rOBJ) # vAA <- v0 + ADVANCE(2) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_aget_short.S b/runtime/interpreter/mterp/mips/op_aget_short.S new file mode 100644 index 0000000000..cd7f7bf62f --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_aget_short.S @@ -0,0 +1 @@ +%include "mips/op_aget.S" { "load":"lh", "shift":"1", "data_offset":"MIRROR_SHORT_ARRAY_DATA_OFFSET" } diff --git a/runtime/interpreter/mterp/mips/op_aget_wide.S b/runtime/interpreter/mterp/mips/op_aget_wide.S new file mode 100644 index 0000000000..08822f56c3 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_aget_wide.S @@ -0,0 +1,22 @@ + /* + * Array get, 64 bits. vAA <- vBB[vCC]. + * + * Arrays of long/double are 64-bit aligned. + */ + /* aget-wide vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # rOBJ <- AA + and a2, a0, 255 # a2 <- BB + srl a3, a0, 8 # a3 <- CC + GET_VREG(a0, a2) # a0 <- vBB (array object) + GET_VREG(a1, a3) # a1 <- vCC (requested index) + # null array object? + beqz a0, common_errNullObject # yes, bail + LOAD_base_offMirrorArray_length(a3, a0) # a3 <- arrayObj->length + EAS3(a0, a0, a1) # a0 <- arrayObj + index*width + bgeu a1, a3, common_errArrayIndex # index >= length, bail + + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + LOAD64_off(a2, a3, a0, MIRROR_WIDE_ARRAY_DATA_OFFSET) + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG64_GOTO(a2, a3, rOBJ, t0) # vAA/vAA+1 <- a2/a3 diff --git a/runtime/interpreter/mterp/mips/op_and_int.S b/runtime/interpreter/mterp/mips/op_and_int.S new file mode 100644 index 0000000000..98fe4af7d2 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_and_int.S @@ -0,0 +1 @@ +%include "mips/binop.S" {"instr":"and a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips/op_and_int_2addr.S b/runtime/interpreter/mterp/mips/op_and_int_2addr.S new file mode 100644 index 0000000000..7f90ed4535 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_and_int_2addr.S @@ -0,0 +1 @@ +%include "mips/binop2addr.S" {"instr":"and a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips/op_and_int_lit16.S b/runtime/interpreter/mterp/mips/op_and_int_lit16.S new file mode 100644 index 0000000000..e46f23ba2e --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_and_int_lit16.S @@ -0,0 +1 @@ +%include "mips/binopLit16.S" {"instr":"and a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips/op_and_int_lit8.S b/runtime/interpreter/mterp/mips/op_and_int_lit8.S new file mode 100644 index 0000000000..3332883dc2 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_and_int_lit8.S @@ -0,0 +1 @@ +%include "mips/binopLit8.S" {"instr":"and a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips/op_and_long.S b/runtime/interpreter/mterp/mips/op_and_long.S new file mode 100644 index 0000000000..a98a6dfbd8 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_and_long.S @@ -0,0 +1 @@ +%include "mips/binopWide.S" {"preinstr":"and a0, a0, a2", "instr":"and a1, a1, a3"} diff --git a/runtime/interpreter/mterp/mips/op_and_long_2addr.S b/runtime/interpreter/mterp/mips/op_and_long_2addr.S new file mode 100644 index 0000000000..350c044f98 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_and_long_2addr.S @@ -0,0 +1 @@ +%include "mips/binopWide2addr.S" {"preinstr":"and a0, a0, a2", "instr":"and a1, a1, a3"} diff --git a/runtime/interpreter/mterp/mips/op_aput.S b/runtime/interpreter/mterp/mips/op_aput.S new file mode 100644 index 0000000000..53d6ae02ff --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_aput.S @@ -0,0 +1,30 @@ +%default { "store":"sw", "shift":"2", "data_offset":"MIRROR_INT_ARRAY_DATA_OFFSET" } + + /* + * Array put, 32 bits or less. vBB[vCC] <- vAA. + * + * for: aput, aput-boolean, aput-byte, aput-char, aput-short + * + * NOTE: this assumes data offset for arrays is the same for all non-wide types. + * If this changes, specialize. + */ + /* op vAA, vBB, vCC */ + FETCH_B(a2, 1, 0) # a2 <- BB + GET_OPA(rOBJ) # rOBJ <- AA + FETCH_B(a3, 1, 1) # a3 <- CC + GET_VREG(a0, a2) # a0 <- vBB (array object) + GET_VREG(a1, a3) # a1 <- vCC (requested index) + # null array object? + beqz a0, common_errNullObject # yes, bail + LOAD_base_offMirrorArray_length(a3, a0) # a3 <- arrayObj->length + .if $shift + EASN(a0, a0, a1, $shift) # a0 <- arrayObj + index*width + .else + addu a0, a0, a1 + .endif + bgeu a1, a3, common_errArrayIndex # index >= length, bail + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_VREG(a2, rOBJ) # a2 <- vAA + GET_INST_OPCODE(t0) # extract opcode from rINST + $store a2, $data_offset(a0) # vBB[vCC] <- a2 + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_aput_boolean.S b/runtime/interpreter/mterp/mips/op_aput_boolean.S new file mode 100644 index 0000000000..9cae5efbaf --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_aput_boolean.S @@ -0,0 +1 @@ +%include "mips/op_aput.S" { "store":"sb", "shift":"0", "data_offset":"MIRROR_BOOLEAN_ARRAY_DATA_OFFSET" } diff --git a/runtime/interpreter/mterp/mips/op_aput_byte.S b/runtime/interpreter/mterp/mips/op_aput_byte.S new file mode 100644 index 0000000000..3bbd12cec1 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_aput_byte.S @@ -0,0 +1 @@ +%include "mips/op_aput.S" { "store":"sb", "shift":"0", "data_offset":"MIRROR_BYTE_ARRAY_DATA_OFFSET" } diff --git a/runtime/interpreter/mterp/mips/op_aput_char.S b/runtime/interpreter/mterp/mips/op_aput_char.S new file mode 100644 index 0000000000..ae697173d7 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_aput_char.S @@ -0,0 +1 @@ +%include "mips/op_aput.S" { "store":"sh", "shift":"1", "data_offset":"MIRROR_CHAR_ARRAY_DATA_OFFSET" } diff --git a/runtime/interpreter/mterp/mips/op_aput_object.S b/runtime/interpreter/mterp/mips/op_aput_object.S new file mode 100644 index 0000000000..55b13b1449 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_aput_object.S @@ -0,0 +1,14 @@ + /* + * Store an object into an array. vBB[vCC] <- vAA. + * + */ + /* op vAA, vBB, vCC */ + EXPORT_PC() + addu a0, rFP, OFF_FP_SHADOWFRAME + move a1, rPC + move a2, rINST + JAL(MterpAputObject) + beqz v0, MterpPossibleException + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_aput_short.S b/runtime/interpreter/mterp/mips/op_aput_short.S new file mode 100644 index 0000000000..9586259a24 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_aput_short.S @@ -0,0 +1 @@ +%include "mips/op_aput.S" { "store":"sh", "shift":"1", "data_offset":"MIRROR_SHORT_ARRAY_DATA_OFFSET" } diff --git a/runtime/interpreter/mterp/mips/op_aput_wide.S b/runtime/interpreter/mterp/mips/op_aput_wide.S new file mode 100644 index 0000000000..ef99261a5c --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_aput_wide.S @@ -0,0 +1,25 @@ + /* + * Array put, 64 bits. vBB[vCC] <- vAA. + * + * Arrays of long/double are 64-bit aligned, so it's okay to use STRD. + */ + /* aput-wide vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(t0) # t0 <- AA + and a2, a0, 255 # a2 <- BB + srl a3, a0, 8 # a3 <- CC + GET_VREG(a0, a2) # a0 <- vBB (array object) + GET_VREG(a1, a3) # a1 <- vCC (requested index) + # null array object? + beqz a0, common_errNullObject # yes, bail + LOAD_base_offMirrorArray_length(a3, a0) # a3 <- arrayObj->length + EAS3(a0, a0, a1) # a0 <- arrayObj + index*width + EAS2(rOBJ, rFP, t0) # rOBJ <- &fp[AA] + # compare unsigned index, length + bgeu a1, a3, common_errArrayIndex # index >= length, bail + + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + LOAD64(a2, a3, rOBJ) # a2/a3 <- vAA/vAA+1 + GET_INST_OPCODE(t0) # extract opcode from rINST + STORE64_off(a2, a3, a0, MIRROR_WIDE_ARRAY_DATA_OFFSET) # a2/a3 <- vBB[vCC] + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_array_length.S b/runtime/interpreter/mterp/mips/op_array_length.S new file mode 100644 index 0000000000..2b4a86f1fd --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_array_length.S @@ -0,0 +1,12 @@ + /* + * Return the length of an array. + */ + GET_OPB(a1) # a1 <- B + GET_OPA4(a2) # a2 <- A+ + GET_VREG(a0, a1) # a0 <- vB (object ref) + # is object null? + beqz a0, common_errNullObject # yup, fail + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + LOAD_base_offMirrorArray_length(a3, a0) # a3 <- array length + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a3, a2, t0) # vA <- length diff --git a/runtime/interpreter/mterp/mips/op_check_cast.S b/runtime/interpreter/mterp/mips/op_check_cast.S new file mode 100644 index 0000000000..9a6cefad6d --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_check_cast.S @@ -0,0 +1,16 @@ + /* + * Check to see if a cast from one class to another is allowed. + */ + # check-cast vAA, class /* BBBB */ + EXPORT_PC() + FETCH(a0, 1) # a0 <- BBBB + GET_OPA(a1) # a1 <- AA + EAS2(a1, rFP, a1) # a1 <- &object + lw a2, OFF_FP_METHOD(rFP) # a2 <- method + move a3, rSELF # a3 <- self + JAL(MterpCheckCast) # v0 <- CheckCast(index, &obj, method, self) + PREFETCH_INST(2) + bnez v0, MterpPossibleException + ADVANCE(2) + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_cmp_long.S b/runtime/interpreter/mterp/mips/op_cmp_long.S new file mode 100644 index 0000000000..44806c3d06 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_cmp_long.S @@ -0,0 +1,34 @@ + /* + * Compare two 64-bit values + * x = y return 0 + * x < y return -1 + * x > y return 1 + * + * I think I can improve on the ARM code by the following observation + * slt t0, x.hi, y.hi; # (x.hi < y.hi) ? 1:0 + * sgt t1, x.hi, y.hi; # (y.hi > x.hi) ? 1:0 + * subu v0, t0, t1 # v0= -1:1:0 for [ < > = ] + */ + /* cmp-long vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # rOBJ <- AA + and a2, a0, 255 # a2 <- BB + srl a3, a0, 8 # a3 <- CC + EAS2(a2, rFP, a2) # a2 <- &fp[BB] + EAS2(a3, rFP, a3) # a3 <- &fp[CC] + LOAD64(a0, a1, a2) # a0/a1 <- vBB/vBB+1 + LOAD64(a2, a3, a3) # a2/a3 <- vCC/vCC+1 + + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + slt t0, a1, a3 # compare hi + sgt t1, a1, a3 + subu v0, t1, t0 # v0 <- (-1, 1, 0) + bnez v0, .L${opcode}_finish + # at this point x.hi==y.hi + sltu t0, a0, a2 # compare lo + sgtu t1, a0, a2 + subu v0, t1, t0 # v0 <- (-1, 1, 0) for [< > =] + +.L${opcode}_finish: + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(v0, rOBJ, t0) # vAA <- v0 diff --git a/runtime/interpreter/mterp/mips/op_cmpg_double.S b/runtime/interpreter/mterp/mips/op_cmpg_double.S new file mode 100644 index 0000000000..e7965a7d8a --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_cmpg_double.S @@ -0,0 +1 @@ +%include "mips/op_cmpl_double.S" { "naninst":"li rTEMP, 1" } diff --git a/runtime/interpreter/mterp/mips/op_cmpg_float.S b/runtime/interpreter/mterp/mips/op_cmpg_float.S new file mode 100644 index 0000000000..53519a6c97 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_cmpg_float.S @@ -0,0 +1 @@ +%include "mips/op_cmpl_float.S" { "naninst":"li rTEMP, 1" } diff --git a/runtime/interpreter/mterp/mips/op_cmpl_double.S b/runtime/interpreter/mterp/mips/op_cmpl_double.S new file mode 100644 index 0000000000..5a47fd7921 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_cmpl_double.S @@ -0,0 +1,54 @@ +%default { "naninst":"li rTEMP, -1" } + /* + * Compare two floating-point values. Puts 0(==), 1(>), or -1(<) + * into the destination register (rTEMP) based on the comparison results. + * + * Provide a "naninst" instruction that puts 1 or -1 into rTEMP depending + * on what value we'd like to return when one of the operands is NaN. + * + * See op_cmpl_float for more details. + * + * For: cmpl-double, cmpg-double + */ + /* op vAA, vBB, vCC */ + + FETCH(a0, 1) # a0 <- CCBB + and rOBJ, a0, 255 # s5 <- BB + srl t0, a0, 8 # t0 <- CC + EAS2(rOBJ, rFP, rOBJ) # s5 <- &fp[BB] + EAS2(t0, rFP, t0) # t0 <- &fp[CC] + LOAD64_F(ft0, ft0f, rOBJ) + LOAD64_F(ft1, ft1f, t0) +#ifdef MIPS32REVGE6 + cmp.ult.d ft2, ft0, ft1 + li rTEMP, -1 + bc1nez ft2, .L${opcode}_finish + cmp.ult.d ft2, ft1, ft0 + li rTEMP, 1 + bc1nez ft2, .L${opcode}_finish + cmp.eq.d ft2, ft0, ft1 + li rTEMP, 0 + bc1nez ft2, .L${opcode}_finish + b .L${opcode}_nan +#else + c.olt.d fcc0, ft0, ft1 + li rTEMP, -1 + bc1t fcc0, .L${opcode}_finish + c.olt.d fcc0, ft1, ft0 + li rTEMP, 1 + bc1t fcc0, .L${opcode}_finish + c.eq.d fcc0, ft0, ft1 + li rTEMP, 0 + bc1t fcc0, .L${opcode}_finish + b .L${opcode}_nan +#endif +%break + +.L${opcode}_nan: + $naninst + +.L${opcode}_finish: + GET_OPA(rOBJ) + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(rTEMP, rOBJ, t0) # vAA <- rTEMP diff --git a/runtime/interpreter/mterp/mips/op_cmpl_float.S b/runtime/interpreter/mterp/mips/op_cmpl_float.S new file mode 100644 index 0000000000..cfd87ee3bc --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_cmpl_float.S @@ -0,0 +1,61 @@ +%default { "naninst":"li rTEMP, -1" } + /* + * Compare two floating-point values. Puts 0, 1, or -1 into the + * destination register rTEMP based on the results of the comparison. + * + * Provide a "naninst" instruction that puts 1 or -1 into rTEMP depending + * on what value we'd like to return when one of the operands is NaN. + * + * The operation we're implementing is: + * if (x == y) + * return 0; + * else if (x < y) + * return -1; + * else if (x > y) + * return 1; + * else + * return {-1 or 1}; // one or both operands was NaN + * + * for: cmpl-float, cmpg-float + */ + /* op vAA, vBB, vCC */ + + /* "clasic" form */ + FETCH(a0, 1) # a0 <- CCBB + and a2, a0, 255 # a2 <- BB + srl a3, a0, 8 + GET_VREG_F(ft0, a2) + GET_VREG_F(ft1, a3) +#ifdef MIPS32REVGE6 + cmp.ult.s ft2, ft0, ft1 # Is ft0 < ft1 + li rTEMP, -1 + bc1nez ft2, .L${opcode}_finish + cmp.ult.s ft2, ft1, ft0 + li rTEMP, 1 + bc1nez ft2, .L${opcode}_finish + cmp.eq.s ft2, ft0, ft1 + li rTEMP, 0 + bc1nez ft2, .L${opcode}_finish + b .L${opcode}_nan +#else + c.olt.s fcc0, ft0, ft1 # Is ft0 < ft1 + li rTEMP, -1 + bc1t fcc0, .L${opcode}_finish + c.olt.s fcc0, ft1, ft0 + li rTEMP, 1 + bc1t fcc0, .L${opcode}_finish + c.eq.s fcc0, ft0, ft1 + li rTEMP, 0 + bc1t fcc0, .L${opcode}_finish + b .L${opcode}_nan +#endif +%break + +.L${opcode}_nan: + $naninst + +.L${opcode}_finish: + GET_OPA(rOBJ) + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(rTEMP, rOBJ, t0) # vAA <- rTEMP diff --git a/runtime/interpreter/mterp/mips/op_const.S b/runtime/interpreter/mterp/mips/op_const.S new file mode 100644 index 0000000000..c505761030 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_const.S @@ -0,0 +1,9 @@ + # const vAA, /* +BBBBbbbb */ + GET_OPA(a3) # a3 <- AA + FETCH(a0, 1) # a0 <- bbbb (low) + FETCH(a1, 2) # a1 <- BBBB (high) + FETCH_ADVANCE_INST(3) # advance rPC, load rINST + sll a1, a1, 16 + or a0, a1, a0 # a0 <- BBBBbbbb + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, a3, t0) # vAA <- a0 diff --git a/runtime/interpreter/mterp/mips/op_const_16.S b/runtime/interpreter/mterp/mips/op_const_16.S new file mode 100644 index 0000000000..5e47633f3b --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_const_16.S @@ -0,0 +1,6 @@ + # const/16 vAA, /* +BBBB */ + FETCH_S(a0, 1) # a0 <- ssssBBBB (sign-extended) + GET_OPA(a3) # a3 <- AA + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, a3, t0) # vAA <- a0 diff --git a/runtime/interpreter/mterp/mips/op_const_4.S b/runtime/interpreter/mterp/mips/op_const_4.S new file mode 100644 index 0000000000..8b662f95e8 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_const_4.S @@ -0,0 +1,8 @@ + # const/4 vA, /* +B */ + sll a1, rINST, 16 # a1 <- Bxxx0000 + GET_OPA(a0) # a0 <- A+ + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + sra a1, a1, 28 # a1 <- sssssssB (sign-extended) + and a0, a0, 15 + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a1, a0, t0) # fp[A] <- a1 diff --git a/runtime/interpreter/mterp/mips/op_const_class.S b/runtime/interpreter/mterp/mips/op_const_class.S new file mode 100644 index 0000000000..7202b11581 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_const_class.S @@ -0,0 +1,12 @@ + # const/class vAA, Class /* BBBB */ + EXPORT_PC() + FETCH(a0, 1) # a0 <- BBBB + GET_OPA(a1) # a1 <- AA + addu a2, rFP, OFF_FP_SHADOWFRAME # a2 <- shadow frame + move a3, rSELF + JAL(MterpConstClass) + PREFETCH_INST(2) # load rINST + bnez v0, MterpPossibleException + ADVANCE(2) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_const_high16.S b/runtime/interpreter/mterp/mips/op_const_high16.S new file mode 100644 index 0000000000..36c1c35049 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_const_high16.S @@ -0,0 +1,7 @@ + # const/high16 vAA, /* +BBBB0000 */ + FETCH(a0, 1) # a0 <- 0000BBBB (zero-extended) + GET_OPA(a3) # a3 <- AA + sll a0, a0, 16 # a0 <- BBBB0000 + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, a3, t0) # vAA <- a0 diff --git a/runtime/interpreter/mterp/mips/op_const_string.S b/runtime/interpreter/mterp/mips/op_const_string.S new file mode 100644 index 0000000000..d8eeb46b83 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_const_string.S @@ -0,0 +1,12 @@ + # const/string vAA, String /* BBBB */ + EXPORT_PC() + FETCH(a0, 1) # a0 <- BBBB + GET_OPA(a1) # a1 <- AA + addu a2, rFP, OFF_FP_SHADOWFRAME # a2 <- shadow frame + move a3, rSELF + JAL(MterpConstString) # v0 <- Mterp(index, tgt_reg, shadow_frame, self) + PREFETCH_INST(2) # load rINST + bnez v0, MterpPossibleException + ADVANCE(2) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_const_string_jumbo.S b/runtime/interpreter/mterp/mips/op_const_string_jumbo.S new file mode 100644 index 0000000000..d732ca151a --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_const_string_jumbo.S @@ -0,0 +1,15 @@ + # const/string vAA, String /* BBBBBBBB */ + EXPORT_PC() + FETCH(a0, 1) # a0 <- bbbb (low) + FETCH(a2, 2) # a2 <- BBBB (high) + GET_OPA(a1) # a1 <- AA + sll a2, a2, 16 + or a0, a0, a2 # a0 <- BBBBbbbb + addu a2, rFP, OFF_FP_SHADOWFRAME # a2 <- shadow frame + move a3, rSELF + JAL(MterpConstString) # v0 <- Mterp(index, tgt_reg, shadow_frame, self) + PREFETCH_INST(3) # load rINST + bnez v0, MterpPossibleException + ADVANCE(3) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_const_wide.S b/runtime/interpreter/mterp/mips/op_const_wide.S new file mode 100644 index 0000000000..01d0f87c69 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_const_wide.S @@ -0,0 +1,14 @@ + # const-wide vAA, /* +HHHHhhhhBBBBbbbb */ + FETCH(a0, 1) # a0 <- bbbb (low) + FETCH(a1, 2) # a1 <- BBBB (low middle) + FETCH(a2, 3) # a2 <- hhhh (high middle) + sll a1, 16 # + or a0, a1 # a0 <- BBBBbbbb (low word) + FETCH(a3, 4) # a3 <- HHHH (high) + GET_OPA(t1) # t1 <- AA + sll a3, 16 + or a1, a3, a2 # a1 <- HHHHhhhh (high word) + FETCH_ADVANCE_INST(5) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG64(a0, a1, t1) # vAA <- a0/a1 + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_const_wide_16.S b/runtime/interpreter/mterp/mips/op_const_wide_16.S new file mode 100644 index 0000000000..583d9efe2a --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_const_wide_16.S @@ -0,0 +1,8 @@ + # const-wide/16 vAA, /* +BBBB */ + FETCH_S(a0, 1) # a0 <- ssssBBBB (sign-extended) + GET_OPA(a3) # a3 <- AA + sra a1, a0, 31 # a1 <- ssssssss + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG64(a0, a1, a3) # vAA <- a0/a1 + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_const_wide_32.S b/runtime/interpreter/mterp/mips/op_const_wide_32.S new file mode 100644 index 0000000000..3eb4574e2f --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_const_wide_32.S @@ -0,0 +1,11 @@ + # const-wide/32 vAA, /* +BBBBbbbb */ + FETCH(a0, 1) # a0 <- 0000bbbb (low) + GET_OPA(a3) # a3 <- AA + FETCH_S(a2, 2) # a2 <- ssssBBBB (high) + FETCH_ADVANCE_INST(3) # advance rPC, load rINST + sll a2, a2, 16 + or a0, a0, a2 # a0 <- BBBBbbbb + sra a1, a0, 31 # a1 <- ssssssss + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG64(a0, a1, a3) # vAA <- a0/a1 + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_const_wide_high16.S b/runtime/interpreter/mterp/mips/op_const_wide_high16.S new file mode 100644 index 0000000000..88382c6fe3 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_const_wide_high16.S @@ -0,0 +1,9 @@ + # const-wide/high16 vAA, /* +BBBB000000000000 */ + FETCH(a1, 1) # a1 <- 0000BBBB (zero-extended) + GET_OPA(a3) # a3 <- AA + li a0, 0 # a0 <- 00000000 + sll a1, 16 # a1 <- BBBB0000 + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG64(a0, a1, a3) # vAA <- a0/a1 + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_div_double.S b/runtime/interpreter/mterp/mips/op_div_double.S new file mode 100644 index 0000000000..84e4c4e317 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_div_double.S @@ -0,0 +1 @@ +%include "mips/fbinopWide.S" {"instr":"div.d fv0, fa0, fa1"} diff --git a/runtime/interpreter/mterp/mips/op_div_double_2addr.S b/runtime/interpreter/mterp/mips/op_div_double_2addr.S new file mode 100644 index 0000000000..65b92e37db --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_div_double_2addr.S @@ -0,0 +1 @@ +%include "mips/fbinopWide2addr.S" {"instr":"div.d fv0, fa0, fa1"} diff --git a/runtime/interpreter/mterp/mips/op_div_float.S b/runtime/interpreter/mterp/mips/op_div_float.S new file mode 100644 index 0000000000..44b8d47e0b --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_div_float.S @@ -0,0 +1 @@ +%include "mips/fbinop.S" {"instr":"div.s fv0, fa0, fa1"} diff --git a/runtime/interpreter/mterp/mips/op_div_float_2addr.S b/runtime/interpreter/mterp/mips/op_div_float_2addr.S new file mode 100644 index 0000000000..e5fff92c8c --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_div_float_2addr.S @@ -0,0 +1 @@ +%include "mips/fbinop2addr.S" {"instr":"div.s fv0, fa0, fa1"} diff --git a/runtime/interpreter/mterp/mips/op_div_int.S b/runtime/interpreter/mterp/mips/op_div_int.S new file mode 100644 index 0000000000..5d28c84d6b --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_div_int.S @@ -0,0 +1,5 @@ +#ifdef MIPS32REVGE6 +%include "mips/binop.S" {"instr":"div a0, a0, a1", "chkzero":"1"} +#else +%include "mips/binop.S" {"preinstr":"div zero, a0, a1", "instr":"mflo a0", "chkzero":"1"} +#endif diff --git a/runtime/interpreter/mterp/mips/op_div_int_2addr.S b/runtime/interpreter/mterp/mips/op_div_int_2addr.S new file mode 100644 index 0000000000..6c079e04c4 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_div_int_2addr.S @@ -0,0 +1,5 @@ +#ifdef MIPS32REVGE6 +%include "mips/binop2addr.S" {"instr":"div a0, a0, a1", "chkzero":"1"} +#else +%include "mips/binop2addr.S" {"preinstr":"div zero, a0, a1", "instr":"mflo a0", "chkzero":"1"} +#endif diff --git a/runtime/interpreter/mterp/mips/op_div_int_lit16.S b/runtime/interpreter/mterp/mips/op_div_int_lit16.S new file mode 100644 index 0000000000..ee7452ce1a --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_div_int_lit16.S @@ -0,0 +1,5 @@ +#ifdef MIPS32REVGE6 +%include "mips/binopLit16.S" {"instr":"div a0, a0, a1", "chkzero":"1"} +#else +%include "mips/binopLit16.S" {"preinstr":"div zero, a0, a1", "instr":"mflo a0", "chkzero":"1"} +#endif diff --git a/runtime/interpreter/mterp/mips/op_div_int_lit8.S b/runtime/interpreter/mterp/mips/op_div_int_lit8.S new file mode 100644 index 0000000000..d2964b8065 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_div_int_lit8.S @@ -0,0 +1,5 @@ +#ifdef MIPS32REVGE6 +%include "mips/binopLit8.S" {"instr":"div a0, a0, a1", "chkzero":"1"} +#else +%include "mips/binopLit8.S" {"preinstr":"div zero, a0, a1", "instr":"mflo a0", "chkzero":"1"} +#endif diff --git a/runtime/interpreter/mterp/mips/op_div_long.S b/runtime/interpreter/mterp/mips/op_div_long.S new file mode 100644 index 0000000000..2097866886 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_div_long.S @@ -0,0 +1 @@ +%include "mips/binopWide.S" {"result0":"v0", "result1":"v1", "instr":"JAL(__divdi3)", "chkzero":"1"} diff --git a/runtime/interpreter/mterp/mips/op_div_long_2addr.S b/runtime/interpreter/mterp/mips/op_div_long_2addr.S new file mode 100644 index 0000000000..c279305142 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_div_long_2addr.S @@ -0,0 +1 @@ +%include "mips/binopWide2addr.S" {"result0":"v0", "result1":"v1", "instr":"JAL(__divdi3)", "chkzero":"1"} diff --git a/runtime/interpreter/mterp/mips/op_double_to_float.S b/runtime/interpreter/mterp/mips/op_double_to_float.S new file mode 100644 index 0000000000..1d32c2e1e4 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_double_to_float.S @@ -0,0 +1 @@ +%include "mips/unopNarrower.S" {"instr":"cvt.s.d fv0, fa0"} diff --git a/runtime/interpreter/mterp/mips/op_double_to_int.S b/runtime/interpreter/mterp/mips/op_double_to_int.S new file mode 100644 index 0000000000..30a0a73e61 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_double_to_int.S @@ -0,0 +1,58 @@ +%include "mips/unopNarrower.S" {"instr":"b d2i_doconv"} +/* + * Convert the double in a0/a1 to an int in a0. + * + * We have to clip values to int min/max per the specification. The + * expected common case is a "reasonable" value that converts directly + * to modest integer. The EABI convert function isn't doing this for us. + */ +%break + +d2i_doconv: +#ifdef MIPS32REVGE6 + la t0, .LDOUBLE_TO_INT_max + LOAD64_F(fa1, fa1f, t0) + cmp.ule.d ft2, fa1, fa0 + l.s fv0, .LDOUBLE_TO_INT_maxret + bc1nez ft2, .L${opcode}_set_vreg_f + + la t0, .LDOUBLE_TO_INT_min + LOAD64_F(fa1, fa1f, t0) + cmp.ule.d ft2, fa0, fa1 + l.s fv0, .LDOUBLE_TO_INT_minret + bc1nez ft2, .L${opcode}_set_vreg_f + + mov.d fa1, fa0 + cmp.un.d ft2, fa0, fa1 + li.s fv0, 0 + bc1nez ft2, .L${opcode}_set_vreg_f +#else + la t0, .LDOUBLE_TO_INT_max + LOAD64_F(fa1, fa1f, t0) + c.ole.d fcc0, fa1, fa0 + l.s fv0, .LDOUBLE_TO_INT_maxret + bc1t .L${opcode}_set_vreg_f + + la t0, .LDOUBLE_TO_INT_min + LOAD64_F(fa1, fa1f, t0) + c.ole.d fcc0, fa0, fa1 + l.s fv0, .LDOUBLE_TO_INT_minret + bc1t .L${opcode}_set_vreg_f + + mov.d fa1, fa0 + c.un.d fcc0, fa0, fa1 + li.s fv0, 0 + bc1t .L${opcode}_set_vreg_f +#endif + + trunc.w.d fv0, fa0 + b .L${opcode}_set_vreg_f + +.LDOUBLE_TO_INT_max: + .dword 0x41dfffffffc00000 +.LDOUBLE_TO_INT_min: + .dword 0xc1e0000000000000 # minint, as a double (high word) +.LDOUBLE_TO_INT_maxret: + .word 0x7fffffff +.LDOUBLE_TO_INT_minret: + .word 0x80000000 diff --git a/runtime/interpreter/mterp/mips/op_double_to_long.S b/runtime/interpreter/mterp/mips/op_double_to_long.S new file mode 100644 index 0000000000..4f9e367279 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_double_to_long.S @@ -0,0 +1,56 @@ +%include "mips/funopWide.S" {"instr":"b d2l_doconv", "st_result":"SET_VREG64(rRESULT0, rRESULT1, rOBJ)"} +%break + +d2l_doconv: +#ifdef MIPS32REVGE6 + la t0, .LDOUBLE_TO_LONG_max + LOAD64_F(fa1, fa1f, t0) + cmp.ule.d ft2, fa1, fa0 + la t0, .LDOUBLE_TO_LONG_ret_max + LOAD64(rRESULT0, rRESULT1, t0) + bc1nez ft2, .L${opcode}_set_vreg + + la t0, .LDOUBLE_TO_LONG_min + LOAD64_F(fa1, fa1f, t0) + cmp.ule.d ft2, fa0, fa1 + la t0, .LDOUBLE_TO_LONG_ret_min + LOAD64(rRESULT0, rRESULT1, t0) + bc1nez ft2, .L${opcode}_set_vreg + + mov.d fa1, fa0 + cmp.un.d ft2, fa0, fa1 + li rRESULT0, 0 + li rRESULT1, 0 + bc1nez ft2, .L${opcode}_set_vreg +#else + la t0, .LDOUBLE_TO_LONG_max + LOAD64_F(fa1, fa1f, t0) + c.ole.d fcc0, fa1, fa0 + la t0, .LDOUBLE_TO_LONG_ret_max + LOAD64(rRESULT0, rRESULT1, t0) + bc1t .L${opcode}_set_vreg + + la t0, .LDOUBLE_TO_LONG_min + LOAD64_F(fa1, fa1f, t0) + c.ole.d fcc0, fa0, fa1 + la t0, .LDOUBLE_TO_LONG_ret_min + LOAD64(rRESULT0, rRESULT1, t0) + bc1t .L${opcode}_set_vreg + + mov.d fa1, fa0 + c.un.d fcc0, fa0, fa1 + li rRESULT0, 0 + li rRESULT1, 0 + bc1t .L${opcode}_set_vreg +#endif + JAL(__fixdfdi) + b .L${opcode}_set_vreg + +.LDOUBLE_TO_LONG_max: + .dword 0x43e0000000000000 # maxlong, as a double (high word) +.LDOUBLE_TO_LONG_min: + .dword 0xc3e0000000000000 # minlong, as a double (high word) +.LDOUBLE_TO_LONG_ret_max: + .dword 0x7fffffffffffffff +.LDOUBLE_TO_LONG_ret_min: + .dword 0x8000000000000000 diff --git a/runtime/interpreter/mterp/mips/op_fill_array_data.S b/runtime/interpreter/mterp/mips/op_fill_array_data.S new file mode 100644 index 0000000000..86057462db --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_fill_array_data.S @@ -0,0 +1,14 @@ + /* fill-array-data vAA, +BBBBBBBB */ + EXPORT_PC() + FETCH(a0, 1) # a0 <- bbbb (lo) + FETCH(a1, 2) # a1 <- BBBB (hi) + GET_OPA(a3) # a3 <- AA + sll a1, a1, 16 # a1 <- BBBBbbbb + or a1, a0, a1 # a1 <- BBBBbbbb + GET_VREG(a0, a3) # a0 <- vAA (array object) + EAS1(a1, rPC, a1) # a1 <- PC + BBBBbbbb*2 (array data off.) + JAL(MterpFillArrayData) # v0 <- Mterp(obj, payload) + beqz v0, MterpPossibleException # has exception + FETCH_ADVANCE_INST(3) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_filled_new_array.S b/runtime/interpreter/mterp/mips/op_filled_new_array.S new file mode 100644 index 0000000000..3f62faef1c --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_filled_new_array.S @@ -0,0 +1,18 @@ +%default { "helper":"MterpFilledNewArray" } + /* + * Create a new array with elements filled from registers. + * + * for: filled-new-array, filled-new-array/range + */ + # op vB, {vD, vE, vF, vG, vA}, class /* CCCC */ + # op {vCCCC..v(CCCC+AA-1)}, type /* BBBB */ + .extern $helper + EXPORT_PC() + addu a0, rFP, OFF_FP_SHADOWFRAME # a0 <- shadow frame + move a1, rPC + move a2, rSELF + JAL($helper) # v0 <- helper(shadow_frame, pc, self) + beqz v0, MterpPossibleException # has exception + FETCH_ADVANCE_INST(3) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_filled_new_array_range.S b/runtime/interpreter/mterp/mips/op_filled_new_array_range.S new file mode 100644 index 0000000000..f8dcb0e037 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_filled_new_array_range.S @@ -0,0 +1 @@ +%include "mips/op_filled_new_array.S" { "helper":"MterpFilledNewArrayRange" } diff --git a/runtime/interpreter/mterp/mips/op_float_to_double.S b/runtime/interpreter/mterp/mips/op_float_to_double.S new file mode 100644 index 0000000000..1315255b5c --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_float_to_double.S @@ -0,0 +1 @@ +%include "mips/funopWider.S" {"instr":"cvt.d.s fv0, fa0"} diff --git a/runtime/interpreter/mterp/mips/op_float_to_int.S b/runtime/interpreter/mterp/mips/op_float_to_int.S new file mode 100644 index 0000000000..e032869873 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_float_to_int.S @@ -0,0 +1,50 @@ +%include "mips/funop.S" {"instr":"b f2i_doconv"} +%break + +/* + * Not an entry point as it is used only once !! + */ +f2i_doconv: +#ifdef MIPS32REVGE6 + l.s fa1, .LFLOAT_TO_INT_max + cmp.ule.s ft2, fa1, fa0 + l.s fv0, .LFLOAT_TO_INT_ret_max + bc1nez ft2, .L${opcode}_set_vreg_f + + l.s fa1, .LFLOAT_TO_INT_min + cmp.ule.s ft2, fa0, fa1 + l.s fv0, .LFLOAT_TO_INT_ret_min + bc1nez ft2, .L${opcode}_set_vreg_f + + mov.s fa1, fa0 + cmp.un.s ft2, fa0, fa1 + li.s fv0, 0 + bc1nez ft2, .L${opcode}_set_vreg_f +#else + l.s fa1, .LFLOAT_TO_INT_max + c.ole.s fcc0, fa1, fa0 + l.s fv0, .LFLOAT_TO_INT_ret_max + bc1t .L${opcode}_set_vreg_f + + l.s fa1, .LFLOAT_TO_INT_min + c.ole.s fcc0, fa0, fa1 + l.s fv0, .LFLOAT_TO_INT_ret_min + bc1t .L${opcode}_set_vreg_f + + mov.s fa1, fa0 + c.un.s fcc0, fa0, fa1 + li.s fv0, 0 + bc1t .L${opcode}_set_vreg_f +#endif + + trunc.w.s fv0, fa0 + b .L${opcode}_set_vreg_f + +.LFLOAT_TO_INT_max: + .word 0x4f000000 +.LFLOAT_TO_INT_min: + .word 0xcf000000 +.LFLOAT_TO_INT_ret_max: + .word 0x7fffffff +.LFLOAT_TO_INT_ret_min: + .word 0x80000000 diff --git a/runtime/interpreter/mterp/mips/op_float_to_long.S b/runtime/interpreter/mterp/mips/op_float_to_long.S new file mode 100644 index 0000000000..77b2c46b4e --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_float_to_long.S @@ -0,0 +1,51 @@ +%include "mips/funopWider.S" {"instr":"b f2l_doconv", "st_result":"SET_VREG64(rRESULT0, rRESULT1, rOBJ)"} +%break + +f2l_doconv: +#ifdef MIPS32REVGE6 + l.s fa1, .LLONG_TO_max + cmp.ule.s ft2, fa1, fa0 + li rRESULT0, ~0 + li rRESULT1, ~0x80000000 + bc1nez ft2, .L${opcode}_set_vreg + + l.s fa1, .LLONG_TO_min + cmp.ule.s ft2, fa0, fa1 + li rRESULT0, 0 + li rRESULT1, 0x80000000 + bc1nez ft2, .L${opcode}_set_vreg + + mov.s fa1, fa0 + cmp.un.s ft2, fa0, fa1 + li rRESULT0, 0 + li rRESULT1, 0 + bc1nez ft2, .L${opcode}_set_vreg +#else + l.s fa1, .LLONG_TO_max + c.ole.s fcc0, fa1, fa0 + li rRESULT0, ~0 + li rRESULT1, ~0x80000000 + bc1t .L${opcode}_set_vreg + + l.s fa1, .LLONG_TO_min + c.ole.s fcc0, fa0, fa1 + li rRESULT0, 0 + li rRESULT1, 0x80000000 + bc1t .L${opcode}_set_vreg + + mov.s fa1, fa0 + c.un.s fcc0, fa0, fa1 + li rRESULT0, 0 + li rRESULT1, 0 + bc1t .L${opcode}_set_vreg +#endif + + JAL(__fixsfdi) + + b .L${opcode}_set_vreg + +.LLONG_TO_max: + .word 0x5f000000 + +.LLONG_TO_min: + .word 0xdf000000 diff --git a/runtime/interpreter/mterp/mips/op_goto.S b/runtime/interpreter/mterp/mips/op_goto.S new file mode 100644 index 0000000000..d6f21c9b2c --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_goto.S @@ -0,0 +1,38 @@ + /* + * Unconditional branch, 8-bit offset. + * + * The branch distance is a signed code-unit offset, which we need to + * double to get a byte offset. + */ + /* goto +AA */ +#if MTERP_PROFILE_BRANCHES + sll a0, rINST, 16 # a0 <- AAxx0000 + sra rINST, a0, 24 # rINST <- ssssssAA (sign-extended) + EXPORT_PC() + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + JAL(MterpProfileBranch) # (self, shadow_frame, offset) + bnez v0, MterpOnStackReplacement # Note: offset must be in rINST + addu a2, rINST, rINST # a2 <- byte offset + FETCH_ADVANCE_INST_RB(a2) # update rPC, load rINST + /* If backwards branch refresh rIBASE */ + bgez a2, 1f + lw ra, THREAD_FLAGS_OFFSET(rSELF) + b MterpCheckSuspendAndContinue +1: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction +#else + sll a0, rINST, 16 # a0 <- AAxx0000 + sra rINST, a0, 24 # rINST <- ssssssAA (sign-extended) + addu a2, rINST, rINST # a2 <- byte offset + FETCH_ADVANCE_INST_RB(a2) # update rPC, load rINST + /* If backwards branch refresh rIBASE */ + bgez a1, 1f + lw ra, THREAD_FLAGS_OFFSET(rSELF) + b MterpCheckSuspendAndContinue +1: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction +#endif diff --git a/runtime/interpreter/mterp/mips/op_goto_16.S b/runtime/interpreter/mterp/mips/op_goto_16.S new file mode 100644 index 0000000000..cec4432599 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_goto_16.S @@ -0,0 +1,34 @@ + /* + * Unconditional branch, 16-bit offset. + * + * The branch distance is a signed code-unit offset, which we need to + * double to get a byte offset. + */ + /* goto/16 +AAAA */ +#if MTERP_PROFILE_BRANCHES + FETCH_S(rINST, 1) # rINST <- ssssAAAA (sign-extended) + EXPORT_PC() + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + JAL(MterpProfileBranch) # (self, shadow_frame, offset) + bnez v0, MterpOnStackReplacement # Note: offset must be in rINST + addu a1, rINST, rINST # a1 <- byte offset, flags set + FETCH_ADVANCE_INST_RB(a1) # update rPC, load rINST + bgez a1, 1f + lw ra, THREAD_FLAGS_OFFSET(rSELF) + b MterpCheckSuspendAndContinue +1: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction +#else + FETCH_S(rINST, 1) # rINST <- ssssAAAA (sign-extended) + addu a1, rINST, rINST # a1 <- byte offset, flags set + FETCH_ADVANCE_INST_RB(a1) # update rPC, load rINST + bgez a1, 1f + lw ra, THREAD_FLAGS_OFFSET(rSELF) + b MterpCheckSuspendAndContinue +1: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction +#endif diff --git a/runtime/interpreter/mterp/mips/op_goto_32.S b/runtime/interpreter/mterp/mips/op_goto_32.S new file mode 100644 index 0000000000..083acd1ef9 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_goto_32.S @@ -0,0 +1,43 @@ + /* + * Unconditional branch, 32-bit offset. + * + * The branch distance is a signed code-unit offset, which we need to + * double to get a byte offset. + * + * Unlike most opcodes, this one is allowed to branch to itself, so + * our "backward branch" test must be "<=0" instead of "<0". + */ + /* goto/32 +AAAAAAAA */ +#if MTERP_PROFILE_BRANCHES + FETCH(a0, 1) # a0 <- aaaa (lo) + FETCH(a1, 2) # a1 <- AAAA (hi) + sll a1, a1, 16 + or rINST, a0, a1 # rINST <- AAAAaaaa + EXPORT_PC() + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + JAL(MterpProfileBranch) # (self, shadow_frame, offset) + bnez v0, MterpOnStackReplacement # Note: offset must be in rINST + addu a1, rINST, rINST # a1 <- byte offset + FETCH_ADVANCE_INST_RB(a1) # update rPC, load rINST + bgtz a1, 1f + lw ra, THREAD_FLAGS_OFFSET(rSELF) + b MterpCheckSuspendAndContinue +1: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction +#else + FETCH(a0, 1) # a0 <- aaaa (lo) + FETCH(a1, 2) # a1 <- AAAA (hi) + sll a1, a1, 16 + or rINST, a0, a1 # rINST <- AAAAaaaa + addu a1, rINST, rINST # a1 <- byte offset + FETCH_ADVANCE_INST_RB(a1) # update rPC, load rINST + bgtz a1, 1f + lw ra, THREAD_FLAGS_OFFSET(rSELF) + b MterpCheckSuspendAndContinue +1: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction +#endif diff --git a/runtime/interpreter/mterp/mips/op_if_eq.S b/runtime/interpreter/mterp/mips/op_if_eq.S new file mode 100644 index 0000000000..e7190d8197 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_if_eq.S @@ -0,0 +1 @@ +%include "mips/bincmp.S" { "revcmp":"ne" } diff --git a/runtime/interpreter/mterp/mips/op_if_eqz.S b/runtime/interpreter/mterp/mips/op_if_eqz.S new file mode 100644 index 0000000000..0a78fd98ac --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_if_eqz.S @@ -0,0 +1 @@ +%include "mips/zcmp.S" { "revcmp":"ne" } diff --git a/runtime/interpreter/mterp/mips/op_if_ge.S b/runtime/interpreter/mterp/mips/op_if_ge.S new file mode 100644 index 0000000000..b2629ba4e9 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_if_ge.S @@ -0,0 +1 @@ +%include "mips/bincmp.S" { "revcmp":"lt" } diff --git a/runtime/interpreter/mterp/mips/op_if_gez.S b/runtime/interpreter/mterp/mips/op_if_gez.S new file mode 100644 index 0000000000..b02f67709f --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_if_gez.S @@ -0,0 +1 @@ +%include "mips/zcmp.S" { "revcmp":"lt" } diff --git a/runtime/interpreter/mterp/mips/op_if_gt.S b/runtime/interpreter/mterp/mips/op_if_gt.S new file mode 100644 index 0000000000..f620d4a1fd --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_if_gt.S @@ -0,0 +1 @@ +%include "mips/bincmp.S" { "revcmp":"le" } diff --git a/runtime/interpreter/mterp/mips/op_if_gtz.S b/runtime/interpreter/mterp/mips/op_if_gtz.S new file mode 100644 index 0000000000..5e5dd708fa --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_if_gtz.S @@ -0,0 +1 @@ +%include "mips/zcmp.S" { "revcmp":"le" } diff --git a/runtime/interpreter/mterp/mips/op_if_le.S b/runtime/interpreter/mterp/mips/op_if_le.S new file mode 100644 index 0000000000..a4e8b1ad51 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_if_le.S @@ -0,0 +1 @@ +%include "mips/bincmp.S" { "revcmp":"gt" } diff --git a/runtime/interpreter/mterp/mips/op_if_lez.S b/runtime/interpreter/mterp/mips/op_if_lez.S new file mode 100644 index 0000000000..af551a62fd --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_if_lez.S @@ -0,0 +1 @@ +%include "mips/zcmp.S" { "revcmp":"gt" } diff --git a/runtime/interpreter/mterp/mips/op_if_lt.S b/runtime/interpreter/mterp/mips/op_if_lt.S new file mode 100644 index 0000000000..f33b9a4c05 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_if_lt.S @@ -0,0 +1 @@ +%include "mips/bincmp.S" { "revcmp":"ge" } diff --git a/runtime/interpreter/mterp/mips/op_if_ltz.S b/runtime/interpreter/mterp/mips/op_if_ltz.S new file mode 100644 index 0000000000..18fcb1d477 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_if_ltz.S @@ -0,0 +1 @@ +%include "mips/zcmp.S" { "revcmp":"ge" } diff --git a/runtime/interpreter/mterp/mips/op_if_ne.S b/runtime/interpreter/mterp/mips/op_if_ne.S new file mode 100644 index 0000000000..e0a102b443 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_if_ne.S @@ -0,0 +1 @@ +%include "mips/bincmp.S" { "revcmp":"eq" } diff --git a/runtime/interpreter/mterp/mips/op_if_nez.S b/runtime/interpreter/mterp/mips/op_if_nez.S new file mode 100644 index 0000000000..d1866a0a02 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_if_nez.S @@ -0,0 +1 @@ +%include "mips/zcmp.S" { "revcmp":"eq" } diff --git a/runtime/interpreter/mterp/mips/op_iget.S b/runtime/interpreter/mterp/mips/op_iget.S new file mode 100644 index 0000000000..86d44fa531 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_iget.S @@ -0,0 +1,25 @@ +%default { "is_object":"0", "helper":"artGet32InstanceFromCode"} + /* + * General instance field get. + * + * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short + */ + EXPORT_PC() + FETCH(a0, 1) # a0 <- field ref CCCC + GET_OPB(a1) # a1 <- B + GET_VREG(a1, a1) # a1 <- fp[B], the object pointer + lw a2, OFF_FP_METHOD(rFP) # a2 <- referrer + move a3, rSELF # a3 <- self + JAL($helper) + lw a3, THREAD_EXCEPTION_OFFSET(rSELF) + GET_OPA4(a2) # a2<- A+ + PREFETCH_INST(2) # load rINST + bnez a3, MterpPossibleException # bail out + .if $is_object + SET_VREG_OBJECT(v0, a2) # fp[A] <- v0 + .else + SET_VREG(v0, a2) # fp[A] <- v0 + .endif + ADVANCE(2) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_iget_boolean.S b/runtime/interpreter/mterp/mips/op_iget_boolean.S new file mode 100644 index 0000000000..e03364e34b --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_iget_boolean.S @@ -0,0 +1 @@ +%include "mips/op_iget.S" { "helper":"artGetBooleanInstanceFromCode" } diff --git a/runtime/interpreter/mterp/mips/op_iget_boolean_quick.S b/runtime/interpreter/mterp/mips/op_iget_boolean_quick.S new file mode 100644 index 0000000000..f3032b3d84 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_iget_boolean_quick.S @@ -0,0 +1 @@ +%include "mips/op_iget_quick.S" { "load":"lbu" } diff --git a/runtime/interpreter/mterp/mips/op_iget_byte.S b/runtime/interpreter/mterp/mips/op_iget_byte.S new file mode 100644 index 0000000000..dc87cfecf8 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_iget_byte.S @@ -0,0 +1 @@ +%include "mips/op_iget.S" { "helper":"artGetByteInstanceFromCode" } diff --git a/runtime/interpreter/mterp/mips/op_iget_byte_quick.S b/runtime/interpreter/mterp/mips/op_iget_byte_quick.S new file mode 100644 index 0000000000..d93f84486d --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_iget_byte_quick.S @@ -0,0 +1 @@ +%include "mips/op_iget_quick.S" { "load":"lb" } diff --git a/runtime/interpreter/mterp/mips/op_iget_char.S b/runtime/interpreter/mterp/mips/op_iget_char.S new file mode 100644 index 0000000000..55f8a93ff4 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_iget_char.S @@ -0,0 +1 @@ +%include "mips/op_iget.S" { "helper":"artGetCharInstanceFromCode" } diff --git a/runtime/interpreter/mterp/mips/op_iget_char_quick.S b/runtime/interpreter/mterp/mips/op_iget_char_quick.S new file mode 100644 index 0000000000..6f6d6088e0 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_iget_char_quick.S @@ -0,0 +1 @@ +%include "mips/op_iget_quick.S" { "load":"lhu" } diff --git a/runtime/interpreter/mterp/mips/op_iget_object.S b/runtime/interpreter/mterp/mips/op_iget_object.S new file mode 100644 index 0000000000..11d93a46d7 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_iget_object.S @@ -0,0 +1 @@ +%include "mips/op_iget.S" { "is_object":"1", "helper":"artGetObjInstanceFromCode" } diff --git a/runtime/interpreter/mterp/mips/op_iget_object_quick.S b/runtime/interpreter/mterp/mips/op_iget_object_quick.S new file mode 100644 index 0000000000..31d94b9cb9 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_iget_object_quick.S @@ -0,0 +1,15 @@ + /* For: iget-object-quick */ + /* op vA, vB, offset@CCCC */ + GET_OPB(a2) # a2 <- B + FETCH(a1, 1) # a1 <- field byte offset + EXPORT_PC() + GET_VREG(a0, a2) # a0 <- object we're operating on + JAL(artIGetObjectFromMterp) # v0 <- GetObj(obj, offset) + lw a3, THREAD_EXCEPTION_OFFSET(rSELF) + GET_OPA4(a2) # a2<- A+ + PREFETCH_INST(2) # load rINST + bnez a3, MterpPossibleException # bail out + SET_VREG_OBJECT(v0, a2) # fp[A] <- v0 + ADVANCE(2) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_iget_quick.S b/runtime/interpreter/mterp/mips/op_iget_quick.S new file mode 100644 index 0000000000..fbafa5b099 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_iget_quick.S @@ -0,0 +1,14 @@ +%default { "load":"lw" } + /* For: iget-quick, iget-boolean-quick, iget-byte-quick, iget-char-quick, iget-short-quick */ + # op vA, vB, offset /* CCCC */ + GET_OPB(a2) # a2 <- B + GET_VREG(a3, a2) # a3 <- object we're operating on + FETCH(a1, 1) # a1 <- field byte offset + GET_OPA4(a2) # a2 <- A(+) + # check object for null + beqz a3, common_errNullObject # object was null + addu t0, a3, a1 + $load a0, 0(t0) # a0 <- obj.field (8/16/32 bits) + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, a2, t0) # fp[A] <- a0 diff --git a/runtime/interpreter/mterp/mips/op_iget_short.S b/runtime/interpreter/mterp/mips/op_iget_short.S new file mode 100644 index 0000000000..9086246c97 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_iget_short.S @@ -0,0 +1 @@ +%include "mips/op_iget.S" { "helper":"artGetShortInstanceFromCode" } diff --git a/runtime/interpreter/mterp/mips/op_iget_short_quick.S b/runtime/interpreter/mterp/mips/op_iget_short_quick.S new file mode 100644 index 0000000000..899a0feb65 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_iget_short_quick.S @@ -0,0 +1 @@ +%include "mips/op_iget_quick.S" { "load":"lh" } diff --git a/runtime/interpreter/mterp/mips/op_iget_wide.S b/runtime/interpreter/mterp/mips/op_iget_wide.S new file mode 100644 index 0000000000..8fe3089eef --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_iget_wide.S @@ -0,0 +1,20 @@ + /* + * 64-bit instance field get. + * + * for: iget-wide + */ + EXPORT_PC() + FETCH(a0, 1) # a0 <- field byte offset + GET_OPB(a1) # a1 <- B + GET_VREG(a1, a1) # a1 <- fp[B], the object pointer + lw a2, OFF_FP_METHOD(rFP) # a2 <- referrer + move a3, rSELF # a3 <- self + JAL(artGet64InstanceFromCode) + lw a3, THREAD_EXCEPTION_OFFSET(rSELF) + GET_OPA4(a2) # a2<- A+ + PREFETCH_INST(2) # load rINST + bnez a3, MterpException # bail out + SET_VREG64(v0, v1, a2) # fp[A] <- v0/v1 + ADVANCE(2) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_iget_wide_quick.S b/runtime/interpreter/mterp/mips/op_iget_wide_quick.S new file mode 100644 index 0000000000..4d2f29187a --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_iget_wide_quick.S @@ -0,0 +1,13 @@ + # iget-wide-quick vA, vB, offset /* CCCC */ + GET_OPB(a2) # a2 <- B + GET_VREG(a3, a2) # a3 <- object we're operating on + FETCH(a1, 1) # a1 <- field byte offset + GET_OPA4(a2) # a2 <- A(+) + # check object for null + beqz a3, common_errNullObject # object was null + addu t0, a3, a1 # t0 <- a3 + a1 + LOAD64(a0, a1, t0) # a0 <- obj.field (64 bits, aligned) + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG64(a0, a1, a2) # fp[A] <- a0/a1 + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_instance_of.S b/runtime/interpreter/mterp/mips/op_instance_of.S new file mode 100644 index 0000000000..d2679bdd00 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_instance_of.S @@ -0,0 +1,21 @@ + /* + * Check to see if an object reference is an instance of a class. + * + * Most common situation is a non-null object, being compared against + * an already-resolved class. + */ + # instance-of vA, vB, class /* CCCC */ + EXPORT_PC() + FETCH(a0, 1) # a0 <- CCCC + GET_OPB(a1) # a1 <- B + EAS2(a1, rFP, a1) # a1 <- &object + lw a2, OFF_FP_METHOD(rFP) # a2 <- method + move a3, rSELF # a3 <- self + GET_OPA4(rOBJ) # rOBJ <- A+ + JAL(MterpInstanceOf) # v0 <- Mterp(index, &obj, method, self) + lw a1, THREAD_EXCEPTION_OFFSET(rSELF) + PREFETCH_INST(2) # load rINST + bnez a1, MterpException + ADVANCE(2) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(v0, rOBJ, t0) # vA <- v0 diff --git a/runtime/interpreter/mterp/mips/op_int_to_byte.S b/runtime/interpreter/mterp/mips/op_int_to_byte.S new file mode 100644 index 0000000000..77314c62aa --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_int_to_byte.S @@ -0,0 +1 @@ +%include "mips/unop.S" {"preinstr":"sll a0, a0, 24", "instr":"sra a0, a0, 24"} diff --git a/runtime/interpreter/mterp/mips/op_int_to_char.S b/runtime/interpreter/mterp/mips/op_int_to_char.S new file mode 100644 index 0000000000..1b74a6e249 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_int_to_char.S @@ -0,0 +1 @@ +%include "mips/unop.S" {"preinstr":"", "instr":"and a0, 0xffff"} diff --git a/runtime/interpreter/mterp/mips/op_int_to_double.S b/runtime/interpreter/mterp/mips/op_int_to_double.S new file mode 100644 index 0000000000..89484ce34f --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_int_to_double.S @@ -0,0 +1 @@ +%include "mips/funopWider.S" {"instr":"cvt.d.w fv0, fa0"} diff --git a/runtime/interpreter/mterp/mips/op_int_to_float.S b/runtime/interpreter/mterp/mips/op_int_to_float.S new file mode 100644 index 0000000000..d6f4b3609f --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_int_to_float.S @@ -0,0 +1 @@ +%include "mips/funop.S" {"instr":"cvt.s.w fv0, fa0"} diff --git a/runtime/interpreter/mterp/mips/op_int_to_long.S b/runtime/interpreter/mterp/mips/op_int_to_long.S new file mode 100644 index 0000000000..9907463950 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_int_to_long.S @@ -0,0 +1 @@ +%include "mips/unopWider.S" {"instr":"sra a1, a0, 31"} diff --git a/runtime/interpreter/mterp/mips/op_int_to_short.S b/runtime/interpreter/mterp/mips/op_int_to_short.S new file mode 100644 index 0000000000..5649c2a849 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_int_to_short.S @@ -0,0 +1 @@ +%include "mips/unop.S" {"preinstr":"sll a0, 16", "instr":"sra a0, 16"} diff --git a/runtime/interpreter/mterp/mips/op_invoke_direct.S b/runtime/interpreter/mterp/mips/op_invoke_direct.S new file mode 100644 index 0000000000..1ef198a434 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_invoke_direct.S @@ -0,0 +1 @@ +%include "mips/invoke.S" { "helper":"MterpInvokeDirect" } diff --git a/runtime/interpreter/mterp/mips/op_invoke_direct_range.S b/runtime/interpreter/mterp/mips/op_invoke_direct_range.S new file mode 100644 index 0000000000..af7477f2cd --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_invoke_direct_range.S @@ -0,0 +1 @@ +%include "mips/invoke.S" { "helper":"MterpInvokeDirectRange" } diff --git a/runtime/interpreter/mterp/mips/op_invoke_interface.S b/runtime/interpreter/mterp/mips/op_invoke_interface.S new file mode 100644 index 0000000000..80a485a077 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_invoke_interface.S @@ -0,0 +1 @@ +%include "mips/invoke.S" { "helper":"MterpInvokeInterface" } diff --git a/runtime/interpreter/mterp/mips/op_invoke_interface_range.S b/runtime/interpreter/mterp/mips/op_invoke_interface_range.S new file mode 100644 index 0000000000..8d725dc204 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_invoke_interface_range.S @@ -0,0 +1 @@ +%include "mips/invoke.S" { "helper":"MterpInvokeInterfaceRange" } diff --git a/runtime/interpreter/mterp/mips/op_invoke_static.S b/runtime/interpreter/mterp/mips/op_invoke_static.S new file mode 100644 index 0000000000..46253cb3a7 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_invoke_static.S @@ -0,0 +1 @@ +%include "mips/invoke.S" { "helper":"MterpInvokeStatic" } diff --git a/runtime/interpreter/mterp/mips/op_invoke_static_range.S b/runtime/interpreter/mterp/mips/op_invoke_static_range.S new file mode 100644 index 0000000000..96abafe41d --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_invoke_static_range.S @@ -0,0 +1 @@ +%include "mips/invoke.S" { "helper":"MterpInvokeStaticRange" } diff --git a/runtime/interpreter/mterp/mips/op_invoke_super.S b/runtime/interpreter/mterp/mips/op_invoke_super.S new file mode 100644 index 0000000000..473951bcce --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_invoke_super.S @@ -0,0 +1 @@ +%include "mips/invoke.S" { "helper":"MterpInvokeSuper" } diff --git a/runtime/interpreter/mterp/mips/op_invoke_super_range.S b/runtime/interpreter/mterp/mips/op_invoke_super_range.S new file mode 100644 index 0000000000..963ff27ec5 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_invoke_super_range.S @@ -0,0 +1 @@ +%include "mips/invoke.S" { "helper":"MterpInvokeSuperRange" } diff --git a/runtime/interpreter/mterp/mips/op_invoke_virtual.S b/runtime/interpreter/mterp/mips/op_invoke_virtual.S new file mode 100644 index 0000000000..ea51e98abc --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_invoke_virtual.S @@ -0,0 +1 @@ +%include "mips/invoke.S" { "helper":"MterpInvokeVirtual" } diff --git a/runtime/interpreter/mterp/mips/op_invoke_virtual_quick.S b/runtime/interpreter/mterp/mips/op_invoke_virtual_quick.S new file mode 100644 index 0000000000..0c00091219 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_invoke_virtual_quick.S @@ -0,0 +1 @@ +%include "mips/invoke.S" { "helper":"MterpInvokeVirtualQuick" } diff --git a/runtime/interpreter/mterp/mips/op_invoke_virtual_range.S b/runtime/interpreter/mterp/mips/op_invoke_virtual_range.S new file mode 100644 index 0000000000..82201e726e --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_invoke_virtual_range.S @@ -0,0 +1 @@ +%include "mips/invoke.S" { "helper":"MterpInvokeVirtualRange" } diff --git a/runtime/interpreter/mterp/mips/op_invoke_virtual_range_quick.S b/runtime/interpreter/mterp/mips/op_invoke_virtual_range_quick.S new file mode 100644 index 0000000000..c783675dae --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_invoke_virtual_range_quick.S @@ -0,0 +1 @@ +%include "mips/invoke.S" { "helper":"MterpInvokeVirtualQuickRange" } diff --git a/runtime/interpreter/mterp/mips/op_iput.S b/runtime/interpreter/mterp/mips/op_iput.S new file mode 100644 index 0000000000..732a9a45f1 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_iput.S @@ -0,0 +1,21 @@ +%default { "handler":"artSet32InstanceFromMterp" } + /* + * General 32-bit instance field put. + * + * for: iput, iput-boolean, iput-byte, iput-char, iput-short + */ + # op vA, vB, field /* CCCC */ + .extern $handler + EXPORT_PC() + FETCH(a0, 1) # a0 <- field ref CCCC + GET_OPB(a1) # a1 <- B + GET_VREG(a1, a1) # a1 <- fp[B], the object pointer + GET_OPA4(a2) # a2 <- A+ + GET_VREG(a2, a2) # a2 <- fp[A] + lw a3, OFF_FP_METHOD(rFP) # a3 <- referrer + PREFETCH_INST(2) # load rINST + JAL($handler) + bnez v0, MterpPossibleException # bail out + ADVANCE(2) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_iput_boolean.S b/runtime/interpreter/mterp/mips/op_iput_boolean.S new file mode 100644 index 0000000000..da28c978a4 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_iput_boolean.S @@ -0,0 +1 @@ +%include "mips/op_iput.S" { "handler":"artSet8InstanceFromMterp" } diff --git a/runtime/interpreter/mterp/mips/op_iput_boolean_quick.S b/runtime/interpreter/mterp/mips/op_iput_boolean_quick.S new file mode 100644 index 0000000000..7d5caf6d6a --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_iput_boolean_quick.S @@ -0,0 +1 @@ +%include "mips/op_iput_quick.S" { "store":"sb" } diff --git a/runtime/interpreter/mterp/mips/op_iput_byte.S b/runtime/interpreter/mterp/mips/op_iput_byte.S new file mode 100644 index 0000000000..da28c978a4 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_iput_byte.S @@ -0,0 +1 @@ +%include "mips/op_iput.S" { "handler":"artSet8InstanceFromMterp" } diff --git a/runtime/interpreter/mterp/mips/op_iput_byte_quick.S b/runtime/interpreter/mterp/mips/op_iput_byte_quick.S new file mode 100644 index 0000000000..7d5caf6d6a --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_iput_byte_quick.S @@ -0,0 +1 @@ +%include "mips/op_iput_quick.S" { "store":"sb" } diff --git a/runtime/interpreter/mterp/mips/op_iput_char.S b/runtime/interpreter/mterp/mips/op_iput_char.S new file mode 100644 index 0000000000..389b0bf19b --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_iput_char.S @@ -0,0 +1 @@ +%include "mips/op_iput.S" { "handler":"artSet16InstanceFromMterp" } diff --git a/runtime/interpreter/mterp/mips/op_iput_char_quick.S b/runtime/interpreter/mterp/mips/op_iput_char_quick.S new file mode 100644 index 0000000000..4bc84eb581 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_iput_char_quick.S @@ -0,0 +1 @@ +%include "mips/op_iput_quick.S" { "store":"sh" } diff --git a/runtime/interpreter/mterp/mips/op_iput_object.S b/runtime/interpreter/mterp/mips/op_iput_object.S new file mode 100644 index 0000000000..6b856e7bb4 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_iput_object.S @@ -0,0 +1,16 @@ + /* + * 32-bit instance field put. + * + * for: iput-object, iput-object-volatile + */ + # op vA, vB, field /* CCCC */ + EXPORT_PC() + addu a0, rFP, OFF_FP_SHADOWFRAME + move a1, rPC + move a2, rINST + move a3, rSELF + JAL(MterpIputObject) + beqz v0, MterpException + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_iput_object_quick.S b/runtime/interpreter/mterp/mips/op_iput_object_quick.S new file mode 100644 index 0000000000..c3f1526551 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_iput_object_quick.S @@ -0,0 +1,11 @@ + /* For: iput-object-quick */ + # op vA, vB, offset /* CCCC */ + EXPORT_PC() + addu a0, rFP, OFF_FP_SHADOWFRAME + move a1, rPC + move a2, rINST + JAL(MterpIputObjectQuick) + beqz v0, MterpException + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_iput_quick.S b/runtime/interpreter/mterp/mips/op_iput_quick.S new file mode 100644 index 0000000000..08296667a9 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_iput_quick.S @@ -0,0 +1,14 @@ +%default { "store":"sw" } + /* For: iput-quick, iput-object-quick */ + # op vA, vB, offset /* CCCC */ + GET_OPB(a2) # a2 <- B + GET_VREG(a3, a2) # a3 <- fp[B], the object pointer + FETCH(a1, 1) # a1 <- field byte offset + GET_OPA4(a2) # a2 <- A(+) + beqz a3, common_errNullObject # object was null + GET_VREG(a0, a2) # a0 <- fp[A] + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + addu t0, a3, a1 + $store a0, 0(t0) # obj.field (8/16/32 bits) <- a0 + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_iput_short.S b/runtime/interpreter/mterp/mips/op_iput_short.S new file mode 100644 index 0000000000..389b0bf19b --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_iput_short.S @@ -0,0 +1 @@ +%include "mips/op_iput.S" { "handler":"artSet16InstanceFromMterp" } diff --git a/runtime/interpreter/mterp/mips/op_iput_short_quick.S b/runtime/interpreter/mterp/mips/op_iput_short_quick.S new file mode 100644 index 0000000000..4bc84eb581 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_iput_short_quick.S @@ -0,0 +1 @@ +%include "mips/op_iput_quick.S" { "store":"sh" } diff --git a/runtime/interpreter/mterp/mips/op_iput_wide.S b/runtime/interpreter/mterp/mips/op_iput_wide.S new file mode 100644 index 0000000000..6d23f8ccfb --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_iput_wide.S @@ -0,0 +1,15 @@ + # iput-wide vA, vB, field /* CCCC */ + .extern artSet64InstanceFromMterp + EXPORT_PC() + FETCH(a0, 1) # a0 <- field ref CCCC + GET_OPB(a1) # a1 <- B + GET_VREG(a1, a1) # a1 <- fp[B], the object pointer + GET_OPA4(a2) # a2 <- A+ + EAS2(a2, rFP, a2) # a2 <- &fp[A] + lw a3, OFF_FP_METHOD(rFP) # a3 <- referrer + PREFETCH_INST(2) # load rINST + JAL(artSet64InstanceFromMterp) + bnez v0, MterpPossibleException # bail out + ADVANCE(2) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_iput_wide_quick.S b/runtime/interpreter/mterp/mips/op_iput_wide_quick.S new file mode 100644 index 0000000000..9fdb847273 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_iput_wide_quick.S @@ -0,0 +1,14 @@ + # iput-wide-quick vA, vB, offset /* CCCC */ + GET_OPA4(a0) # a0 <- A(+) + GET_OPB(a1) # a1 <- B + GET_VREG(a2, a1) # a2 <- fp[B], the object pointer + # check object for null + beqz a2, common_errNullObject # object was null + EAS2(a3, rFP, a0) # a3 <- &fp[A] + LOAD64(a0, a1, a3) # a0/a1 <- fp[A] + FETCH(a3, 1) # a3 <- field byte offset + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + addu a2, a2, a3 # obj.field (64 bits, aligned) <- a0/a1 + STORE64(a0, a1, a2) # obj.field (64 bits, aligned) <- a0/a1 + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_long_to_double.S b/runtime/interpreter/mterp/mips/op_long_to_double.S new file mode 100644 index 0000000000..b83aaf41d8 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_long_to_double.S @@ -0,0 +1 @@ +%include "mips/funopWide.S" {"instr":"JAL(__floatdidf)", "ld_arg":"LOAD64(rARG0, rARG1, a3)"} diff --git a/runtime/interpreter/mterp/mips/op_long_to_float.S b/runtime/interpreter/mterp/mips/op_long_to_float.S new file mode 100644 index 0000000000..27faba57dd --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_long_to_float.S @@ -0,0 +1 @@ +%include "mips/unopNarrower.S" {"instr":"JAL(__floatdisf)", "load":"LOAD64(rARG0, rARG1, a3)"} diff --git a/runtime/interpreter/mterp/mips/op_long_to_int.S b/runtime/interpreter/mterp/mips/op_long_to_int.S new file mode 100644 index 0000000000..949c180f31 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_long_to_int.S @@ -0,0 +1,2 @@ +/* we ignore the high word, making this equivalent to a 32-bit reg move */ +%include "mips/op_move.S" diff --git a/runtime/interpreter/mterp/mips/op_monitor_enter.S b/runtime/interpreter/mterp/mips/op_monitor_enter.S new file mode 100644 index 0000000000..20d90294ac --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_monitor_enter.S @@ -0,0 +1,13 @@ + /* + * Synchronize on an object. + */ + /* monitor-enter vAA */ + EXPORT_PC() + GET_OPA(a2) # a2 <- AA + GET_VREG(a0, a2) # a0 <- vAA (object) + move a1, rSELF # a1 <- self + JAL(artLockObjectFromCode) # v0 <- artLockObject(obj, self) + bnez v0, MterpException + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_monitor_exit.S b/runtime/interpreter/mterp/mips/op_monitor_exit.S new file mode 100644 index 0000000000..1eadff923b --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_monitor_exit.S @@ -0,0 +1,17 @@ + /* + * Unlock an object. + * + * Exceptions that occur when unlocking a monitor need to appear as + * if they happened at the following instruction. See the Dalvik + * instruction spec. + */ + /* monitor-exit vAA */ + EXPORT_PC() + GET_OPA(a2) # a2 <- AA + GET_VREG(a0, a2) # a0 <- vAA (object) + move a1, rSELF # a1 <- self + JAL(artUnlockObjectFromCode) # v0 <- artUnlockObject(obj, self) + bnez v0, MterpException + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_move.S b/runtime/interpreter/mterp/mips/op_move.S new file mode 100644 index 0000000000..76588ba39e --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_move.S @@ -0,0 +1,14 @@ +%default { "is_object":"0" } + /* for move, move-object, long-to-int */ + /* op vA, vB */ + GET_OPB(a1) # a1 <- B from 15:12 + GET_OPA4(a0) # a0 <- A from 11:8 + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + GET_VREG(a2, a1) # a2 <- fp[B] + GET_INST_OPCODE(t0) # t0 <- opcode from rINST + .if $is_object + SET_VREG_OBJECT(a2, a0) # fp[A] <- a2 + .else + SET_VREG(a2, a0) # fp[A] <- a2 + .endif + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_move_16.S b/runtime/interpreter/mterp/mips/op_move_16.S new file mode 100644 index 0000000000..f7de6c20b5 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_move_16.S @@ -0,0 +1,14 @@ +%default { "is_object":"0" } + /* for: move/16, move-object/16 */ + /* op vAAAA, vBBBB */ + FETCH(a1, 2) # a1 <- BBBB + FETCH(a0, 1) # a0 <- AAAA + FETCH_ADVANCE_INST(3) # advance rPC, load rINST + GET_VREG(a2, a1) # a2 <- fp[BBBB] + GET_INST_OPCODE(t0) # extract opcode from rINST + .if $is_object + SET_VREG_OBJECT(a2, a0) # fp[AAAA] <- a2 + .else + SET_VREG(a2, a0) # fp[AAAA] <- a2 + .endif + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_move_exception.S b/runtime/interpreter/mterp/mips/op_move_exception.S new file mode 100644 index 0000000000..f04a035513 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_move_exception.S @@ -0,0 +1,8 @@ + /* move-exception vAA */ + GET_OPA(a2) # a2 <- AA + lw a3, THREAD_EXCEPTION_OFFSET(rSELF) # get exception obj + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + SET_VREG_OBJECT(a3, a2) # fp[AA] <- exception obj + GET_INST_OPCODE(t0) # extract opcode from rINST + sw zero, THREAD_EXCEPTION_OFFSET(rSELF) # clear exception + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_move_from16.S b/runtime/interpreter/mterp/mips/op_move_from16.S new file mode 100644 index 0000000000..b8be7411aa --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_move_from16.S @@ -0,0 +1,14 @@ +%default { "is_object":"0" } + /* for: move/from16, move-object/from16 */ + /* op vAA, vBBBB */ + FETCH(a1, 1) # a1 <- BBBB + GET_OPA(a0) # a0 <- AA + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_VREG(a2, a1) # a2 <- fp[BBBB] + GET_INST_OPCODE(t0) # extract opcode from rINST + .if $is_object + SET_VREG_OBJECT(a2, a0) # fp[AA] <- a2 + .else + SET_VREG(a2, a0) # fp[AA] <- a2 + .endif + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_move_object.S b/runtime/interpreter/mterp/mips/op_move_object.S new file mode 100644 index 0000000000..9420ff359f --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_move_object.S @@ -0,0 +1 @@ +%include "mips/op_move.S" {"is_object":"1"} diff --git a/runtime/interpreter/mterp/mips/op_move_object_16.S b/runtime/interpreter/mterp/mips/op_move_object_16.S new file mode 100644 index 0000000000..d6454c222d --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_move_object_16.S @@ -0,0 +1 @@ +%include "mips/op_move_16.S" {"is_object":"1"} diff --git a/runtime/interpreter/mterp/mips/op_move_object_from16.S b/runtime/interpreter/mterp/mips/op_move_object_from16.S new file mode 100644 index 0000000000..db0aca1f83 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_move_object_from16.S @@ -0,0 +1 @@ +%include "mips/op_move_from16.S" {"is_object":"1"} diff --git a/runtime/interpreter/mterp/mips/op_move_result.S b/runtime/interpreter/mterp/mips/op_move_result.S new file mode 100644 index 0000000000..315c68ef3f --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_move_result.S @@ -0,0 +1,14 @@ +%default { "is_object":"0" } + /* for: move-result, move-result-object */ + /* op vAA */ + GET_OPA(a2) # a2 <- AA + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + lw a0, OFF_FP_RESULT_REGISTER(rFP) # get pointer to result JType + lw a0, 0(a0) # a0 <- result.i + GET_INST_OPCODE(t0) # extract opcode from rINST + .if $is_object + SET_VREG_OBJECT(a0, a2) # fp[AA] <- a0 + .else + SET_VREG(a0, a2) # fp[AA] <- a0 + .endif + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_move_result_object.S b/runtime/interpreter/mterp/mips/op_move_result_object.S new file mode 100644 index 0000000000..fcbffee281 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_move_result_object.S @@ -0,0 +1 @@ +%include "mips/op_move_result.S" {"is_object":"1"} diff --git a/runtime/interpreter/mterp/mips/op_move_result_wide.S b/runtime/interpreter/mterp/mips/op_move_result_wide.S new file mode 100644 index 0000000000..940c1ff9b2 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_move_result_wide.S @@ -0,0 +1,8 @@ + /* move-result-wide vAA */ + GET_OPA(a2) # a2 <- AA + lw a3, OFF_FP_RESULT_REGISTER(rFP) # get pointer to result JType + LOAD64(a0, a1, a3) # a0/a1 <- retval.j + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + SET_VREG64(a0, a1, a2) # fp[AA] <- a0/a1 + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_move_wide.S b/runtime/interpreter/mterp/mips/op_move_wide.S new file mode 100644 index 0000000000..dd224c390e --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_move_wide.S @@ -0,0 +1,10 @@ + /* move-wide vA, vB */ + /* NOTE: regs can overlap, e.g. "move v6, v7" or "move v7, v6" */ + GET_OPA4(a2) # a2 <- A(+) + GET_OPB(a3) # a3 <- B + EAS2(a3, rFP, a3) # a3 <- &fp[B] + LOAD64(a0, a1, a3) # a0/a1 <- fp[B] + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + SET_VREG64(a0, a1, a2) # fp[A] <- a0/a1 + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_move_wide_16.S b/runtime/interpreter/mterp/mips/op_move_wide_16.S new file mode 100644 index 0000000000..d8761eb2ef --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_move_wide_16.S @@ -0,0 +1,10 @@ + /* move-wide/16 vAAAA, vBBBB */ + /* NOTE: regs can overlap, e.g. "move v6, v7" or "move v7, v6" */ + FETCH(a3, 2) # a3 <- BBBB + FETCH(a2, 1) # a2 <- AAAA + EAS2(a3, rFP, a3) # a3 <- &fp[BBBB] + LOAD64(a0, a1, a3) # a0/a1 <- fp[BBBB] + FETCH_ADVANCE_INST(3) # advance rPC, load rINST + SET_VREG64(a0, a1, a2) # fp[AAAA] <- a0/a1 + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_move_wide_from16.S b/runtime/interpreter/mterp/mips/op_move_wide_from16.S new file mode 100644 index 0000000000..2103fa19ae --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_move_wide_from16.S @@ -0,0 +1,10 @@ + /* move-wide/from16 vAA, vBBBB */ + /* NOTE: regs can overlap, e.g. "move v6, v7" or "move v7, v6" */ + FETCH(a3, 1) # a3 <- BBBB + GET_OPA(a2) # a2 <- AA + EAS2(a3, rFP, a3) # a3 <- &fp[BBBB] + LOAD64(a0, a1, a3) # a0/a1 <- fp[BBBB] + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + SET_VREG64(a0, a1, a2) # fp[AA] <- a0/a1 + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_mul_double.S b/runtime/interpreter/mterp/mips/op_mul_double.S new file mode 100644 index 0000000000..44a473bac1 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_mul_double.S @@ -0,0 +1 @@ +%include "mips/fbinopWide.S" {"instr":"mul.d fv0, fa0, fa1"} diff --git a/runtime/interpreter/mterp/mips/op_mul_double_2addr.S b/runtime/interpreter/mterp/mips/op_mul_double_2addr.S new file mode 100644 index 0000000000..4e5c230bc5 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_mul_double_2addr.S @@ -0,0 +1 @@ +%include "mips/fbinopWide2addr.S" {"instr":"mul.d fv0, fa0, fa1"} diff --git a/runtime/interpreter/mterp/mips/op_mul_float.S b/runtime/interpreter/mterp/mips/op_mul_float.S new file mode 100644 index 0000000000..abc9390543 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_mul_float.S @@ -0,0 +1 @@ +%include "mips/fbinop.S" {"instr":"mul.s fv0, fa0, fa1"} diff --git a/runtime/interpreter/mterp/mips/op_mul_float_2addr.S b/runtime/interpreter/mterp/mips/op_mul_float_2addr.S new file mode 100644 index 0000000000..2469109518 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_mul_float_2addr.S @@ -0,0 +1 @@ +%include "mips/fbinop2addr.S" {"instr":"mul.s fv0, fa0, fa1"} diff --git a/runtime/interpreter/mterp/mips/op_mul_int.S b/runtime/interpreter/mterp/mips/op_mul_int.S new file mode 100644 index 0000000000..266823c2c7 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_mul_int.S @@ -0,0 +1 @@ +%include "mips/binop.S" {"instr":"mul a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips/op_mul_int_2addr.S b/runtime/interpreter/mterp/mips/op_mul_int_2addr.S new file mode 100644 index 0000000000..b7dc5d3fb1 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_mul_int_2addr.S @@ -0,0 +1 @@ +%include "mips/binop2addr.S" {"instr":"mul a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips/op_mul_int_lit16.S b/runtime/interpreter/mterp/mips/op_mul_int_lit16.S new file mode 100644 index 0000000000..fb4c8ec27d --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_mul_int_lit16.S @@ -0,0 +1 @@ +%include "mips/binopLit16.S" {"instr":"mul a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips/op_mul_int_lit8.S b/runtime/interpreter/mterp/mips/op_mul_int_lit8.S new file mode 100644 index 0000000000..6d2e7ded0c --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_mul_int_lit8.S @@ -0,0 +1 @@ +%include "mips/binopLit8.S" {"instr":"mul a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips/op_mul_long.S b/runtime/interpreter/mterp/mips/op_mul_long.S new file mode 100644 index 0000000000..803bbecf17 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_mul_long.S @@ -0,0 +1,43 @@ + /* + * Signed 64-bit integer multiply. + * a1 a0 + * x a3 a2 + * ------------- + * a2a1 a2a0 + * a3a0 + * a3a1 (<= unused) + * --------------- + * v1 v0 + */ + /* mul-long vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + and t0, a0, 255 # a2 <- BB + srl t1, a0, 8 # a3 <- CC + EAS2(t0, rFP, t0) # t0 <- &fp[BB] + LOAD64(a0, a1, t0) # a0/a1 <- vBB/vBB+1 + + EAS2(t1, rFP, t1) # t0 <- &fp[CC] + LOAD64(a2, a3, t1) # a2/a3 <- vCC/vCC+1 + + mul v1, a3, a0 # v1= a3a0 +#ifdef MIPS32REVGE6 + mulu v0, a2, a0 # v0= a2a0 + muhu t1, a2, a0 +#else + multu a2, a0 + mfhi t1 + mflo v0 # v0= a2a0 +#endif + mul t0, a2, a1 # t0= a2a1 + addu v1, v1, t1 # v1+= hi(a2a0) + addu v1, v1, t0 # v1= a3a0 + a2a1; + + GET_OPA(a0) # a0 <- AA + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + b .L${opcode}_finish +%break + +.L${opcode}_finish: + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG64(v0, v1, a0) # vAA::vAA+1 <- v0(low) :: v1(high) + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_mul_long_2addr.S b/runtime/interpreter/mterp/mips/op_mul_long_2addr.S new file mode 100644 index 0000000000..6950b7170b --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_mul_long_2addr.S @@ -0,0 +1,31 @@ + /* + * See op_mul_long.S for more details + */ + /* mul-long/2addr vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + + EAS2(t0, rFP, rOBJ) # t0 <- &fp[A] + LOAD64(a0, a1, t0) # vAA.low / high + + GET_OPB(t1) # t1 <- B + EAS2(t1, rFP, t1) # t1 <- &fp[B] + LOAD64(a2, a3, t1) # vBB.low / high + + mul v1, a3, a0 # v1= a3a0 +#ifdef MIPS32REVGE6 + mulu v0, a2, a0 # v0= a2a0 + muhu t1, a2, a0 +#else + multu a2, a0 + mfhi t1 + mflo v0 # v0= a2a0 + #endif + mul t2, a2, a1 # t2= a2a1 + addu v1, v1, t1 # v1= a3a0 + hi(a2a0) + addu v1, v1, t2 # v1= v1 + a2a1; + + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + GET_INST_OPCODE(t1) # extract opcode from rINST + # vAA <- v0 (low) + SET_VREG64(v0, v1, rOBJ) # vAA+1 <- v1 (high) + GOTO_OPCODE(t1) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_neg_double.S b/runtime/interpreter/mterp/mips/op_neg_double.S new file mode 100644 index 0000000000..89cc918b80 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_neg_double.S @@ -0,0 +1 @@ +%include "mips/unopWide.S" {"instr":"addu a1, a1, 0x80000000"} diff --git a/runtime/interpreter/mterp/mips/op_neg_float.S b/runtime/interpreter/mterp/mips/op_neg_float.S new file mode 100644 index 0000000000..e702755f11 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_neg_float.S @@ -0,0 +1 @@ +%include "mips/unop.S" {"instr":"addu a0, a0, 0x80000000"} diff --git a/runtime/interpreter/mterp/mips/op_neg_int.S b/runtime/interpreter/mterp/mips/op_neg_int.S new file mode 100644 index 0000000000..4461731465 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_neg_int.S @@ -0,0 +1 @@ +%include "mips/unop.S" {"instr":"negu a0, a0"} diff --git a/runtime/interpreter/mterp/mips/op_neg_long.S b/runtime/interpreter/mterp/mips/op_neg_long.S new file mode 100644 index 0000000000..71e60f59be --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_neg_long.S @@ -0,0 +1 @@ +%include "mips/unopWide.S" {"result0":"v0", "result1":"v1", "preinstr":"negu v0, a0", "instr":"negu v1, a1; sltu a0, zero, v0; subu v1, v1, a0"} diff --git a/runtime/interpreter/mterp/mips/op_new_array.S b/runtime/interpreter/mterp/mips/op_new_array.S new file mode 100644 index 0000000000..4a6512d7c5 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_new_array.S @@ -0,0 +1,18 @@ + /* + * Allocate an array of objects, specified with the array class + * and a count. + * + * The verifier guarantees that this is an array class, so we don't + * check for it here. + */ + /* new-array vA, vB, class@CCCC */ + EXPORT_PC() + addu a0, rFP, OFF_FP_SHADOWFRAME + move a1, rPC + move a2, rINST + move a3, rSELF + JAL(MterpNewArray) + beqz v0, MterpPossibleException + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_new_instance.S b/runtime/interpreter/mterp/mips/op_new_instance.S new file mode 100644 index 0000000000..51a09b2e90 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_new_instance.S @@ -0,0 +1,13 @@ + /* + * Create a new instance of a class. + */ + # new-instance vAA, class /* BBBB */ + EXPORT_PC() + addu a0, rFP, OFF_FP_SHADOWFRAME + move a1, rSELF + move a2, rINST + JAL(MterpNewInstance) + beqz v0, MterpPossibleException + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_nop.S b/runtime/interpreter/mterp/mips/op_nop.S new file mode 100644 index 0000000000..3565631e03 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_nop.S @@ -0,0 +1,3 @@ + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_not_int.S b/runtime/interpreter/mterp/mips/op_not_int.S new file mode 100644 index 0000000000..55d8cc11d1 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_not_int.S @@ -0,0 +1 @@ +%include "mips/unop.S" {"instr":"not a0, a0"} diff --git a/runtime/interpreter/mterp/mips/op_not_long.S b/runtime/interpreter/mterp/mips/op_not_long.S new file mode 100644 index 0000000000..9e7c95bcea --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_not_long.S @@ -0,0 +1 @@ +%include "mips/unopWide.S" {"preinstr":"not a0, a0", "instr":"not a1, a1"} diff --git a/runtime/interpreter/mterp/mips/op_or_int.S b/runtime/interpreter/mterp/mips/op_or_int.S new file mode 100644 index 0000000000..c7ce760a73 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_or_int.S @@ -0,0 +1 @@ +%include "mips/binop.S" {"instr":"or a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips/op_or_int_2addr.S b/runtime/interpreter/mterp/mips/op_or_int_2addr.S new file mode 100644 index 0000000000..192d611e8d --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_or_int_2addr.S @@ -0,0 +1 @@ +%include "mips/binop2addr.S" {"instr":"or a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips/op_or_int_lit16.S b/runtime/interpreter/mterp/mips/op_or_int_lit16.S new file mode 100644 index 0000000000..f4ef75fff8 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_or_int_lit16.S @@ -0,0 +1 @@ +%include "mips/binopLit16.S" {"instr":"or a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips/op_or_int_lit8.S b/runtime/interpreter/mterp/mips/op_or_int_lit8.S new file mode 100644 index 0000000000..f6212e217d --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_or_int_lit8.S @@ -0,0 +1 @@ +%include "mips/binopLit8.S" {"instr":"or a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips/op_or_long.S b/runtime/interpreter/mterp/mips/op_or_long.S new file mode 100644 index 0000000000..0f94486e42 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_or_long.S @@ -0,0 +1 @@ +%include "mips/binopWide.S" {"preinstr":"or a0, a0, a2", "instr":"or a1, a1, a3"} diff --git a/runtime/interpreter/mterp/mips/op_or_long_2addr.S b/runtime/interpreter/mterp/mips/op_or_long_2addr.S new file mode 100644 index 0000000000..43c3d05d41 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_or_long_2addr.S @@ -0,0 +1 @@ +%include "mips/binopWide2addr.S" {"preinstr":"or a0, a0, a2", "instr":"or a1, a1, a3"} diff --git a/runtime/interpreter/mterp/mips/op_packed_switch.S b/runtime/interpreter/mterp/mips/op_packed_switch.S new file mode 100644 index 0000000000..93fae973e5 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_packed_switch.S @@ -0,0 +1,57 @@ +%default { "func":"MterpDoPackedSwitch" } + /* + * Handle a packed-switch or sparse-switch instruction. In both cases + * we decode it and hand it off to a helper function. + * + * We don't really expect backward branches in a switch statement, but + * they're perfectly legal, so we check for them here. + * + * for: packed-switch, sparse-switch + */ + /* op vAA, +BBBB */ +#if MTERP_PROFILE_BRANCHES + FETCH(a0, 1) # a0 <- bbbb (lo) + FETCH(a1, 2) # a1 <- BBBB (hi) + GET_OPA(a3) # a3 <- AA + sll t0, a1, 16 + or a0, a0, t0 # a0 <- BBBBbbbb + GET_VREG(a1, a3) # a1 <- vAA + EAS1(a0, rPC, a0) # a0 <- PC + BBBBbbbb*2 + JAL($func) # a0 <- code-unit branch offset + move rINST, v0 + EXPORT_PC() + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + JAL(MterpProfileBranch) # (self, shadow_frame, offset) + bnez v0, MterpOnStackReplacement # Note: offset must be in rINST + addu a1, rINST, rINST # a1 <- byte offset + FETCH_ADVANCE_INST_RB(a1) # update rPC, load rINST + bgtz a1, .L${opcode}_finish + lw ra, THREAD_FLAGS_OFFSET(rSELF) + b MterpCheckSuspendAndContinue +#else + FETCH(a0, 1) # a0 <- bbbb (lo) + FETCH(a1, 2) # a1 <- BBBB (hi) + GET_OPA(a3) # a3 <- AA + sll t0, a1, 16 + or a0, a0, t0 # a0 <- BBBBbbbb + GET_VREG(a1, a3) # a1 <- vAA + EAS1(a0, rPC, a0) # a0 <- PC + BBBBbbbb*2 + JAL($func) # a0 <- code-unit branch offset + move rINST, v0 + addu a1, rINST, rINST # a1 <- byte offset + FETCH_ADVANCE_INST_RB(a1) # update rPC, load rINST + bgtz a1, 1f + lw ra, THREAD_FLAGS_OFFSET(rSELF) + b MterpCheckSuspendAndContinue +1: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction +#endif + +%break + +.L${opcode}_finish: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_rem_double.S b/runtime/interpreter/mterp/mips/op_rem_double.S new file mode 100644 index 0000000000..a6890a8029 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_rem_double.S @@ -0,0 +1 @@ +%include "mips/fbinopWide.S" {"instr":"JAL(fmod)"} diff --git a/runtime/interpreter/mterp/mips/op_rem_double_2addr.S b/runtime/interpreter/mterp/mips/op_rem_double_2addr.S new file mode 100644 index 0000000000..a24e1604fa --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_rem_double_2addr.S @@ -0,0 +1 @@ +%include "mips/fbinopWide2addr.S" {"instr":"JAL(fmod)"} diff --git a/runtime/interpreter/mterp/mips/op_rem_float.S b/runtime/interpreter/mterp/mips/op_rem_float.S new file mode 100644 index 0000000000..ac3d50ce75 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_rem_float.S @@ -0,0 +1 @@ +%include "mips/fbinop.S" {"instr":"JAL(fmodf)"} diff --git a/runtime/interpreter/mterp/mips/op_rem_float_2addr.S b/runtime/interpreter/mterp/mips/op_rem_float_2addr.S new file mode 100644 index 0000000000..7f0a9320c8 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_rem_float_2addr.S @@ -0,0 +1 @@ +%include "mips/fbinop2addr.S" {"instr":"JAL(fmodf)"} diff --git a/runtime/interpreter/mterp/mips/op_rem_int.S b/runtime/interpreter/mterp/mips/op_rem_int.S new file mode 100644 index 0000000000..c2a334a879 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_rem_int.S @@ -0,0 +1,5 @@ +#ifdef MIPS32REVGE6 +%include "mips/binop.S" {"instr":"mod a0, a0, a1", "chkzero":"1"} +#else +%include "mips/binop.S" {"preinstr":"div zero, a0, a1", "instr":"mfhi a0", "chkzero":"1"} +#endif diff --git a/runtime/interpreter/mterp/mips/op_rem_int_2addr.S b/runtime/interpreter/mterp/mips/op_rem_int_2addr.S new file mode 100644 index 0000000000..46c353fa83 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_rem_int_2addr.S @@ -0,0 +1,5 @@ +#ifdef MIPS32REVGE6 +%include "mips/binop2addr.S" {"instr":"mod a0, a0, a1", "chkzero":"1"} +#else +%include "mips/binop2addr.S" {"preinstr":"div zero, a0, a1", "instr":"mfhi a0", "chkzero":"1"} +#endif diff --git a/runtime/interpreter/mterp/mips/op_rem_int_lit16.S b/runtime/interpreter/mterp/mips/op_rem_int_lit16.S new file mode 100644 index 0000000000..2894ad37a2 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_rem_int_lit16.S @@ -0,0 +1,5 @@ +#ifdef MIPS32REVGE6 +%include "mips/binopLit16.S" {"instr":"mod a0, a0, a1", "chkzero":"1"} +#else +%include "mips/binopLit16.S" {"preinstr":"div zero, a0, a1", "instr":"mfhi a0", "chkzero":"1"} +#endif diff --git a/runtime/interpreter/mterp/mips/op_rem_int_lit8.S b/runtime/interpreter/mterp/mips/op_rem_int_lit8.S new file mode 100644 index 0000000000..582248ba8f --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_rem_int_lit8.S @@ -0,0 +1,5 @@ +#ifdef MIPS32REVGE6 +%include "mips/binopLit8.S" {"instr":"mod a0, a0, a1", "chkzero":"1"} +#else +%include "mips/binopLit8.S" {"preinstr":"div zero, a0, a1", "instr":"mfhi a0", "chkzero":"1"} +#endif diff --git a/runtime/interpreter/mterp/mips/op_rem_long.S b/runtime/interpreter/mterp/mips/op_rem_long.S new file mode 100644 index 0000000000..e3eb19bed4 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_rem_long.S @@ -0,0 +1 @@ +%include "mips/binopWide.S" { "result0":"v0", "result1":"v1", "instr":"JAL(__moddi3)", "chkzero":"1"} diff --git a/runtime/interpreter/mterp/mips/op_rem_long_2addr.S b/runtime/interpreter/mterp/mips/op_rem_long_2addr.S new file mode 100644 index 0000000000..8fc9fdb15f --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_rem_long_2addr.S @@ -0,0 +1 @@ +%include "mips/binopWide2addr.S" { "result0":"v0", "result1":"v1", "instr":"JAL(__moddi3)", "chkzero":"1"} diff --git a/runtime/interpreter/mterp/mips/op_return.S b/runtime/interpreter/mterp/mips/op_return.S new file mode 100644 index 0000000000..894ae18de0 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_return.S @@ -0,0 +1,18 @@ + /* + * Return a 32-bit value. + * + * for: return, return-object + */ + /* op vAA */ + .extern MterpThreadFenceForConstructor + JAL(MterpThreadFenceForConstructor) + lw ra, THREAD_FLAGS_OFFSET(rSELF) + move a0, rSELF + and ra, (THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + beqz ra, 1f + JAL(MterpSuspendCheck) # (self) +1: + GET_OPA(a2) # a2 <- AA + GET_VREG(v0, a2) # v0 <- vAA + move v1, zero + b MterpReturn diff --git a/runtime/interpreter/mterp/mips/op_return_object.S b/runtime/interpreter/mterp/mips/op_return_object.S new file mode 100644 index 0000000000..7350e008c5 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_return_object.S @@ -0,0 +1 @@ +%include "mips/op_return.S" diff --git a/runtime/interpreter/mterp/mips/op_return_void.S b/runtime/interpreter/mterp/mips/op_return_void.S new file mode 100644 index 0000000000..35c1326306 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_return_void.S @@ -0,0 +1,11 @@ + .extern MterpThreadFenceForConstructor + JAL(MterpThreadFenceForConstructor) + lw ra, THREAD_FLAGS_OFFSET(rSELF) + move a0, rSELF + and ra, (THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + beqz ra, 1f + JAL(MterpSuspendCheck) # (self) +1: + move v0, zero + move v1, zero + b MterpReturn diff --git a/runtime/interpreter/mterp/mips/op_return_void_no_barrier.S b/runtime/interpreter/mterp/mips/op_return_void_no_barrier.S new file mode 100644 index 0000000000..56968b5fc4 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_return_void_no_barrier.S @@ -0,0 +1,9 @@ + lw ra, THREAD_FLAGS_OFFSET(rSELF) + move a0, rSELF + and ra, (THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + beqz ra, 1f + JAL(MterpSuspendCheck) # (self) +1: + move v0, zero + move v1, zero + b MterpReturn diff --git a/runtime/interpreter/mterp/mips/op_return_wide.S b/runtime/interpreter/mterp/mips/op_return_wide.S new file mode 100644 index 0000000000..91d62bf550 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_return_wide.S @@ -0,0 +1,16 @@ + /* + * Return a 64-bit value. + */ + /* return-wide vAA */ + .extern MterpThreadFenceForConstructor + JAL(MterpThreadFenceForConstructor) + lw ra, THREAD_FLAGS_OFFSET(rSELF) + move a0, rSELF + and ra, (THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + beqz ra, 1f + JAL(MterpSuspendCheck) # (self) +1: + GET_OPA(a2) # a2 <- AA + EAS2(a2, rFP, a2) # a2 <- &fp[AA] + LOAD64(v0, v1, a2) # v0/v1 <- vAA/vAA+1 + b MterpReturn diff --git a/runtime/interpreter/mterp/mips/op_rsub_int.S b/runtime/interpreter/mterp/mips/op_rsub_int.S new file mode 100644 index 0000000000..f7e61bb2e9 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_rsub_int.S @@ -0,0 +1,2 @@ +/* this op is "rsub-int", but can be thought of as "rsub-int/lit16" */ +%include "mips/binopLit16.S" {"instr":"subu a0, a1, a0"} diff --git a/runtime/interpreter/mterp/mips/op_rsub_int_lit8.S b/runtime/interpreter/mterp/mips/op_rsub_int_lit8.S new file mode 100644 index 0000000000..3968a5ef8c --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_rsub_int_lit8.S @@ -0,0 +1 @@ +%include "mips/binopLit8.S" {"instr":"subu a0, a1, a0"} diff --git a/runtime/interpreter/mterp/mips/op_sget.S b/runtime/interpreter/mterp/mips/op_sget.S new file mode 100644 index 0000000000..3efcfbb7a5 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_sget.S @@ -0,0 +1,25 @@ +%default { "is_object":"0", "helper":"artGet32StaticFromCode" } + /* + * General SGET handler. + * + * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short + */ + # op vAA, field /* BBBB */ + .extern $helper + EXPORT_PC() + FETCH(a0, 1) # a0 <- field ref BBBB + lw a1, OFF_FP_METHOD(rFP) # a1 <- method + move a2, rSELF # a2 <- self + JAL($helper) + lw a3, THREAD_EXCEPTION_OFFSET(rSELF) + GET_OPA(a2) # a2 <- AA + PREFETCH_INST(2) + bnez a3, MterpException # bail out +.if $is_object + SET_VREG_OBJECT(v0, a2) # fp[AA] <- v0 +.else + SET_VREG(v0, a2) # fp[AA] <- v0 +.endif + ADVANCE(2) + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_sget_boolean.S b/runtime/interpreter/mterp/mips/op_sget_boolean.S new file mode 100644 index 0000000000..45a5a70228 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_sget_boolean.S @@ -0,0 +1 @@ +%include "mips/op_sget.S" {"helper":"artGetBooleanStaticFromCode"} diff --git a/runtime/interpreter/mterp/mips/op_sget_byte.S b/runtime/interpreter/mterp/mips/op_sget_byte.S new file mode 100644 index 0000000000..319122cac0 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_sget_byte.S @@ -0,0 +1 @@ +%include "mips/op_sget.S" {"helper":"artGetByteStaticFromCode"} diff --git a/runtime/interpreter/mterp/mips/op_sget_char.S b/runtime/interpreter/mterp/mips/op_sget_char.S new file mode 100644 index 0000000000..71038478e0 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_sget_char.S @@ -0,0 +1 @@ +%include "mips/op_sget.S" {"helper":"artGetCharStaticFromCode"} diff --git a/runtime/interpreter/mterp/mips/op_sget_object.S b/runtime/interpreter/mterp/mips/op_sget_object.S new file mode 100644 index 0000000000..b205f513aa --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_sget_object.S @@ -0,0 +1 @@ +%include "mips/op_sget.S" {"is_object":"1", "helper":"artGetObjStaticFromCode"} diff --git a/runtime/interpreter/mterp/mips/op_sget_short.S b/runtime/interpreter/mterp/mips/op_sget_short.S new file mode 100644 index 0000000000..3301823d86 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_sget_short.S @@ -0,0 +1 @@ +%include "mips/op_sget.S" {"helper":"artGetShortStaticFromCode"} diff --git a/runtime/interpreter/mterp/mips/op_sget_wide.S b/runtime/interpreter/mterp/mips/op_sget_wide.S new file mode 100644 index 0000000000..7aee38655c --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_sget_wide.S @@ -0,0 +1,17 @@ + /* + * 64-bit SGET handler. + */ + # sget-wide vAA, field /* BBBB */ + .extern artGet64StaticFromCode + EXPORT_PC() + FETCH(a0, 1) # a0 <- field ref BBBB + lw a1, OFF_FP_METHOD(rFP) # a1 <- method + move a2, rSELF # a2 <- self + JAL(artGet64StaticFromCode) + lw a3, THREAD_EXCEPTION_OFFSET(rSELF) + bnez a3, MterpException + GET_OPA(a1) # a1 <- AA + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + SET_VREG64(v0, v1, a1) # vAA/vAA+1 <- v0/v1 + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_shl_int.S b/runtime/interpreter/mterp/mips/op_shl_int.S new file mode 100644 index 0000000000..15cbe94113 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_shl_int.S @@ -0,0 +1 @@ +%include "mips/binop.S" {"instr":"sll a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips/op_shl_int_2addr.S b/runtime/interpreter/mterp/mips/op_shl_int_2addr.S new file mode 100644 index 0000000000..ef9bd655ab --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_shl_int_2addr.S @@ -0,0 +1 @@ +%include "mips/binop2addr.S" {"instr":"sll a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips/op_shl_int_lit8.S b/runtime/interpreter/mterp/mips/op_shl_int_lit8.S new file mode 100644 index 0000000000..d2afb53e14 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_shl_int_lit8.S @@ -0,0 +1 @@ +%include "mips/binopLit8.S" {"instr":"sll a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips/op_shl_long.S b/runtime/interpreter/mterp/mips/op_shl_long.S new file mode 100644 index 0000000000..0121669a10 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_shl_long.S @@ -0,0 +1,31 @@ + /* + * Long integer shift. This is different from the generic 32/64-bit + * binary operations because vAA/vBB are 64-bit but vCC (the shift + * distance) is 32-bit. Also, Dalvik requires us to mask off the low + * 6 bits of the shift distance. + */ + /* shl-long vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(t2) # t2 <- AA + and a3, a0, 255 # a3 <- BB + srl a0, a0, 8 # a0 <- CC + EAS2(a3, rFP, a3) # a3 <- &fp[BB] + GET_VREG(a2, a0) # a2 <- vCC + LOAD64(a0, a1, a3) # a0/a1 <- vBB/vBB+1 + + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + + andi v1, a2, 0x20 # shift< shift & 0x20 + sll v0, a0, a2 # rlo<- alo << (shift&31) + bnez v1, .L${opcode}_finish + not v1, a2 # rhi<- 31-shift (shift is 5b) + srl a0, 1 + srl a0, v1 # alo<- alo >> (32-(shift&31)) + sll v1, a1, a2 # rhi<- ahi << (shift&31) + or v1, a0 # rhi<- rhi | alo + SET_VREG64_GOTO(v0, v1, t2, t0) # vAA/vAA+1 <- a0/a1 +%break + +.L${opcode}_finish: + SET_VREG64_GOTO(zero, v0, t2, t0) # vAA/vAA+1 <- rlo/rhi diff --git a/runtime/interpreter/mterp/mips/op_shl_long_2addr.S b/runtime/interpreter/mterp/mips/op_shl_long_2addr.S new file mode 100644 index 0000000000..8ce6058ce2 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_shl_long_2addr.S @@ -0,0 +1,27 @@ + /* + * Long integer shift, 2addr version. vA is 64-bit value/result, vB is + * 32-bit shift distance. + */ + /* shl-long/2addr vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a3) # a3 <- B + GET_VREG(a2, a3) # a2 <- vB + EAS2(t2, rFP, rOBJ) # t2 <- &fp[A] + LOAD64(a0, a1, t2) # a0/a1 <- vAA/vAA+1 + + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + + andi v1, a2, 0x20 # shift< shift & 0x20 + sll v0, a0, a2 # rlo<- alo << (shift&31) + bnez v1, .L${opcode}_finish + not v1, a2 # rhi<- 31-shift (shift is 5b) + srl a0, 1 + srl a0, v1 # alo<- alo >> (32-(shift&31)) + sll v1, a1, a2 # rhi<- ahi << (shift&31) + or v1, a0 # rhi<- rhi | alo + SET_VREG64_GOTO(v0, v1, rOBJ, t0) # vAA/vAA+1 <- a0/a1 +%break + +.L${opcode}_finish: + SET_VREG64_GOTO(zero, v0, rOBJ, t0) # vAA/vAA+1 <- rlo/rhi diff --git a/runtime/interpreter/mterp/mips/op_shr_int.S b/runtime/interpreter/mterp/mips/op_shr_int.S new file mode 100644 index 0000000000..6110839999 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_shr_int.S @@ -0,0 +1 @@ +%include "mips/binop.S" {"instr":"sra a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips/op_shr_int_2addr.S b/runtime/interpreter/mterp/mips/op_shr_int_2addr.S new file mode 100644 index 0000000000..e00ff5b2e6 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_shr_int_2addr.S @@ -0,0 +1 @@ +%include "mips/binop2addr.S" {"instr":"sra a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips/op_shr_int_lit8.S b/runtime/interpreter/mterp/mips/op_shr_int_lit8.S new file mode 100644 index 0000000000..d058f5862c --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_shr_int_lit8.S @@ -0,0 +1 @@ +%include "mips/binopLit8.S" {"instr":"sra a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips/op_shr_long.S b/runtime/interpreter/mterp/mips/op_shr_long.S new file mode 100644 index 0000000000..4c42758b83 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_shr_long.S @@ -0,0 +1,31 @@ + /* + * Long integer shift. This is different from the generic 32/64-bit + * binary operations because vAA/vBB are 64-bit but vCC (the shift + * distance) is 32-bit. Also, Dalvik requires us to mask off the low + * 6 bits of the shift distance. + */ + /* shr-long vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(t3) # t3 <- AA + and a3, a0, 255 # a3 <- BB + srl a0, a0, 8 # a0 <- CC + EAS2(a3, rFP, a3) # a3 <- &fp[BB] + GET_VREG(a2, a0) # a2 <- vCC + LOAD64(a0, a1, a3) # a0/a1 <- vBB/vBB+1 + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + + andi v0, a2, 0x20 # shift & 0x20 + sra v1, a1, a2 # rhi<- ahi >> (shift&31) + bnez v0, .L${opcode}_finish + srl v0, a0, a2 # rlo<- alo >> (shift&31) + not a0, a2 # alo<- 31-shift (shift is 5b) + sll a1, 1 + sll a1, a0 # ahi<- ahi << (32-(shift&31)) + or v0, a1 # rlo<- rlo | ahi + SET_VREG64_GOTO(v0, v1, t3, t0) # vAA/VAA+1 <- v0/v0 +%break + +.L${opcode}_finish: + sra a3, a1, 31 # a3<- sign(ah) + SET_VREG64_GOTO(v1, a3, t3, t0) # vAA/VAA+1 <- rlo/rhi diff --git a/runtime/interpreter/mterp/mips/op_shr_long_2addr.S b/runtime/interpreter/mterp/mips/op_shr_long_2addr.S new file mode 100644 index 0000000000..3adc085abf --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_shr_long_2addr.S @@ -0,0 +1,27 @@ + /* + * Long integer shift, 2addr version. vA is 64-bit value/result, vB is + * 32-bit shift distance. + */ + /* shr-long/2addr vA, vB */ + GET_OPA4(t2) # t2 <- A+ + GET_OPB(a3) # a3 <- B + GET_VREG(a2, a3) # a2 <- vB + EAS2(t0, rFP, t2) # t0 <- &fp[A] + LOAD64(a0, a1, t0) # a0/a1 <- vAA/vAA+1 + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + + andi v0, a2, 0x20 # shift & 0x20 + sra v1, a1, a2 # rhi<- ahi >> (shift&31) + bnez v0, .L${opcode}_finish + srl v0, a0, a2 # rlo<- alo >> (shift&31) + not a0, a2 # alo<- 31-shift (shift is 5b) + sll a1, 1 + sll a1, a0 # ahi<- ahi << (32-(shift&31)) + or v0, a1 # rlo<- rlo | ahi + SET_VREG64_GOTO(v0, v1, t2, t0) # vAA/vAA+1 <- a0/a1 +%break + +.L${opcode}_finish: + sra a3, a1, 31 # a3<- sign(ah) + SET_VREG64_GOTO(v1, a3, t2, t0) # vAA/vAA+1 <- rlo/rhi diff --git a/runtime/interpreter/mterp/mips/op_sparse_switch.S b/runtime/interpreter/mterp/mips/op_sparse_switch.S new file mode 100644 index 0000000000..670f4648a8 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_sparse_switch.S @@ -0,0 +1 @@ +%include "mips/op_packed_switch.S" { "func":"MterpDoSparseSwitch" } diff --git a/runtime/interpreter/mterp/mips/op_sput.S b/runtime/interpreter/mterp/mips/op_sput.S new file mode 100644 index 0000000000..ee313b9ecc --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_sput.S @@ -0,0 +1,19 @@ +%default { "helper":"artSet32StaticFromCode"} + /* + * General SPUT handler. + * + * for: sput, sput-boolean, sput-byte, sput-char, sput-short + */ + # op vAA, field /* BBBB */ + EXPORT_PC() + FETCH(a0, 1) # a0 <- field ref BBBB + GET_OPA(a3) # a3 <- AA + GET_VREG(a1, a3) # a1 <- fp[AA], the object pointer + lw a2, OFF_FP_METHOD(rFP) # a2 <- method + move a3, rSELF # a3 <- self + PREFETCH_INST(2) # load rINST + JAL($helper) + bnez v0, MterpException # bail out + ADVANCE(2) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_sput_boolean.S b/runtime/interpreter/mterp/mips/op_sput_boolean.S new file mode 100644 index 0000000000..7909ef5622 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_sput_boolean.S @@ -0,0 +1 @@ +%include "mips/op_sput.S" {"helper":"artSet8StaticFromCode"} diff --git a/runtime/interpreter/mterp/mips/op_sput_byte.S b/runtime/interpreter/mterp/mips/op_sput_byte.S new file mode 100644 index 0000000000..7909ef5622 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_sput_byte.S @@ -0,0 +1 @@ +%include "mips/op_sput.S" {"helper":"artSet8StaticFromCode"} diff --git a/runtime/interpreter/mterp/mips/op_sput_char.S b/runtime/interpreter/mterp/mips/op_sput_char.S new file mode 100644 index 0000000000..188195cc3a --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_sput_char.S @@ -0,0 +1 @@ +%include "mips/op_sput.S" {"helper":"artSet16StaticFromCode"} diff --git a/runtime/interpreter/mterp/mips/op_sput_object.S b/runtime/interpreter/mterp/mips/op_sput_object.S new file mode 100644 index 0000000000..4f9034ec0e --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_sput_object.S @@ -0,0 +1,16 @@ + /* + * General 32-bit SPUT handler. + * + * for: sput-object, + */ + /* op vAA, field@BBBB */ + EXPORT_PC() + addu a0, rFP, OFF_FP_SHADOWFRAME + move a1, rPC + move a2, rINST + move a3, rSELF + JAL(MterpSputObject) + beqz v0, MterpException + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_sput_short.S b/runtime/interpreter/mterp/mips/op_sput_short.S new file mode 100644 index 0000000000..188195cc3a --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_sput_short.S @@ -0,0 +1 @@ +%include "mips/op_sput.S" {"helper":"artSet16StaticFromCode"} diff --git a/runtime/interpreter/mterp/mips/op_sput_wide.S b/runtime/interpreter/mterp/mips/op_sput_wide.S new file mode 100644 index 0000000000..1e11466670 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_sput_wide.S @@ -0,0 +1,17 @@ + /* + * 64-bit SPUT handler. + */ + # sput-wide vAA, field /* BBBB */ + .extern artSet64IndirectStaticFromMterp + EXPORT_PC() + FETCH(a0, 1) # a0 <- field ref CCCC + lw a1, OFF_FP_METHOD(rFP) # a1 <- method + GET_OPA(a2) # a2 <- AA + EAS2(a2, rFP, a2) # a2 <- &fp[AA] + move a3, rSELF # a3 <- self + PREFETCH_INST(2) # load rINST + JAL(artSet64IndirectStaticFromMterp) + bnez v0, MterpException # bail out + ADVANCE(2) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_sub_double.S b/runtime/interpreter/mterp/mips/op_sub_double.S new file mode 100644 index 0000000000..9473218e89 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_sub_double.S @@ -0,0 +1 @@ +%include "mips/fbinopWide.S" {"instr":"sub.d fv0, fa0, fa1"} diff --git a/runtime/interpreter/mterp/mips/op_sub_double_2addr.S b/runtime/interpreter/mterp/mips/op_sub_double_2addr.S new file mode 100644 index 0000000000..7ce7c74330 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_sub_double_2addr.S @@ -0,0 +1 @@ +%include "mips/fbinopWide2addr.S" {"instr":"sub.d fv0, fa0, fa1"} diff --git a/runtime/interpreter/mterp/mips/op_sub_float.S b/runtime/interpreter/mterp/mips/op_sub_float.S new file mode 100644 index 0000000000..04650d9125 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_sub_float.S @@ -0,0 +1 @@ +%include "mips/fbinop.S" {"instr":"sub.s fv0, fa0, fa1"} diff --git a/runtime/interpreter/mterp/mips/op_sub_float_2addr.S b/runtime/interpreter/mterp/mips/op_sub_float_2addr.S new file mode 100644 index 0000000000..dfe935c8cf --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_sub_float_2addr.S @@ -0,0 +1 @@ +%include "mips/fbinop2addr.S" {"instr":"sub.s fv0, fa0, fa1"} diff --git a/runtime/interpreter/mterp/mips/op_sub_int.S b/runtime/interpreter/mterp/mips/op_sub_int.S new file mode 100644 index 0000000000..43da1b617a --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_sub_int.S @@ -0,0 +1 @@ +%include "mips/binop.S" {"instr":"subu a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips/op_sub_int_2addr.S b/runtime/interpreter/mterp/mips/op_sub_int_2addr.S new file mode 100644 index 0000000000..cf34aa69dc --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_sub_int_2addr.S @@ -0,0 +1 @@ +%include "mips/binop2addr.S" {"instr":"subu a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips/op_sub_long.S b/runtime/interpreter/mterp/mips/op_sub_long.S new file mode 100644 index 0000000000..0f58e8e891 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_sub_long.S @@ -0,0 +1,8 @@ +/* + * For little endian the code sequence looks as follows: + * subu v0,a0,a2 + * subu v1,a1,a3 + * sltu a0,a0,v0 + * subu v1,v1,a0 + */ +%include "mips/binopWide.S" { "result0":"v0", "result1":"v1", "preinstr":"subu v0, a0, a2", "instr":"subu v1, a1, a3; sltu a0, a0, v0; subu v1, v1, a0" } diff --git a/runtime/interpreter/mterp/mips/op_sub_long_2addr.S b/runtime/interpreter/mterp/mips/op_sub_long_2addr.S new file mode 100644 index 0000000000..aa256c20f8 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_sub_long_2addr.S @@ -0,0 +1,4 @@ +/* + * See op_sub_long.S for more details + */ +%include "mips/binopWide2addr.S" { "result0":"v0", "result1":"v1", "preinstr":"subu v0, a0, a2", "instr":"subu v1, a1, a3; sltu a0, a0, v0; subu v1, v1, a0" } diff --git a/runtime/interpreter/mterp/mips/op_throw.S b/runtime/interpreter/mterp/mips/op_throw.S new file mode 100644 index 0000000000..adc8b047ca --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_throw.S @@ -0,0 +1,11 @@ + /* + * Throw an exception object in the current thread. + */ + /* throw vAA */ + EXPORT_PC() # exception handler can throw + GET_OPA(a2) # a2 <- AA + GET_VREG(a1, a2) # a1 <- vAA (exception object) + # null object? + beqz a1, common_errNullObject # yes, throw an NPE instead + sw a1, THREAD_EXCEPTION_OFFSET(rSELF) # thread->exception <- obj + b MterpException diff --git a/runtime/interpreter/mterp/mips/op_unused_3e.S b/runtime/interpreter/mterp/mips/op_unused_3e.S new file mode 100644 index 0000000000..99ef3cf308 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_unused_3e.S @@ -0,0 +1 @@ +%include "mips/unused.S" diff --git a/runtime/interpreter/mterp/mips/op_unused_3f.S b/runtime/interpreter/mterp/mips/op_unused_3f.S new file mode 100644 index 0000000000..99ef3cf308 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_unused_3f.S @@ -0,0 +1 @@ +%include "mips/unused.S" diff --git a/runtime/interpreter/mterp/mips/op_unused_40.S b/runtime/interpreter/mterp/mips/op_unused_40.S new file mode 100644 index 0000000000..99ef3cf308 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_unused_40.S @@ -0,0 +1 @@ +%include "mips/unused.S" diff --git a/runtime/interpreter/mterp/mips/op_unused_41.S b/runtime/interpreter/mterp/mips/op_unused_41.S new file mode 100644 index 0000000000..99ef3cf308 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_unused_41.S @@ -0,0 +1 @@ +%include "mips/unused.S" diff --git a/runtime/interpreter/mterp/mips/op_unused_42.S b/runtime/interpreter/mterp/mips/op_unused_42.S new file mode 100644 index 0000000000..99ef3cf308 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_unused_42.S @@ -0,0 +1 @@ +%include "mips/unused.S" diff --git a/runtime/interpreter/mterp/mips/op_unused_43.S b/runtime/interpreter/mterp/mips/op_unused_43.S new file mode 100644 index 0000000000..99ef3cf308 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_unused_43.S @@ -0,0 +1 @@ +%include "mips/unused.S" diff --git a/runtime/interpreter/mterp/mips/op_unused_73.S b/runtime/interpreter/mterp/mips/op_unused_73.S new file mode 100644 index 0000000000..99ef3cf308 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_unused_73.S @@ -0,0 +1 @@ +%include "mips/unused.S" diff --git a/runtime/interpreter/mterp/mips/op_unused_79.S b/runtime/interpreter/mterp/mips/op_unused_79.S new file mode 100644 index 0000000000..99ef3cf308 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_unused_79.S @@ -0,0 +1 @@ +%include "mips/unused.S" diff --git a/runtime/interpreter/mterp/mips/op_unused_7a.S b/runtime/interpreter/mterp/mips/op_unused_7a.S new file mode 100644 index 0000000000..99ef3cf308 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_unused_7a.S @@ -0,0 +1 @@ +%include "mips/unused.S" diff --git a/runtime/interpreter/mterp/mips/op_unused_f3.S b/runtime/interpreter/mterp/mips/op_unused_f3.S new file mode 100644 index 0000000000..99ef3cf308 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_unused_f3.S @@ -0,0 +1 @@ +%include "mips/unused.S" diff --git a/runtime/interpreter/mterp/mips/op_unused_f4.S b/runtime/interpreter/mterp/mips/op_unused_f4.S new file mode 100644 index 0000000000..99ef3cf308 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_unused_f4.S @@ -0,0 +1 @@ +%include "mips/unused.S" diff --git a/runtime/interpreter/mterp/mips/op_unused_f5.S b/runtime/interpreter/mterp/mips/op_unused_f5.S new file mode 100644 index 0000000000..99ef3cf308 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_unused_f5.S @@ -0,0 +1 @@ +%include "mips/unused.S" diff --git a/runtime/interpreter/mterp/mips/op_unused_f6.S b/runtime/interpreter/mterp/mips/op_unused_f6.S new file mode 100644 index 0000000000..99ef3cf308 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_unused_f6.S @@ -0,0 +1 @@ +%include "mips/unused.S" diff --git a/runtime/interpreter/mterp/mips/op_unused_f7.S b/runtime/interpreter/mterp/mips/op_unused_f7.S new file mode 100644 index 0000000000..99ef3cf308 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_unused_f7.S @@ -0,0 +1 @@ +%include "mips/unused.S" diff --git a/runtime/interpreter/mterp/mips/op_unused_f8.S b/runtime/interpreter/mterp/mips/op_unused_f8.S new file mode 100644 index 0000000000..99ef3cf308 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_unused_f8.S @@ -0,0 +1 @@ +%include "mips/unused.S" diff --git a/runtime/interpreter/mterp/mips/op_unused_f9.S b/runtime/interpreter/mterp/mips/op_unused_f9.S new file mode 100644 index 0000000000..99ef3cf308 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_unused_f9.S @@ -0,0 +1 @@ +%include "mips/unused.S" diff --git a/runtime/interpreter/mterp/mips/op_unused_fa.S b/runtime/interpreter/mterp/mips/op_unused_fa.S new file mode 100644 index 0000000000..99ef3cf308 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_unused_fa.S @@ -0,0 +1 @@ +%include "mips/unused.S" diff --git a/runtime/interpreter/mterp/mips/op_unused_fb.S b/runtime/interpreter/mterp/mips/op_unused_fb.S new file mode 100644 index 0000000000..99ef3cf308 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_unused_fb.S @@ -0,0 +1 @@ +%include "mips/unused.S" diff --git a/runtime/interpreter/mterp/mips/op_unused_fc.S b/runtime/interpreter/mterp/mips/op_unused_fc.S new file mode 100644 index 0000000000..99ef3cf308 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_unused_fc.S @@ -0,0 +1 @@ +%include "mips/unused.S" diff --git a/runtime/interpreter/mterp/mips/op_unused_fd.S b/runtime/interpreter/mterp/mips/op_unused_fd.S new file mode 100644 index 0000000000..99ef3cf308 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_unused_fd.S @@ -0,0 +1 @@ +%include "mips/unused.S" diff --git a/runtime/interpreter/mterp/mips/op_unused_fe.S b/runtime/interpreter/mterp/mips/op_unused_fe.S new file mode 100644 index 0000000000..99ef3cf308 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_unused_fe.S @@ -0,0 +1 @@ +%include "mips/unused.S" diff --git a/runtime/interpreter/mterp/mips/op_unused_ff.S b/runtime/interpreter/mterp/mips/op_unused_ff.S new file mode 100644 index 0000000000..99ef3cf308 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_unused_ff.S @@ -0,0 +1 @@ +%include "mips/unused.S" diff --git a/runtime/interpreter/mterp/mips/op_ushr_int.S b/runtime/interpreter/mterp/mips/op_ushr_int.S new file mode 100644 index 0000000000..b95472b30e --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_ushr_int.S @@ -0,0 +1 @@ +%include "mips/binop.S" {"instr":"srl a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips/op_ushr_int_2addr.S b/runtime/interpreter/mterp/mips/op_ushr_int_2addr.S new file mode 100644 index 0000000000..fc17778100 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_ushr_int_2addr.S @@ -0,0 +1 @@ +%include "mips/binop2addr.S" {"instr":"srl a0, a0, a1 "} diff --git a/runtime/interpreter/mterp/mips/op_ushr_int_lit8.S b/runtime/interpreter/mterp/mips/op_ushr_int_lit8.S new file mode 100644 index 0000000000..c82cfba15c --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_ushr_int_lit8.S @@ -0,0 +1 @@ +%include "mips/binopLit8.S" {"instr":"srl a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips/op_ushr_long.S b/runtime/interpreter/mterp/mips/op_ushr_long.S new file mode 100644 index 0000000000..2e227a94af --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_ushr_long.S @@ -0,0 +1,31 @@ + /* + * Long integer shift. This is different from the generic 32/64-bit + * binary operations because vAA/vBB are 64-bit but vCC (the shift + * distance) is 32-bit. Also, Dalvik requires us to mask off the low + * 6 bits of the shift distance. + */ + /* ushr-long vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # rOBJ <- AA + and a3, a0, 255 # a3 <- BB + srl a0, a0, 8 # a0 <- CC + EAS2(a3, rFP, a3) # a3 <- &fp[BB] + GET_VREG(a2, a0) # a2 <- vCC + LOAD64(a0, a1, a3) # a0/a1 <- vBB/vBB+1 + + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + + andi v0, a2, 0x20 # shift & 0x20 + srl v1, a1, a2 # rhi<- ahi >> (shift&31) + bnez v0, .L${opcode}_finish + srl v0, a0, a2 # rlo<- alo >> (shift&31) + not a0, a2 # alo<- 31-n (shift is 5b) + sll a1, 1 + sll a1, a0 # ahi<- ahi << (32-(shift&31)) + or v0, a1 # rlo<- rlo | ahi + SET_VREG64_GOTO(v0, v1, rOBJ, t0) # vAA/vAA+1 <- v0/v1 +%break + +.L${opcode}_finish: + SET_VREG64_GOTO(v1, zero, rOBJ, t0) # vAA/vAA+1 <- rlo/rhi diff --git a/runtime/interpreter/mterp/mips/op_ushr_long_2addr.S b/runtime/interpreter/mterp/mips/op_ushr_long_2addr.S new file mode 100644 index 0000000000..ccf1f7e8f8 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_ushr_long_2addr.S @@ -0,0 +1,27 @@ + /* + * Long integer shift, 2addr version. vA is 64-bit value/result, vB is + * 32-bit shift distance. + */ + /* ushr-long/2addr vA, vB */ + GET_OPA4(t3) # t3 <- A+ + GET_OPB(a3) # a3 <- B + GET_VREG(a2, a3) # a2 <- vB + EAS2(t0, rFP, t3) # t0 <- &fp[A] + LOAD64(a0, a1, t0) # a0/a1 <- vAA/vAA+1 + + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + + andi v0, a2, 0x20 # shift & 0x20 + srl v1, a1, a2 # rhi<- ahi >> (shift&31) + bnez v0, .L${opcode}_finish + srl v0, a0, a2 # rlo<- alo >> (shift&31) + not a0, a2 # alo<- 31-n (shift is 5b) + sll a1, 1 + sll a1, a0 # ahi<- ahi << (32-(shift&31)) + or v0, a1 # rlo<- rlo | ahi + SET_VREG64_GOTO(v0, v1, t3, t0) # vAA/vAA+1 <- a0/a1 +%break + +.L${opcode}_finish: + SET_VREG64_GOTO(v1, zero, t3, t0) # vAA/vAA+1 <- rlo/rhi diff --git a/runtime/interpreter/mterp/mips/op_xor_int.S b/runtime/interpreter/mterp/mips/op_xor_int.S new file mode 100644 index 0000000000..6c23f1f378 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_xor_int.S @@ -0,0 +1 @@ +%include "mips/binop.S" {"instr":"xor a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips/op_xor_int_2addr.S b/runtime/interpreter/mterp/mips/op_xor_int_2addr.S new file mode 100644 index 0000000000..5ee1667f8d --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_xor_int_2addr.S @@ -0,0 +1 @@ +%include "mips/binop2addr.S" {"instr":"xor a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips/op_xor_int_lit16.S b/runtime/interpreter/mterp/mips/op_xor_int_lit16.S new file mode 100644 index 0000000000..2af37a6116 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_xor_int_lit16.S @@ -0,0 +1 @@ +%include "mips/binopLit16.S" {"instr":"xor a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips/op_xor_int_lit8.S b/runtime/interpreter/mterp/mips/op_xor_int_lit8.S new file mode 100644 index 0000000000..944ed69231 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_xor_int_lit8.S @@ -0,0 +1 @@ +%include "mips/binopLit8.S" {"instr":"xor a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips/op_xor_long.S b/runtime/interpreter/mterp/mips/op_xor_long.S new file mode 100644 index 0000000000..93f8f70a21 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_xor_long.S @@ -0,0 +1 @@ +%include "mips/binopWide.S" {"preinstr":"xor a0, a0, a2", "instr":"xor a1, a1, a3"} diff --git a/runtime/interpreter/mterp/mips/op_xor_long_2addr.S b/runtime/interpreter/mterp/mips/op_xor_long_2addr.S new file mode 100644 index 0000000000..49f3fa42f4 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_xor_long_2addr.S @@ -0,0 +1 @@ +%include "mips/binopWide2addr.S" {"preinstr":"xor a0, a0, a2", "instr":"xor a1, a1, a3"} diff --git a/runtime/interpreter/mterp/mips/unop.S b/runtime/interpreter/mterp/mips/unop.S new file mode 100644 index 0000000000..52a8f0ac98 --- /dev/null +++ b/runtime/interpreter/mterp/mips/unop.S @@ -0,0 +1,19 @@ +%default {"preinstr":"", "result0":"a0"} + /* + * Generic 32-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "result = op a0". + * This could be a MIPS instruction or a function call. + * + * for: neg-int, not-int, neg-float, int-to-float, float-to-int, + * int-to-byte, int-to-char, int-to-short + */ + /* unop vA, vB */ + GET_OPB(a3) # a3 <- B + GET_OPA4(t0) # t0 <- A+ + GET_VREG(a0, a3) # a0 <- vB + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + $preinstr # optional op + $instr # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t1) # extract opcode from rINST + SET_VREG_GOTO($result0, t0, t1) # vAA <- result0 + /* 9-10 instructions */ diff --git a/runtime/interpreter/mterp/mips/unopNarrower.S b/runtime/interpreter/mterp/mips/unopNarrower.S new file mode 100644 index 0000000000..9c38badba5 --- /dev/null +++ b/runtime/interpreter/mterp/mips/unopNarrower.S @@ -0,0 +1,24 @@ +%default {"load":"LOAD64_F(fa0, fa0f, a3)"} + /* + * Generic 64bit-to-32bit unary operation. Provide an "instr" line + * that specifies an instruction that performs "result = op a0/a1", where + * "result" is a 32-bit quantity in a0. + * + * For: long-to-float, double-to-int, double-to-float + * If hard floating point support is available, use fa0 as the parameter, + * except for long-to-float opcode. + * (This would work for long-to-int, but that instruction is actually + * an exact match for OP_MOVE.) + */ + /* unop vA, vB */ + GET_OPB(a3) # a3 <- B + GET_OPA4(rOBJ) # t1 <- A+ + EAS2(a3, rFP, a3) # a3 <- &fp[B] + $load + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + $instr + +.L${opcode}_set_vreg_f: + SET_VREG_F(fv0, rOBJ) # vA <- result0 + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/unopWide.S b/runtime/interpreter/mterp/mips/unopWide.S new file mode 100644 index 0000000000..fd25dffa3f --- /dev/null +++ b/runtime/interpreter/mterp/mips/unopWide.S @@ -0,0 +1,20 @@ +%default {"preinstr":"", "result0":"a0", "result1":"a1"} + /* + * Generic 64-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "result = op a0/a1". + * This could be MIPS instruction or a function call. + * + * For: neg-long, not-long, neg-double, + */ + /* unop vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a3) # a3 <- B + EAS2(a3, rFP, a3) # a3 <- &fp[B] + LOAD64(a0, a1, a3) # a0/a1 <- vAA + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + $preinstr # optional op + $instr # a0/a1 <- op, a2-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG64($result0, $result1, rOBJ) # vAA <- a0/a1 + GOTO_OPCODE(t0) # jump to next instruction + /* 12-13 instructions */ diff --git a/runtime/interpreter/mterp/mips/unopWider.S b/runtime/interpreter/mterp/mips/unopWider.S new file mode 100644 index 0000000000..1c18837775 --- /dev/null +++ b/runtime/interpreter/mterp/mips/unopWider.S @@ -0,0 +1,19 @@ +%default {"preinstr":"", "result0":"a0", "result1":"a1"} + /* + * Generic 32bit-to-64bit unary operation. Provide an "instr" line + * that specifies an instruction that performs "result = op a0", where + * "result" is a 64-bit quantity in a0/a1. + * + * For: int-to-long + */ + /* unop vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a3) # a3 <- B + GET_VREG(a0, a3) # a0 <- vB + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + $preinstr # optional op + $instr # result <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG64($result0, $result1, rOBJ) # vA/vA+1 <- a0/a1 + GOTO_OPCODE(t0) # jump to next instruction + /* 10-11 instructions */ diff --git a/runtime/interpreter/mterp/mips/unused.S b/runtime/interpreter/mterp/mips/unused.S new file mode 100644 index 0000000000..ffa00becfd --- /dev/null +++ b/runtime/interpreter/mterp/mips/unused.S @@ -0,0 +1,4 @@ +/* + * Bail to reference interpreter to throw. + */ + b MterpFallback diff --git a/runtime/interpreter/mterp/mips/zcmp.S b/runtime/interpreter/mterp/mips/zcmp.S new file mode 100644 index 0000000000..1fa13851c7 --- /dev/null +++ b/runtime/interpreter/mterp/mips/zcmp.S @@ -0,0 +1,32 @@ + /* + * Generic one-operand compare-and-branch operation. Provide a "revcmp" + * fragment that specifies the *reverse* comparison to perform, e.g. + * for "if-le" you would use "gt". + * + * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez + */ + /* if-cmp vAA, +BBBB */ + GET_OPA(a0) # a0 <- AA + GET_VREG(a2, a0) # a2 <- vAA + FETCH_S(rINST, 1) # rINST <- branch offset, in code units + b${revcmp} a2, zero, 1f # branch to 1 if comparison failed + b 2f +1: + li rINST, 2 # rINST- BYTE branch dist for not-taken +2: +#if MTERP_PROFILE_BRANCHES + EXPORT_PC() + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + JAL(MterpProfileBranch) # (self, shadow_frame, offset) + bnez v0, MterpOnStackReplacement # Note: offset must be in rINST +#endif + addu a1, rINST, rINST # convert to bytes + FETCH_ADVANCE_INST_RB(a1) # update rPC, load rINST + bgez a1, 3f + lw ra, THREAD_FLAGS_OFFSET(rSELF) + b MterpCheckSuspendAndContinue +3: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mterp.cc b/runtime/interpreter/mterp/mterp.cc index b443c69718..e1bde1ba01 100644 --- a/runtime/interpreter/mterp/mterp.cc +++ b/runtime/interpreter/mterp/mterp.cc @@ -149,7 +149,8 @@ extern "C" bool MterpShouldSwitchInterpreters() Runtime::Current()->GetInstrumentation(); bool unhandled_instrumentation; // TODO: enable for other targets after more extensive testing. - if ((kRuntimeISA == kArm64) || (kRuntimeISA == kArm) || (kRuntimeISA == kX86)) { + if ((kRuntimeISA == kArm64) || (kRuntimeISA == kArm) || + (kRuntimeISA == kX86) || (kRuntimeISA == kMips)) { unhandled_instrumentation = instrumentation->NonJitProfilingActive(); } else { unhandled_instrumentation = instrumentation->IsActive(); diff --git a/runtime/interpreter/mterp/out/mterp_mips.S b/runtime/interpreter/mterp/out/mterp_mips.S new file mode 100644 index 0000000000..7ae1ab110d --- /dev/null +++ b/runtime/interpreter/mterp/out/mterp_mips.S @@ -0,0 +1,13157 @@ +/* + * This file was generated automatically by gen-mterp.py for 'mips'. + * + * --> DO NOT EDIT <-- + */ + +/* File: mips/header.S */ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + Art assembly interpreter notes: + + First validate assembly code by implementing ExecuteXXXImpl() style body (doesn't + handle invoke, allows higher-level code to create frame & shadow frame. + + Once that's working, support direct entry code & eliminate shadow frame (and + excess locals allocation. + + Some (hopefully) temporary ugliness. We'll treat rFP as pointing to the + base of the vreg array within the shadow frame. Access the other fields, + dex_pc_, method_ and number_of_vregs_ via negative offsets. For now, we'll continue + the shadow frame mechanism of double-storing object references - via rFP & + number_of_vregs_. + + */ + +#include "asm_support.h" + +#if (__mips==32) && (__mips_isa_rev>=2) +#define MIPS32REVGE2 /* mips32r2 and greater */ +#if (__mips==32) && (__mips_isa_rev>=5) +#define FPU64 /* 64 bit FPU */ +#if (__mips==32) && (__mips_isa_rev>=6) +#define MIPS32REVGE6 /* mips32r6 and greater */ +#endif +#endif +#endif + +/* MIPS definitions and declarations + + reg nick purpose + s0 rPC interpreted program counter, used for fetching instructions + s1 rFP interpreted frame pointer, used for accessing locals and args + s2 rSELF self (Thread) pointer + s3 rIBASE interpreted instruction base pointer, used for computed goto + s4 rINST first 16-bit code unit of current instruction + s6 rREFS base of object references in shadow frame (ideally, we'll get rid of this later). +*/ + +/* single-purpose registers, given names for clarity */ +#define rPC s0 +#define rFP s1 +#define rSELF s2 +#define rIBASE s3 +#define rINST s4 +#define rOBJ s5 +#define rREFS s6 +#define rTEMP s7 + +#define rARG0 a0 +#define rARG1 a1 +#define rARG2 a2 +#define rARG3 a3 +#define rRESULT0 v0 +#define rRESULT1 v1 + +/* GP register definitions */ +#define zero $0 /* always zero */ +#define AT $at /* assembler temp */ +#define v0 $2 /* return value */ +#define v1 $3 +#define a0 $4 /* argument registers */ +#define a1 $5 +#define a2 $6 +#define a3 $7 +#define t0 $8 /* temp registers (not saved across subroutine calls) */ +#define t1 $9 +#define t2 $10 +#define t3 $11 +#define t4 $12 +#define t5 $13 +#define t6 $14 +#define t7 $15 +#define ta0 $12 /* alias */ +#define ta1 $13 +#define ta2 $14 +#define ta3 $15 +#define s0 $16 /* saved across subroutine calls (callee saved) */ +#define s1 $17 +#define s2 $18 +#define s3 $19 +#define s4 $20 +#define s5 $21 +#define s6 $22 +#define s7 $23 +#define t8 $24 /* two more temp registers */ +#define t9 $25 +#define k0 $26 /* kernel temporary */ +#define k1 $27 +#define gp $28 /* global pointer */ +#define sp $29 /* stack pointer */ +#define s8 $30 /* one more callee saved */ +#define ra $31 /* return address */ + +/* FP register definitions */ +#define fv0 $f0 +#define fv0f $f1 +#define fv1 $f2 +#define fv1f $f3 +#define fa0 $f12 +#define fa0f $f13 +#define fa1 $f14 +#define fa1f $f15 +#define ft0 $f4 +#define ft0f $f5 +#define ft1 $f6 +#define ft1f $f7 +#define ft2 $f8 +#define ft2f $f9 +#define ft3 $f10 +#define ft3f $f11 +#define ft4 $f16 +#define ft4f $f17 +#define ft5 $f18 +#define ft5f $f19 +#define fs0 $f20 +#define fs0f $f21 +#define fs1 $f22 +#define fs1f $f23 +#define fs2 $f24 +#define fs2f $f25 +#define fs3 $f26 +#define fs3f $f27 +#define fs4 $f28 +#define fs4f $f29 +#define fs5 $f30 +#define fs5f $f31 + +#ifndef MIPS32REVGE6 +#define fcc0 $fcc0 +#define fcc1 $fcc1 +#endif + +/* + * Instead of holding a pointer to the shadow frame, we keep rFP at the base of the vregs. So, + * to access other shadow frame fields, we need to use a backwards offset. Define those here. + */ +#define OFF_FP(a) (a - SHADOWFRAME_VREGS_OFFSET) +#define OFF_FP_NUMBER_OF_VREGS OFF_FP(SHADOWFRAME_NUMBER_OF_VREGS_OFFSET) +#define OFF_FP_DEX_PC OFF_FP(SHADOWFRAME_DEX_PC_OFFSET) +#define OFF_FP_LINK OFF_FP(SHADOWFRAME_LINK_OFFSET) +#define OFF_FP_METHOD OFF_FP(SHADOWFRAME_METHOD_OFFSET) +#define OFF_FP_RESULT_REGISTER OFF_FP(SHADOWFRAME_RESULT_REGISTER_OFFSET) +#define OFF_FP_DEX_PC_PTR OFF_FP(SHADOWFRAME_DEX_PC_PTR_OFFSET) +#define OFF_FP_CODE_ITEM OFF_FP(SHADOWFRAME_CODE_ITEM_OFFSET) +#define OFF_FP_SHADOWFRAME (-SHADOWFRAME_VREGS_OFFSET) + +#define MTERP_PROFILE_BRANCHES 1 +#define MTERP_LOGGING 0 + +/* + * "export" the PC to dex_pc field in the shadow frame, f/b/o future exception objects. Must + * be done *before* something throws. + * + * It's okay to do this more than once. + * + * NOTE: the fast interpreter keeps track of dex pc as a direct pointer to the mapped + * dex byte codes. However, the rest of the runtime expects dex pc to be an instruction + * offset into the code_items_[] array. For effiency, we will "export" the + * current dex pc as a direct pointer using the EXPORT_PC macro, and rely on GetDexPC + * to convert to a dex pc when needed. + */ +#define EXPORT_PC() \ + sw rPC, OFF_FP_DEX_PC_PTR(rFP) + +#define EXPORT_DEX_PC(tmp) \ + lw tmp, OFF_FP_CODE_ITEM(rFP) \ + sw rPC, OFF_FP_DEX_PC_PTR(rFP) \ + addu tmp, CODEITEM_INSNS_OFFSET \ + subu tmp, rPC, tmp \ + sra tmp, tmp, 1 \ + sw tmp, OFF_FP_DEX_PC(rFP) + +/* + * Fetch the next instruction from rPC into rINST. Does not advance rPC. + */ +#define FETCH_INST() lhu rINST, (rPC) + +/* + * Fetch the next instruction from the specified offset. Advances rPC + * to point to the next instruction. "_count" is in 16-bit code units. + * + * This must come AFTER anything that can throw an exception, or the + * exception catch may miss. (This also implies that it must come after + * EXPORT_PC().) + */ +#define FETCH_ADVANCE_INST(_count) lhu rINST, ((_count)*2)(rPC); \ + addu rPC, rPC, ((_count) * 2) + +/* + * The operation performed here is similar to FETCH_ADVANCE_INST, except the + * src and dest registers are parameterized (not hard-wired to rPC and rINST). + */ +#define PREFETCH_ADVANCE_INST(_dreg, _sreg, _count) \ + lhu _dreg, ((_count)*2)(_sreg) ; \ + addu _sreg, _sreg, (_count)*2 + +/* + * Similar to FETCH_ADVANCE_INST, but does not update rPC. Used to load + * rINST ahead of possible exception point. Be sure to manually advance rPC + * later. + */ +#define PREFETCH_INST(_count) lhu rINST, ((_count)*2)(rPC) + +/* Advance rPC by some number of code units. */ +#define ADVANCE(_count) addu rPC, rPC, ((_count) * 2) + +/* + * Fetch the next instruction from an offset specified by rd. Updates + * rPC to point to the next instruction. "rd" must specify the distance + * in bytes, *not* 16-bit code units, and may be a signed value. + */ +#define FETCH_ADVANCE_INST_RB(rd) addu rPC, rPC, rd; \ + lhu rINST, (rPC) + +/* + * Fetch a half-word code unit from an offset past the current PC. The + * "_count" value is in 16-bit code units. Does not advance rPC. + * + * The "_S" variant works the same but treats the value as signed. + */ +#define FETCH(rd, _count) lhu rd, ((_count) * 2)(rPC) +#define FETCH_S(rd, _count) lh rd, ((_count) * 2)(rPC) + +/* + * Fetch one byte from an offset past the current PC. Pass in the same + * "_count" as you would for FETCH, and an additional 0/1 indicating which + * byte of the halfword you want (lo/hi). + */ +#define FETCH_B(rd, _count, _byte) lbu rd, ((_count) * 2 + _byte)(rPC) + +/* + * Put the instruction's opcode field into the specified register. + */ +#define GET_INST_OPCODE(rd) and rd, rINST, 0xFF + +/* + * Put the prefetched instruction's opcode field into the specified register. + */ +#define GET_PREFETCHED_OPCODE(dreg, sreg) andi dreg, sreg, 255 + +/* + * Begin executing the opcode in rd. + */ +#define GOTO_OPCODE(rd) sll rd, rd, 7; \ + addu rd, rIBASE, rd; \ + jalr zero, rd + +#define GOTO_OPCODE_BASE(_base, rd) sll rd, rd, 7; \ + addu rd, _base, rd; \ + jalr zero, rd + +/* + * Get/set the 32-bit value from a Dalvik register. + */ +#define GET_VREG(rd, rix) LOAD_eas2(rd, rFP, rix) + +#define GET_VREG_F(rd, rix) EAS2(AT, rFP, rix); \ + .set noat; l.s rd, (AT); .set at + +#define SET_VREG(rd, rix) .set noat; \ + sll AT, rix, 2; \ + addu t8, rFP, AT; \ + sw rd, 0(t8); \ + addu t8, rREFS, AT; \ + .set at; \ + sw zero, 0(t8) + +#define SET_VREG64(rlo, rhi, rix) .set noat; \ + sll AT, rix, 2; \ + addu t8, rFP, AT; \ + sw rlo, 0(t8); \ + sw rhi, 4(t8); \ + addu t8, rREFS, AT; \ + .set at; \ + sw zero, 0(t8); \ + sw zero, 4(t8) + +#ifdef FPU64 +#define SET_VREG64_F(rlo, rhi, rix) .set noat; \ + sll AT, rix, 2; \ + addu t8, rREFS, AT; \ + sw zero, 0(t8); \ + sw zero, 4(t8); \ + addu t8, rFP, AT; \ + mfhc1 AT, rlo; \ + sw AT, 4(t8); \ + .set at; \ + s.s rlo, 0(t8) +#else +#define SET_VREG64_F(rlo, rhi, rix) .set noat; \ + sll AT, rix, 2; \ + addu t8, rFP, AT; \ + s.s rlo, 0(t8); \ + s.s rhi, 4(t8); \ + addu t8, rREFS, AT; \ + .set at; \ + sw zero, 0(t8); \ + sw zero, 4(t8) +#endif + +#define SET_VREG_OBJECT(rd, rix) .set noat; \ + sll AT, rix, 2; \ + addu t8, rFP, AT; \ + sw rd, 0(t8); \ + addu t8, rREFS, AT; \ + .set at; \ + sw rd, 0(t8) + +/* Combination of the SET_VREG and GOTO_OPCODE functions to save 1 instruction */ +#define SET_VREG_GOTO(rd, rix, dst) .set noreorder; \ + sll dst, dst, 7; \ + addu dst, rIBASE, dst; \ + .set noat; \ + sll AT, rix, 2; \ + addu t8, rFP, AT; \ + sw rd, 0(t8); \ + addu t8, rREFS, AT; \ + .set at; \ + jalr zero, dst; \ + sw zero, 0(t8); \ + .set reorder + +/* Combination of the SET_VREG64 and GOTO_OPCODE functions to save 1 instruction */ +#define SET_VREG64_GOTO(rlo, rhi, rix, dst) .set noreorder; \ + sll dst, dst, 7; \ + addu dst, rIBASE, dst; \ + .set noat; \ + sll AT, rix, 2; \ + addu t8, rFP, AT; \ + sw rlo, 0(t8); \ + sw rhi, 4(t8); \ + addu t8, rREFS, AT; \ + .set at; \ + sw zero, 0(t8); \ + jalr zero, dst; \ + sw zero, 4(t8); \ + .set reorder + +#define SET_VREG_F(rd, rix) .set noat; \ + sll AT, rix, 2; \ + addu t8, rFP, AT; \ + s.s rd, 0(t8); \ + addu t8, rREFS, AT; \ + .set at; \ + sw zero, 0(t8) + +#define GET_OPA(rd) srl rd, rINST, 8 +#ifdef MIPS32REVGE2 +#define GET_OPA4(rd) ext rd, rINST, 8, 4 +#else +#define GET_OPA4(rd) GET_OPA(rd); and rd, 0xf +#endif +#define GET_OPB(rd) srl rd, rINST, 12 + +/* + * Form an Effective Address rd = rbase + roff<>n; + * Uses reg AT + */ +#define ESRN(rd, rbase, roff, rshift) .set noat; \ + srl AT, roff, rshift; \ + addu rd, rbase, AT; \ + .set at + +#define LOAD_eas2(rd, rbase, roff) EAS2(AT, rbase, roff); \ + .set noat; lw rd, 0(AT); .set at + +#define STORE_eas2(rd, rbase, roff) EAS2(AT, rbase, roff); \ + .set noat; sw rd, 0(AT); .set at + +#define LOAD_RB_OFF(rd, rbase, off) lw rd, off(rbase) +#define STORE_RB_OFF(rd, rbase, off) sw rd, off(rbase) + +#define STORE64_off(rlo, rhi, rbase, off) sw rlo, off(rbase); \ + sw rhi, (off+4)(rbase) +#define LOAD64_off(rlo, rhi, rbase, off) lw rlo, off(rbase); \ + lw rhi, (off+4)(rbase) + +#define STORE64(rlo, rhi, rbase) STORE64_off(rlo, rhi, rbase, 0) +#define LOAD64(rlo, rhi, rbase) LOAD64_off(rlo, rhi, rbase, 0) + +#ifdef FPU64 +#define STORE64_off_F(rlo, rhi, rbase, off) s.s rlo, off(rbase); \ + .set noat; \ + mfhc1 AT, rlo; \ + sw AT, (off+4)(rbase); \ + .set at +#define LOAD64_off_F(rlo, rhi, rbase, off) l.s rlo, off(rbase); \ + .set noat; \ + lw AT, (off+4)(rbase); \ + mthc1 AT, rlo; \ + .set at +#else +#define STORE64_off_F(rlo, rhi, rbase, off) s.s rlo, off(rbase); \ + s.s rhi, (off+4)(rbase) +#define LOAD64_off_F(rlo, rhi, rbase, off) l.s rlo, off(rbase); \ + l.s rhi, (off+4)(rbase) +#endif + +#define STORE64_F(rlo, rhi, rbase) STORE64_off_F(rlo, rhi, rbase, 0) +#define LOAD64_F(rlo, rhi, rbase) LOAD64_off_F(rlo, rhi, rbase, 0) + + +#define LOAD_base_offMirrorArray_length(rd, rbase) LOAD_RB_OFF(rd, rbase, MIRROR_ARRAY_LENGTH_OFFSET) + +#define STACK_STORE(rd, off) sw rd, off(sp) +#define STACK_LOAD(rd, off) lw rd, off(sp) +#define CREATE_STACK(n) subu sp, sp, n +#define DELETE_STACK(n) addu sp, sp, n + +#define LOAD_ADDR(dest, addr) la dest, addr +#define LOAD_IMM(dest, imm) li dest, imm +#define MOVE_REG(dest, src) move dest, src +#define STACK_SIZE 128 + +#define STACK_OFFSET_ARG04 16 +#define STACK_OFFSET_ARG05 20 +#define STACK_OFFSET_ARG06 24 +#define STACK_OFFSET_ARG07 28 +#define STACK_OFFSET_GP 84 + +#define JAL(n) jal n +#define BAL(n) bal n + +/* + * FP register usage restrictions: + * 1) We don't use the callee save FP registers so we don't have to save them. + * 2) We don't use the odd FP registers so we can share code with mips32r6. + */ +#define STACK_STORE_FULL() CREATE_STACK(STACK_SIZE); \ + STACK_STORE(ra, 124); \ + STACK_STORE(s8, 120); \ + STACK_STORE(s0, 116); \ + STACK_STORE(s1, 112); \ + STACK_STORE(s2, 108); \ + STACK_STORE(s3, 104); \ + STACK_STORE(s4, 100); \ + STACK_STORE(s5, 96); \ + STACK_STORE(s6, 92); \ + STACK_STORE(s7, 88); + +#define STACK_LOAD_FULL() STACK_LOAD(gp, STACK_OFFSET_GP); \ + STACK_LOAD(s7, 88); \ + STACK_LOAD(s6, 92); \ + STACK_LOAD(s5, 96); \ + STACK_LOAD(s4, 100); \ + STACK_LOAD(s3, 104); \ + STACK_LOAD(s2, 108); \ + STACK_LOAD(s1, 112); \ + STACK_LOAD(s0, 116); \ + STACK_LOAD(s8, 120); \ + STACK_LOAD(ra, 124); \ + DELETE_STACK(STACK_SIZE) + +/* File: mips/entry.S */ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * Interpreter entry point. + */ + + .text + .align 2 + .global ExecuteMterpImpl + .ent ExecuteMterpImpl + .frame sp, STACK_SIZE, ra +/* + * On entry: + * a0 Thread* self + * a1 code_item + * a2 ShadowFrame + * a3 JValue* result_register + * + */ + +ExecuteMterpImpl: + .set noreorder + .cpload t9 + .set reorder +/* Save to the stack. Frame size = STACK_SIZE */ + STACK_STORE_FULL() +/* This directive will make sure all subsequent jal restore gp at a known offset */ + .cprestore STACK_OFFSET_GP + + /* Remember the return register */ + sw a3, SHADOWFRAME_RESULT_REGISTER_OFFSET(a2) + + /* Remember the code_item */ + sw a1, SHADOWFRAME_CODE_ITEM_OFFSET(a2) + + /* set up "named" registers */ + move rSELF, a0 + lw a0, SHADOWFRAME_NUMBER_OF_VREGS_OFFSET(a2) + addu rFP, a2, SHADOWFRAME_VREGS_OFFSET # point to insns[] (i.e. - the dalivk byte code). + EAS2(rREFS, rFP, a0) # point to reference array in shadow frame + lw a0, SHADOWFRAME_DEX_PC_OFFSET(a2) # Get starting dex_pc + addu rPC, a1, CODEITEM_INSNS_OFFSET # Point to base of insns[] + EAS1(rPC, rPC, a0) # Create direct pointer to 1st dex opcode + + EXPORT_PC() + + /* Starting ibase */ + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) + + /* start executing the instruction at rPC */ + FETCH_INST() # load rINST from rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + /* NOTE: no fallthrough */ + + + .global artMterpAsmInstructionStart + .type artMterpAsmInstructionStart, %function +artMterpAsmInstructionStart = .L_op_nop + .text + +/* ------------------------------ */ + .balign 128 +.L_op_nop: /* 0x00 */ +/* File: mips/op_nop.S */ + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_move: /* 0x01 */ +/* File: mips/op_move.S */ + /* for move, move-object, long-to-int */ + /* op vA, vB */ + GET_OPB(a1) # a1 <- B from 15:12 + GET_OPA4(a0) # a0 <- A from 11:8 + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + GET_VREG(a2, a1) # a2 <- fp[B] + GET_INST_OPCODE(t0) # t0 <- opcode from rINST + .if 0 + SET_VREG_OBJECT(a2, a0) # fp[A] <- a2 + .else + SET_VREG(a2, a0) # fp[A] <- a2 + .endif + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_move_from16: /* 0x02 */ +/* File: mips/op_move_from16.S */ + /* for: move/from16, move-object/from16 */ + /* op vAA, vBBBB */ + FETCH(a1, 1) # a1 <- BBBB + GET_OPA(a0) # a0 <- AA + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_VREG(a2, a1) # a2 <- fp[BBBB] + GET_INST_OPCODE(t0) # extract opcode from rINST + .if 0 + SET_VREG_OBJECT(a2, a0) # fp[AA] <- a2 + .else + SET_VREG(a2, a0) # fp[AA] <- a2 + .endif + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_move_16: /* 0x03 */ +/* File: mips/op_move_16.S */ + /* for: move/16, move-object/16 */ + /* op vAAAA, vBBBB */ + FETCH(a1, 2) # a1 <- BBBB + FETCH(a0, 1) # a0 <- AAAA + FETCH_ADVANCE_INST(3) # advance rPC, load rINST + GET_VREG(a2, a1) # a2 <- fp[BBBB] + GET_INST_OPCODE(t0) # extract opcode from rINST + .if 0 + SET_VREG_OBJECT(a2, a0) # fp[AAAA] <- a2 + .else + SET_VREG(a2, a0) # fp[AAAA] <- a2 + .endif + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_move_wide: /* 0x04 */ +/* File: mips/op_move_wide.S */ + /* move-wide vA, vB */ + /* NOTE: regs can overlap, e.g. "move v6, v7" or "move v7, v6" */ + GET_OPA4(a2) # a2 <- A(+) + GET_OPB(a3) # a3 <- B + EAS2(a3, rFP, a3) # a3 <- &fp[B] + LOAD64(a0, a1, a3) # a0/a1 <- fp[B] + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + SET_VREG64(a0, a1, a2) # fp[A] <- a0/a1 + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_move_wide_from16: /* 0x05 */ +/* File: mips/op_move_wide_from16.S */ + /* move-wide/from16 vAA, vBBBB */ + /* NOTE: regs can overlap, e.g. "move v6, v7" or "move v7, v6" */ + FETCH(a3, 1) # a3 <- BBBB + GET_OPA(a2) # a2 <- AA + EAS2(a3, rFP, a3) # a3 <- &fp[BBBB] + LOAD64(a0, a1, a3) # a0/a1 <- fp[BBBB] + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + SET_VREG64(a0, a1, a2) # fp[AA] <- a0/a1 + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_move_wide_16: /* 0x06 */ +/* File: mips/op_move_wide_16.S */ + /* move-wide/16 vAAAA, vBBBB */ + /* NOTE: regs can overlap, e.g. "move v6, v7" or "move v7, v6" */ + FETCH(a3, 2) # a3 <- BBBB + FETCH(a2, 1) # a2 <- AAAA + EAS2(a3, rFP, a3) # a3 <- &fp[BBBB] + LOAD64(a0, a1, a3) # a0/a1 <- fp[BBBB] + FETCH_ADVANCE_INST(3) # advance rPC, load rINST + SET_VREG64(a0, a1, a2) # fp[AAAA] <- a0/a1 + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_move_object: /* 0x07 */ +/* File: mips/op_move_object.S */ +/* File: mips/op_move.S */ + /* for move, move-object, long-to-int */ + /* op vA, vB */ + GET_OPB(a1) # a1 <- B from 15:12 + GET_OPA4(a0) # a0 <- A from 11:8 + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + GET_VREG(a2, a1) # a2 <- fp[B] + GET_INST_OPCODE(t0) # t0 <- opcode from rINST + .if 1 + SET_VREG_OBJECT(a2, a0) # fp[A] <- a2 + .else + SET_VREG(a2, a0) # fp[A] <- a2 + .endif + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_move_object_from16: /* 0x08 */ +/* File: mips/op_move_object_from16.S */ +/* File: mips/op_move_from16.S */ + /* for: move/from16, move-object/from16 */ + /* op vAA, vBBBB */ + FETCH(a1, 1) # a1 <- BBBB + GET_OPA(a0) # a0 <- AA + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_VREG(a2, a1) # a2 <- fp[BBBB] + GET_INST_OPCODE(t0) # extract opcode from rINST + .if 1 + SET_VREG_OBJECT(a2, a0) # fp[AA] <- a2 + .else + SET_VREG(a2, a0) # fp[AA] <- a2 + .endif + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_move_object_16: /* 0x09 */ +/* File: mips/op_move_object_16.S */ +/* File: mips/op_move_16.S */ + /* for: move/16, move-object/16 */ + /* op vAAAA, vBBBB */ + FETCH(a1, 2) # a1 <- BBBB + FETCH(a0, 1) # a0 <- AAAA + FETCH_ADVANCE_INST(3) # advance rPC, load rINST + GET_VREG(a2, a1) # a2 <- fp[BBBB] + GET_INST_OPCODE(t0) # extract opcode from rINST + .if 1 + SET_VREG_OBJECT(a2, a0) # fp[AAAA] <- a2 + .else + SET_VREG(a2, a0) # fp[AAAA] <- a2 + .endif + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_move_result: /* 0x0a */ +/* File: mips/op_move_result.S */ + /* for: move-result, move-result-object */ + /* op vAA */ + GET_OPA(a2) # a2 <- AA + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + lw a0, OFF_FP_RESULT_REGISTER(rFP) # get pointer to result JType + lw a0, 0(a0) # a0 <- result.i + GET_INST_OPCODE(t0) # extract opcode from rINST + .if 0 + SET_VREG_OBJECT(a0, a2) # fp[AA] <- a0 + .else + SET_VREG(a0, a2) # fp[AA] <- a0 + .endif + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_move_result_wide: /* 0x0b */ +/* File: mips/op_move_result_wide.S */ + /* move-result-wide vAA */ + GET_OPA(a2) # a2 <- AA + lw a3, OFF_FP_RESULT_REGISTER(rFP) # get pointer to result JType + LOAD64(a0, a1, a3) # a0/a1 <- retval.j + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + SET_VREG64(a0, a1, a2) # fp[AA] <- a0/a1 + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_move_result_object: /* 0x0c */ +/* File: mips/op_move_result_object.S */ +/* File: mips/op_move_result.S */ + /* for: move-result, move-result-object */ + /* op vAA */ + GET_OPA(a2) # a2 <- AA + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + lw a0, OFF_FP_RESULT_REGISTER(rFP) # get pointer to result JType + lw a0, 0(a0) # a0 <- result.i + GET_INST_OPCODE(t0) # extract opcode from rINST + .if 1 + SET_VREG_OBJECT(a0, a2) # fp[AA] <- a0 + .else + SET_VREG(a0, a2) # fp[AA] <- a0 + .endif + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_move_exception: /* 0x0d */ +/* File: mips/op_move_exception.S */ + /* move-exception vAA */ + GET_OPA(a2) # a2 <- AA + lw a3, THREAD_EXCEPTION_OFFSET(rSELF) # get exception obj + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + SET_VREG_OBJECT(a3, a2) # fp[AA] <- exception obj + GET_INST_OPCODE(t0) # extract opcode from rINST + sw zero, THREAD_EXCEPTION_OFFSET(rSELF) # clear exception + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_return_void: /* 0x0e */ +/* File: mips/op_return_void.S */ + .extern MterpThreadFenceForConstructor + JAL(MterpThreadFenceForConstructor) + lw ra, THREAD_FLAGS_OFFSET(rSELF) + move a0, rSELF + and ra, (THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + beqz ra, 1f + JAL(MterpSuspendCheck) # (self) +1: + move v0, zero + move v1, zero + b MterpReturn + +/* ------------------------------ */ + .balign 128 +.L_op_return: /* 0x0f */ +/* File: mips/op_return.S */ + /* + * Return a 32-bit value. + * + * for: return, return-object + */ + /* op vAA */ + .extern MterpThreadFenceForConstructor + JAL(MterpThreadFenceForConstructor) + lw ra, THREAD_FLAGS_OFFSET(rSELF) + move a0, rSELF + and ra, (THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + beqz ra, 1f + JAL(MterpSuspendCheck) # (self) +1: + GET_OPA(a2) # a2 <- AA + GET_VREG(v0, a2) # v0 <- vAA + move v1, zero + b MterpReturn + +/* ------------------------------ */ + .balign 128 +.L_op_return_wide: /* 0x10 */ +/* File: mips/op_return_wide.S */ + /* + * Return a 64-bit value. + */ + /* return-wide vAA */ + .extern MterpThreadFenceForConstructor + JAL(MterpThreadFenceForConstructor) + lw ra, THREAD_FLAGS_OFFSET(rSELF) + move a0, rSELF + and ra, (THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + beqz ra, 1f + JAL(MterpSuspendCheck) # (self) +1: + GET_OPA(a2) # a2 <- AA + EAS2(a2, rFP, a2) # a2 <- &fp[AA] + LOAD64(v0, v1, a2) # v0/v1 <- vAA/vAA+1 + b MterpReturn + +/* ------------------------------ */ + .balign 128 +.L_op_return_object: /* 0x11 */ +/* File: mips/op_return_object.S */ +/* File: mips/op_return.S */ + /* + * Return a 32-bit value. + * + * for: return, return-object + */ + /* op vAA */ + .extern MterpThreadFenceForConstructor + JAL(MterpThreadFenceForConstructor) + lw ra, THREAD_FLAGS_OFFSET(rSELF) + move a0, rSELF + and ra, (THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + beqz ra, 1f + JAL(MterpSuspendCheck) # (self) +1: + GET_OPA(a2) # a2 <- AA + GET_VREG(v0, a2) # v0 <- vAA + move v1, zero + b MterpReturn + + +/* ------------------------------ */ + .balign 128 +.L_op_const_4: /* 0x12 */ +/* File: mips/op_const_4.S */ + # const/4 vA, /* +B */ + sll a1, rINST, 16 # a1 <- Bxxx0000 + GET_OPA(a0) # a0 <- A+ + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + sra a1, a1, 28 # a1 <- sssssssB (sign-extended) + and a0, a0, 15 + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a1, a0, t0) # fp[A] <- a1 + +/* ------------------------------ */ + .balign 128 +.L_op_const_16: /* 0x13 */ +/* File: mips/op_const_16.S */ + # const/16 vAA, /* +BBBB */ + FETCH_S(a0, 1) # a0 <- ssssBBBB (sign-extended) + GET_OPA(a3) # a3 <- AA + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, a3, t0) # vAA <- a0 + +/* ------------------------------ */ + .balign 128 +.L_op_const: /* 0x14 */ +/* File: mips/op_const.S */ + # const vAA, /* +BBBBbbbb */ + GET_OPA(a3) # a3 <- AA + FETCH(a0, 1) # a0 <- bbbb (low) + FETCH(a1, 2) # a1 <- BBBB (high) + FETCH_ADVANCE_INST(3) # advance rPC, load rINST + sll a1, a1, 16 + or a0, a1, a0 # a0 <- BBBBbbbb + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, a3, t0) # vAA <- a0 + +/* ------------------------------ */ + .balign 128 +.L_op_const_high16: /* 0x15 */ +/* File: mips/op_const_high16.S */ + # const/high16 vAA, /* +BBBB0000 */ + FETCH(a0, 1) # a0 <- 0000BBBB (zero-extended) + GET_OPA(a3) # a3 <- AA + sll a0, a0, 16 # a0 <- BBBB0000 + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, a3, t0) # vAA <- a0 + +/* ------------------------------ */ + .balign 128 +.L_op_const_wide_16: /* 0x16 */ +/* File: mips/op_const_wide_16.S */ + # const-wide/16 vAA, /* +BBBB */ + FETCH_S(a0, 1) # a0 <- ssssBBBB (sign-extended) + GET_OPA(a3) # a3 <- AA + sra a1, a0, 31 # a1 <- ssssssss + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG64(a0, a1, a3) # vAA <- a0/a1 + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_const_wide_32: /* 0x17 */ +/* File: mips/op_const_wide_32.S */ + # const-wide/32 vAA, /* +BBBBbbbb */ + FETCH(a0, 1) # a0 <- 0000bbbb (low) + GET_OPA(a3) # a3 <- AA + FETCH_S(a2, 2) # a2 <- ssssBBBB (high) + FETCH_ADVANCE_INST(3) # advance rPC, load rINST + sll a2, a2, 16 + or a0, a0, a2 # a0 <- BBBBbbbb + sra a1, a0, 31 # a1 <- ssssssss + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG64(a0, a1, a3) # vAA <- a0/a1 + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_const_wide: /* 0x18 */ +/* File: mips/op_const_wide.S */ + # const-wide vAA, /* +HHHHhhhhBBBBbbbb */ + FETCH(a0, 1) # a0 <- bbbb (low) + FETCH(a1, 2) # a1 <- BBBB (low middle) + FETCH(a2, 3) # a2 <- hhhh (high middle) + sll a1, 16 # + or a0, a1 # a0 <- BBBBbbbb (low word) + FETCH(a3, 4) # a3 <- HHHH (high) + GET_OPA(t1) # t1 <- AA + sll a3, 16 + or a1, a3, a2 # a1 <- HHHHhhhh (high word) + FETCH_ADVANCE_INST(5) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG64(a0, a1, t1) # vAA <- a0/a1 + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_const_wide_high16: /* 0x19 */ +/* File: mips/op_const_wide_high16.S */ + # const-wide/high16 vAA, /* +BBBB000000000000 */ + FETCH(a1, 1) # a1 <- 0000BBBB (zero-extended) + GET_OPA(a3) # a3 <- AA + li a0, 0 # a0 <- 00000000 + sll a1, 16 # a1 <- BBBB0000 + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG64(a0, a1, a3) # vAA <- a0/a1 + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_const_string: /* 0x1a */ +/* File: mips/op_const_string.S */ + # const/string vAA, String /* BBBB */ + EXPORT_PC() + FETCH(a0, 1) # a0 <- BBBB + GET_OPA(a1) # a1 <- AA + addu a2, rFP, OFF_FP_SHADOWFRAME # a2 <- shadow frame + move a3, rSELF + JAL(MterpConstString) # v0 <- Mterp(index, tgt_reg, shadow_frame, self) + PREFETCH_INST(2) # load rINST + bnez v0, MterpPossibleException + ADVANCE(2) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_const_string_jumbo: /* 0x1b */ +/* File: mips/op_const_string_jumbo.S */ + # const/string vAA, String /* BBBBBBBB */ + EXPORT_PC() + FETCH(a0, 1) # a0 <- bbbb (low) + FETCH(a2, 2) # a2 <- BBBB (high) + GET_OPA(a1) # a1 <- AA + sll a2, a2, 16 + or a0, a0, a2 # a0 <- BBBBbbbb + addu a2, rFP, OFF_FP_SHADOWFRAME # a2 <- shadow frame + move a3, rSELF + JAL(MterpConstString) # v0 <- Mterp(index, tgt_reg, shadow_frame, self) + PREFETCH_INST(3) # load rINST + bnez v0, MterpPossibleException + ADVANCE(3) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_const_class: /* 0x1c */ +/* File: mips/op_const_class.S */ + # const/class vAA, Class /* BBBB */ + EXPORT_PC() + FETCH(a0, 1) # a0 <- BBBB + GET_OPA(a1) # a1 <- AA + addu a2, rFP, OFF_FP_SHADOWFRAME # a2 <- shadow frame + move a3, rSELF + JAL(MterpConstClass) + PREFETCH_INST(2) # load rINST + bnez v0, MterpPossibleException + ADVANCE(2) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_monitor_enter: /* 0x1d */ +/* File: mips/op_monitor_enter.S */ + /* + * Synchronize on an object. + */ + /* monitor-enter vAA */ + EXPORT_PC() + GET_OPA(a2) # a2 <- AA + GET_VREG(a0, a2) # a0 <- vAA (object) + move a1, rSELF # a1 <- self + JAL(artLockObjectFromCode) # v0 <- artLockObject(obj, self) + bnez v0, MterpException + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_monitor_exit: /* 0x1e */ +/* File: mips/op_monitor_exit.S */ + /* + * Unlock an object. + * + * Exceptions that occur when unlocking a monitor need to appear as + * if they happened at the following instruction. See the Dalvik + * instruction spec. + */ + /* monitor-exit vAA */ + EXPORT_PC() + GET_OPA(a2) # a2 <- AA + GET_VREG(a0, a2) # a0 <- vAA (object) + move a1, rSELF # a1 <- self + JAL(artUnlockObjectFromCode) # v0 <- artUnlockObject(obj, self) + bnez v0, MterpException + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_check_cast: /* 0x1f */ +/* File: mips/op_check_cast.S */ + /* + * Check to see if a cast from one class to another is allowed. + */ + # check-cast vAA, class /* BBBB */ + EXPORT_PC() + FETCH(a0, 1) # a0 <- BBBB + GET_OPA(a1) # a1 <- AA + EAS2(a1, rFP, a1) # a1 <- &object + lw a2, OFF_FP_METHOD(rFP) # a2 <- method + move a3, rSELF # a3 <- self + JAL(MterpCheckCast) # v0 <- CheckCast(index, &obj, method, self) + PREFETCH_INST(2) + bnez v0, MterpPossibleException + ADVANCE(2) + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_instance_of: /* 0x20 */ +/* File: mips/op_instance_of.S */ + /* + * Check to see if an object reference is an instance of a class. + * + * Most common situation is a non-null object, being compared against + * an already-resolved class. + */ + # instance-of vA, vB, class /* CCCC */ + EXPORT_PC() + FETCH(a0, 1) # a0 <- CCCC + GET_OPB(a1) # a1 <- B + EAS2(a1, rFP, a1) # a1 <- &object + lw a2, OFF_FP_METHOD(rFP) # a2 <- method + move a3, rSELF # a3 <- self + GET_OPA4(rOBJ) # rOBJ <- A+ + JAL(MterpInstanceOf) # v0 <- Mterp(index, &obj, method, self) + lw a1, THREAD_EXCEPTION_OFFSET(rSELF) + PREFETCH_INST(2) # load rINST + bnez a1, MterpException + ADVANCE(2) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(v0, rOBJ, t0) # vA <- v0 + +/* ------------------------------ */ + .balign 128 +.L_op_array_length: /* 0x21 */ +/* File: mips/op_array_length.S */ + /* + * Return the length of an array. + */ + GET_OPB(a1) # a1 <- B + GET_OPA4(a2) # a2 <- A+ + GET_VREG(a0, a1) # a0 <- vB (object ref) + # is object null? + beqz a0, common_errNullObject # yup, fail + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + LOAD_base_offMirrorArray_length(a3, a0) # a3 <- array length + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a3, a2, t0) # vA <- length + +/* ------------------------------ */ + .balign 128 +.L_op_new_instance: /* 0x22 */ +/* File: mips/op_new_instance.S */ + /* + * Create a new instance of a class. + */ + # new-instance vAA, class /* BBBB */ + EXPORT_PC() + addu a0, rFP, OFF_FP_SHADOWFRAME + move a1, rSELF + move a2, rINST + JAL(MterpNewInstance) + beqz v0, MterpPossibleException + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_new_array: /* 0x23 */ +/* File: mips/op_new_array.S */ + /* + * Allocate an array of objects, specified with the array class + * and a count. + * + * The verifier guarantees that this is an array class, so we don't + * check for it here. + */ + /* new-array vA, vB, class@CCCC */ + EXPORT_PC() + addu a0, rFP, OFF_FP_SHADOWFRAME + move a1, rPC + move a2, rINST + move a3, rSELF + JAL(MterpNewArray) + beqz v0, MterpPossibleException + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_filled_new_array: /* 0x24 */ +/* File: mips/op_filled_new_array.S */ + /* + * Create a new array with elements filled from registers. + * + * for: filled-new-array, filled-new-array/range + */ + # op vB, {vD, vE, vF, vG, vA}, class /* CCCC */ + # op {vCCCC..v(CCCC+AA-1)}, type /* BBBB */ + .extern MterpFilledNewArray + EXPORT_PC() + addu a0, rFP, OFF_FP_SHADOWFRAME # a0 <- shadow frame + move a1, rPC + move a2, rSELF + JAL(MterpFilledNewArray) # v0 <- helper(shadow_frame, pc, self) + beqz v0, MterpPossibleException # has exception + FETCH_ADVANCE_INST(3) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_filled_new_array_range: /* 0x25 */ +/* File: mips/op_filled_new_array_range.S */ +/* File: mips/op_filled_new_array.S */ + /* + * Create a new array with elements filled from registers. + * + * for: filled-new-array, filled-new-array/range + */ + # op vB, {vD, vE, vF, vG, vA}, class /* CCCC */ + # op {vCCCC..v(CCCC+AA-1)}, type /* BBBB */ + .extern MterpFilledNewArrayRange + EXPORT_PC() + addu a0, rFP, OFF_FP_SHADOWFRAME # a0 <- shadow frame + move a1, rPC + move a2, rSELF + JAL(MterpFilledNewArrayRange) # v0 <- helper(shadow_frame, pc, self) + beqz v0, MterpPossibleException # has exception + FETCH_ADVANCE_INST(3) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_fill_array_data: /* 0x26 */ +/* File: mips/op_fill_array_data.S */ + /* fill-array-data vAA, +BBBBBBBB */ + EXPORT_PC() + FETCH(a0, 1) # a0 <- bbbb (lo) + FETCH(a1, 2) # a1 <- BBBB (hi) + GET_OPA(a3) # a3 <- AA + sll a1, a1, 16 # a1 <- BBBBbbbb + or a1, a0, a1 # a1 <- BBBBbbbb + GET_VREG(a0, a3) # a0 <- vAA (array object) + EAS1(a1, rPC, a1) # a1 <- PC + BBBBbbbb*2 (array data off.) + JAL(MterpFillArrayData) # v0 <- Mterp(obj, payload) + beqz v0, MterpPossibleException # has exception + FETCH_ADVANCE_INST(3) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_throw: /* 0x27 */ +/* File: mips/op_throw.S */ + /* + * Throw an exception object in the current thread. + */ + /* throw vAA */ + EXPORT_PC() # exception handler can throw + GET_OPA(a2) # a2 <- AA + GET_VREG(a1, a2) # a1 <- vAA (exception object) + # null object? + beqz a1, common_errNullObject # yes, throw an NPE instead + sw a1, THREAD_EXCEPTION_OFFSET(rSELF) # thread->exception <- obj + b MterpException + +/* ------------------------------ */ + .balign 128 +.L_op_goto: /* 0x28 */ +/* File: mips/op_goto.S */ + /* + * Unconditional branch, 8-bit offset. + * + * The branch distance is a signed code-unit offset, which we need to + * double to get a byte offset. + */ + /* goto +AA */ +#if MTERP_PROFILE_BRANCHES + sll a0, rINST, 16 # a0 <- AAxx0000 + sra rINST, a0, 24 # rINST <- ssssssAA (sign-extended) + EXPORT_PC() + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + JAL(MterpProfileBranch) # (self, shadow_frame, offset) + bnez v0, MterpOnStackReplacement # Note: offset must be in rINST + addu a2, rINST, rINST # a2 <- byte offset + FETCH_ADVANCE_INST_RB(a2) # update rPC, load rINST + /* If backwards branch refresh rIBASE */ + bgez a2, 1f + lw ra, THREAD_FLAGS_OFFSET(rSELF) + b MterpCheckSuspendAndContinue +1: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction +#else + sll a0, rINST, 16 # a0 <- AAxx0000 + sra rINST, a0, 24 # rINST <- ssssssAA (sign-extended) + addu a2, rINST, rINST # a2 <- byte offset + FETCH_ADVANCE_INST_RB(a2) # update rPC, load rINST + /* If backwards branch refresh rIBASE */ + bgez a1, 1f + lw ra, THREAD_FLAGS_OFFSET(rSELF) + b MterpCheckSuspendAndContinue +1: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction +#endif + +/* ------------------------------ */ + .balign 128 +.L_op_goto_16: /* 0x29 */ +/* File: mips/op_goto_16.S */ + /* + * Unconditional branch, 16-bit offset. + * + * The branch distance is a signed code-unit offset, which we need to + * double to get a byte offset. + */ + /* goto/16 +AAAA */ +#if MTERP_PROFILE_BRANCHES + FETCH_S(rINST, 1) # rINST <- ssssAAAA (sign-extended) + EXPORT_PC() + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + JAL(MterpProfileBranch) # (self, shadow_frame, offset) + bnez v0, MterpOnStackReplacement # Note: offset must be in rINST + addu a1, rINST, rINST # a1 <- byte offset, flags set + FETCH_ADVANCE_INST_RB(a1) # update rPC, load rINST + bgez a1, 1f + lw ra, THREAD_FLAGS_OFFSET(rSELF) + b MterpCheckSuspendAndContinue +1: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction +#else + FETCH_S(rINST, 1) # rINST <- ssssAAAA (sign-extended) + addu a1, rINST, rINST # a1 <- byte offset, flags set + FETCH_ADVANCE_INST_RB(a1) # update rPC, load rINST + bgez a1, 1f + lw ra, THREAD_FLAGS_OFFSET(rSELF) + b MterpCheckSuspendAndContinue +1: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction +#endif + +/* ------------------------------ */ + .balign 128 +.L_op_goto_32: /* 0x2a */ +/* File: mips/op_goto_32.S */ + /* + * Unconditional branch, 32-bit offset. + * + * The branch distance is a signed code-unit offset, which we need to + * double to get a byte offset. + * + * Unlike most opcodes, this one is allowed to branch to itself, so + * our "backward branch" test must be "<=0" instead of "<0". + */ + /* goto/32 +AAAAAAAA */ +#if MTERP_PROFILE_BRANCHES + FETCH(a0, 1) # a0 <- aaaa (lo) + FETCH(a1, 2) # a1 <- AAAA (hi) + sll a1, a1, 16 + or rINST, a0, a1 # rINST <- AAAAaaaa + EXPORT_PC() + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + JAL(MterpProfileBranch) # (self, shadow_frame, offset) + bnez v0, MterpOnStackReplacement # Note: offset must be in rINST + addu a1, rINST, rINST # a1 <- byte offset + FETCH_ADVANCE_INST_RB(a1) # update rPC, load rINST + bgtz a1, 1f + lw ra, THREAD_FLAGS_OFFSET(rSELF) + b MterpCheckSuspendAndContinue +1: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction +#else + FETCH(a0, 1) # a0 <- aaaa (lo) + FETCH(a1, 2) # a1 <- AAAA (hi) + sll a1, a1, 16 + or rINST, a0, a1 # rINST <- AAAAaaaa + addu a1, rINST, rINST # a1 <- byte offset + FETCH_ADVANCE_INST_RB(a1) # update rPC, load rINST + bgtz a1, 1f + lw ra, THREAD_FLAGS_OFFSET(rSELF) + b MterpCheckSuspendAndContinue +1: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction +#endif + +/* ------------------------------ */ + .balign 128 +.L_op_packed_switch: /* 0x2b */ +/* File: mips/op_packed_switch.S */ + /* + * Handle a packed-switch or sparse-switch instruction. In both cases + * we decode it and hand it off to a helper function. + * + * We don't really expect backward branches in a switch statement, but + * they're perfectly legal, so we check for them here. + * + * for: packed-switch, sparse-switch + */ + /* op vAA, +BBBB */ +#if MTERP_PROFILE_BRANCHES + FETCH(a0, 1) # a0 <- bbbb (lo) + FETCH(a1, 2) # a1 <- BBBB (hi) + GET_OPA(a3) # a3 <- AA + sll t0, a1, 16 + or a0, a0, t0 # a0 <- BBBBbbbb + GET_VREG(a1, a3) # a1 <- vAA + EAS1(a0, rPC, a0) # a0 <- PC + BBBBbbbb*2 + JAL(MterpDoPackedSwitch) # a0 <- code-unit branch offset + move rINST, v0 + EXPORT_PC() + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + JAL(MterpProfileBranch) # (self, shadow_frame, offset) + bnez v0, MterpOnStackReplacement # Note: offset must be in rINST + addu a1, rINST, rINST # a1 <- byte offset + FETCH_ADVANCE_INST_RB(a1) # update rPC, load rINST + bgtz a1, .Lop_packed_switch_finish + lw ra, THREAD_FLAGS_OFFSET(rSELF) + b MterpCheckSuspendAndContinue +#else + FETCH(a0, 1) # a0 <- bbbb (lo) + FETCH(a1, 2) # a1 <- BBBB (hi) + GET_OPA(a3) # a3 <- AA + sll t0, a1, 16 + or a0, a0, t0 # a0 <- BBBBbbbb + GET_VREG(a1, a3) # a1 <- vAA + EAS1(a0, rPC, a0) # a0 <- PC + BBBBbbbb*2 + JAL(MterpDoPackedSwitch) # a0 <- code-unit branch offset + move rINST, v0 + addu a1, rINST, rINST # a1 <- byte offset + FETCH_ADVANCE_INST_RB(a1) # update rPC, load rINST + bgtz a1, 1f + lw ra, THREAD_FLAGS_OFFSET(rSELF) + b MterpCheckSuspendAndContinue +1: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction +#endif + + +/* ------------------------------ */ + .balign 128 +.L_op_sparse_switch: /* 0x2c */ +/* File: mips/op_sparse_switch.S */ +/* File: mips/op_packed_switch.S */ + /* + * Handle a packed-switch or sparse-switch instruction. In both cases + * we decode it and hand it off to a helper function. + * + * We don't really expect backward branches in a switch statement, but + * they're perfectly legal, so we check for them here. + * + * for: packed-switch, sparse-switch + */ + /* op vAA, +BBBB */ +#if MTERP_PROFILE_BRANCHES + FETCH(a0, 1) # a0 <- bbbb (lo) + FETCH(a1, 2) # a1 <- BBBB (hi) + GET_OPA(a3) # a3 <- AA + sll t0, a1, 16 + or a0, a0, t0 # a0 <- BBBBbbbb + GET_VREG(a1, a3) # a1 <- vAA + EAS1(a0, rPC, a0) # a0 <- PC + BBBBbbbb*2 + JAL(MterpDoSparseSwitch) # a0 <- code-unit branch offset + move rINST, v0 + EXPORT_PC() + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + JAL(MterpProfileBranch) # (self, shadow_frame, offset) + bnez v0, MterpOnStackReplacement # Note: offset must be in rINST + addu a1, rINST, rINST # a1 <- byte offset + FETCH_ADVANCE_INST_RB(a1) # update rPC, load rINST + bgtz a1, .Lop_sparse_switch_finish + lw ra, THREAD_FLAGS_OFFSET(rSELF) + b MterpCheckSuspendAndContinue +#else + FETCH(a0, 1) # a0 <- bbbb (lo) + FETCH(a1, 2) # a1 <- BBBB (hi) + GET_OPA(a3) # a3 <- AA + sll t0, a1, 16 + or a0, a0, t0 # a0 <- BBBBbbbb + GET_VREG(a1, a3) # a1 <- vAA + EAS1(a0, rPC, a0) # a0 <- PC + BBBBbbbb*2 + JAL(MterpDoSparseSwitch) # a0 <- code-unit branch offset + move rINST, v0 + addu a1, rINST, rINST # a1 <- byte offset + FETCH_ADVANCE_INST_RB(a1) # update rPC, load rINST + bgtz a1, 1f + lw ra, THREAD_FLAGS_OFFSET(rSELF) + b MterpCheckSuspendAndContinue +1: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction +#endif + + + +/* ------------------------------ */ + .balign 128 +.L_op_cmpl_float: /* 0x2d */ +/* File: mips/op_cmpl_float.S */ + /* + * Compare two floating-point values. Puts 0, 1, or -1 into the + * destination register rTEMP based on the results of the comparison. + * + * Provide a "naninst" instruction that puts 1 or -1 into rTEMP depending + * on what value we'd like to return when one of the operands is NaN. + * + * The operation we're implementing is: + * if (x == y) + * return 0; + * else if (x < y) + * return -1; + * else if (x > y) + * return 1; + * else + * return {-1 or 1}; // one or both operands was NaN + * + * for: cmpl-float, cmpg-float + */ + /* op vAA, vBB, vCC */ + + /* "clasic" form */ + FETCH(a0, 1) # a0 <- CCBB + and a2, a0, 255 # a2 <- BB + srl a3, a0, 8 + GET_VREG_F(ft0, a2) + GET_VREG_F(ft1, a3) +#ifdef MIPS32REVGE6 + cmp.ult.s ft2, ft0, ft1 # Is ft0 < ft1 + li rTEMP, -1 + bc1nez ft2, .Lop_cmpl_float_finish + cmp.ult.s ft2, ft1, ft0 + li rTEMP, 1 + bc1nez ft2, .Lop_cmpl_float_finish + cmp.eq.s ft2, ft0, ft1 + li rTEMP, 0 + bc1nez ft2, .Lop_cmpl_float_finish + b .Lop_cmpl_float_nan +#else + c.olt.s fcc0, ft0, ft1 # Is ft0 < ft1 + li rTEMP, -1 + bc1t fcc0, .Lop_cmpl_float_finish + c.olt.s fcc0, ft1, ft0 + li rTEMP, 1 + bc1t fcc0, .Lop_cmpl_float_finish + c.eq.s fcc0, ft0, ft1 + li rTEMP, 0 + bc1t fcc0, .Lop_cmpl_float_finish + b .Lop_cmpl_float_nan +#endif + +/* ------------------------------ */ + .balign 128 +.L_op_cmpg_float: /* 0x2e */ +/* File: mips/op_cmpg_float.S */ +/* File: mips/op_cmpl_float.S */ + /* + * Compare two floating-point values. Puts 0, 1, or -1 into the + * destination register rTEMP based on the results of the comparison. + * + * Provide a "naninst" instruction that puts 1 or -1 into rTEMP depending + * on what value we'd like to return when one of the operands is NaN. + * + * The operation we're implementing is: + * if (x == y) + * return 0; + * else if (x < y) + * return -1; + * else if (x > y) + * return 1; + * else + * return {-1 or 1}; // one or both operands was NaN + * + * for: cmpl-float, cmpg-float + */ + /* op vAA, vBB, vCC */ + + /* "clasic" form */ + FETCH(a0, 1) # a0 <- CCBB + and a2, a0, 255 # a2 <- BB + srl a3, a0, 8 + GET_VREG_F(ft0, a2) + GET_VREG_F(ft1, a3) +#ifdef MIPS32REVGE6 + cmp.ult.s ft2, ft0, ft1 # Is ft0 < ft1 + li rTEMP, -1 + bc1nez ft2, .Lop_cmpg_float_finish + cmp.ult.s ft2, ft1, ft0 + li rTEMP, 1 + bc1nez ft2, .Lop_cmpg_float_finish + cmp.eq.s ft2, ft0, ft1 + li rTEMP, 0 + bc1nez ft2, .Lop_cmpg_float_finish + b .Lop_cmpg_float_nan +#else + c.olt.s fcc0, ft0, ft1 # Is ft0 < ft1 + li rTEMP, -1 + bc1t fcc0, .Lop_cmpg_float_finish + c.olt.s fcc0, ft1, ft0 + li rTEMP, 1 + bc1t fcc0, .Lop_cmpg_float_finish + c.eq.s fcc0, ft0, ft1 + li rTEMP, 0 + bc1t fcc0, .Lop_cmpg_float_finish + b .Lop_cmpg_float_nan +#endif + + +/* ------------------------------ */ + .balign 128 +.L_op_cmpl_double: /* 0x2f */ +/* File: mips/op_cmpl_double.S */ + /* + * Compare two floating-point values. Puts 0(==), 1(>), or -1(<) + * into the destination register (rTEMP) based on the comparison results. + * + * Provide a "naninst" instruction that puts 1 or -1 into rTEMP depending + * on what value we'd like to return when one of the operands is NaN. + * + * See op_cmpl_float for more details. + * + * For: cmpl-double, cmpg-double + */ + /* op vAA, vBB, vCC */ + + FETCH(a0, 1) # a0 <- CCBB + and rOBJ, a0, 255 # s5 <- BB + srl t0, a0, 8 # t0 <- CC + EAS2(rOBJ, rFP, rOBJ) # s5 <- &fp[BB] + EAS2(t0, rFP, t0) # t0 <- &fp[CC] + LOAD64_F(ft0, ft0f, rOBJ) + LOAD64_F(ft1, ft1f, t0) +#ifdef MIPS32REVGE6 + cmp.ult.d ft2, ft0, ft1 + li rTEMP, -1 + bc1nez ft2, .Lop_cmpl_double_finish + cmp.ult.d ft2, ft1, ft0 + li rTEMP, 1 + bc1nez ft2, .Lop_cmpl_double_finish + cmp.eq.d ft2, ft0, ft1 + li rTEMP, 0 + bc1nez ft2, .Lop_cmpl_double_finish + b .Lop_cmpl_double_nan +#else + c.olt.d fcc0, ft0, ft1 + li rTEMP, -1 + bc1t fcc0, .Lop_cmpl_double_finish + c.olt.d fcc0, ft1, ft0 + li rTEMP, 1 + bc1t fcc0, .Lop_cmpl_double_finish + c.eq.d fcc0, ft0, ft1 + li rTEMP, 0 + bc1t fcc0, .Lop_cmpl_double_finish + b .Lop_cmpl_double_nan +#endif + +/* ------------------------------ */ + .balign 128 +.L_op_cmpg_double: /* 0x30 */ +/* File: mips/op_cmpg_double.S */ +/* File: mips/op_cmpl_double.S */ + /* + * Compare two floating-point values. Puts 0(==), 1(>), or -1(<) + * into the destination register (rTEMP) based on the comparison results. + * + * Provide a "naninst" instruction that puts 1 or -1 into rTEMP depending + * on what value we'd like to return when one of the operands is NaN. + * + * See op_cmpl_float for more details. + * + * For: cmpl-double, cmpg-double + */ + /* op vAA, vBB, vCC */ + + FETCH(a0, 1) # a0 <- CCBB + and rOBJ, a0, 255 # s5 <- BB + srl t0, a0, 8 # t0 <- CC + EAS2(rOBJ, rFP, rOBJ) # s5 <- &fp[BB] + EAS2(t0, rFP, t0) # t0 <- &fp[CC] + LOAD64_F(ft0, ft0f, rOBJ) + LOAD64_F(ft1, ft1f, t0) +#ifdef MIPS32REVGE6 + cmp.ult.d ft2, ft0, ft1 + li rTEMP, -1 + bc1nez ft2, .Lop_cmpg_double_finish + cmp.ult.d ft2, ft1, ft0 + li rTEMP, 1 + bc1nez ft2, .Lop_cmpg_double_finish + cmp.eq.d ft2, ft0, ft1 + li rTEMP, 0 + bc1nez ft2, .Lop_cmpg_double_finish + b .Lop_cmpg_double_nan +#else + c.olt.d fcc0, ft0, ft1 + li rTEMP, -1 + bc1t fcc0, .Lop_cmpg_double_finish + c.olt.d fcc0, ft1, ft0 + li rTEMP, 1 + bc1t fcc0, .Lop_cmpg_double_finish + c.eq.d fcc0, ft0, ft1 + li rTEMP, 0 + bc1t fcc0, .Lop_cmpg_double_finish + b .Lop_cmpg_double_nan +#endif + + +/* ------------------------------ */ + .balign 128 +.L_op_cmp_long: /* 0x31 */ +/* File: mips/op_cmp_long.S */ + /* + * Compare two 64-bit values + * x = y return 0 + * x < y return -1 + * x > y return 1 + * + * I think I can improve on the ARM code by the following observation + * slt t0, x.hi, y.hi; # (x.hi < y.hi) ? 1:0 + * sgt t1, x.hi, y.hi; # (y.hi > x.hi) ? 1:0 + * subu v0, t0, t1 # v0= -1:1:0 for [ < > = ] + */ + /* cmp-long vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # rOBJ <- AA + and a2, a0, 255 # a2 <- BB + srl a3, a0, 8 # a3 <- CC + EAS2(a2, rFP, a2) # a2 <- &fp[BB] + EAS2(a3, rFP, a3) # a3 <- &fp[CC] + LOAD64(a0, a1, a2) # a0/a1 <- vBB/vBB+1 + LOAD64(a2, a3, a3) # a2/a3 <- vCC/vCC+1 + + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + slt t0, a1, a3 # compare hi + sgt t1, a1, a3 + subu v0, t1, t0 # v0 <- (-1, 1, 0) + bnez v0, .Lop_cmp_long_finish + # at this point x.hi==y.hi + sltu t0, a0, a2 # compare lo + sgtu t1, a0, a2 + subu v0, t1, t0 # v0 <- (-1, 1, 0) for [< > =] + +.Lop_cmp_long_finish: + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(v0, rOBJ, t0) # vAA <- v0 + +/* ------------------------------ */ + .balign 128 +.L_op_if_eq: /* 0x32 */ +/* File: mips/op_if_eq.S */ +/* File: mips/bincmp.S */ + /* + * Generic two-operand compare-and-branch operation. Provide a "revcmp" + * fragment that specifies the *reverse* comparison to perform, e.g. + * for "if-le" you would use "gt". + * + * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le + */ + /* if-cmp vA, vB, +CCCC */ + GET_OPA4(a0) # a0 <- A+ + GET_OPB(a1) # a1 <- B + GET_VREG(a3, a1) # a3 <- vB + GET_VREG(a2, a0) # a2 <- vA + bne a2, a3, 1f # branch to 1 if comparison failed + FETCH_S(rINST, 1) # rINST<- branch offset, in code units + b 2f +1: + li rINST, 2 # rINST- BYTE branch dist for not-taken +2: +#if MTERP_PROFILE_BRANCHES + EXPORT_PC() + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + JAL(MterpProfileBranch) # (self, shadow_frame, offset) + bnez v0, MterpOnStackReplacement # Note: offset must be in rINST +#endif + addu a2, rINST, rINST # convert to bytes + FETCH_ADVANCE_INST_RB(a2) # update rPC, load rINST + bgez a2, .L_op_if_eq_finish + lw ra, THREAD_FLAGS_OFFSET(rSELF) + b MterpCheckSuspendAndContinue + + + +/* ------------------------------ */ + .balign 128 +.L_op_if_ne: /* 0x33 */ +/* File: mips/op_if_ne.S */ +/* File: mips/bincmp.S */ + /* + * Generic two-operand compare-and-branch operation. Provide a "revcmp" + * fragment that specifies the *reverse* comparison to perform, e.g. + * for "if-le" you would use "gt". + * + * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le + */ + /* if-cmp vA, vB, +CCCC */ + GET_OPA4(a0) # a0 <- A+ + GET_OPB(a1) # a1 <- B + GET_VREG(a3, a1) # a3 <- vB + GET_VREG(a2, a0) # a2 <- vA + beq a2, a3, 1f # branch to 1 if comparison failed + FETCH_S(rINST, 1) # rINST<- branch offset, in code units + b 2f +1: + li rINST, 2 # rINST- BYTE branch dist for not-taken +2: +#if MTERP_PROFILE_BRANCHES + EXPORT_PC() + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + JAL(MterpProfileBranch) # (self, shadow_frame, offset) + bnez v0, MterpOnStackReplacement # Note: offset must be in rINST +#endif + addu a2, rINST, rINST # convert to bytes + FETCH_ADVANCE_INST_RB(a2) # update rPC, load rINST + bgez a2, .L_op_if_ne_finish + lw ra, THREAD_FLAGS_OFFSET(rSELF) + b MterpCheckSuspendAndContinue + + + +/* ------------------------------ */ + .balign 128 +.L_op_if_lt: /* 0x34 */ +/* File: mips/op_if_lt.S */ +/* File: mips/bincmp.S */ + /* + * Generic two-operand compare-and-branch operation. Provide a "revcmp" + * fragment that specifies the *reverse* comparison to perform, e.g. + * for "if-le" you would use "gt". + * + * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le + */ + /* if-cmp vA, vB, +CCCC */ + GET_OPA4(a0) # a0 <- A+ + GET_OPB(a1) # a1 <- B + GET_VREG(a3, a1) # a3 <- vB + GET_VREG(a2, a0) # a2 <- vA + bge a2, a3, 1f # branch to 1 if comparison failed + FETCH_S(rINST, 1) # rINST<- branch offset, in code units + b 2f +1: + li rINST, 2 # rINST- BYTE branch dist for not-taken +2: +#if MTERP_PROFILE_BRANCHES + EXPORT_PC() + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + JAL(MterpProfileBranch) # (self, shadow_frame, offset) + bnez v0, MterpOnStackReplacement # Note: offset must be in rINST +#endif + addu a2, rINST, rINST # convert to bytes + FETCH_ADVANCE_INST_RB(a2) # update rPC, load rINST + bgez a2, .L_op_if_lt_finish + lw ra, THREAD_FLAGS_OFFSET(rSELF) + b MterpCheckSuspendAndContinue + + + +/* ------------------------------ */ + .balign 128 +.L_op_if_ge: /* 0x35 */ +/* File: mips/op_if_ge.S */ +/* File: mips/bincmp.S */ + /* + * Generic two-operand compare-and-branch operation. Provide a "revcmp" + * fragment that specifies the *reverse* comparison to perform, e.g. + * for "if-le" you would use "gt". + * + * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le + */ + /* if-cmp vA, vB, +CCCC */ + GET_OPA4(a0) # a0 <- A+ + GET_OPB(a1) # a1 <- B + GET_VREG(a3, a1) # a3 <- vB + GET_VREG(a2, a0) # a2 <- vA + blt a2, a3, 1f # branch to 1 if comparison failed + FETCH_S(rINST, 1) # rINST<- branch offset, in code units + b 2f +1: + li rINST, 2 # rINST- BYTE branch dist for not-taken +2: +#if MTERP_PROFILE_BRANCHES + EXPORT_PC() + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + JAL(MterpProfileBranch) # (self, shadow_frame, offset) + bnez v0, MterpOnStackReplacement # Note: offset must be in rINST +#endif + addu a2, rINST, rINST # convert to bytes + FETCH_ADVANCE_INST_RB(a2) # update rPC, load rINST + bgez a2, .L_op_if_ge_finish + lw ra, THREAD_FLAGS_OFFSET(rSELF) + b MterpCheckSuspendAndContinue + + + +/* ------------------------------ */ + .balign 128 +.L_op_if_gt: /* 0x36 */ +/* File: mips/op_if_gt.S */ +/* File: mips/bincmp.S */ + /* + * Generic two-operand compare-and-branch operation. Provide a "revcmp" + * fragment that specifies the *reverse* comparison to perform, e.g. + * for "if-le" you would use "gt". + * + * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le + */ + /* if-cmp vA, vB, +CCCC */ + GET_OPA4(a0) # a0 <- A+ + GET_OPB(a1) # a1 <- B + GET_VREG(a3, a1) # a3 <- vB + GET_VREG(a2, a0) # a2 <- vA + ble a2, a3, 1f # branch to 1 if comparison failed + FETCH_S(rINST, 1) # rINST<- branch offset, in code units + b 2f +1: + li rINST, 2 # rINST- BYTE branch dist for not-taken +2: +#if MTERP_PROFILE_BRANCHES + EXPORT_PC() + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + JAL(MterpProfileBranch) # (self, shadow_frame, offset) + bnez v0, MterpOnStackReplacement # Note: offset must be in rINST +#endif + addu a2, rINST, rINST # convert to bytes + FETCH_ADVANCE_INST_RB(a2) # update rPC, load rINST + bgez a2, .L_op_if_gt_finish + lw ra, THREAD_FLAGS_OFFSET(rSELF) + b MterpCheckSuspendAndContinue + + + +/* ------------------------------ */ + .balign 128 +.L_op_if_le: /* 0x37 */ +/* File: mips/op_if_le.S */ +/* File: mips/bincmp.S */ + /* + * Generic two-operand compare-and-branch operation. Provide a "revcmp" + * fragment that specifies the *reverse* comparison to perform, e.g. + * for "if-le" you would use "gt". + * + * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le + */ + /* if-cmp vA, vB, +CCCC */ + GET_OPA4(a0) # a0 <- A+ + GET_OPB(a1) # a1 <- B + GET_VREG(a3, a1) # a3 <- vB + GET_VREG(a2, a0) # a2 <- vA + bgt a2, a3, 1f # branch to 1 if comparison failed + FETCH_S(rINST, 1) # rINST<- branch offset, in code units + b 2f +1: + li rINST, 2 # rINST- BYTE branch dist for not-taken +2: +#if MTERP_PROFILE_BRANCHES + EXPORT_PC() + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + JAL(MterpProfileBranch) # (self, shadow_frame, offset) + bnez v0, MterpOnStackReplacement # Note: offset must be in rINST +#endif + addu a2, rINST, rINST # convert to bytes + FETCH_ADVANCE_INST_RB(a2) # update rPC, load rINST + bgez a2, .L_op_if_le_finish + lw ra, THREAD_FLAGS_OFFSET(rSELF) + b MterpCheckSuspendAndContinue + + + +/* ------------------------------ */ + .balign 128 +.L_op_if_eqz: /* 0x38 */ +/* File: mips/op_if_eqz.S */ +/* File: mips/zcmp.S */ + /* + * Generic one-operand compare-and-branch operation. Provide a "revcmp" + * fragment that specifies the *reverse* comparison to perform, e.g. + * for "if-le" you would use "gt". + * + * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez + */ + /* if-cmp vAA, +BBBB */ + GET_OPA(a0) # a0 <- AA + GET_VREG(a2, a0) # a2 <- vAA + FETCH_S(rINST, 1) # rINST <- branch offset, in code units + bne a2, zero, 1f # branch to 1 if comparison failed + b 2f +1: + li rINST, 2 # rINST- BYTE branch dist for not-taken +2: +#if MTERP_PROFILE_BRANCHES + EXPORT_PC() + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + JAL(MterpProfileBranch) # (self, shadow_frame, offset) + bnez v0, MterpOnStackReplacement # Note: offset must be in rINST +#endif + addu a1, rINST, rINST # convert to bytes + FETCH_ADVANCE_INST_RB(a1) # update rPC, load rINST + bgez a1, 3f + lw ra, THREAD_FLAGS_OFFSET(rSELF) + b MterpCheckSuspendAndContinue +3: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_if_nez: /* 0x39 */ +/* File: mips/op_if_nez.S */ +/* File: mips/zcmp.S */ + /* + * Generic one-operand compare-and-branch operation. Provide a "revcmp" + * fragment that specifies the *reverse* comparison to perform, e.g. + * for "if-le" you would use "gt". + * + * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez + */ + /* if-cmp vAA, +BBBB */ + GET_OPA(a0) # a0 <- AA + GET_VREG(a2, a0) # a2 <- vAA + FETCH_S(rINST, 1) # rINST <- branch offset, in code units + beq a2, zero, 1f # branch to 1 if comparison failed + b 2f +1: + li rINST, 2 # rINST- BYTE branch dist for not-taken +2: +#if MTERP_PROFILE_BRANCHES + EXPORT_PC() + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + JAL(MterpProfileBranch) # (self, shadow_frame, offset) + bnez v0, MterpOnStackReplacement # Note: offset must be in rINST +#endif + addu a1, rINST, rINST # convert to bytes + FETCH_ADVANCE_INST_RB(a1) # update rPC, load rINST + bgez a1, 3f + lw ra, THREAD_FLAGS_OFFSET(rSELF) + b MterpCheckSuspendAndContinue +3: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_if_ltz: /* 0x3a */ +/* File: mips/op_if_ltz.S */ +/* File: mips/zcmp.S */ + /* + * Generic one-operand compare-and-branch operation. Provide a "revcmp" + * fragment that specifies the *reverse* comparison to perform, e.g. + * for "if-le" you would use "gt". + * + * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez + */ + /* if-cmp vAA, +BBBB */ + GET_OPA(a0) # a0 <- AA + GET_VREG(a2, a0) # a2 <- vAA + FETCH_S(rINST, 1) # rINST <- branch offset, in code units + bge a2, zero, 1f # branch to 1 if comparison failed + b 2f +1: + li rINST, 2 # rINST- BYTE branch dist for not-taken +2: +#if MTERP_PROFILE_BRANCHES + EXPORT_PC() + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + JAL(MterpProfileBranch) # (self, shadow_frame, offset) + bnez v0, MterpOnStackReplacement # Note: offset must be in rINST +#endif + addu a1, rINST, rINST # convert to bytes + FETCH_ADVANCE_INST_RB(a1) # update rPC, load rINST + bgez a1, 3f + lw ra, THREAD_FLAGS_OFFSET(rSELF) + b MterpCheckSuspendAndContinue +3: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_if_gez: /* 0x3b */ +/* File: mips/op_if_gez.S */ +/* File: mips/zcmp.S */ + /* + * Generic one-operand compare-and-branch operation. Provide a "revcmp" + * fragment that specifies the *reverse* comparison to perform, e.g. + * for "if-le" you would use "gt". + * + * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez + */ + /* if-cmp vAA, +BBBB */ + GET_OPA(a0) # a0 <- AA + GET_VREG(a2, a0) # a2 <- vAA + FETCH_S(rINST, 1) # rINST <- branch offset, in code units + blt a2, zero, 1f # branch to 1 if comparison failed + b 2f +1: + li rINST, 2 # rINST- BYTE branch dist for not-taken +2: +#if MTERP_PROFILE_BRANCHES + EXPORT_PC() + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + JAL(MterpProfileBranch) # (self, shadow_frame, offset) + bnez v0, MterpOnStackReplacement # Note: offset must be in rINST +#endif + addu a1, rINST, rINST # convert to bytes + FETCH_ADVANCE_INST_RB(a1) # update rPC, load rINST + bgez a1, 3f + lw ra, THREAD_FLAGS_OFFSET(rSELF) + b MterpCheckSuspendAndContinue +3: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_if_gtz: /* 0x3c */ +/* File: mips/op_if_gtz.S */ +/* File: mips/zcmp.S */ + /* + * Generic one-operand compare-and-branch operation. Provide a "revcmp" + * fragment that specifies the *reverse* comparison to perform, e.g. + * for "if-le" you would use "gt". + * + * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez + */ + /* if-cmp vAA, +BBBB */ + GET_OPA(a0) # a0 <- AA + GET_VREG(a2, a0) # a2 <- vAA + FETCH_S(rINST, 1) # rINST <- branch offset, in code units + ble a2, zero, 1f # branch to 1 if comparison failed + b 2f +1: + li rINST, 2 # rINST- BYTE branch dist for not-taken +2: +#if MTERP_PROFILE_BRANCHES + EXPORT_PC() + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + JAL(MterpProfileBranch) # (self, shadow_frame, offset) + bnez v0, MterpOnStackReplacement # Note: offset must be in rINST +#endif + addu a1, rINST, rINST # convert to bytes + FETCH_ADVANCE_INST_RB(a1) # update rPC, load rINST + bgez a1, 3f + lw ra, THREAD_FLAGS_OFFSET(rSELF) + b MterpCheckSuspendAndContinue +3: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_if_lez: /* 0x3d */ +/* File: mips/op_if_lez.S */ +/* File: mips/zcmp.S */ + /* + * Generic one-operand compare-and-branch operation. Provide a "revcmp" + * fragment that specifies the *reverse* comparison to perform, e.g. + * for "if-le" you would use "gt". + * + * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez + */ + /* if-cmp vAA, +BBBB */ + GET_OPA(a0) # a0 <- AA + GET_VREG(a2, a0) # a2 <- vAA + FETCH_S(rINST, 1) # rINST <- branch offset, in code units + bgt a2, zero, 1f # branch to 1 if comparison failed + b 2f +1: + li rINST, 2 # rINST- BYTE branch dist for not-taken +2: +#if MTERP_PROFILE_BRANCHES + EXPORT_PC() + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + JAL(MterpProfileBranch) # (self, shadow_frame, offset) + bnez v0, MterpOnStackReplacement # Note: offset must be in rINST +#endif + addu a1, rINST, rINST # convert to bytes + FETCH_ADVANCE_INST_RB(a1) # update rPC, load rINST + bgez a1, 3f + lw ra, THREAD_FLAGS_OFFSET(rSELF) + b MterpCheckSuspendAndContinue +3: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_3e: /* 0x3e */ +/* File: mips/op_unused_3e.S */ +/* File: mips/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + b MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_3f: /* 0x3f */ +/* File: mips/op_unused_3f.S */ +/* File: mips/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + b MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_40: /* 0x40 */ +/* File: mips/op_unused_40.S */ +/* File: mips/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + b MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_41: /* 0x41 */ +/* File: mips/op_unused_41.S */ +/* File: mips/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + b MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_42: /* 0x42 */ +/* File: mips/op_unused_42.S */ +/* File: mips/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + b MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_43: /* 0x43 */ +/* File: mips/op_unused_43.S */ +/* File: mips/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + b MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_aget: /* 0x44 */ +/* File: mips/op_aget.S */ + /* + * Array get, 32 bits or less. vAA <- vBB[vCC]. + * + * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17 + * instructions. We use a pair of FETCH_Bs instead. + * + * for: aget, aget-boolean, aget-byte, aget-char, aget-short + * + * NOTE: assumes data offset for arrays is the same for all non-wide types. + * If this changes, specialize. + */ + /* op vAA, vBB, vCC */ + FETCH_B(a2, 1, 0) # a2 <- BB + GET_OPA(rOBJ) # rOBJ <- AA + FETCH_B(a3, 1, 1) # a3 <- CC + GET_VREG(a0, a2) # a0 <- vBB (array object) + GET_VREG(a1, a3) # a1 <- vCC (requested index) + # null array object? + beqz a0, common_errNullObject # yes, bail + LOAD_base_offMirrorArray_length(a3, a0) # a3 <- arrayObj->length + .if 2 + EASN(a0, a0, a1, 2) # a0 <- arrayObj + index*width + .else + addu a0, a0, a1 + .endif + # a1 >= a3; compare unsigned index + bgeu a1, a3, common_errArrayIndex # index >= length, bail + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + lw a2, MIRROR_INT_ARRAY_DATA_OFFSET(a0) # a2 <- vBB[vCC] + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a2, rOBJ, t0) # vAA <- a2 + +/* ------------------------------ */ + .balign 128 +.L_op_aget_wide: /* 0x45 */ +/* File: mips/op_aget_wide.S */ + /* + * Array get, 64 bits. vAA <- vBB[vCC]. + * + * Arrays of long/double are 64-bit aligned. + */ + /* aget-wide vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # rOBJ <- AA + and a2, a0, 255 # a2 <- BB + srl a3, a0, 8 # a3 <- CC + GET_VREG(a0, a2) # a0 <- vBB (array object) + GET_VREG(a1, a3) # a1 <- vCC (requested index) + # null array object? + beqz a0, common_errNullObject # yes, bail + LOAD_base_offMirrorArray_length(a3, a0) # a3 <- arrayObj->length + EAS3(a0, a0, a1) # a0 <- arrayObj + index*width + bgeu a1, a3, common_errArrayIndex # index >= length, bail + + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + LOAD64_off(a2, a3, a0, MIRROR_WIDE_ARRAY_DATA_OFFSET) + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG64_GOTO(a2, a3, rOBJ, t0) # vAA/vAA+1 <- a2/a3 + +/* ------------------------------ */ + .balign 128 +.L_op_aget_object: /* 0x46 */ +/* File: mips/op_aget_object.S */ + /* + * Array object get. vAA <- vBB[vCC]. + * + * for: aget-object + */ + /* op vAA, vBB, vCC */ + FETCH_B(a2, 1, 0) # a2 <- BB + GET_OPA(rOBJ) # rOBJ <- AA + FETCH_B(a3, 1, 1) # a3 <- CC + EXPORT_PC() + GET_VREG(a0, a2) # a0 <- vBB (array object) + GET_VREG(a1, a3) # a1 <- vCC (requested index) + JAL(artAGetObjectFromMterp) # v0 <- GetObj(array, index) + lw a1, THREAD_EXCEPTION_OFFSET(rSELF) + PREFETCH_INST(2) # load rINST + bnez a1, MterpException + SET_VREG_OBJECT(v0, rOBJ) # vAA <- v0 + ADVANCE(2) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_aget_boolean: /* 0x47 */ +/* File: mips/op_aget_boolean.S */ +/* File: mips/op_aget.S */ + /* + * Array get, 32 bits or less. vAA <- vBB[vCC]. + * + * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17 + * instructions. We use a pair of FETCH_Bs instead. + * + * for: aget, aget-boolean, aget-byte, aget-char, aget-short + * + * NOTE: assumes data offset for arrays is the same for all non-wide types. + * If this changes, specialize. + */ + /* op vAA, vBB, vCC */ + FETCH_B(a2, 1, 0) # a2 <- BB + GET_OPA(rOBJ) # rOBJ <- AA + FETCH_B(a3, 1, 1) # a3 <- CC + GET_VREG(a0, a2) # a0 <- vBB (array object) + GET_VREG(a1, a3) # a1 <- vCC (requested index) + # null array object? + beqz a0, common_errNullObject # yes, bail + LOAD_base_offMirrorArray_length(a3, a0) # a3 <- arrayObj->length + .if 0 + EASN(a0, a0, a1, 0) # a0 <- arrayObj + index*width + .else + addu a0, a0, a1 + .endif + # a1 >= a3; compare unsigned index + bgeu a1, a3, common_errArrayIndex # index >= length, bail + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + lbu a2, MIRROR_BOOLEAN_ARRAY_DATA_OFFSET(a0) # a2 <- vBB[vCC] + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a2, rOBJ, t0) # vAA <- a2 + + +/* ------------------------------ */ + .balign 128 +.L_op_aget_byte: /* 0x48 */ +/* File: mips/op_aget_byte.S */ +/* File: mips/op_aget.S */ + /* + * Array get, 32 bits or less. vAA <- vBB[vCC]. + * + * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17 + * instructions. We use a pair of FETCH_Bs instead. + * + * for: aget, aget-boolean, aget-byte, aget-char, aget-short + * + * NOTE: assumes data offset for arrays is the same for all non-wide types. + * If this changes, specialize. + */ + /* op vAA, vBB, vCC */ + FETCH_B(a2, 1, 0) # a2 <- BB + GET_OPA(rOBJ) # rOBJ <- AA + FETCH_B(a3, 1, 1) # a3 <- CC + GET_VREG(a0, a2) # a0 <- vBB (array object) + GET_VREG(a1, a3) # a1 <- vCC (requested index) + # null array object? + beqz a0, common_errNullObject # yes, bail + LOAD_base_offMirrorArray_length(a3, a0) # a3 <- arrayObj->length + .if 0 + EASN(a0, a0, a1, 0) # a0 <- arrayObj + index*width + .else + addu a0, a0, a1 + .endif + # a1 >= a3; compare unsigned index + bgeu a1, a3, common_errArrayIndex # index >= length, bail + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + lb a2, MIRROR_BYTE_ARRAY_DATA_OFFSET(a0) # a2 <- vBB[vCC] + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a2, rOBJ, t0) # vAA <- a2 + + +/* ------------------------------ */ + .balign 128 +.L_op_aget_char: /* 0x49 */ +/* File: mips/op_aget_char.S */ +/* File: mips/op_aget.S */ + /* + * Array get, 32 bits or less. vAA <- vBB[vCC]. + * + * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17 + * instructions. We use a pair of FETCH_Bs instead. + * + * for: aget, aget-boolean, aget-byte, aget-char, aget-short + * + * NOTE: assumes data offset for arrays is the same for all non-wide types. + * If this changes, specialize. + */ + /* op vAA, vBB, vCC */ + FETCH_B(a2, 1, 0) # a2 <- BB + GET_OPA(rOBJ) # rOBJ <- AA + FETCH_B(a3, 1, 1) # a3 <- CC + GET_VREG(a0, a2) # a0 <- vBB (array object) + GET_VREG(a1, a3) # a1 <- vCC (requested index) + # null array object? + beqz a0, common_errNullObject # yes, bail + LOAD_base_offMirrorArray_length(a3, a0) # a3 <- arrayObj->length + .if 1 + EASN(a0, a0, a1, 1) # a0 <- arrayObj + index*width + .else + addu a0, a0, a1 + .endif + # a1 >= a3; compare unsigned index + bgeu a1, a3, common_errArrayIndex # index >= length, bail + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + lhu a2, MIRROR_CHAR_ARRAY_DATA_OFFSET(a0) # a2 <- vBB[vCC] + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a2, rOBJ, t0) # vAA <- a2 + + +/* ------------------------------ */ + .balign 128 +.L_op_aget_short: /* 0x4a */ +/* File: mips/op_aget_short.S */ +/* File: mips/op_aget.S */ + /* + * Array get, 32 bits or less. vAA <- vBB[vCC]. + * + * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17 + * instructions. We use a pair of FETCH_Bs instead. + * + * for: aget, aget-boolean, aget-byte, aget-char, aget-short + * + * NOTE: assumes data offset for arrays is the same for all non-wide types. + * If this changes, specialize. + */ + /* op vAA, vBB, vCC */ + FETCH_B(a2, 1, 0) # a2 <- BB + GET_OPA(rOBJ) # rOBJ <- AA + FETCH_B(a3, 1, 1) # a3 <- CC + GET_VREG(a0, a2) # a0 <- vBB (array object) + GET_VREG(a1, a3) # a1 <- vCC (requested index) + # null array object? + beqz a0, common_errNullObject # yes, bail + LOAD_base_offMirrorArray_length(a3, a0) # a3 <- arrayObj->length + .if 1 + EASN(a0, a0, a1, 1) # a0 <- arrayObj + index*width + .else + addu a0, a0, a1 + .endif + # a1 >= a3; compare unsigned index + bgeu a1, a3, common_errArrayIndex # index >= length, bail + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + lh a2, MIRROR_SHORT_ARRAY_DATA_OFFSET(a0) # a2 <- vBB[vCC] + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a2, rOBJ, t0) # vAA <- a2 + + +/* ------------------------------ */ + .balign 128 +.L_op_aput: /* 0x4b */ +/* File: mips/op_aput.S */ + + /* + * Array put, 32 bits or less. vBB[vCC] <- vAA. + * + * for: aput, aput-boolean, aput-byte, aput-char, aput-short + * + * NOTE: this assumes data offset for arrays is the same for all non-wide types. + * If this changes, specialize. + */ + /* op vAA, vBB, vCC */ + FETCH_B(a2, 1, 0) # a2 <- BB + GET_OPA(rOBJ) # rOBJ <- AA + FETCH_B(a3, 1, 1) # a3 <- CC + GET_VREG(a0, a2) # a0 <- vBB (array object) + GET_VREG(a1, a3) # a1 <- vCC (requested index) + # null array object? + beqz a0, common_errNullObject # yes, bail + LOAD_base_offMirrorArray_length(a3, a0) # a3 <- arrayObj->length + .if 2 + EASN(a0, a0, a1, 2) # a0 <- arrayObj + index*width + .else + addu a0, a0, a1 + .endif + bgeu a1, a3, common_errArrayIndex # index >= length, bail + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_VREG(a2, rOBJ) # a2 <- vAA + GET_INST_OPCODE(t0) # extract opcode from rINST + sw a2, MIRROR_INT_ARRAY_DATA_OFFSET(a0) # vBB[vCC] <- a2 + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_aput_wide: /* 0x4c */ +/* File: mips/op_aput_wide.S */ + /* + * Array put, 64 bits. vBB[vCC] <- vAA. + * + * Arrays of long/double are 64-bit aligned, so it's okay to use STRD. + */ + /* aput-wide vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(t0) # t0 <- AA + and a2, a0, 255 # a2 <- BB + srl a3, a0, 8 # a3 <- CC + GET_VREG(a0, a2) # a0 <- vBB (array object) + GET_VREG(a1, a3) # a1 <- vCC (requested index) + # null array object? + beqz a0, common_errNullObject # yes, bail + LOAD_base_offMirrorArray_length(a3, a0) # a3 <- arrayObj->length + EAS3(a0, a0, a1) # a0 <- arrayObj + index*width + EAS2(rOBJ, rFP, t0) # rOBJ <- &fp[AA] + # compare unsigned index, length + bgeu a1, a3, common_errArrayIndex # index >= length, bail + + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + LOAD64(a2, a3, rOBJ) # a2/a3 <- vAA/vAA+1 + GET_INST_OPCODE(t0) # extract opcode from rINST + STORE64_off(a2, a3, a0, MIRROR_WIDE_ARRAY_DATA_OFFSET) # a2/a3 <- vBB[vCC] + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_aput_object: /* 0x4d */ +/* File: mips/op_aput_object.S */ + /* + * Store an object into an array. vBB[vCC] <- vAA. + * + */ + /* op vAA, vBB, vCC */ + EXPORT_PC() + addu a0, rFP, OFF_FP_SHADOWFRAME + move a1, rPC + move a2, rINST + JAL(MterpAputObject) + beqz v0, MterpPossibleException + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_aput_boolean: /* 0x4e */ +/* File: mips/op_aput_boolean.S */ +/* File: mips/op_aput.S */ + + /* + * Array put, 32 bits or less. vBB[vCC] <- vAA. + * + * for: aput, aput-boolean, aput-byte, aput-char, aput-short + * + * NOTE: this assumes data offset for arrays is the same for all non-wide types. + * If this changes, specialize. + */ + /* op vAA, vBB, vCC */ + FETCH_B(a2, 1, 0) # a2 <- BB + GET_OPA(rOBJ) # rOBJ <- AA + FETCH_B(a3, 1, 1) # a3 <- CC + GET_VREG(a0, a2) # a0 <- vBB (array object) + GET_VREG(a1, a3) # a1 <- vCC (requested index) + # null array object? + beqz a0, common_errNullObject # yes, bail + LOAD_base_offMirrorArray_length(a3, a0) # a3 <- arrayObj->length + .if 0 + EASN(a0, a0, a1, 0) # a0 <- arrayObj + index*width + .else + addu a0, a0, a1 + .endif + bgeu a1, a3, common_errArrayIndex # index >= length, bail + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_VREG(a2, rOBJ) # a2 <- vAA + GET_INST_OPCODE(t0) # extract opcode from rINST + sb a2, MIRROR_BOOLEAN_ARRAY_DATA_OFFSET(a0) # vBB[vCC] <- a2 + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_aput_byte: /* 0x4f */ +/* File: mips/op_aput_byte.S */ +/* File: mips/op_aput.S */ + + /* + * Array put, 32 bits or less. vBB[vCC] <- vAA. + * + * for: aput, aput-boolean, aput-byte, aput-char, aput-short + * + * NOTE: this assumes data offset for arrays is the same for all non-wide types. + * If this changes, specialize. + */ + /* op vAA, vBB, vCC */ + FETCH_B(a2, 1, 0) # a2 <- BB + GET_OPA(rOBJ) # rOBJ <- AA + FETCH_B(a3, 1, 1) # a3 <- CC + GET_VREG(a0, a2) # a0 <- vBB (array object) + GET_VREG(a1, a3) # a1 <- vCC (requested index) + # null array object? + beqz a0, common_errNullObject # yes, bail + LOAD_base_offMirrorArray_length(a3, a0) # a3 <- arrayObj->length + .if 0 + EASN(a0, a0, a1, 0) # a0 <- arrayObj + index*width + .else + addu a0, a0, a1 + .endif + bgeu a1, a3, common_errArrayIndex # index >= length, bail + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_VREG(a2, rOBJ) # a2 <- vAA + GET_INST_OPCODE(t0) # extract opcode from rINST + sb a2, MIRROR_BYTE_ARRAY_DATA_OFFSET(a0) # vBB[vCC] <- a2 + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_aput_char: /* 0x50 */ +/* File: mips/op_aput_char.S */ +/* File: mips/op_aput.S */ + + /* + * Array put, 32 bits or less. vBB[vCC] <- vAA. + * + * for: aput, aput-boolean, aput-byte, aput-char, aput-short + * + * NOTE: this assumes data offset for arrays is the same for all non-wide types. + * If this changes, specialize. + */ + /* op vAA, vBB, vCC */ + FETCH_B(a2, 1, 0) # a2 <- BB + GET_OPA(rOBJ) # rOBJ <- AA + FETCH_B(a3, 1, 1) # a3 <- CC + GET_VREG(a0, a2) # a0 <- vBB (array object) + GET_VREG(a1, a3) # a1 <- vCC (requested index) + # null array object? + beqz a0, common_errNullObject # yes, bail + LOAD_base_offMirrorArray_length(a3, a0) # a3 <- arrayObj->length + .if 1 + EASN(a0, a0, a1, 1) # a0 <- arrayObj + index*width + .else + addu a0, a0, a1 + .endif + bgeu a1, a3, common_errArrayIndex # index >= length, bail + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_VREG(a2, rOBJ) # a2 <- vAA + GET_INST_OPCODE(t0) # extract opcode from rINST + sh a2, MIRROR_CHAR_ARRAY_DATA_OFFSET(a0) # vBB[vCC] <- a2 + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_aput_short: /* 0x51 */ +/* File: mips/op_aput_short.S */ +/* File: mips/op_aput.S */ + + /* + * Array put, 32 bits or less. vBB[vCC] <- vAA. + * + * for: aput, aput-boolean, aput-byte, aput-char, aput-short + * + * NOTE: this assumes data offset for arrays is the same for all non-wide types. + * If this changes, specialize. + */ + /* op vAA, vBB, vCC */ + FETCH_B(a2, 1, 0) # a2 <- BB + GET_OPA(rOBJ) # rOBJ <- AA + FETCH_B(a3, 1, 1) # a3 <- CC + GET_VREG(a0, a2) # a0 <- vBB (array object) + GET_VREG(a1, a3) # a1 <- vCC (requested index) + # null array object? + beqz a0, common_errNullObject # yes, bail + LOAD_base_offMirrorArray_length(a3, a0) # a3 <- arrayObj->length + .if 1 + EASN(a0, a0, a1, 1) # a0 <- arrayObj + index*width + .else + addu a0, a0, a1 + .endif + bgeu a1, a3, common_errArrayIndex # index >= length, bail + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_VREG(a2, rOBJ) # a2 <- vAA + GET_INST_OPCODE(t0) # extract opcode from rINST + sh a2, MIRROR_SHORT_ARRAY_DATA_OFFSET(a0) # vBB[vCC] <- a2 + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_iget: /* 0x52 */ +/* File: mips/op_iget.S */ + /* + * General instance field get. + * + * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short + */ + EXPORT_PC() + FETCH(a0, 1) # a0 <- field ref CCCC + GET_OPB(a1) # a1 <- B + GET_VREG(a1, a1) # a1 <- fp[B], the object pointer + lw a2, OFF_FP_METHOD(rFP) # a2 <- referrer + move a3, rSELF # a3 <- self + JAL(artGet32InstanceFromCode) + lw a3, THREAD_EXCEPTION_OFFSET(rSELF) + GET_OPA4(a2) # a2<- A+ + PREFETCH_INST(2) # load rINST + bnez a3, MterpPossibleException # bail out + .if 0 + SET_VREG_OBJECT(v0, a2) # fp[A] <- v0 + .else + SET_VREG(v0, a2) # fp[A] <- v0 + .endif + ADVANCE(2) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_iget_wide: /* 0x53 */ +/* File: mips/op_iget_wide.S */ + /* + * 64-bit instance field get. + * + * for: iget-wide + */ + EXPORT_PC() + FETCH(a0, 1) # a0 <- field byte offset + GET_OPB(a1) # a1 <- B + GET_VREG(a1, a1) # a1 <- fp[B], the object pointer + lw a2, OFF_FP_METHOD(rFP) # a2 <- referrer + move a3, rSELF # a3 <- self + JAL(artGet64InstanceFromCode) + lw a3, THREAD_EXCEPTION_OFFSET(rSELF) + GET_OPA4(a2) # a2<- A+ + PREFETCH_INST(2) # load rINST + bnez a3, MterpException # bail out + SET_VREG64(v0, v1, a2) # fp[A] <- v0/v1 + ADVANCE(2) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_iget_object: /* 0x54 */ +/* File: mips/op_iget_object.S */ +/* File: mips/op_iget.S */ + /* + * General instance field get. + * + * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short + */ + EXPORT_PC() + FETCH(a0, 1) # a0 <- field ref CCCC + GET_OPB(a1) # a1 <- B + GET_VREG(a1, a1) # a1 <- fp[B], the object pointer + lw a2, OFF_FP_METHOD(rFP) # a2 <- referrer + move a3, rSELF # a3 <- self + JAL(artGetObjInstanceFromCode) + lw a3, THREAD_EXCEPTION_OFFSET(rSELF) + GET_OPA4(a2) # a2<- A+ + PREFETCH_INST(2) # load rINST + bnez a3, MterpPossibleException # bail out + .if 1 + SET_VREG_OBJECT(v0, a2) # fp[A] <- v0 + .else + SET_VREG(v0, a2) # fp[A] <- v0 + .endif + ADVANCE(2) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_boolean: /* 0x55 */ +/* File: mips/op_iget_boolean.S */ +/* File: mips/op_iget.S */ + /* + * General instance field get. + * + * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short + */ + EXPORT_PC() + FETCH(a0, 1) # a0 <- field ref CCCC + GET_OPB(a1) # a1 <- B + GET_VREG(a1, a1) # a1 <- fp[B], the object pointer + lw a2, OFF_FP_METHOD(rFP) # a2 <- referrer + move a3, rSELF # a3 <- self + JAL(artGetBooleanInstanceFromCode) + lw a3, THREAD_EXCEPTION_OFFSET(rSELF) + GET_OPA4(a2) # a2<- A+ + PREFETCH_INST(2) # load rINST + bnez a3, MterpPossibleException # bail out + .if 0 + SET_VREG_OBJECT(v0, a2) # fp[A] <- v0 + .else + SET_VREG(v0, a2) # fp[A] <- v0 + .endif + ADVANCE(2) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_byte: /* 0x56 */ +/* File: mips/op_iget_byte.S */ +/* File: mips/op_iget.S */ + /* + * General instance field get. + * + * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short + */ + EXPORT_PC() + FETCH(a0, 1) # a0 <- field ref CCCC + GET_OPB(a1) # a1 <- B + GET_VREG(a1, a1) # a1 <- fp[B], the object pointer + lw a2, OFF_FP_METHOD(rFP) # a2 <- referrer + move a3, rSELF # a3 <- self + JAL(artGetByteInstanceFromCode) + lw a3, THREAD_EXCEPTION_OFFSET(rSELF) + GET_OPA4(a2) # a2<- A+ + PREFETCH_INST(2) # load rINST + bnez a3, MterpPossibleException # bail out + .if 0 + SET_VREG_OBJECT(v0, a2) # fp[A] <- v0 + .else + SET_VREG(v0, a2) # fp[A] <- v0 + .endif + ADVANCE(2) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_char: /* 0x57 */ +/* File: mips/op_iget_char.S */ +/* File: mips/op_iget.S */ + /* + * General instance field get. + * + * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short + */ + EXPORT_PC() + FETCH(a0, 1) # a0 <- field ref CCCC + GET_OPB(a1) # a1 <- B + GET_VREG(a1, a1) # a1 <- fp[B], the object pointer + lw a2, OFF_FP_METHOD(rFP) # a2 <- referrer + move a3, rSELF # a3 <- self + JAL(artGetCharInstanceFromCode) + lw a3, THREAD_EXCEPTION_OFFSET(rSELF) + GET_OPA4(a2) # a2<- A+ + PREFETCH_INST(2) # load rINST + bnez a3, MterpPossibleException # bail out + .if 0 + SET_VREG_OBJECT(v0, a2) # fp[A] <- v0 + .else + SET_VREG(v0, a2) # fp[A] <- v0 + .endif + ADVANCE(2) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_short: /* 0x58 */ +/* File: mips/op_iget_short.S */ +/* File: mips/op_iget.S */ + /* + * General instance field get. + * + * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short + */ + EXPORT_PC() + FETCH(a0, 1) # a0 <- field ref CCCC + GET_OPB(a1) # a1 <- B + GET_VREG(a1, a1) # a1 <- fp[B], the object pointer + lw a2, OFF_FP_METHOD(rFP) # a2 <- referrer + move a3, rSELF # a3 <- self + JAL(artGetShortInstanceFromCode) + lw a3, THREAD_EXCEPTION_OFFSET(rSELF) + GET_OPA4(a2) # a2<- A+ + PREFETCH_INST(2) # load rINST + bnez a3, MterpPossibleException # bail out + .if 0 + SET_VREG_OBJECT(v0, a2) # fp[A] <- v0 + .else + SET_VREG(v0, a2) # fp[A] <- v0 + .endif + ADVANCE(2) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_iput: /* 0x59 */ +/* File: mips/op_iput.S */ + /* + * General 32-bit instance field put. + * + * for: iput, iput-boolean, iput-byte, iput-char, iput-short + */ + # op vA, vB, field /* CCCC */ + .extern artSet32InstanceFromMterp + EXPORT_PC() + FETCH(a0, 1) # a0 <- field ref CCCC + GET_OPB(a1) # a1 <- B + GET_VREG(a1, a1) # a1 <- fp[B], the object pointer + GET_OPA4(a2) # a2 <- A+ + GET_VREG(a2, a2) # a2 <- fp[A] + lw a3, OFF_FP_METHOD(rFP) # a3 <- referrer + PREFETCH_INST(2) # load rINST + JAL(artSet32InstanceFromMterp) + bnez v0, MterpPossibleException # bail out + ADVANCE(2) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_iput_wide: /* 0x5a */ +/* File: mips/op_iput_wide.S */ + # iput-wide vA, vB, field /* CCCC */ + .extern artSet64InstanceFromMterp + EXPORT_PC() + FETCH(a0, 1) # a0 <- field ref CCCC + GET_OPB(a1) # a1 <- B + GET_VREG(a1, a1) # a1 <- fp[B], the object pointer + GET_OPA4(a2) # a2 <- A+ + EAS2(a2, rFP, a2) # a2 <- &fp[A] + lw a3, OFF_FP_METHOD(rFP) # a3 <- referrer + PREFETCH_INST(2) # load rINST + JAL(artSet64InstanceFromMterp) + bnez v0, MterpPossibleException # bail out + ADVANCE(2) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_iput_object: /* 0x5b */ +/* File: mips/op_iput_object.S */ + /* + * 32-bit instance field put. + * + * for: iput-object, iput-object-volatile + */ + # op vA, vB, field /* CCCC */ + EXPORT_PC() + addu a0, rFP, OFF_FP_SHADOWFRAME + move a1, rPC + move a2, rINST + move a3, rSELF + JAL(MterpIputObject) + beqz v0, MterpException + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_iput_boolean: /* 0x5c */ +/* File: mips/op_iput_boolean.S */ +/* File: mips/op_iput.S */ + /* + * General 32-bit instance field put. + * + * for: iput, iput-boolean, iput-byte, iput-char, iput-short + */ + # op vA, vB, field /* CCCC */ + .extern artSet8InstanceFromMterp + EXPORT_PC() + FETCH(a0, 1) # a0 <- field ref CCCC + GET_OPB(a1) # a1 <- B + GET_VREG(a1, a1) # a1 <- fp[B], the object pointer + GET_OPA4(a2) # a2 <- A+ + GET_VREG(a2, a2) # a2 <- fp[A] + lw a3, OFF_FP_METHOD(rFP) # a3 <- referrer + PREFETCH_INST(2) # load rINST + JAL(artSet8InstanceFromMterp) + bnez v0, MterpPossibleException # bail out + ADVANCE(2) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_iput_byte: /* 0x5d */ +/* File: mips/op_iput_byte.S */ +/* File: mips/op_iput.S */ + /* + * General 32-bit instance field put. + * + * for: iput, iput-boolean, iput-byte, iput-char, iput-short + */ + # op vA, vB, field /* CCCC */ + .extern artSet8InstanceFromMterp + EXPORT_PC() + FETCH(a0, 1) # a0 <- field ref CCCC + GET_OPB(a1) # a1 <- B + GET_VREG(a1, a1) # a1 <- fp[B], the object pointer + GET_OPA4(a2) # a2 <- A+ + GET_VREG(a2, a2) # a2 <- fp[A] + lw a3, OFF_FP_METHOD(rFP) # a3 <- referrer + PREFETCH_INST(2) # load rINST + JAL(artSet8InstanceFromMterp) + bnez v0, MterpPossibleException # bail out + ADVANCE(2) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_iput_char: /* 0x5e */ +/* File: mips/op_iput_char.S */ +/* File: mips/op_iput.S */ + /* + * General 32-bit instance field put. + * + * for: iput, iput-boolean, iput-byte, iput-char, iput-short + */ + # op vA, vB, field /* CCCC */ + .extern artSet16InstanceFromMterp + EXPORT_PC() + FETCH(a0, 1) # a0 <- field ref CCCC + GET_OPB(a1) # a1 <- B + GET_VREG(a1, a1) # a1 <- fp[B], the object pointer + GET_OPA4(a2) # a2 <- A+ + GET_VREG(a2, a2) # a2 <- fp[A] + lw a3, OFF_FP_METHOD(rFP) # a3 <- referrer + PREFETCH_INST(2) # load rINST + JAL(artSet16InstanceFromMterp) + bnez v0, MterpPossibleException # bail out + ADVANCE(2) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_iput_short: /* 0x5f */ +/* File: mips/op_iput_short.S */ +/* File: mips/op_iput.S */ + /* + * General 32-bit instance field put. + * + * for: iput, iput-boolean, iput-byte, iput-char, iput-short + */ + # op vA, vB, field /* CCCC */ + .extern artSet16InstanceFromMterp + EXPORT_PC() + FETCH(a0, 1) # a0 <- field ref CCCC + GET_OPB(a1) # a1 <- B + GET_VREG(a1, a1) # a1 <- fp[B], the object pointer + GET_OPA4(a2) # a2 <- A+ + GET_VREG(a2, a2) # a2 <- fp[A] + lw a3, OFF_FP_METHOD(rFP) # a3 <- referrer + PREFETCH_INST(2) # load rINST + JAL(artSet16InstanceFromMterp) + bnez v0, MterpPossibleException # bail out + ADVANCE(2) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_sget: /* 0x60 */ +/* File: mips/op_sget.S */ + /* + * General SGET handler. + * + * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short + */ + # op vAA, field /* BBBB */ + .extern artGet32StaticFromCode + EXPORT_PC() + FETCH(a0, 1) # a0 <- field ref BBBB + lw a1, OFF_FP_METHOD(rFP) # a1 <- method + move a2, rSELF # a2 <- self + JAL(artGet32StaticFromCode) + lw a3, THREAD_EXCEPTION_OFFSET(rSELF) + GET_OPA(a2) # a2 <- AA + PREFETCH_INST(2) + bnez a3, MterpException # bail out +.if 0 + SET_VREG_OBJECT(v0, a2) # fp[AA] <- v0 +.else + SET_VREG(v0, a2) # fp[AA] <- v0 +.endif + ADVANCE(2) + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_sget_wide: /* 0x61 */ +/* File: mips/op_sget_wide.S */ + /* + * 64-bit SGET handler. + */ + # sget-wide vAA, field /* BBBB */ + .extern artGet64StaticFromCode + EXPORT_PC() + FETCH(a0, 1) # a0 <- field ref BBBB + lw a1, OFF_FP_METHOD(rFP) # a1 <- method + move a2, rSELF # a2 <- self + JAL(artGet64StaticFromCode) + lw a3, THREAD_EXCEPTION_OFFSET(rSELF) + bnez a3, MterpException + GET_OPA(a1) # a1 <- AA + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + SET_VREG64(v0, v1, a1) # vAA/vAA+1 <- v0/v1 + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_sget_object: /* 0x62 */ +/* File: mips/op_sget_object.S */ +/* File: mips/op_sget.S */ + /* + * General SGET handler. + * + * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short + */ + # op vAA, field /* BBBB */ + .extern artGetObjStaticFromCode + EXPORT_PC() + FETCH(a0, 1) # a0 <- field ref BBBB + lw a1, OFF_FP_METHOD(rFP) # a1 <- method + move a2, rSELF # a2 <- self + JAL(artGetObjStaticFromCode) + lw a3, THREAD_EXCEPTION_OFFSET(rSELF) + GET_OPA(a2) # a2 <- AA + PREFETCH_INST(2) + bnez a3, MterpException # bail out +.if 1 + SET_VREG_OBJECT(v0, a2) # fp[AA] <- v0 +.else + SET_VREG(v0, a2) # fp[AA] <- v0 +.endif + ADVANCE(2) + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_sget_boolean: /* 0x63 */ +/* File: mips/op_sget_boolean.S */ +/* File: mips/op_sget.S */ + /* + * General SGET handler. + * + * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short + */ + # op vAA, field /* BBBB */ + .extern artGetBooleanStaticFromCode + EXPORT_PC() + FETCH(a0, 1) # a0 <- field ref BBBB + lw a1, OFF_FP_METHOD(rFP) # a1 <- method + move a2, rSELF # a2 <- self + JAL(artGetBooleanStaticFromCode) + lw a3, THREAD_EXCEPTION_OFFSET(rSELF) + GET_OPA(a2) # a2 <- AA + PREFETCH_INST(2) + bnez a3, MterpException # bail out +.if 0 + SET_VREG_OBJECT(v0, a2) # fp[AA] <- v0 +.else + SET_VREG(v0, a2) # fp[AA] <- v0 +.endif + ADVANCE(2) + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_sget_byte: /* 0x64 */ +/* File: mips/op_sget_byte.S */ +/* File: mips/op_sget.S */ + /* + * General SGET handler. + * + * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short + */ + # op vAA, field /* BBBB */ + .extern artGetByteStaticFromCode + EXPORT_PC() + FETCH(a0, 1) # a0 <- field ref BBBB + lw a1, OFF_FP_METHOD(rFP) # a1 <- method + move a2, rSELF # a2 <- self + JAL(artGetByteStaticFromCode) + lw a3, THREAD_EXCEPTION_OFFSET(rSELF) + GET_OPA(a2) # a2 <- AA + PREFETCH_INST(2) + bnez a3, MterpException # bail out +.if 0 + SET_VREG_OBJECT(v0, a2) # fp[AA] <- v0 +.else + SET_VREG(v0, a2) # fp[AA] <- v0 +.endif + ADVANCE(2) + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_sget_char: /* 0x65 */ +/* File: mips/op_sget_char.S */ +/* File: mips/op_sget.S */ + /* + * General SGET handler. + * + * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short + */ + # op vAA, field /* BBBB */ + .extern artGetCharStaticFromCode + EXPORT_PC() + FETCH(a0, 1) # a0 <- field ref BBBB + lw a1, OFF_FP_METHOD(rFP) # a1 <- method + move a2, rSELF # a2 <- self + JAL(artGetCharStaticFromCode) + lw a3, THREAD_EXCEPTION_OFFSET(rSELF) + GET_OPA(a2) # a2 <- AA + PREFETCH_INST(2) + bnez a3, MterpException # bail out +.if 0 + SET_VREG_OBJECT(v0, a2) # fp[AA] <- v0 +.else + SET_VREG(v0, a2) # fp[AA] <- v0 +.endif + ADVANCE(2) + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_sget_short: /* 0x66 */ +/* File: mips/op_sget_short.S */ +/* File: mips/op_sget.S */ + /* + * General SGET handler. + * + * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short + */ + # op vAA, field /* BBBB */ + .extern artGetShortStaticFromCode + EXPORT_PC() + FETCH(a0, 1) # a0 <- field ref BBBB + lw a1, OFF_FP_METHOD(rFP) # a1 <- method + move a2, rSELF # a2 <- self + JAL(artGetShortStaticFromCode) + lw a3, THREAD_EXCEPTION_OFFSET(rSELF) + GET_OPA(a2) # a2 <- AA + PREFETCH_INST(2) + bnez a3, MterpException # bail out +.if 0 + SET_VREG_OBJECT(v0, a2) # fp[AA] <- v0 +.else + SET_VREG(v0, a2) # fp[AA] <- v0 +.endif + ADVANCE(2) + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_sput: /* 0x67 */ +/* File: mips/op_sput.S */ + /* + * General SPUT handler. + * + * for: sput, sput-boolean, sput-byte, sput-char, sput-short + */ + # op vAA, field /* BBBB */ + EXPORT_PC() + FETCH(a0, 1) # a0 <- field ref BBBB + GET_OPA(a3) # a3 <- AA + GET_VREG(a1, a3) # a1 <- fp[AA], the object pointer + lw a2, OFF_FP_METHOD(rFP) # a2 <- method + move a3, rSELF # a3 <- self + PREFETCH_INST(2) # load rINST + JAL(artSet32StaticFromCode) + bnez v0, MterpException # bail out + ADVANCE(2) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_sput_wide: /* 0x68 */ +/* File: mips/op_sput_wide.S */ + /* + * 64-bit SPUT handler. + */ + # sput-wide vAA, field /* BBBB */ + .extern artSet64IndirectStaticFromMterp + EXPORT_PC() + FETCH(a0, 1) # a0 <- field ref CCCC + lw a1, OFF_FP_METHOD(rFP) # a1 <- method + GET_OPA(a2) # a2 <- AA + EAS2(a2, rFP, a2) # a2 <- &fp[AA] + move a3, rSELF # a3 <- self + PREFETCH_INST(2) # load rINST + JAL(artSet64IndirectStaticFromMterp) + bnez v0, MterpException # bail out + ADVANCE(2) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_sput_object: /* 0x69 */ +/* File: mips/op_sput_object.S */ + /* + * General 32-bit SPUT handler. + * + * for: sput-object, + */ + /* op vAA, field@BBBB */ + EXPORT_PC() + addu a0, rFP, OFF_FP_SHADOWFRAME + move a1, rPC + move a2, rINST + move a3, rSELF + JAL(MterpSputObject) + beqz v0, MterpException + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_sput_boolean: /* 0x6a */ +/* File: mips/op_sput_boolean.S */ +/* File: mips/op_sput.S */ + /* + * General SPUT handler. + * + * for: sput, sput-boolean, sput-byte, sput-char, sput-short + */ + # op vAA, field /* BBBB */ + EXPORT_PC() + FETCH(a0, 1) # a0 <- field ref BBBB + GET_OPA(a3) # a3 <- AA + GET_VREG(a1, a3) # a1 <- fp[AA], the object pointer + lw a2, OFF_FP_METHOD(rFP) # a2 <- method + move a3, rSELF # a3 <- self + PREFETCH_INST(2) # load rINST + JAL(artSet8StaticFromCode) + bnez v0, MterpException # bail out + ADVANCE(2) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_sput_byte: /* 0x6b */ +/* File: mips/op_sput_byte.S */ +/* File: mips/op_sput.S */ + /* + * General SPUT handler. + * + * for: sput, sput-boolean, sput-byte, sput-char, sput-short + */ + # op vAA, field /* BBBB */ + EXPORT_PC() + FETCH(a0, 1) # a0 <- field ref BBBB + GET_OPA(a3) # a3 <- AA + GET_VREG(a1, a3) # a1 <- fp[AA], the object pointer + lw a2, OFF_FP_METHOD(rFP) # a2 <- method + move a3, rSELF # a3 <- self + PREFETCH_INST(2) # load rINST + JAL(artSet8StaticFromCode) + bnez v0, MterpException # bail out + ADVANCE(2) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_sput_char: /* 0x6c */ +/* File: mips/op_sput_char.S */ +/* File: mips/op_sput.S */ + /* + * General SPUT handler. + * + * for: sput, sput-boolean, sput-byte, sput-char, sput-short + */ + # op vAA, field /* BBBB */ + EXPORT_PC() + FETCH(a0, 1) # a0 <- field ref BBBB + GET_OPA(a3) # a3 <- AA + GET_VREG(a1, a3) # a1 <- fp[AA], the object pointer + lw a2, OFF_FP_METHOD(rFP) # a2 <- method + move a3, rSELF # a3 <- self + PREFETCH_INST(2) # load rINST + JAL(artSet16StaticFromCode) + bnez v0, MterpException # bail out + ADVANCE(2) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_sput_short: /* 0x6d */ +/* File: mips/op_sput_short.S */ +/* File: mips/op_sput.S */ + /* + * General SPUT handler. + * + * for: sput, sput-boolean, sput-byte, sput-char, sput-short + */ + # op vAA, field /* BBBB */ + EXPORT_PC() + FETCH(a0, 1) # a0 <- field ref BBBB + GET_OPA(a3) # a3 <- AA + GET_VREG(a1, a3) # a1 <- fp[AA], the object pointer + lw a2, OFF_FP_METHOD(rFP) # a2 <- method + move a3, rSELF # a3 <- self + PREFETCH_INST(2) # load rINST + JAL(artSet16StaticFromCode) + bnez v0, MterpException # bail out + ADVANCE(2) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_virtual: /* 0x6e */ +/* File: mips/op_invoke_virtual.S */ +/* File: mips/invoke.S */ + /* + * Generic invoke handler wrapper. + */ + # op vB, {vD, vE, vF, vG, vA}, class /* CCCC */ + # op {vCCCC..v(CCCC+AA-1)}, meth /* BBBB */ + .extern MterpInvokeVirtual + EXPORT_PC() + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rPC + move a3, rINST + JAL(MterpInvokeVirtual) + beqz v0, MterpException + FETCH_ADVANCE_INST(3) + JAL(MterpShouldSwitchInterpreters) + bnez v0, MterpFallback + GET_INST_OPCODE(t0) + GOTO_OPCODE(t0) + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_super: /* 0x6f */ +/* File: mips/op_invoke_super.S */ +/* File: mips/invoke.S */ + /* + * Generic invoke handler wrapper. + */ + # op vB, {vD, vE, vF, vG, vA}, class /* CCCC */ + # op {vCCCC..v(CCCC+AA-1)}, meth /* BBBB */ + .extern MterpInvokeSuper + EXPORT_PC() + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rPC + move a3, rINST + JAL(MterpInvokeSuper) + beqz v0, MterpException + FETCH_ADVANCE_INST(3) + JAL(MterpShouldSwitchInterpreters) + bnez v0, MterpFallback + GET_INST_OPCODE(t0) + GOTO_OPCODE(t0) + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_direct: /* 0x70 */ +/* File: mips/op_invoke_direct.S */ +/* File: mips/invoke.S */ + /* + * Generic invoke handler wrapper. + */ + # op vB, {vD, vE, vF, vG, vA}, class /* CCCC */ + # op {vCCCC..v(CCCC+AA-1)}, meth /* BBBB */ + .extern MterpInvokeDirect + EXPORT_PC() + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rPC + move a3, rINST + JAL(MterpInvokeDirect) + beqz v0, MterpException + FETCH_ADVANCE_INST(3) + JAL(MterpShouldSwitchInterpreters) + bnez v0, MterpFallback + GET_INST_OPCODE(t0) + GOTO_OPCODE(t0) + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_static: /* 0x71 */ +/* File: mips/op_invoke_static.S */ +/* File: mips/invoke.S */ + /* + * Generic invoke handler wrapper. + */ + # op vB, {vD, vE, vF, vG, vA}, class /* CCCC */ + # op {vCCCC..v(CCCC+AA-1)}, meth /* BBBB */ + .extern MterpInvokeStatic + EXPORT_PC() + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rPC + move a3, rINST + JAL(MterpInvokeStatic) + beqz v0, MterpException + FETCH_ADVANCE_INST(3) + JAL(MterpShouldSwitchInterpreters) + bnez v0, MterpFallback + GET_INST_OPCODE(t0) + GOTO_OPCODE(t0) + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_interface: /* 0x72 */ +/* File: mips/op_invoke_interface.S */ +/* File: mips/invoke.S */ + /* + * Generic invoke handler wrapper. + */ + # op vB, {vD, vE, vF, vG, vA}, class /* CCCC */ + # op {vCCCC..v(CCCC+AA-1)}, meth /* BBBB */ + .extern MterpInvokeInterface + EXPORT_PC() + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rPC + move a3, rINST + JAL(MterpInvokeInterface) + beqz v0, MterpException + FETCH_ADVANCE_INST(3) + JAL(MterpShouldSwitchInterpreters) + bnez v0, MterpFallback + GET_INST_OPCODE(t0) + GOTO_OPCODE(t0) + + +/* ------------------------------ */ + .balign 128 +.L_op_return_void_no_barrier: /* 0x73 */ +/* File: mips/op_return_void_no_barrier.S */ + lw ra, THREAD_FLAGS_OFFSET(rSELF) + move a0, rSELF + and ra, (THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + beqz ra, 1f + JAL(MterpSuspendCheck) # (self) +1: + move v0, zero + move v1, zero + b MterpReturn + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_virtual_range: /* 0x74 */ +/* File: mips/op_invoke_virtual_range.S */ +/* File: mips/invoke.S */ + /* + * Generic invoke handler wrapper. + */ + # op vB, {vD, vE, vF, vG, vA}, class /* CCCC */ + # op {vCCCC..v(CCCC+AA-1)}, meth /* BBBB */ + .extern MterpInvokeVirtualRange + EXPORT_PC() + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rPC + move a3, rINST + JAL(MterpInvokeVirtualRange) + beqz v0, MterpException + FETCH_ADVANCE_INST(3) + JAL(MterpShouldSwitchInterpreters) + bnez v0, MterpFallback + GET_INST_OPCODE(t0) + GOTO_OPCODE(t0) + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_super_range: /* 0x75 */ +/* File: mips/op_invoke_super_range.S */ +/* File: mips/invoke.S */ + /* + * Generic invoke handler wrapper. + */ + # op vB, {vD, vE, vF, vG, vA}, class /* CCCC */ + # op {vCCCC..v(CCCC+AA-1)}, meth /* BBBB */ + .extern MterpInvokeSuperRange + EXPORT_PC() + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rPC + move a3, rINST + JAL(MterpInvokeSuperRange) + beqz v0, MterpException + FETCH_ADVANCE_INST(3) + JAL(MterpShouldSwitchInterpreters) + bnez v0, MterpFallback + GET_INST_OPCODE(t0) + GOTO_OPCODE(t0) + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_direct_range: /* 0x76 */ +/* File: mips/op_invoke_direct_range.S */ +/* File: mips/invoke.S */ + /* + * Generic invoke handler wrapper. + */ + # op vB, {vD, vE, vF, vG, vA}, class /* CCCC */ + # op {vCCCC..v(CCCC+AA-1)}, meth /* BBBB */ + .extern MterpInvokeDirectRange + EXPORT_PC() + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rPC + move a3, rINST + JAL(MterpInvokeDirectRange) + beqz v0, MterpException + FETCH_ADVANCE_INST(3) + JAL(MterpShouldSwitchInterpreters) + bnez v0, MterpFallback + GET_INST_OPCODE(t0) + GOTO_OPCODE(t0) + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_static_range: /* 0x77 */ +/* File: mips/op_invoke_static_range.S */ +/* File: mips/invoke.S */ + /* + * Generic invoke handler wrapper. + */ + # op vB, {vD, vE, vF, vG, vA}, class /* CCCC */ + # op {vCCCC..v(CCCC+AA-1)}, meth /* BBBB */ + .extern MterpInvokeStaticRange + EXPORT_PC() + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rPC + move a3, rINST + JAL(MterpInvokeStaticRange) + beqz v0, MterpException + FETCH_ADVANCE_INST(3) + JAL(MterpShouldSwitchInterpreters) + bnez v0, MterpFallback + GET_INST_OPCODE(t0) + GOTO_OPCODE(t0) + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_interface_range: /* 0x78 */ +/* File: mips/op_invoke_interface_range.S */ +/* File: mips/invoke.S */ + /* + * Generic invoke handler wrapper. + */ + # op vB, {vD, vE, vF, vG, vA}, class /* CCCC */ + # op {vCCCC..v(CCCC+AA-1)}, meth /* BBBB */ + .extern MterpInvokeInterfaceRange + EXPORT_PC() + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rPC + move a3, rINST + JAL(MterpInvokeInterfaceRange) + beqz v0, MterpException + FETCH_ADVANCE_INST(3) + JAL(MterpShouldSwitchInterpreters) + bnez v0, MterpFallback + GET_INST_OPCODE(t0) + GOTO_OPCODE(t0) + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_79: /* 0x79 */ +/* File: mips/op_unused_79.S */ +/* File: mips/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + b MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_7a: /* 0x7a */ +/* File: mips/op_unused_7a.S */ +/* File: mips/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + b MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_neg_int: /* 0x7b */ +/* File: mips/op_neg_int.S */ +/* File: mips/unop.S */ + /* + * Generic 32-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "result = op a0". + * This could be a MIPS instruction or a function call. + * + * for: neg-int, not-int, neg-float, int-to-float, float-to-int, + * int-to-byte, int-to-char, int-to-short + */ + /* unop vA, vB */ + GET_OPB(a3) # a3 <- B + GET_OPA4(t0) # t0 <- A+ + GET_VREG(a0, a3) # a0 <- vB + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + # optional op + negu a0, a0 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t1) # extract opcode from rINST + SET_VREG_GOTO(a0, t0, t1) # vAA <- result0 + /* 9-10 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_not_int: /* 0x7c */ +/* File: mips/op_not_int.S */ +/* File: mips/unop.S */ + /* + * Generic 32-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "result = op a0". + * This could be a MIPS instruction or a function call. + * + * for: neg-int, not-int, neg-float, int-to-float, float-to-int, + * int-to-byte, int-to-char, int-to-short + */ + /* unop vA, vB */ + GET_OPB(a3) # a3 <- B + GET_OPA4(t0) # t0 <- A+ + GET_VREG(a0, a3) # a0 <- vB + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + # optional op + not a0, a0 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t1) # extract opcode from rINST + SET_VREG_GOTO(a0, t0, t1) # vAA <- result0 + /* 9-10 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_neg_long: /* 0x7d */ +/* File: mips/op_neg_long.S */ +/* File: mips/unopWide.S */ + /* + * Generic 64-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "result = op a0/a1". + * This could be MIPS instruction or a function call. + * + * For: neg-long, not-long, neg-double, + */ + /* unop vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a3) # a3 <- B + EAS2(a3, rFP, a3) # a3 <- &fp[B] + LOAD64(a0, a1, a3) # a0/a1 <- vAA + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + negu v0, a0 # optional op + negu v1, a1; sltu a0, zero, v0; subu v1, v1, a0 # a0/a1 <- op, a2-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG64(v0, v1, rOBJ) # vAA <- a0/a1 + GOTO_OPCODE(t0) # jump to next instruction + /* 12-13 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_not_long: /* 0x7e */ +/* File: mips/op_not_long.S */ +/* File: mips/unopWide.S */ + /* + * Generic 64-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "result = op a0/a1". + * This could be MIPS instruction or a function call. + * + * For: neg-long, not-long, neg-double, + */ + /* unop vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a3) # a3 <- B + EAS2(a3, rFP, a3) # a3 <- &fp[B] + LOAD64(a0, a1, a3) # a0/a1 <- vAA + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + not a0, a0 # optional op + not a1, a1 # a0/a1 <- op, a2-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG64(a0, a1, rOBJ) # vAA <- a0/a1 + GOTO_OPCODE(t0) # jump to next instruction + /* 12-13 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_neg_float: /* 0x7f */ +/* File: mips/op_neg_float.S */ +/* File: mips/unop.S */ + /* + * Generic 32-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "result = op a0". + * This could be a MIPS instruction or a function call. + * + * for: neg-int, not-int, neg-float, int-to-float, float-to-int, + * int-to-byte, int-to-char, int-to-short + */ + /* unop vA, vB */ + GET_OPB(a3) # a3 <- B + GET_OPA4(t0) # t0 <- A+ + GET_VREG(a0, a3) # a0 <- vB + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + # optional op + addu a0, a0, 0x80000000 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t1) # extract opcode from rINST + SET_VREG_GOTO(a0, t0, t1) # vAA <- result0 + /* 9-10 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_neg_double: /* 0x80 */ +/* File: mips/op_neg_double.S */ +/* File: mips/unopWide.S */ + /* + * Generic 64-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "result = op a0/a1". + * This could be MIPS instruction or a function call. + * + * For: neg-long, not-long, neg-double, + */ + /* unop vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a3) # a3 <- B + EAS2(a3, rFP, a3) # a3 <- &fp[B] + LOAD64(a0, a1, a3) # a0/a1 <- vAA + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + # optional op + addu a1, a1, 0x80000000 # a0/a1 <- op, a2-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG64(a0, a1, rOBJ) # vAA <- a0/a1 + GOTO_OPCODE(t0) # jump to next instruction + /* 12-13 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_int_to_long: /* 0x81 */ +/* File: mips/op_int_to_long.S */ +/* File: mips/unopWider.S */ + /* + * Generic 32bit-to-64bit unary operation. Provide an "instr" line + * that specifies an instruction that performs "result = op a0", where + * "result" is a 64-bit quantity in a0/a1. + * + * For: int-to-long + */ + /* unop vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a3) # a3 <- B + GET_VREG(a0, a3) # a0 <- vB + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + # optional op + sra a1, a0, 31 # result <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG64(a0, a1, rOBJ) # vA/vA+1 <- a0/a1 + GOTO_OPCODE(t0) # jump to next instruction + /* 10-11 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_int_to_float: /* 0x82 */ +/* File: mips/op_int_to_float.S */ +/* File: mips/funop.S */ + /* + * Generic 32-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "result = op a0". + * This could be a MIPS instruction or a function call. + * + * for: int-to-float, float-to-int + */ + /* unop vA, vB */ + GET_OPB(a3) # a3 <- B + GET_OPA4(rOBJ) # t0 <- A+ + GET_VREG_F(fa0, a3) + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + cvt.s.w fv0, fa0 + +.Lop_int_to_float_set_vreg_f: + SET_VREG_F(fv0, rOBJ) + GET_INST_OPCODE(t1) # extract opcode from rINST + GOTO_OPCODE(t1) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_int_to_double: /* 0x83 */ +/* File: mips/op_int_to_double.S */ +/* File: mips/funopWider.S */ + /* + * Generic 32bit-to-64bit unary operation. Provide an "instr" line + * that specifies an instruction that performs "result = op a0", where + * "result" is a 64-bit quantity in a0/a1. + * + * For: int-to-double, float-to-long, float-to-double + */ + /* unop vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a3) # a3 <- B + GET_VREG_F(fa0, a3) + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + cvt.d.w fv0, fa0 + +.Lop_int_to_double_set_vreg: + SET_VREG64_F(fv0, fv0f, rOBJ) # vA/vA+1 <- a0/a1 + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_long_to_int: /* 0x84 */ +/* File: mips/op_long_to_int.S */ +/* we ignore the high word, making this equivalent to a 32-bit reg move */ +/* File: mips/op_move.S */ + /* for move, move-object, long-to-int */ + /* op vA, vB */ + GET_OPB(a1) # a1 <- B from 15:12 + GET_OPA4(a0) # a0 <- A from 11:8 + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + GET_VREG(a2, a1) # a2 <- fp[B] + GET_INST_OPCODE(t0) # t0 <- opcode from rINST + .if 0 + SET_VREG_OBJECT(a2, a0) # fp[A] <- a2 + .else + SET_VREG(a2, a0) # fp[A] <- a2 + .endif + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_long_to_float: /* 0x85 */ +/* File: mips/op_long_to_float.S */ +/* File: mips/unopNarrower.S */ + /* + * Generic 64bit-to-32bit unary operation. Provide an "instr" line + * that specifies an instruction that performs "result = op a0/a1", where + * "result" is a 32-bit quantity in a0. + * + * For: long-to-float, double-to-int, double-to-float + * If hard floating point support is available, use fa0 as the parameter, + * except for long-to-float opcode. + * (This would work for long-to-int, but that instruction is actually + * an exact match for OP_MOVE.) + */ + /* unop vA, vB */ + GET_OPB(a3) # a3 <- B + GET_OPA4(rOBJ) # t1 <- A+ + EAS2(a3, rFP, a3) # a3 <- &fp[B] + LOAD64(rARG0, rARG1, a3) + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + JAL(__floatdisf) + +.Lop_long_to_float_set_vreg_f: + SET_VREG_F(fv0, rOBJ) # vA <- result0 + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_long_to_double: /* 0x86 */ +/* File: mips/op_long_to_double.S */ +/* File: mips/funopWide.S */ + /* + * Generic 64-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "result = op a0/a1". + * This could be a MIPS instruction or a function call. + * + * long-to-double, double-to-long + */ + /* unop vA, vB */ + GET_OPA4(rOBJ) # t1 <- A+ + GET_OPB(a3) # a3 <- B + EAS2(a3, rFP, a3) # a3 <- &fp[B] + LOAD64(rARG0, rARG1, a3) + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + # optional op + JAL(__floatdidf) # a0/a1 <- op, a2-a3 changed + +.Lop_long_to_double_set_vreg: + SET_VREG64_F(fv0, fv0f, rOBJ) # vAA <- a0/a1 + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + /* 12-13 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_float_to_int: /* 0x87 */ +/* File: mips/op_float_to_int.S */ +/* File: mips/funop.S */ + /* + * Generic 32-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "result = op a0". + * This could be a MIPS instruction or a function call. + * + * for: int-to-float, float-to-int + */ + /* unop vA, vB */ + GET_OPB(a3) # a3 <- B + GET_OPA4(rOBJ) # t0 <- A+ + GET_VREG_F(fa0, a3) + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + b f2i_doconv + +.Lop_float_to_int_set_vreg_f: + SET_VREG_F(fv0, rOBJ) + GET_INST_OPCODE(t1) # extract opcode from rINST + GOTO_OPCODE(t1) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_float_to_long: /* 0x88 */ +/* File: mips/op_float_to_long.S */ +/* File: mips/funopWider.S */ + /* + * Generic 32bit-to-64bit unary operation. Provide an "instr" line + * that specifies an instruction that performs "result = op a0", where + * "result" is a 64-bit quantity in a0/a1. + * + * For: int-to-double, float-to-long, float-to-double + */ + /* unop vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a3) # a3 <- B + GET_VREG_F(fa0, a3) + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + b f2l_doconv + +.Lop_float_to_long_set_vreg: + SET_VREG64(rRESULT0, rRESULT1, rOBJ) # vA/vA+1 <- a0/a1 + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_float_to_double: /* 0x89 */ +/* File: mips/op_float_to_double.S */ +/* File: mips/funopWider.S */ + /* + * Generic 32bit-to-64bit unary operation. Provide an "instr" line + * that specifies an instruction that performs "result = op a0", where + * "result" is a 64-bit quantity in a0/a1. + * + * For: int-to-double, float-to-long, float-to-double + */ + /* unop vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a3) # a3 <- B + GET_VREG_F(fa0, a3) + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + cvt.d.s fv0, fa0 + +.Lop_float_to_double_set_vreg: + SET_VREG64_F(fv0, fv0f, rOBJ) # vA/vA+1 <- a0/a1 + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_double_to_int: /* 0x8a */ +/* File: mips/op_double_to_int.S */ +/* File: mips/unopNarrower.S */ + /* + * Generic 64bit-to-32bit unary operation. Provide an "instr" line + * that specifies an instruction that performs "result = op a0/a1", where + * "result" is a 32-bit quantity in a0. + * + * For: long-to-float, double-to-int, double-to-float + * If hard floating point support is available, use fa0 as the parameter, + * except for long-to-float opcode. + * (This would work for long-to-int, but that instruction is actually + * an exact match for OP_MOVE.) + */ + /* unop vA, vB */ + GET_OPB(a3) # a3 <- B + GET_OPA4(rOBJ) # t1 <- A+ + EAS2(a3, rFP, a3) # a3 <- &fp[B] + LOAD64_F(fa0, fa0f, a3) + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + b d2i_doconv + +.Lop_double_to_int_set_vreg_f: + SET_VREG_F(fv0, rOBJ) # vA <- result0 + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* + * Convert the double in a0/a1 to an int in a0. + * + * We have to clip values to int min/max per the specification. The + * expected common case is a "reasonable" value that converts directly + * to modest integer. The EABI convert function isn't doing this for us. + */ + +/* ------------------------------ */ + .balign 128 +.L_op_double_to_long: /* 0x8b */ +/* File: mips/op_double_to_long.S */ +/* File: mips/funopWide.S */ + /* + * Generic 64-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "result = op a0/a1". + * This could be a MIPS instruction or a function call. + * + * long-to-double, double-to-long + */ + /* unop vA, vB */ + GET_OPA4(rOBJ) # t1 <- A+ + GET_OPB(a3) # a3 <- B + EAS2(a3, rFP, a3) # a3 <- &fp[B] + LOAD64_F(fa0, fa0f, a3) + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + # optional op + b d2l_doconv # a0/a1 <- op, a2-a3 changed + +.Lop_double_to_long_set_vreg: + SET_VREG64(rRESULT0, rRESULT1, rOBJ) # vAA <- a0/a1 + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + /* 12-13 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_double_to_float: /* 0x8c */ +/* File: mips/op_double_to_float.S */ +/* File: mips/unopNarrower.S */ + /* + * Generic 64bit-to-32bit unary operation. Provide an "instr" line + * that specifies an instruction that performs "result = op a0/a1", where + * "result" is a 32-bit quantity in a0. + * + * For: long-to-float, double-to-int, double-to-float + * If hard floating point support is available, use fa0 as the parameter, + * except for long-to-float opcode. + * (This would work for long-to-int, but that instruction is actually + * an exact match for OP_MOVE.) + */ + /* unop vA, vB */ + GET_OPB(a3) # a3 <- B + GET_OPA4(rOBJ) # t1 <- A+ + EAS2(a3, rFP, a3) # a3 <- &fp[B] + LOAD64_F(fa0, fa0f, a3) + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + cvt.s.d fv0, fa0 + +.Lop_double_to_float_set_vreg_f: + SET_VREG_F(fv0, rOBJ) # vA <- result0 + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_int_to_byte: /* 0x8d */ +/* File: mips/op_int_to_byte.S */ +/* File: mips/unop.S */ + /* + * Generic 32-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "result = op a0". + * This could be a MIPS instruction or a function call. + * + * for: neg-int, not-int, neg-float, int-to-float, float-to-int, + * int-to-byte, int-to-char, int-to-short + */ + /* unop vA, vB */ + GET_OPB(a3) # a3 <- B + GET_OPA4(t0) # t0 <- A+ + GET_VREG(a0, a3) # a0 <- vB + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + sll a0, a0, 24 # optional op + sra a0, a0, 24 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t1) # extract opcode from rINST + SET_VREG_GOTO(a0, t0, t1) # vAA <- result0 + /* 9-10 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_int_to_char: /* 0x8e */ +/* File: mips/op_int_to_char.S */ +/* File: mips/unop.S */ + /* + * Generic 32-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "result = op a0". + * This could be a MIPS instruction or a function call. + * + * for: neg-int, not-int, neg-float, int-to-float, float-to-int, + * int-to-byte, int-to-char, int-to-short + */ + /* unop vA, vB */ + GET_OPB(a3) # a3 <- B + GET_OPA4(t0) # t0 <- A+ + GET_VREG(a0, a3) # a0 <- vB + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + # optional op + and a0, 0xffff # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t1) # extract opcode from rINST + SET_VREG_GOTO(a0, t0, t1) # vAA <- result0 + /* 9-10 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_int_to_short: /* 0x8f */ +/* File: mips/op_int_to_short.S */ +/* File: mips/unop.S */ + /* + * Generic 32-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "result = op a0". + * This could be a MIPS instruction or a function call. + * + * for: neg-int, not-int, neg-float, int-to-float, float-to-int, + * int-to-byte, int-to-char, int-to-short + */ + /* unop vA, vB */ + GET_OPB(a3) # a3 <- B + GET_OPA4(t0) # t0 <- A+ + GET_VREG(a0, a3) # a0 <- vB + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + sll a0, 16 # optional op + sra a0, 16 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t1) # extract opcode from rINST + SET_VREG_GOTO(a0, t0, t1) # vAA <- result0 + /* 9-10 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_add_int: /* 0x90 */ +/* File: mips/op_add_int.S */ +/* File: mips/binop.S */ + /* + * Generic 32-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the ARM math lib + * handles it correctly. + * + * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int, + * xor-int, shl-int, shr-int, ushr-int + */ + /* binop vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # rOBJ <- AA + srl a3, a0, 8 # a3 <- CC + and a2, a0, 255 # a2 <- BB + GET_VREG(a1, a3) # a1 <- vCC + GET_VREG(a0, a2) # a0 <- vBB + .if 0 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + # optional op + addu a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 11-14 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_sub_int: /* 0x91 */ +/* File: mips/op_sub_int.S */ +/* File: mips/binop.S */ + /* + * Generic 32-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the ARM math lib + * handles it correctly. + * + * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int, + * xor-int, shl-int, shr-int, ushr-int + */ + /* binop vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # rOBJ <- AA + srl a3, a0, 8 # a3 <- CC + and a2, a0, 255 # a2 <- BB + GET_VREG(a1, a3) # a1 <- vCC + GET_VREG(a0, a2) # a0 <- vBB + .if 0 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + # optional op + subu a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 11-14 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_int: /* 0x92 */ +/* File: mips/op_mul_int.S */ +/* File: mips/binop.S */ + /* + * Generic 32-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the ARM math lib + * handles it correctly. + * + * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int, + * xor-int, shl-int, shr-int, ushr-int + */ + /* binop vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # rOBJ <- AA + srl a3, a0, 8 # a3 <- CC + and a2, a0, 255 # a2 <- BB + GET_VREG(a1, a3) # a1 <- vCC + GET_VREG(a0, a2) # a0 <- vBB + .if 0 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + # optional op + mul a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 11-14 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_div_int: /* 0x93 */ +/* File: mips/op_div_int.S */ +#ifdef MIPS32REVGE6 +/* File: mips/binop.S */ + /* + * Generic 32-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the ARM math lib + * handles it correctly. + * + * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int, + * xor-int, shl-int, shr-int, ushr-int + */ + /* binop vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # rOBJ <- AA + srl a3, a0, 8 # a3 <- CC + and a2, a0, 255 # a2 <- BB + GET_VREG(a1, a3) # a1 <- vCC + GET_VREG(a0, a2) # a0 <- vBB + .if 1 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + # optional op + div a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 11-14 instructions */ + +#else +/* File: mips/binop.S */ + /* + * Generic 32-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the ARM math lib + * handles it correctly. + * + * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int, + * xor-int, shl-int, shr-int, ushr-int + */ + /* binop vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # rOBJ <- AA + srl a3, a0, 8 # a3 <- CC + and a2, a0, 255 # a2 <- BB + GET_VREG(a1, a3) # a1 <- vCC + GET_VREG(a0, a2) # a0 <- vBB + .if 1 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + div zero, a0, a1 # optional op + mflo a0 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 11-14 instructions */ + +#endif + +/* ------------------------------ */ + .balign 128 +.L_op_rem_int: /* 0x94 */ +/* File: mips/op_rem_int.S */ +#ifdef MIPS32REVGE6 +/* File: mips/binop.S */ + /* + * Generic 32-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the ARM math lib + * handles it correctly. + * + * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int, + * xor-int, shl-int, shr-int, ushr-int + */ + /* binop vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # rOBJ <- AA + srl a3, a0, 8 # a3 <- CC + and a2, a0, 255 # a2 <- BB + GET_VREG(a1, a3) # a1 <- vCC + GET_VREG(a0, a2) # a0 <- vBB + .if 1 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + # optional op + mod a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 11-14 instructions */ + +#else +/* File: mips/binop.S */ + /* + * Generic 32-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the ARM math lib + * handles it correctly. + * + * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int, + * xor-int, shl-int, shr-int, ushr-int + */ + /* binop vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # rOBJ <- AA + srl a3, a0, 8 # a3 <- CC + and a2, a0, 255 # a2 <- BB + GET_VREG(a1, a3) # a1 <- vCC + GET_VREG(a0, a2) # a0 <- vBB + .if 1 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + div zero, a0, a1 # optional op + mfhi a0 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 11-14 instructions */ + +#endif + +/* ------------------------------ */ + .balign 128 +.L_op_and_int: /* 0x95 */ +/* File: mips/op_and_int.S */ +/* File: mips/binop.S */ + /* + * Generic 32-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the ARM math lib + * handles it correctly. + * + * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int, + * xor-int, shl-int, shr-int, ushr-int + */ + /* binop vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # rOBJ <- AA + srl a3, a0, 8 # a3 <- CC + and a2, a0, 255 # a2 <- BB + GET_VREG(a1, a3) # a1 <- vCC + GET_VREG(a0, a2) # a0 <- vBB + .if 0 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + # optional op + and a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 11-14 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_or_int: /* 0x96 */ +/* File: mips/op_or_int.S */ +/* File: mips/binop.S */ + /* + * Generic 32-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the ARM math lib + * handles it correctly. + * + * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int, + * xor-int, shl-int, shr-int, ushr-int + */ + /* binop vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # rOBJ <- AA + srl a3, a0, 8 # a3 <- CC + and a2, a0, 255 # a2 <- BB + GET_VREG(a1, a3) # a1 <- vCC + GET_VREG(a0, a2) # a0 <- vBB + .if 0 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + # optional op + or a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 11-14 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_xor_int: /* 0x97 */ +/* File: mips/op_xor_int.S */ +/* File: mips/binop.S */ + /* + * Generic 32-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the ARM math lib + * handles it correctly. + * + * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int, + * xor-int, shl-int, shr-int, ushr-int + */ + /* binop vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # rOBJ <- AA + srl a3, a0, 8 # a3 <- CC + and a2, a0, 255 # a2 <- BB + GET_VREG(a1, a3) # a1 <- vCC + GET_VREG(a0, a2) # a0 <- vBB + .if 0 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + # optional op + xor a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 11-14 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_shl_int: /* 0x98 */ +/* File: mips/op_shl_int.S */ +/* File: mips/binop.S */ + /* + * Generic 32-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the ARM math lib + * handles it correctly. + * + * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int, + * xor-int, shl-int, shr-int, ushr-int + */ + /* binop vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # rOBJ <- AA + srl a3, a0, 8 # a3 <- CC + and a2, a0, 255 # a2 <- BB + GET_VREG(a1, a3) # a1 <- vCC + GET_VREG(a0, a2) # a0 <- vBB + .if 0 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + # optional op + sll a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 11-14 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_shr_int: /* 0x99 */ +/* File: mips/op_shr_int.S */ +/* File: mips/binop.S */ + /* + * Generic 32-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the ARM math lib + * handles it correctly. + * + * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int, + * xor-int, shl-int, shr-int, ushr-int + */ + /* binop vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # rOBJ <- AA + srl a3, a0, 8 # a3 <- CC + and a2, a0, 255 # a2 <- BB + GET_VREG(a1, a3) # a1 <- vCC + GET_VREG(a0, a2) # a0 <- vBB + .if 0 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + # optional op + sra a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 11-14 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_ushr_int: /* 0x9a */ +/* File: mips/op_ushr_int.S */ +/* File: mips/binop.S */ + /* + * Generic 32-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the ARM math lib + * handles it correctly. + * + * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int, + * xor-int, shl-int, shr-int, ushr-int + */ + /* binop vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # rOBJ <- AA + srl a3, a0, 8 # a3 <- CC + and a2, a0, 255 # a2 <- BB + GET_VREG(a1, a3) # a1 <- vCC + GET_VREG(a0, a2) # a0 <- vBB + .if 0 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + # optional op + srl a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 11-14 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_add_long: /* 0x9b */ +/* File: mips/op_add_long.S */ +/* + * The compiler generates the following sequence for + * [v1 v0] = [a1 a0] + [a3 a2]; + * addu v0,a2,a0 + * addu a1,a3,a1 + * sltu v1,v0,a2 + * addu v1,v1,a1 + */ +/* File: mips/binopWide.S */ + /* + * Generic 64-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0-a1 op a2-a3". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * for: add-long, sub-long, div-long, rem-long, and-long, or-long, + * xor-long + * + * IMPORTANT: you may specify "chkzero" or "preinstr" but not both. + */ + /* binop vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # rOBJ <- AA + and a2, a0, 255 # a2 <- BB + srl a3, a0, 8 # a3 <- CC + EAS2(a2, rFP, a2) # a2 <- &fp[BB] + EAS2(t1, rFP, a3) # a3 <- &fp[CC] + LOAD64(a0, a1, a2) # a0/a1 <- vBB/vBB+1 + LOAD64(a2, a3, t1) # a2/a3 <- vCC/vCC+1 + .if 0 + or t0, a2, a3 # second arg (a2-a3) is zero? + beqz t0, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + + addu v0, a2, a0 # optional op + addu a1, a3, a1; sltu v1, v0, a2; addu v1, v1, a1 # result <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG64_GOTO(v0, v1, rOBJ, t0) # vAA/vAA+1 <- v0/v1 + /* 14-17 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_sub_long: /* 0x9c */ +/* File: mips/op_sub_long.S */ +/* + * For little endian the code sequence looks as follows: + * subu v0,a0,a2 + * subu v1,a1,a3 + * sltu a0,a0,v0 + * subu v1,v1,a0 + */ +/* File: mips/binopWide.S */ + /* + * Generic 64-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0-a1 op a2-a3". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * for: add-long, sub-long, div-long, rem-long, and-long, or-long, + * xor-long + * + * IMPORTANT: you may specify "chkzero" or "preinstr" but not both. + */ + /* binop vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # rOBJ <- AA + and a2, a0, 255 # a2 <- BB + srl a3, a0, 8 # a3 <- CC + EAS2(a2, rFP, a2) # a2 <- &fp[BB] + EAS2(t1, rFP, a3) # a3 <- &fp[CC] + LOAD64(a0, a1, a2) # a0/a1 <- vBB/vBB+1 + LOAD64(a2, a3, t1) # a2/a3 <- vCC/vCC+1 + .if 0 + or t0, a2, a3 # second arg (a2-a3) is zero? + beqz t0, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + + subu v0, a0, a2 # optional op + subu v1, a1, a3; sltu a0, a0, v0; subu v1, v1, a0 # result <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG64_GOTO(v0, v1, rOBJ, t0) # vAA/vAA+1 <- v0/v1 + /* 14-17 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_long: /* 0x9d */ +/* File: mips/op_mul_long.S */ + /* + * Signed 64-bit integer multiply. + * a1 a0 + * x a3 a2 + * ------------- + * a2a1 a2a0 + * a3a0 + * a3a1 (<= unused) + * --------------- + * v1 v0 + */ + /* mul-long vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + and t0, a0, 255 # a2 <- BB + srl t1, a0, 8 # a3 <- CC + EAS2(t0, rFP, t0) # t0 <- &fp[BB] + LOAD64(a0, a1, t0) # a0/a1 <- vBB/vBB+1 + + EAS2(t1, rFP, t1) # t0 <- &fp[CC] + LOAD64(a2, a3, t1) # a2/a3 <- vCC/vCC+1 + + mul v1, a3, a0 # v1= a3a0 +#ifdef MIPS32REVGE6 + mulu v0, a2, a0 # v0= a2a0 + muhu t1, a2, a0 +#else + multu a2, a0 + mfhi t1 + mflo v0 # v0= a2a0 +#endif + mul t0, a2, a1 # t0= a2a1 + addu v1, v1, t1 # v1+= hi(a2a0) + addu v1, v1, t0 # v1= a3a0 + a2a1; + + GET_OPA(a0) # a0 <- AA + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + b .Lop_mul_long_finish + +/* ------------------------------ */ + .balign 128 +.L_op_div_long: /* 0x9e */ +/* File: mips/op_div_long.S */ +/* File: mips/binopWide.S */ + /* + * Generic 64-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0-a1 op a2-a3". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * for: add-long, sub-long, div-long, rem-long, and-long, or-long, + * xor-long + * + * IMPORTANT: you may specify "chkzero" or "preinstr" but not both. + */ + /* binop vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # rOBJ <- AA + and a2, a0, 255 # a2 <- BB + srl a3, a0, 8 # a3 <- CC + EAS2(a2, rFP, a2) # a2 <- &fp[BB] + EAS2(t1, rFP, a3) # a3 <- &fp[CC] + LOAD64(a0, a1, a2) # a0/a1 <- vBB/vBB+1 + LOAD64(a2, a3, t1) # a2/a3 <- vCC/vCC+1 + .if 1 + or t0, a2, a3 # second arg (a2-a3) is zero? + beqz t0, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + + # optional op + JAL(__divdi3) # result <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG64_GOTO(v0, v1, rOBJ, t0) # vAA/vAA+1 <- v0/v1 + /* 14-17 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_rem_long: /* 0x9f */ +/* File: mips/op_rem_long.S */ +/* File: mips/binopWide.S */ + /* + * Generic 64-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0-a1 op a2-a3". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * for: add-long, sub-long, div-long, rem-long, and-long, or-long, + * xor-long + * + * IMPORTANT: you may specify "chkzero" or "preinstr" but not both. + */ + /* binop vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # rOBJ <- AA + and a2, a0, 255 # a2 <- BB + srl a3, a0, 8 # a3 <- CC + EAS2(a2, rFP, a2) # a2 <- &fp[BB] + EAS2(t1, rFP, a3) # a3 <- &fp[CC] + LOAD64(a0, a1, a2) # a0/a1 <- vBB/vBB+1 + LOAD64(a2, a3, t1) # a2/a3 <- vCC/vCC+1 + .if 1 + or t0, a2, a3 # second arg (a2-a3) is zero? + beqz t0, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + + # optional op + JAL(__moddi3) # result <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG64_GOTO(v0, v1, rOBJ, t0) # vAA/vAA+1 <- v0/v1 + /* 14-17 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_and_long: /* 0xa0 */ +/* File: mips/op_and_long.S */ +/* File: mips/binopWide.S */ + /* + * Generic 64-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0-a1 op a2-a3". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * for: add-long, sub-long, div-long, rem-long, and-long, or-long, + * xor-long + * + * IMPORTANT: you may specify "chkzero" or "preinstr" but not both. + */ + /* binop vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # rOBJ <- AA + and a2, a0, 255 # a2 <- BB + srl a3, a0, 8 # a3 <- CC + EAS2(a2, rFP, a2) # a2 <- &fp[BB] + EAS2(t1, rFP, a3) # a3 <- &fp[CC] + LOAD64(a0, a1, a2) # a0/a1 <- vBB/vBB+1 + LOAD64(a2, a3, t1) # a2/a3 <- vCC/vCC+1 + .if 0 + or t0, a2, a3 # second arg (a2-a3) is zero? + beqz t0, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + + and a0, a0, a2 # optional op + and a1, a1, a3 # result <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG64_GOTO(a0, a1, rOBJ, t0) # vAA/vAA+1 <- a0/a1 + /* 14-17 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_or_long: /* 0xa1 */ +/* File: mips/op_or_long.S */ +/* File: mips/binopWide.S */ + /* + * Generic 64-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0-a1 op a2-a3". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * for: add-long, sub-long, div-long, rem-long, and-long, or-long, + * xor-long + * + * IMPORTANT: you may specify "chkzero" or "preinstr" but not both. + */ + /* binop vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # rOBJ <- AA + and a2, a0, 255 # a2 <- BB + srl a3, a0, 8 # a3 <- CC + EAS2(a2, rFP, a2) # a2 <- &fp[BB] + EAS2(t1, rFP, a3) # a3 <- &fp[CC] + LOAD64(a0, a1, a2) # a0/a1 <- vBB/vBB+1 + LOAD64(a2, a3, t1) # a2/a3 <- vCC/vCC+1 + .if 0 + or t0, a2, a3 # second arg (a2-a3) is zero? + beqz t0, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + + or a0, a0, a2 # optional op + or a1, a1, a3 # result <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG64_GOTO(a0, a1, rOBJ, t0) # vAA/vAA+1 <- a0/a1 + /* 14-17 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_xor_long: /* 0xa2 */ +/* File: mips/op_xor_long.S */ +/* File: mips/binopWide.S */ + /* + * Generic 64-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0-a1 op a2-a3". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * for: add-long, sub-long, div-long, rem-long, and-long, or-long, + * xor-long + * + * IMPORTANT: you may specify "chkzero" or "preinstr" but not both. + */ + /* binop vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # rOBJ <- AA + and a2, a0, 255 # a2 <- BB + srl a3, a0, 8 # a3 <- CC + EAS2(a2, rFP, a2) # a2 <- &fp[BB] + EAS2(t1, rFP, a3) # a3 <- &fp[CC] + LOAD64(a0, a1, a2) # a0/a1 <- vBB/vBB+1 + LOAD64(a2, a3, t1) # a2/a3 <- vCC/vCC+1 + .if 0 + or t0, a2, a3 # second arg (a2-a3) is zero? + beqz t0, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + + xor a0, a0, a2 # optional op + xor a1, a1, a3 # result <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG64_GOTO(a0, a1, rOBJ, t0) # vAA/vAA+1 <- a0/a1 + /* 14-17 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_shl_long: /* 0xa3 */ +/* File: mips/op_shl_long.S */ + /* + * Long integer shift. This is different from the generic 32/64-bit + * binary operations because vAA/vBB are 64-bit but vCC (the shift + * distance) is 32-bit. Also, Dalvik requires us to mask off the low + * 6 bits of the shift distance. + */ + /* shl-long vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(t2) # t2 <- AA + and a3, a0, 255 # a3 <- BB + srl a0, a0, 8 # a0 <- CC + EAS2(a3, rFP, a3) # a3 <- &fp[BB] + GET_VREG(a2, a0) # a2 <- vCC + LOAD64(a0, a1, a3) # a0/a1 <- vBB/vBB+1 + + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + + andi v1, a2, 0x20 # shift< shift & 0x20 + sll v0, a0, a2 # rlo<- alo << (shift&31) + bnez v1, .Lop_shl_long_finish + not v1, a2 # rhi<- 31-shift (shift is 5b) + srl a0, 1 + srl a0, v1 # alo<- alo >> (32-(shift&31)) + sll v1, a1, a2 # rhi<- ahi << (shift&31) + or v1, a0 # rhi<- rhi | alo + SET_VREG64_GOTO(v0, v1, t2, t0) # vAA/vAA+1 <- a0/a1 + +/* ------------------------------ */ + .balign 128 +.L_op_shr_long: /* 0xa4 */ +/* File: mips/op_shr_long.S */ + /* + * Long integer shift. This is different from the generic 32/64-bit + * binary operations because vAA/vBB are 64-bit but vCC (the shift + * distance) is 32-bit. Also, Dalvik requires us to mask off the low + * 6 bits of the shift distance. + */ + /* shr-long vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(t3) # t3 <- AA + and a3, a0, 255 # a3 <- BB + srl a0, a0, 8 # a0 <- CC + EAS2(a3, rFP, a3) # a3 <- &fp[BB] + GET_VREG(a2, a0) # a2 <- vCC + LOAD64(a0, a1, a3) # a0/a1 <- vBB/vBB+1 + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + + andi v0, a2, 0x20 # shift & 0x20 + sra v1, a1, a2 # rhi<- ahi >> (shift&31) + bnez v0, .Lop_shr_long_finish + srl v0, a0, a2 # rlo<- alo >> (shift&31) + not a0, a2 # alo<- 31-shift (shift is 5b) + sll a1, 1 + sll a1, a0 # ahi<- ahi << (32-(shift&31)) + or v0, a1 # rlo<- rlo | ahi + SET_VREG64_GOTO(v0, v1, t3, t0) # vAA/VAA+1 <- v0/v0 + +/* ------------------------------ */ + .balign 128 +.L_op_ushr_long: /* 0xa5 */ +/* File: mips/op_ushr_long.S */ + /* + * Long integer shift. This is different from the generic 32/64-bit + * binary operations because vAA/vBB are 64-bit but vCC (the shift + * distance) is 32-bit. Also, Dalvik requires us to mask off the low + * 6 bits of the shift distance. + */ + /* ushr-long vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # rOBJ <- AA + and a3, a0, 255 # a3 <- BB + srl a0, a0, 8 # a0 <- CC + EAS2(a3, rFP, a3) # a3 <- &fp[BB] + GET_VREG(a2, a0) # a2 <- vCC + LOAD64(a0, a1, a3) # a0/a1 <- vBB/vBB+1 + + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + + andi v0, a2, 0x20 # shift & 0x20 + srl v1, a1, a2 # rhi<- ahi >> (shift&31) + bnez v0, .Lop_ushr_long_finish + srl v0, a0, a2 # rlo<- alo >> (shift&31) + not a0, a2 # alo<- 31-n (shift is 5b) + sll a1, 1 + sll a1, a0 # ahi<- ahi << (32-(shift&31)) + or v0, a1 # rlo<- rlo | ahi + SET_VREG64_GOTO(v0, v1, rOBJ, t0) # vAA/vAA+1 <- v0/v1 + +/* ------------------------------ */ + .balign 128 +.L_op_add_float: /* 0xa6 */ +/* File: mips/op_add_float.S */ +/* File: mips/fbinop.S */ + /* + * Generic 32-bit binary float operation. + * + * For: add-fp, sub-fp, mul-fp, div-fp, rem-fp + */ + + /* binop vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # s5 <- AA + srl a3, a0, 8 # a3 <- CC + and a2, a0, 255 # a2 <- BB + GET_VREG_F(fa1, a3) # a1 <- vCC + GET_VREG_F(fa0, a2) # a0 <- vBB + + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + add.s fv0, fa0, fa1 # f0 = result + SET_VREG_F(fv0, rOBJ) # vAA <- fv0 + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_sub_float: /* 0xa7 */ +/* File: mips/op_sub_float.S */ +/* File: mips/fbinop.S */ + /* + * Generic 32-bit binary float operation. + * + * For: add-fp, sub-fp, mul-fp, div-fp, rem-fp + */ + + /* binop vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # s5 <- AA + srl a3, a0, 8 # a3 <- CC + and a2, a0, 255 # a2 <- BB + GET_VREG_F(fa1, a3) # a1 <- vCC + GET_VREG_F(fa0, a2) # a0 <- vBB + + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + sub.s fv0, fa0, fa1 # f0 = result + SET_VREG_F(fv0, rOBJ) # vAA <- fv0 + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_float: /* 0xa8 */ +/* File: mips/op_mul_float.S */ +/* File: mips/fbinop.S */ + /* + * Generic 32-bit binary float operation. + * + * For: add-fp, sub-fp, mul-fp, div-fp, rem-fp + */ + + /* binop vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # s5 <- AA + srl a3, a0, 8 # a3 <- CC + and a2, a0, 255 # a2 <- BB + GET_VREG_F(fa1, a3) # a1 <- vCC + GET_VREG_F(fa0, a2) # a0 <- vBB + + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + mul.s fv0, fa0, fa1 # f0 = result + SET_VREG_F(fv0, rOBJ) # vAA <- fv0 + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_div_float: /* 0xa9 */ +/* File: mips/op_div_float.S */ +/* File: mips/fbinop.S */ + /* + * Generic 32-bit binary float operation. + * + * For: add-fp, sub-fp, mul-fp, div-fp, rem-fp + */ + + /* binop vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # s5 <- AA + srl a3, a0, 8 # a3 <- CC + and a2, a0, 255 # a2 <- BB + GET_VREG_F(fa1, a3) # a1 <- vCC + GET_VREG_F(fa0, a2) # a0 <- vBB + + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + div.s fv0, fa0, fa1 # f0 = result + SET_VREG_F(fv0, rOBJ) # vAA <- fv0 + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_rem_float: /* 0xaa */ +/* File: mips/op_rem_float.S */ +/* File: mips/fbinop.S */ + /* + * Generic 32-bit binary float operation. + * + * For: add-fp, sub-fp, mul-fp, div-fp, rem-fp + */ + + /* binop vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # s5 <- AA + srl a3, a0, 8 # a3 <- CC + and a2, a0, 255 # a2 <- BB + GET_VREG_F(fa1, a3) # a1 <- vCC + GET_VREG_F(fa0, a2) # a0 <- vBB + + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + JAL(fmodf) # f0 = result + SET_VREG_F(fv0, rOBJ) # vAA <- fv0 + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_add_double: /* 0xab */ +/* File: mips/op_add_double.S */ +/* File: mips/fbinopWide.S */ + /* + * Generic 64-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0-a1 op a2-a3". + * This could be an MIPS instruction or a function call. + * + * for: add-double, sub-double, mul-double, div-double, + * rem-double + * + */ + /* binop vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # s5 <- AA + and a2, a0, 255 # a2 <- BB + srl a3, a0, 8 # a3 <- CC + EAS2(a2, rFP, a2) # a2 <- &fp[BB] + EAS2(t1, rFP, a3) # a3 <- &fp[CC] + LOAD64_F(fa0, fa0f, a2) + LOAD64_F(fa1, fa1f, t1) + + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + add.d fv0, fa0, fa1 + SET_VREG64_F(fv0, fv0f, rOBJ) + b .Lop_add_double_finish + + +/* ------------------------------ */ + .balign 128 +.L_op_sub_double: /* 0xac */ +/* File: mips/op_sub_double.S */ +/* File: mips/fbinopWide.S */ + /* + * Generic 64-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0-a1 op a2-a3". + * This could be an MIPS instruction or a function call. + * + * for: add-double, sub-double, mul-double, div-double, + * rem-double + * + */ + /* binop vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # s5 <- AA + and a2, a0, 255 # a2 <- BB + srl a3, a0, 8 # a3 <- CC + EAS2(a2, rFP, a2) # a2 <- &fp[BB] + EAS2(t1, rFP, a3) # a3 <- &fp[CC] + LOAD64_F(fa0, fa0f, a2) + LOAD64_F(fa1, fa1f, t1) + + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + sub.d fv0, fa0, fa1 + SET_VREG64_F(fv0, fv0f, rOBJ) + b .Lop_sub_double_finish + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_double: /* 0xad */ +/* File: mips/op_mul_double.S */ +/* File: mips/fbinopWide.S */ + /* + * Generic 64-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0-a1 op a2-a3". + * This could be an MIPS instruction or a function call. + * + * for: add-double, sub-double, mul-double, div-double, + * rem-double + * + */ + /* binop vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # s5 <- AA + and a2, a0, 255 # a2 <- BB + srl a3, a0, 8 # a3 <- CC + EAS2(a2, rFP, a2) # a2 <- &fp[BB] + EAS2(t1, rFP, a3) # a3 <- &fp[CC] + LOAD64_F(fa0, fa0f, a2) + LOAD64_F(fa1, fa1f, t1) + + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + mul.d fv0, fa0, fa1 + SET_VREG64_F(fv0, fv0f, rOBJ) + b .Lop_mul_double_finish + + +/* ------------------------------ */ + .balign 128 +.L_op_div_double: /* 0xae */ +/* File: mips/op_div_double.S */ +/* File: mips/fbinopWide.S */ + /* + * Generic 64-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0-a1 op a2-a3". + * This could be an MIPS instruction or a function call. + * + * for: add-double, sub-double, mul-double, div-double, + * rem-double + * + */ + /* binop vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # s5 <- AA + and a2, a0, 255 # a2 <- BB + srl a3, a0, 8 # a3 <- CC + EAS2(a2, rFP, a2) # a2 <- &fp[BB] + EAS2(t1, rFP, a3) # a3 <- &fp[CC] + LOAD64_F(fa0, fa0f, a2) + LOAD64_F(fa1, fa1f, t1) + + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + div.d fv0, fa0, fa1 + SET_VREG64_F(fv0, fv0f, rOBJ) + b .Lop_div_double_finish + + +/* ------------------------------ */ + .balign 128 +.L_op_rem_double: /* 0xaf */ +/* File: mips/op_rem_double.S */ +/* File: mips/fbinopWide.S */ + /* + * Generic 64-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0-a1 op a2-a3". + * This could be an MIPS instruction or a function call. + * + * for: add-double, sub-double, mul-double, div-double, + * rem-double + * + */ + /* binop vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # s5 <- AA + and a2, a0, 255 # a2 <- BB + srl a3, a0, 8 # a3 <- CC + EAS2(a2, rFP, a2) # a2 <- &fp[BB] + EAS2(t1, rFP, a3) # a3 <- &fp[CC] + LOAD64_F(fa0, fa0f, a2) + LOAD64_F(fa1, fa1f, t1) + + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + JAL(fmod) + SET_VREG64_F(fv0, fv0f, rOBJ) + b .Lop_rem_double_finish + + +/* ------------------------------ */ + .balign 128 +.L_op_add_int_2addr: /* 0xb0 */ +/* File: mips/op_add_int_2addr.S */ +/* File: mips/binop2addr.S */ + /* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, + * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, + * shl-int/2addr, shr-int/2addr, ushr-int/2addr + */ + /* binop/2addr vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a3) # a3 <- B + GET_VREG(a0, rOBJ) # a0 <- vA + GET_VREG(a1, a3) # a1 <- vB + .if 0 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + + # optional op + addu a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 10-13 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_sub_int_2addr: /* 0xb1 */ +/* File: mips/op_sub_int_2addr.S */ +/* File: mips/binop2addr.S */ + /* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, + * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, + * shl-int/2addr, shr-int/2addr, ushr-int/2addr + */ + /* binop/2addr vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a3) # a3 <- B + GET_VREG(a0, rOBJ) # a0 <- vA + GET_VREG(a1, a3) # a1 <- vB + .if 0 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + + # optional op + subu a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 10-13 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_int_2addr: /* 0xb2 */ +/* File: mips/op_mul_int_2addr.S */ +/* File: mips/binop2addr.S */ + /* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, + * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, + * shl-int/2addr, shr-int/2addr, ushr-int/2addr + */ + /* binop/2addr vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a3) # a3 <- B + GET_VREG(a0, rOBJ) # a0 <- vA + GET_VREG(a1, a3) # a1 <- vB + .if 0 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + + # optional op + mul a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 10-13 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_div_int_2addr: /* 0xb3 */ +/* File: mips/op_div_int_2addr.S */ +#ifdef MIPS32REVGE6 +/* File: mips/binop2addr.S */ + /* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, + * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, + * shl-int/2addr, shr-int/2addr, ushr-int/2addr + */ + /* binop/2addr vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a3) # a3 <- B + GET_VREG(a0, rOBJ) # a0 <- vA + GET_VREG(a1, a3) # a1 <- vB + .if 1 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + + # optional op + div a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 10-13 instructions */ + +#else +/* File: mips/binop2addr.S */ + /* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, + * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, + * shl-int/2addr, shr-int/2addr, ushr-int/2addr + */ + /* binop/2addr vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a3) # a3 <- B + GET_VREG(a0, rOBJ) # a0 <- vA + GET_VREG(a1, a3) # a1 <- vB + .if 1 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + + div zero, a0, a1 # optional op + mflo a0 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 10-13 instructions */ + +#endif + +/* ------------------------------ */ + .balign 128 +.L_op_rem_int_2addr: /* 0xb4 */ +/* File: mips/op_rem_int_2addr.S */ +#ifdef MIPS32REVGE6 +/* File: mips/binop2addr.S */ + /* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, + * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, + * shl-int/2addr, shr-int/2addr, ushr-int/2addr + */ + /* binop/2addr vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a3) # a3 <- B + GET_VREG(a0, rOBJ) # a0 <- vA + GET_VREG(a1, a3) # a1 <- vB + .if 1 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + + # optional op + mod a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 10-13 instructions */ + +#else +/* File: mips/binop2addr.S */ + /* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, + * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, + * shl-int/2addr, shr-int/2addr, ushr-int/2addr + */ + /* binop/2addr vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a3) # a3 <- B + GET_VREG(a0, rOBJ) # a0 <- vA + GET_VREG(a1, a3) # a1 <- vB + .if 1 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + + div zero, a0, a1 # optional op + mfhi a0 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 10-13 instructions */ + +#endif + +/* ------------------------------ */ + .balign 128 +.L_op_and_int_2addr: /* 0xb5 */ +/* File: mips/op_and_int_2addr.S */ +/* File: mips/binop2addr.S */ + /* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, + * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, + * shl-int/2addr, shr-int/2addr, ushr-int/2addr + */ + /* binop/2addr vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a3) # a3 <- B + GET_VREG(a0, rOBJ) # a0 <- vA + GET_VREG(a1, a3) # a1 <- vB + .if 0 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + + # optional op + and a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 10-13 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_or_int_2addr: /* 0xb6 */ +/* File: mips/op_or_int_2addr.S */ +/* File: mips/binop2addr.S */ + /* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, + * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, + * shl-int/2addr, shr-int/2addr, ushr-int/2addr + */ + /* binop/2addr vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a3) # a3 <- B + GET_VREG(a0, rOBJ) # a0 <- vA + GET_VREG(a1, a3) # a1 <- vB + .if 0 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + + # optional op + or a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 10-13 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_xor_int_2addr: /* 0xb7 */ +/* File: mips/op_xor_int_2addr.S */ +/* File: mips/binop2addr.S */ + /* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, + * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, + * shl-int/2addr, shr-int/2addr, ushr-int/2addr + */ + /* binop/2addr vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a3) # a3 <- B + GET_VREG(a0, rOBJ) # a0 <- vA + GET_VREG(a1, a3) # a1 <- vB + .if 0 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + + # optional op + xor a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 10-13 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_shl_int_2addr: /* 0xb8 */ +/* File: mips/op_shl_int_2addr.S */ +/* File: mips/binop2addr.S */ + /* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, + * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, + * shl-int/2addr, shr-int/2addr, ushr-int/2addr + */ + /* binop/2addr vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a3) # a3 <- B + GET_VREG(a0, rOBJ) # a0 <- vA + GET_VREG(a1, a3) # a1 <- vB + .if 0 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + + # optional op + sll a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 10-13 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_shr_int_2addr: /* 0xb9 */ +/* File: mips/op_shr_int_2addr.S */ +/* File: mips/binop2addr.S */ + /* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, + * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, + * shl-int/2addr, shr-int/2addr, ushr-int/2addr + */ + /* binop/2addr vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a3) # a3 <- B + GET_VREG(a0, rOBJ) # a0 <- vA + GET_VREG(a1, a3) # a1 <- vB + .if 0 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + + # optional op + sra a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 10-13 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_ushr_int_2addr: /* 0xba */ +/* File: mips/op_ushr_int_2addr.S */ +/* File: mips/binop2addr.S */ + /* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, + * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, + * shl-int/2addr, shr-int/2addr, ushr-int/2addr + */ + /* binop/2addr vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a3) # a3 <- B + GET_VREG(a0, rOBJ) # a0 <- vA + GET_VREG(a1, a3) # a1 <- vB + .if 0 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + + # optional op + srl a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 10-13 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_add_long_2addr: /* 0xbb */ +/* File: mips/op_add_long_2addr.S */ +/* + * See op_add_long.S for details + */ +/* File: mips/binopWide2addr.S */ + /* + * Generic 64-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0-a1 op a2-a3". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr, + * and-long/2addr, or-long/2addr, xor-long/2addr + * rem-double/2addr + */ + /* binop/2addr vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a1) # a1 <- B + EAS2(a1, rFP, a1) # a1 <- &fp[B] + EAS2(t0, rFP, rOBJ) # t0 <- &fp[A] + LOAD64(a2, a3, a1) # a2/a3 <- vBB/vBB+1 + LOAD64(a0, a1, t0) # a0/a1 <- vAA/vAA+1 + .if 0 + or t0, a2, a3 # second arg (a2-a3) is zero? + beqz t0, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + + addu v0, a2, a0 # optional op + addu a1, a3, a1; sltu v1, v0, a2; addu v1, v1, a1 # result <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG64(v0, v1, rOBJ) # vAA/vAA+1 <- v0/v1 + GOTO_OPCODE(t0) # jump to next instruction + /* 12-15 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_sub_long_2addr: /* 0xbc */ +/* File: mips/op_sub_long_2addr.S */ +/* + * See op_sub_long.S for more details + */ +/* File: mips/binopWide2addr.S */ + /* + * Generic 64-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0-a1 op a2-a3". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr, + * and-long/2addr, or-long/2addr, xor-long/2addr + * rem-double/2addr + */ + /* binop/2addr vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a1) # a1 <- B + EAS2(a1, rFP, a1) # a1 <- &fp[B] + EAS2(t0, rFP, rOBJ) # t0 <- &fp[A] + LOAD64(a2, a3, a1) # a2/a3 <- vBB/vBB+1 + LOAD64(a0, a1, t0) # a0/a1 <- vAA/vAA+1 + .if 0 + or t0, a2, a3 # second arg (a2-a3) is zero? + beqz t0, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + + subu v0, a0, a2 # optional op + subu v1, a1, a3; sltu a0, a0, v0; subu v1, v1, a0 # result <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG64(v0, v1, rOBJ) # vAA/vAA+1 <- v0/v1 + GOTO_OPCODE(t0) # jump to next instruction + /* 12-15 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_long_2addr: /* 0xbd */ +/* File: mips/op_mul_long_2addr.S */ + /* + * See op_mul_long.S for more details + */ + /* mul-long/2addr vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + + EAS2(t0, rFP, rOBJ) # t0 <- &fp[A] + LOAD64(a0, a1, t0) # vAA.low / high + + GET_OPB(t1) # t1 <- B + EAS2(t1, rFP, t1) # t1 <- &fp[B] + LOAD64(a2, a3, t1) # vBB.low / high + + mul v1, a3, a0 # v1= a3a0 +#ifdef MIPS32REVGE6 + mulu v0, a2, a0 # v0= a2a0 + muhu t1, a2, a0 +#else + multu a2, a0 + mfhi t1 + mflo v0 # v0= a2a0 + #endif + mul t2, a2, a1 # t2= a2a1 + addu v1, v1, t1 # v1= a3a0 + hi(a2a0) + addu v1, v1, t2 # v1= v1 + a2a1; + + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + GET_INST_OPCODE(t1) # extract opcode from rINST + # vAA <- v0 (low) + SET_VREG64(v0, v1, rOBJ) # vAA+1 <- v1 (high) + GOTO_OPCODE(t1) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_div_long_2addr: /* 0xbe */ +/* File: mips/op_div_long_2addr.S */ +/* File: mips/binopWide2addr.S */ + /* + * Generic 64-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0-a1 op a2-a3". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr, + * and-long/2addr, or-long/2addr, xor-long/2addr + * rem-double/2addr + */ + /* binop/2addr vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a1) # a1 <- B + EAS2(a1, rFP, a1) # a1 <- &fp[B] + EAS2(t0, rFP, rOBJ) # t0 <- &fp[A] + LOAD64(a2, a3, a1) # a2/a3 <- vBB/vBB+1 + LOAD64(a0, a1, t0) # a0/a1 <- vAA/vAA+1 + .if 1 + or t0, a2, a3 # second arg (a2-a3) is zero? + beqz t0, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + + # optional op + JAL(__divdi3) # result <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG64(v0, v1, rOBJ) # vAA/vAA+1 <- v0/v1 + GOTO_OPCODE(t0) # jump to next instruction + /* 12-15 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_rem_long_2addr: /* 0xbf */ +/* File: mips/op_rem_long_2addr.S */ +/* File: mips/binopWide2addr.S */ + /* + * Generic 64-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0-a1 op a2-a3". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr, + * and-long/2addr, or-long/2addr, xor-long/2addr + * rem-double/2addr + */ + /* binop/2addr vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a1) # a1 <- B + EAS2(a1, rFP, a1) # a1 <- &fp[B] + EAS2(t0, rFP, rOBJ) # t0 <- &fp[A] + LOAD64(a2, a3, a1) # a2/a3 <- vBB/vBB+1 + LOAD64(a0, a1, t0) # a0/a1 <- vAA/vAA+1 + .if 1 + or t0, a2, a3 # second arg (a2-a3) is zero? + beqz t0, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + + # optional op + JAL(__moddi3) # result <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG64(v0, v1, rOBJ) # vAA/vAA+1 <- v0/v1 + GOTO_OPCODE(t0) # jump to next instruction + /* 12-15 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_and_long_2addr: /* 0xc0 */ +/* File: mips/op_and_long_2addr.S */ +/* File: mips/binopWide2addr.S */ + /* + * Generic 64-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0-a1 op a2-a3". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr, + * and-long/2addr, or-long/2addr, xor-long/2addr + * rem-double/2addr + */ + /* binop/2addr vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a1) # a1 <- B + EAS2(a1, rFP, a1) # a1 <- &fp[B] + EAS2(t0, rFP, rOBJ) # t0 <- &fp[A] + LOAD64(a2, a3, a1) # a2/a3 <- vBB/vBB+1 + LOAD64(a0, a1, t0) # a0/a1 <- vAA/vAA+1 + .if 0 + or t0, a2, a3 # second arg (a2-a3) is zero? + beqz t0, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + + and a0, a0, a2 # optional op + and a1, a1, a3 # result <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG64(a0, a1, rOBJ) # vAA/vAA+1 <- a0/a1 + GOTO_OPCODE(t0) # jump to next instruction + /* 12-15 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_or_long_2addr: /* 0xc1 */ +/* File: mips/op_or_long_2addr.S */ +/* File: mips/binopWide2addr.S */ + /* + * Generic 64-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0-a1 op a2-a3". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr, + * and-long/2addr, or-long/2addr, xor-long/2addr + * rem-double/2addr + */ + /* binop/2addr vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a1) # a1 <- B + EAS2(a1, rFP, a1) # a1 <- &fp[B] + EAS2(t0, rFP, rOBJ) # t0 <- &fp[A] + LOAD64(a2, a3, a1) # a2/a3 <- vBB/vBB+1 + LOAD64(a0, a1, t0) # a0/a1 <- vAA/vAA+1 + .if 0 + or t0, a2, a3 # second arg (a2-a3) is zero? + beqz t0, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + + or a0, a0, a2 # optional op + or a1, a1, a3 # result <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG64(a0, a1, rOBJ) # vAA/vAA+1 <- a0/a1 + GOTO_OPCODE(t0) # jump to next instruction + /* 12-15 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_xor_long_2addr: /* 0xc2 */ +/* File: mips/op_xor_long_2addr.S */ +/* File: mips/binopWide2addr.S */ + /* + * Generic 64-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0-a1 op a2-a3". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr, + * and-long/2addr, or-long/2addr, xor-long/2addr + * rem-double/2addr + */ + /* binop/2addr vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a1) # a1 <- B + EAS2(a1, rFP, a1) # a1 <- &fp[B] + EAS2(t0, rFP, rOBJ) # t0 <- &fp[A] + LOAD64(a2, a3, a1) # a2/a3 <- vBB/vBB+1 + LOAD64(a0, a1, t0) # a0/a1 <- vAA/vAA+1 + .if 0 + or t0, a2, a3 # second arg (a2-a3) is zero? + beqz t0, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + + xor a0, a0, a2 # optional op + xor a1, a1, a3 # result <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG64(a0, a1, rOBJ) # vAA/vAA+1 <- a0/a1 + GOTO_OPCODE(t0) # jump to next instruction + /* 12-15 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_shl_long_2addr: /* 0xc3 */ +/* File: mips/op_shl_long_2addr.S */ + /* + * Long integer shift, 2addr version. vA is 64-bit value/result, vB is + * 32-bit shift distance. + */ + /* shl-long/2addr vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a3) # a3 <- B + GET_VREG(a2, a3) # a2 <- vB + EAS2(t2, rFP, rOBJ) # t2 <- &fp[A] + LOAD64(a0, a1, t2) # a0/a1 <- vAA/vAA+1 + + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + + andi v1, a2, 0x20 # shift< shift & 0x20 + sll v0, a0, a2 # rlo<- alo << (shift&31) + bnez v1, .Lop_shl_long_2addr_finish + not v1, a2 # rhi<- 31-shift (shift is 5b) + srl a0, 1 + srl a0, v1 # alo<- alo >> (32-(shift&31)) + sll v1, a1, a2 # rhi<- ahi << (shift&31) + or v1, a0 # rhi<- rhi | alo + SET_VREG64_GOTO(v0, v1, rOBJ, t0) # vAA/vAA+1 <- a0/a1 + +/* ------------------------------ */ + .balign 128 +.L_op_shr_long_2addr: /* 0xc4 */ +/* File: mips/op_shr_long_2addr.S */ + /* + * Long integer shift, 2addr version. vA is 64-bit value/result, vB is + * 32-bit shift distance. + */ + /* shr-long/2addr vA, vB */ + GET_OPA4(t2) # t2 <- A+ + GET_OPB(a3) # a3 <- B + GET_VREG(a2, a3) # a2 <- vB + EAS2(t0, rFP, t2) # t0 <- &fp[A] + LOAD64(a0, a1, t0) # a0/a1 <- vAA/vAA+1 + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + + andi v0, a2, 0x20 # shift & 0x20 + sra v1, a1, a2 # rhi<- ahi >> (shift&31) + bnez v0, .Lop_shr_long_2addr_finish + srl v0, a0, a2 # rlo<- alo >> (shift&31) + not a0, a2 # alo<- 31-shift (shift is 5b) + sll a1, 1 + sll a1, a0 # ahi<- ahi << (32-(shift&31)) + or v0, a1 # rlo<- rlo | ahi + SET_VREG64_GOTO(v0, v1, t2, t0) # vAA/vAA+1 <- a0/a1 + +/* ------------------------------ */ + .balign 128 +.L_op_ushr_long_2addr: /* 0xc5 */ +/* File: mips/op_ushr_long_2addr.S */ + /* + * Long integer shift, 2addr version. vA is 64-bit value/result, vB is + * 32-bit shift distance. + */ + /* ushr-long/2addr vA, vB */ + GET_OPA4(t3) # t3 <- A+ + GET_OPB(a3) # a3 <- B + GET_VREG(a2, a3) # a2 <- vB + EAS2(t0, rFP, t3) # t0 <- &fp[A] + LOAD64(a0, a1, t0) # a0/a1 <- vAA/vAA+1 + + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + + andi v0, a2, 0x20 # shift & 0x20 + srl v1, a1, a2 # rhi<- ahi >> (shift&31) + bnez v0, .Lop_ushr_long_2addr_finish + srl v0, a0, a2 # rlo<- alo >> (shift&31) + not a0, a2 # alo<- 31-n (shift is 5b) + sll a1, 1 + sll a1, a0 # ahi<- ahi << (32-(shift&31)) + or v0, a1 # rlo<- rlo | ahi + SET_VREG64_GOTO(v0, v1, t3, t0) # vAA/vAA+1 <- a0/a1 + +/* ------------------------------ */ + .balign 128 +.L_op_add_float_2addr: /* 0xc6 */ +/* File: mips/op_add_float_2addr.S */ +/* File: mips/fbinop2addr.S */ + /* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. + * + * For: add-float/2addr, sub-float/2addr, mul-float/2addr, + * div-float/2addr, rem-float/2addr + */ + /* binop/2addr vA, vB */ + GET_OPA4(rOBJ) # t1 <- A+ + GET_OPB(a3) # a3 <- B + GET_VREG_F(fa0, rOBJ) + GET_VREG_F(fa1, a3) + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + + add.s fv0, fa0, fa1 + SET_VREG_F(fv0, rOBJ) # vAA <- result + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_sub_float_2addr: /* 0xc7 */ +/* File: mips/op_sub_float_2addr.S */ +/* File: mips/fbinop2addr.S */ + /* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. + * + * For: add-float/2addr, sub-float/2addr, mul-float/2addr, + * div-float/2addr, rem-float/2addr + */ + /* binop/2addr vA, vB */ + GET_OPA4(rOBJ) # t1 <- A+ + GET_OPB(a3) # a3 <- B + GET_VREG_F(fa0, rOBJ) + GET_VREG_F(fa1, a3) + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + + sub.s fv0, fa0, fa1 + SET_VREG_F(fv0, rOBJ) # vAA <- result + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_float_2addr: /* 0xc8 */ +/* File: mips/op_mul_float_2addr.S */ +/* File: mips/fbinop2addr.S */ + /* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. + * + * For: add-float/2addr, sub-float/2addr, mul-float/2addr, + * div-float/2addr, rem-float/2addr + */ + /* binop/2addr vA, vB */ + GET_OPA4(rOBJ) # t1 <- A+ + GET_OPB(a3) # a3 <- B + GET_VREG_F(fa0, rOBJ) + GET_VREG_F(fa1, a3) + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + + mul.s fv0, fa0, fa1 + SET_VREG_F(fv0, rOBJ) # vAA <- result + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_div_float_2addr: /* 0xc9 */ +/* File: mips/op_div_float_2addr.S */ +/* File: mips/fbinop2addr.S */ + /* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. + * + * For: add-float/2addr, sub-float/2addr, mul-float/2addr, + * div-float/2addr, rem-float/2addr + */ + /* binop/2addr vA, vB */ + GET_OPA4(rOBJ) # t1 <- A+ + GET_OPB(a3) # a3 <- B + GET_VREG_F(fa0, rOBJ) + GET_VREG_F(fa1, a3) + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + + div.s fv0, fa0, fa1 + SET_VREG_F(fv0, rOBJ) # vAA <- result + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_rem_float_2addr: /* 0xca */ +/* File: mips/op_rem_float_2addr.S */ +/* File: mips/fbinop2addr.S */ + /* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. + * + * For: add-float/2addr, sub-float/2addr, mul-float/2addr, + * div-float/2addr, rem-float/2addr + */ + /* binop/2addr vA, vB */ + GET_OPA4(rOBJ) # t1 <- A+ + GET_OPB(a3) # a3 <- B + GET_VREG_F(fa0, rOBJ) + GET_VREG_F(fa1, a3) + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + + JAL(fmodf) + SET_VREG_F(fv0, rOBJ) # vAA <- result + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_add_double_2addr: /* 0xcb */ +/* File: mips/op_add_double_2addr.S */ +/* File: mips/fbinopWide2addr.S */ + /* + * Generic 64-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0-a1 op a2-a3". + * This could be an MIPS instruction or a function call. + * + * For: add-double/2addr, sub-double/2addr, mul-double/2addr, + * div-double/2addr, rem-double/2addr + */ + /* binop/2addr vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a1) # a1 <- B + EAS2(a1, rFP, a1) # a1 <- &fp[B] + EAS2(t0, rFP, rOBJ) # t0 <- &fp[A] + LOAD64_F(fa0, fa0f, t0) + LOAD64_F(fa1, fa1f, a1) + + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + add.d fv0, fa0, fa1 + SET_VREG64_F(fv0, fv0f, rOBJ) + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_sub_double_2addr: /* 0xcc */ +/* File: mips/op_sub_double_2addr.S */ +/* File: mips/fbinopWide2addr.S */ + /* + * Generic 64-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0-a1 op a2-a3". + * This could be an MIPS instruction or a function call. + * + * For: add-double/2addr, sub-double/2addr, mul-double/2addr, + * div-double/2addr, rem-double/2addr + */ + /* binop/2addr vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a1) # a1 <- B + EAS2(a1, rFP, a1) # a1 <- &fp[B] + EAS2(t0, rFP, rOBJ) # t0 <- &fp[A] + LOAD64_F(fa0, fa0f, t0) + LOAD64_F(fa1, fa1f, a1) + + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + sub.d fv0, fa0, fa1 + SET_VREG64_F(fv0, fv0f, rOBJ) + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_double_2addr: /* 0xcd */ +/* File: mips/op_mul_double_2addr.S */ +/* File: mips/fbinopWide2addr.S */ + /* + * Generic 64-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0-a1 op a2-a3". + * This could be an MIPS instruction or a function call. + * + * For: add-double/2addr, sub-double/2addr, mul-double/2addr, + * div-double/2addr, rem-double/2addr + */ + /* binop/2addr vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a1) # a1 <- B + EAS2(a1, rFP, a1) # a1 <- &fp[B] + EAS2(t0, rFP, rOBJ) # t0 <- &fp[A] + LOAD64_F(fa0, fa0f, t0) + LOAD64_F(fa1, fa1f, a1) + + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + mul.d fv0, fa0, fa1 + SET_VREG64_F(fv0, fv0f, rOBJ) + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_div_double_2addr: /* 0xce */ +/* File: mips/op_div_double_2addr.S */ +/* File: mips/fbinopWide2addr.S */ + /* + * Generic 64-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0-a1 op a2-a3". + * This could be an MIPS instruction or a function call. + * + * For: add-double/2addr, sub-double/2addr, mul-double/2addr, + * div-double/2addr, rem-double/2addr + */ + /* binop/2addr vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a1) # a1 <- B + EAS2(a1, rFP, a1) # a1 <- &fp[B] + EAS2(t0, rFP, rOBJ) # t0 <- &fp[A] + LOAD64_F(fa0, fa0f, t0) + LOAD64_F(fa1, fa1f, a1) + + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + div.d fv0, fa0, fa1 + SET_VREG64_F(fv0, fv0f, rOBJ) + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_rem_double_2addr: /* 0xcf */ +/* File: mips/op_rem_double_2addr.S */ +/* File: mips/fbinopWide2addr.S */ + /* + * Generic 64-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0-a1 op a2-a3". + * This could be an MIPS instruction or a function call. + * + * For: add-double/2addr, sub-double/2addr, mul-double/2addr, + * div-double/2addr, rem-double/2addr + */ + /* binop/2addr vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a1) # a1 <- B + EAS2(a1, rFP, a1) # a1 <- &fp[B] + EAS2(t0, rFP, rOBJ) # t0 <- &fp[A] + LOAD64_F(fa0, fa0f, t0) + LOAD64_F(fa1, fa1f, a1) + + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + JAL(fmod) + SET_VREG64_F(fv0, fv0f, rOBJ) + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_add_int_lit16: /* 0xd0 */ +/* File: mips/op_add_int_lit16.S */ +/* File: mips/binopLit16.S */ + /* + * Generic 32-bit "lit16" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16, + * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16 + */ + # binop/lit16 vA, vB, /* +CCCC */ + FETCH_S(a1, 1) # a1 <- ssssCCCC (sign-extended) + GET_OPB(a2) # a2 <- B + GET_OPA(rOBJ) # rOBJ <- A+ + GET_VREG(a0, a2) # a0 <- vB + and rOBJ, rOBJ, 15 + .if 0 + # cmp a1, 0; is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + + # optional op + addu a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 10-13 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_rsub_int: /* 0xd1 */ +/* File: mips/op_rsub_int.S */ +/* this op is "rsub-int", but can be thought of as "rsub-int/lit16" */ +/* File: mips/binopLit16.S */ + /* + * Generic 32-bit "lit16" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16, + * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16 + */ + # binop/lit16 vA, vB, /* +CCCC */ + FETCH_S(a1, 1) # a1 <- ssssCCCC (sign-extended) + GET_OPB(a2) # a2 <- B + GET_OPA(rOBJ) # rOBJ <- A+ + GET_VREG(a0, a2) # a0 <- vB + and rOBJ, rOBJ, 15 + .if 0 + # cmp a1, 0; is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + + # optional op + subu a0, a1, a0 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 10-13 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_int_lit16: /* 0xd2 */ +/* File: mips/op_mul_int_lit16.S */ +/* File: mips/binopLit16.S */ + /* + * Generic 32-bit "lit16" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16, + * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16 + */ + # binop/lit16 vA, vB, /* +CCCC */ + FETCH_S(a1, 1) # a1 <- ssssCCCC (sign-extended) + GET_OPB(a2) # a2 <- B + GET_OPA(rOBJ) # rOBJ <- A+ + GET_VREG(a0, a2) # a0 <- vB + and rOBJ, rOBJ, 15 + .if 0 + # cmp a1, 0; is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + + # optional op + mul a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 10-13 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_div_int_lit16: /* 0xd3 */ +/* File: mips/op_div_int_lit16.S */ +#ifdef MIPS32REVGE6 +/* File: mips/binopLit16.S */ + /* + * Generic 32-bit "lit16" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16, + * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16 + */ + # binop/lit16 vA, vB, /* +CCCC */ + FETCH_S(a1, 1) # a1 <- ssssCCCC (sign-extended) + GET_OPB(a2) # a2 <- B + GET_OPA(rOBJ) # rOBJ <- A+ + GET_VREG(a0, a2) # a0 <- vB + and rOBJ, rOBJ, 15 + .if 1 + # cmp a1, 0; is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + + # optional op + div a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 10-13 instructions */ + +#else +/* File: mips/binopLit16.S */ + /* + * Generic 32-bit "lit16" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16, + * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16 + */ + # binop/lit16 vA, vB, /* +CCCC */ + FETCH_S(a1, 1) # a1 <- ssssCCCC (sign-extended) + GET_OPB(a2) # a2 <- B + GET_OPA(rOBJ) # rOBJ <- A+ + GET_VREG(a0, a2) # a0 <- vB + and rOBJ, rOBJ, 15 + .if 1 + # cmp a1, 0; is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + + div zero, a0, a1 # optional op + mflo a0 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 10-13 instructions */ + +#endif + +/* ------------------------------ */ + .balign 128 +.L_op_rem_int_lit16: /* 0xd4 */ +/* File: mips/op_rem_int_lit16.S */ +#ifdef MIPS32REVGE6 +/* File: mips/binopLit16.S */ + /* + * Generic 32-bit "lit16" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16, + * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16 + */ + # binop/lit16 vA, vB, /* +CCCC */ + FETCH_S(a1, 1) # a1 <- ssssCCCC (sign-extended) + GET_OPB(a2) # a2 <- B + GET_OPA(rOBJ) # rOBJ <- A+ + GET_VREG(a0, a2) # a0 <- vB + and rOBJ, rOBJ, 15 + .if 1 + # cmp a1, 0; is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + + # optional op + mod a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 10-13 instructions */ + +#else +/* File: mips/binopLit16.S */ + /* + * Generic 32-bit "lit16" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16, + * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16 + */ + # binop/lit16 vA, vB, /* +CCCC */ + FETCH_S(a1, 1) # a1 <- ssssCCCC (sign-extended) + GET_OPB(a2) # a2 <- B + GET_OPA(rOBJ) # rOBJ <- A+ + GET_VREG(a0, a2) # a0 <- vB + and rOBJ, rOBJ, 15 + .if 1 + # cmp a1, 0; is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + + div zero, a0, a1 # optional op + mfhi a0 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 10-13 instructions */ + +#endif + +/* ------------------------------ */ + .balign 128 +.L_op_and_int_lit16: /* 0xd5 */ +/* File: mips/op_and_int_lit16.S */ +/* File: mips/binopLit16.S */ + /* + * Generic 32-bit "lit16" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16, + * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16 + */ + # binop/lit16 vA, vB, /* +CCCC */ + FETCH_S(a1, 1) # a1 <- ssssCCCC (sign-extended) + GET_OPB(a2) # a2 <- B + GET_OPA(rOBJ) # rOBJ <- A+ + GET_VREG(a0, a2) # a0 <- vB + and rOBJ, rOBJ, 15 + .if 0 + # cmp a1, 0; is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + + # optional op + and a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 10-13 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_or_int_lit16: /* 0xd6 */ +/* File: mips/op_or_int_lit16.S */ +/* File: mips/binopLit16.S */ + /* + * Generic 32-bit "lit16" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16, + * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16 + */ + # binop/lit16 vA, vB, /* +CCCC */ + FETCH_S(a1, 1) # a1 <- ssssCCCC (sign-extended) + GET_OPB(a2) # a2 <- B + GET_OPA(rOBJ) # rOBJ <- A+ + GET_VREG(a0, a2) # a0 <- vB + and rOBJ, rOBJ, 15 + .if 0 + # cmp a1, 0; is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + + # optional op + or a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 10-13 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_xor_int_lit16: /* 0xd7 */ +/* File: mips/op_xor_int_lit16.S */ +/* File: mips/binopLit16.S */ + /* + * Generic 32-bit "lit16" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16, + * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16 + */ + # binop/lit16 vA, vB, /* +CCCC */ + FETCH_S(a1, 1) # a1 <- ssssCCCC (sign-extended) + GET_OPB(a2) # a2 <- B + GET_OPA(rOBJ) # rOBJ <- A+ + GET_VREG(a0, a2) # a0 <- vB + and rOBJ, rOBJ, 15 + .if 0 + # cmp a1, 0; is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + + # optional op + xor a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 10-13 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_add_int_lit8: /* 0xd8 */ +/* File: mips/op_add_int_lit8.S */ +/* File: mips/binopLit8.S */ + /* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8, + * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + # binop/lit8 vAA, vBB, /* +CC */ + FETCH_S(a3, 1) # a3 <- ssssCCBB (sign-extended for CC) + GET_OPA(rOBJ) # rOBJ <- AA + and a2, a3, 255 # a2 <- BB + GET_VREG(a0, a2) # a0 <- vBB + sra a1, a3, 8 # a1 <- ssssssCC (sign extended) + .if 0 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + + # optional op + addu a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 10-12 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_rsub_int_lit8: /* 0xd9 */ +/* File: mips/op_rsub_int_lit8.S */ +/* File: mips/binopLit8.S */ + /* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8, + * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + # binop/lit8 vAA, vBB, /* +CC */ + FETCH_S(a3, 1) # a3 <- ssssCCBB (sign-extended for CC) + GET_OPA(rOBJ) # rOBJ <- AA + and a2, a3, 255 # a2 <- BB + GET_VREG(a0, a2) # a0 <- vBB + sra a1, a3, 8 # a1 <- ssssssCC (sign extended) + .if 0 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + + # optional op + subu a0, a1, a0 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 10-12 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_int_lit8: /* 0xda */ +/* File: mips/op_mul_int_lit8.S */ +/* File: mips/binopLit8.S */ + /* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8, + * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + # binop/lit8 vAA, vBB, /* +CC */ + FETCH_S(a3, 1) # a3 <- ssssCCBB (sign-extended for CC) + GET_OPA(rOBJ) # rOBJ <- AA + and a2, a3, 255 # a2 <- BB + GET_VREG(a0, a2) # a0 <- vBB + sra a1, a3, 8 # a1 <- ssssssCC (sign extended) + .if 0 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + + # optional op + mul a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 10-12 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_div_int_lit8: /* 0xdb */ +/* File: mips/op_div_int_lit8.S */ +#ifdef MIPS32REVGE6 +/* File: mips/binopLit8.S */ + /* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8, + * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + # binop/lit8 vAA, vBB, /* +CC */ + FETCH_S(a3, 1) # a3 <- ssssCCBB (sign-extended for CC) + GET_OPA(rOBJ) # rOBJ <- AA + and a2, a3, 255 # a2 <- BB + GET_VREG(a0, a2) # a0 <- vBB + sra a1, a3, 8 # a1 <- ssssssCC (sign extended) + .if 1 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + + # optional op + div a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 10-12 instructions */ + +#else +/* File: mips/binopLit8.S */ + /* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8, + * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + # binop/lit8 vAA, vBB, /* +CC */ + FETCH_S(a3, 1) # a3 <- ssssCCBB (sign-extended for CC) + GET_OPA(rOBJ) # rOBJ <- AA + and a2, a3, 255 # a2 <- BB + GET_VREG(a0, a2) # a0 <- vBB + sra a1, a3, 8 # a1 <- ssssssCC (sign extended) + .if 1 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + + div zero, a0, a1 # optional op + mflo a0 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 10-12 instructions */ + +#endif + +/* ------------------------------ */ + .balign 128 +.L_op_rem_int_lit8: /* 0xdc */ +/* File: mips/op_rem_int_lit8.S */ +#ifdef MIPS32REVGE6 +/* File: mips/binopLit8.S */ + /* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8, + * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + # binop/lit8 vAA, vBB, /* +CC */ + FETCH_S(a3, 1) # a3 <- ssssCCBB (sign-extended for CC) + GET_OPA(rOBJ) # rOBJ <- AA + and a2, a3, 255 # a2 <- BB + GET_VREG(a0, a2) # a0 <- vBB + sra a1, a3, 8 # a1 <- ssssssCC (sign extended) + .if 1 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + + # optional op + mod a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 10-12 instructions */ + +#else +/* File: mips/binopLit8.S */ + /* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8, + * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + # binop/lit8 vAA, vBB, /* +CC */ + FETCH_S(a3, 1) # a3 <- ssssCCBB (sign-extended for CC) + GET_OPA(rOBJ) # rOBJ <- AA + and a2, a3, 255 # a2 <- BB + GET_VREG(a0, a2) # a0 <- vBB + sra a1, a3, 8 # a1 <- ssssssCC (sign extended) + .if 1 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + + div zero, a0, a1 # optional op + mfhi a0 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 10-12 instructions */ + +#endif + +/* ------------------------------ */ + .balign 128 +.L_op_and_int_lit8: /* 0xdd */ +/* File: mips/op_and_int_lit8.S */ +/* File: mips/binopLit8.S */ + /* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8, + * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + # binop/lit8 vAA, vBB, /* +CC */ + FETCH_S(a3, 1) # a3 <- ssssCCBB (sign-extended for CC) + GET_OPA(rOBJ) # rOBJ <- AA + and a2, a3, 255 # a2 <- BB + GET_VREG(a0, a2) # a0 <- vBB + sra a1, a3, 8 # a1 <- ssssssCC (sign extended) + .if 0 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + + # optional op + and a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 10-12 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_or_int_lit8: /* 0xde */ +/* File: mips/op_or_int_lit8.S */ +/* File: mips/binopLit8.S */ + /* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8, + * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + # binop/lit8 vAA, vBB, /* +CC */ + FETCH_S(a3, 1) # a3 <- ssssCCBB (sign-extended for CC) + GET_OPA(rOBJ) # rOBJ <- AA + and a2, a3, 255 # a2 <- BB + GET_VREG(a0, a2) # a0 <- vBB + sra a1, a3, 8 # a1 <- ssssssCC (sign extended) + .if 0 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + + # optional op + or a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 10-12 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_xor_int_lit8: /* 0xdf */ +/* File: mips/op_xor_int_lit8.S */ +/* File: mips/binopLit8.S */ + /* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8, + * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + # binop/lit8 vAA, vBB, /* +CC */ + FETCH_S(a3, 1) # a3 <- ssssCCBB (sign-extended for CC) + GET_OPA(rOBJ) # rOBJ <- AA + and a2, a3, 255 # a2 <- BB + GET_VREG(a0, a2) # a0 <- vBB + sra a1, a3, 8 # a1 <- ssssssCC (sign extended) + .if 0 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + + # optional op + xor a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 10-12 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_shl_int_lit8: /* 0xe0 */ +/* File: mips/op_shl_int_lit8.S */ +/* File: mips/binopLit8.S */ + /* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8, + * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + # binop/lit8 vAA, vBB, /* +CC */ + FETCH_S(a3, 1) # a3 <- ssssCCBB (sign-extended for CC) + GET_OPA(rOBJ) # rOBJ <- AA + and a2, a3, 255 # a2 <- BB + GET_VREG(a0, a2) # a0 <- vBB + sra a1, a3, 8 # a1 <- ssssssCC (sign extended) + .if 0 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + + # optional op + sll a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 10-12 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_shr_int_lit8: /* 0xe1 */ +/* File: mips/op_shr_int_lit8.S */ +/* File: mips/binopLit8.S */ + /* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8, + * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + # binop/lit8 vAA, vBB, /* +CC */ + FETCH_S(a3, 1) # a3 <- ssssCCBB (sign-extended for CC) + GET_OPA(rOBJ) # rOBJ <- AA + and a2, a3, 255 # a2 <- BB + GET_VREG(a0, a2) # a0 <- vBB + sra a1, a3, 8 # a1 <- ssssssCC (sign extended) + .if 0 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + + # optional op + sra a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 10-12 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_ushr_int_lit8: /* 0xe2 */ +/* File: mips/op_ushr_int_lit8.S */ +/* File: mips/binopLit8.S */ + /* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8, + * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + # binop/lit8 vAA, vBB, /* +CC */ + FETCH_S(a3, 1) # a3 <- ssssCCBB (sign-extended for CC) + GET_OPA(rOBJ) # rOBJ <- AA + and a2, a3, 255 # a2 <- BB + GET_VREG(a0, a2) # a0 <- vBB + sra a1, a3, 8 # a1 <- ssssssCC (sign extended) + .if 0 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + + # optional op + srl a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 10-12 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_quick: /* 0xe3 */ +/* File: mips/op_iget_quick.S */ + /* For: iget-quick, iget-boolean-quick, iget-byte-quick, iget-char-quick, iget-short-quick */ + # op vA, vB, offset /* CCCC */ + GET_OPB(a2) # a2 <- B + GET_VREG(a3, a2) # a3 <- object we're operating on + FETCH(a1, 1) # a1 <- field byte offset + GET_OPA4(a2) # a2 <- A(+) + # check object for null + beqz a3, common_errNullObject # object was null + addu t0, a3, a1 + lw a0, 0(t0) # a0 <- obj.field (8/16/32 bits) + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, a2, t0) # fp[A] <- a0 + +/* ------------------------------ */ + .balign 128 +.L_op_iget_wide_quick: /* 0xe4 */ +/* File: mips/op_iget_wide_quick.S */ + # iget-wide-quick vA, vB, offset /* CCCC */ + GET_OPB(a2) # a2 <- B + GET_VREG(a3, a2) # a3 <- object we're operating on + FETCH(a1, 1) # a1 <- field byte offset + GET_OPA4(a2) # a2 <- A(+) + # check object for null + beqz a3, common_errNullObject # object was null + addu t0, a3, a1 # t0 <- a3 + a1 + LOAD64(a0, a1, t0) # a0 <- obj.field (64 bits, aligned) + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG64(a0, a1, a2) # fp[A] <- a0/a1 + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_iget_object_quick: /* 0xe5 */ +/* File: mips/op_iget_object_quick.S */ + /* For: iget-object-quick */ + /* op vA, vB, offset@CCCC */ + GET_OPB(a2) # a2 <- B + FETCH(a1, 1) # a1 <- field byte offset + EXPORT_PC() + GET_VREG(a0, a2) # a0 <- object we're operating on + JAL(artIGetObjectFromMterp) # v0 <- GetObj(obj, offset) + lw a3, THREAD_EXCEPTION_OFFSET(rSELF) + GET_OPA4(a2) # a2<- A+ + PREFETCH_INST(2) # load rINST + bnez a3, MterpPossibleException # bail out + SET_VREG_OBJECT(v0, a2) # fp[A] <- v0 + ADVANCE(2) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_iput_quick: /* 0xe6 */ +/* File: mips/op_iput_quick.S */ + /* For: iput-quick, iput-object-quick */ + # op vA, vB, offset /* CCCC */ + GET_OPB(a2) # a2 <- B + GET_VREG(a3, a2) # a3 <- fp[B], the object pointer + FETCH(a1, 1) # a1 <- field byte offset + GET_OPA4(a2) # a2 <- A(+) + beqz a3, common_errNullObject # object was null + GET_VREG(a0, a2) # a0 <- fp[A] + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + addu t0, a3, a1 + sw a0, 0(t0) # obj.field (8/16/32 bits) <- a0 + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_iput_wide_quick: /* 0xe7 */ +/* File: mips/op_iput_wide_quick.S */ + # iput-wide-quick vA, vB, offset /* CCCC */ + GET_OPA4(a0) # a0 <- A(+) + GET_OPB(a1) # a1 <- B + GET_VREG(a2, a1) # a2 <- fp[B], the object pointer + # check object for null + beqz a2, common_errNullObject # object was null + EAS2(a3, rFP, a0) # a3 <- &fp[A] + LOAD64(a0, a1, a3) # a0/a1 <- fp[A] + FETCH(a3, 1) # a3 <- field byte offset + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + addu a2, a2, a3 # obj.field (64 bits, aligned) <- a0/a1 + STORE64(a0, a1, a2) # obj.field (64 bits, aligned) <- a0/a1 + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_iput_object_quick: /* 0xe8 */ +/* File: mips/op_iput_object_quick.S */ + /* For: iput-object-quick */ + # op vA, vB, offset /* CCCC */ + EXPORT_PC() + addu a0, rFP, OFF_FP_SHADOWFRAME + move a1, rPC + move a2, rINST + JAL(MterpIputObjectQuick) + beqz v0, MterpException + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_virtual_quick: /* 0xe9 */ +/* File: mips/op_invoke_virtual_quick.S */ +/* File: mips/invoke.S */ + /* + * Generic invoke handler wrapper. + */ + # op vB, {vD, vE, vF, vG, vA}, class /* CCCC */ + # op {vCCCC..v(CCCC+AA-1)}, meth /* BBBB */ + .extern MterpInvokeVirtualQuick + EXPORT_PC() + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rPC + move a3, rINST + JAL(MterpInvokeVirtualQuick) + beqz v0, MterpException + FETCH_ADVANCE_INST(3) + JAL(MterpShouldSwitchInterpreters) + bnez v0, MterpFallback + GET_INST_OPCODE(t0) + GOTO_OPCODE(t0) + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_virtual_range_quick: /* 0xea */ +/* File: mips/op_invoke_virtual_range_quick.S */ +/* File: mips/invoke.S */ + /* + * Generic invoke handler wrapper. + */ + # op vB, {vD, vE, vF, vG, vA}, class /* CCCC */ + # op {vCCCC..v(CCCC+AA-1)}, meth /* BBBB */ + .extern MterpInvokeVirtualQuickRange + EXPORT_PC() + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rPC + move a3, rINST + JAL(MterpInvokeVirtualQuickRange) + beqz v0, MterpException + FETCH_ADVANCE_INST(3) + JAL(MterpShouldSwitchInterpreters) + bnez v0, MterpFallback + GET_INST_OPCODE(t0) + GOTO_OPCODE(t0) + + +/* ------------------------------ */ + .balign 128 +.L_op_iput_boolean_quick: /* 0xeb */ +/* File: mips/op_iput_boolean_quick.S */ +/* File: mips/op_iput_quick.S */ + /* For: iput-quick, iput-object-quick */ + # op vA, vB, offset /* CCCC */ + GET_OPB(a2) # a2 <- B + GET_VREG(a3, a2) # a3 <- fp[B], the object pointer + FETCH(a1, 1) # a1 <- field byte offset + GET_OPA4(a2) # a2 <- A(+) + beqz a3, common_errNullObject # object was null + GET_VREG(a0, a2) # a0 <- fp[A] + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + addu t0, a3, a1 + sb a0, 0(t0) # obj.field (8/16/32 bits) <- a0 + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_iput_byte_quick: /* 0xec */ +/* File: mips/op_iput_byte_quick.S */ +/* File: mips/op_iput_quick.S */ + /* For: iput-quick, iput-object-quick */ + # op vA, vB, offset /* CCCC */ + GET_OPB(a2) # a2 <- B + GET_VREG(a3, a2) # a3 <- fp[B], the object pointer + FETCH(a1, 1) # a1 <- field byte offset + GET_OPA4(a2) # a2 <- A(+) + beqz a3, common_errNullObject # object was null + GET_VREG(a0, a2) # a0 <- fp[A] + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + addu t0, a3, a1 + sb a0, 0(t0) # obj.field (8/16/32 bits) <- a0 + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_iput_char_quick: /* 0xed */ +/* File: mips/op_iput_char_quick.S */ +/* File: mips/op_iput_quick.S */ + /* For: iput-quick, iput-object-quick */ + # op vA, vB, offset /* CCCC */ + GET_OPB(a2) # a2 <- B + GET_VREG(a3, a2) # a3 <- fp[B], the object pointer + FETCH(a1, 1) # a1 <- field byte offset + GET_OPA4(a2) # a2 <- A(+) + beqz a3, common_errNullObject # object was null + GET_VREG(a0, a2) # a0 <- fp[A] + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + addu t0, a3, a1 + sh a0, 0(t0) # obj.field (8/16/32 bits) <- a0 + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_iput_short_quick: /* 0xee */ +/* File: mips/op_iput_short_quick.S */ +/* File: mips/op_iput_quick.S */ + /* For: iput-quick, iput-object-quick */ + # op vA, vB, offset /* CCCC */ + GET_OPB(a2) # a2 <- B + GET_VREG(a3, a2) # a3 <- fp[B], the object pointer + FETCH(a1, 1) # a1 <- field byte offset + GET_OPA4(a2) # a2 <- A(+) + beqz a3, common_errNullObject # object was null + GET_VREG(a0, a2) # a0 <- fp[A] + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + addu t0, a3, a1 + sh a0, 0(t0) # obj.field (8/16/32 bits) <- a0 + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_boolean_quick: /* 0xef */ +/* File: mips/op_iget_boolean_quick.S */ +/* File: mips/op_iget_quick.S */ + /* For: iget-quick, iget-boolean-quick, iget-byte-quick, iget-char-quick, iget-short-quick */ + # op vA, vB, offset /* CCCC */ + GET_OPB(a2) # a2 <- B + GET_VREG(a3, a2) # a3 <- object we're operating on + FETCH(a1, 1) # a1 <- field byte offset + GET_OPA4(a2) # a2 <- A(+) + # check object for null + beqz a3, common_errNullObject # object was null + addu t0, a3, a1 + lbu a0, 0(t0) # a0 <- obj.field (8/16/32 bits) + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, a2, t0) # fp[A] <- a0 + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_byte_quick: /* 0xf0 */ +/* File: mips/op_iget_byte_quick.S */ +/* File: mips/op_iget_quick.S */ + /* For: iget-quick, iget-boolean-quick, iget-byte-quick, iget-char-quick, iget-short-quick */ + # op vA, vB, offset /* CCCC */ + GET_OPB(a2) # a2 <- B + GET_VREG(a3, a2) # a3 <- object we're operating on + FETCH(a1, 1) # a1 <- field byte offset + GET_OPA4(a2) # a2 <- A(+) + # check object for null + beqz a3, common_errNullObject # object was null + addu t0, a3, a1 + lb a0, 0(t0) # a0 <- obj.field (8/16/32 bits) + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, a2, t0) # fp[A] <- a0 + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_char_quick: /* 0xf1 */ +/* File: mips/op_iget_char_quick.S */ +/* File: mips/op_iget_quick.S */ + /* For: iget-quick, iget-boolean-quick, iget-byte-quick, iget-char-quick, iget-short-quick */ + # op vA, vB, offset /* CCCC */ + GET_OPB(a2) # a2 <- B + GET_VREG(a3, a2) # a3 <- object we're operating on + FETCH(a1, 1) # a1 <- field byte offset + GET_OPA4(a2) # a2 <- A(+) + # check object for null + beqz a3, common_errNullObject # object was null + addu t0, a3, a1 + lhu a0, 0(t0) # a0 <- obj.field (8/16/32 bits) + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, a2, t0) # fp[A] <- a0 + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_short_quick: /* 0xf2 */ +/* File: mips/op_iget_short_quick.S */ +/* File: mips/op_iget_quick.S */ + /* For: iget-quick, iget-boolean-quick, iget-byte-quick, iget-char-quick, iget-short-quick */ + # op vA, vB, offset /* CCCC */ + GET_OPB(a2) # a2 <- B + GET_VREG(a3, a2) # a3 <- object we're operating on + FETCH(a1, 1) # a1 <- field byte offset + GET_OPA4(a2) # a2 <- A(+) + # check object for null + beqz a3, common_errNullObject # object was null + addu t0, a3, a1 + lh a0, 0(t0) # a0 <- obj.field (8/16/32 bits) + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, a2, t0) # fp[A] <- a0 + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_lambda: /* 0xf3 */ +/* Transfer stub to alternate interpreter */ + b MterpFallback + +/* ------------------------------ */ + .balign 128 +.L_op_unused_f4: /* 0xf4 */ +/* File: mips/op_unused_f4.S */ +/* File: mips/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + b MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_capture_variable: /* 0xf5 */ +/* Transfer stub to alternate interpreter */ + b MterpFallback + +/* ------------------------------ */ + .balign 128 +.L_op_create_lambda: /* 0xf6 */ +/* Transfer stub to alternate interpreter */ + b MterpFallback + +/* ------------------------------ */ + .balign 128 +.L_op_liberate_variable: /* 0xf7 */ +/* Transfer stub to alternate interpreter */ + b MterpFallback + +/* ------------------------------ */ + .balign 128 +.L_op_box_lambda: /* 0xf8 */ +/* Transfer stub to alternate interpreter */ + b MterpFallback + +/* ------------------------------ */ + .balign 128 +.L_op_unbox_lambda: /* 0xf9 */ +/* Transfer stub to alternate interpreter */ + b MterpFallback + +/* ------------------------------ */ + .balign 128 +.L_op_unused_fa: /* 0xfa */ +/* File: mips/op_unused_fa.S */ +/* File: mips/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + b MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_fb: /* 0xfb */ +/* File: mips/op_unused_fb.S */ +/* File: mips/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + b MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_fc: /* 0xfc */ +/* File: mips/op_unused_fc.S */ +/* File: mips/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + b MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_fd: /* 0xfd */ +/* File: mips/op_unused_fd.S */ +/* File: mips/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + b MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_fe: /* 0xfe */ +/* File: mips/op_unused_fe.S */ +/* File: mips/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + b MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_ff: /* 0xff */ +/* File: mips/op_unused_ff.S */ +/* File: mips/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + b MterpFallback + + + .balign 128 + .size artMterpAsmInstructionStart, .-artMterpAsmInstructionStart + .global artMterpAsmInstructionEnd +artMterpAsmInstructionEnd: + +/* + * =========================================================================== + * Sister implementations + * =========================================================================== + */ + .global artMterpAsmSisterStart + .type artMterpAsmSisterStart, %function + .text + .balign 4 +artMterpAsmSisterStart: + +/* continuation for op_packed_switch */ + +.Lop_packed_switch_finish: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* continuation for op_sparse_switch */ + +.Lop_sparse_switch_finish: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* continuation for op_cmpl_float */ + +.Lop_cmpl_float_nan: + li rTEMP, -1 + +.Lop_cmpl_float_finish: + GET_OPA(rOBJ) + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(rTEMP, rOBJ, t0) # vAA <- rTEMP + +/* continuation for op_cmpg_float */ + +.Lop_cmpg_float_nan: + li rTEMP, 1 + +.Lop_cmpg_float_finish: + GET_OPA(rOBJ) + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(rTEMP, rOBJ, t0) # vAA <- rTEMP + +/* continuation for op_cmpl_double */ + +.Lop_cmpl_double_nan: + li rTEMP, -1 + +.Lop_cmpl_double_finish: + GET_OPA(rOBJ) + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(rTEMP, rOBJ, t0) # vAA <- rTEMP + +/* continuation for op_cmpg_double */ + +.Lop_cmpg_double_nan: + li rTEMP, 1 + +.Lop_cmpg_double_finish: + GET_OPA(rOBJ) + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(rTEMP, rOBJ, t0) # vAA <- rTEMP + +/* continuation for op_if_eq */ + +.L_op_if_eq_finish: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* continuation for op_if_ne */ + +.L_op_if_ne_finish: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* continuation for op_if_lt */ + +.L_op_if_lt_finish: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* continuation for op_if_ge */ + +.L_op_if_ge_finish: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* continuation for op_if_gt */ + +.L_op_if_gt_finish: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* continuation for op_if_le */ + +.L_op_if_le_finish: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* continuation for op_float_to_int */ + +/* + * Not an entry point as it is used only once !! + */ +f2i_doconv: +#ifdef MIPS32REVGE6 + l.s fa1, .LFLOAT_TO_INT_max + cmp.ule.s ft2, fa1, fa0 + l.s fv0, .LFLOAT_TO_INT_ret_max + bc1nez ft2, .Lop_float_to_int_set_vreg_f + + l.s fa1, .LFLOAT_TO_INT_min + cmp.ule.s ft2, fa0, fa1 + l.s fv0, .LFLOAT_TO_INT_ret_min + bc1nez ft2, .Lop_float_to_int_set_vreg_f + + mov.s fa1, fa0 + cmp.un.s ft2, fa0, fa1 + li.s fv0, 0 + bc1nez ft2, .Lop_float_to_int_set_vreg_f +#else + l.s fa1, .LFLOAT_TO_INT_max + c.ole.s fcc0, fa1, fa0 + l.s fv0, .LFLOAT_TO_INT_ret_max + bc1t .Lop_float_to_int_set_vreg_f + + l.s fa1, .LFLOAT_TO_INT_min + c.ole.s fcc0, fa0, fa1 + l.s fv0, .LFLOAT_TO_INT_ret_min + bc1t .Lop_float_to_int_set_vreg_f + + mov.s fa1, fa0 + c.un.s fcc0, fa0, fa1 + li.s fv0, 0 + bc1t .Lop_float_to_int_set_vreg_f +#endif + + trunc.w.s fv0, fa0 + b .Lop_float_to_int_set_vreg_f + +.LFLOAT_TO_INT_max: + .word 0x4f000000 +.LFLOAT_TO_INT_min: + .word 0xcf000000 +.LFLOAT_TO_INT_ret_max: + .word 0x7fffffff +.LFLOAT_TO_INT_ret_min: + .word 0x80000000 + +/* continuation for op_float_to_long */ + +f2l_doconv: +#ifdef MIPS32REVGE6 + l.s fa1, .LLONG_TO_max + cmp.ule.s ft2, fa1, fa0 + li rRESULT0, ~0 + li rRESULT1, ~0x80000000 + bc1nez ft2, .Lop_float_to_long_set_vreg + + l.s fa1, .LLONG_TO_min + cmp.ule.s ft2, fa0, fa1 + li rRESULT0, 0 + li rRESULT1, 0x80000000 + bc1nez ft2, .Lop_float_to_long_set_vreg + + mov.s fa1, fa0 + cmp.un.s ft2, fa0, fa1 + li rRESULT0, 0 + li rRESULT1, 0 + bc1nez ft2, .Lop_float_to_long_set_vreg +#else + l.s fa1, .LLONG_TO_max + c.ole.s fcc0, fa1, fa0 + li rRESULT0, ~0 + li rRESULT1, ~0x80000000 + bc1t .Lop_float_to_long_set_vreg + + l.s fa1, .LLONG_TO_min + c.ole.s fcc0, fa0, fa1 + li rRESULT0, 0 + li rRESULT1, 0x80000000 + bc1t .Lop_float_to_long_set_vreg + + mov.s fa1, fa0 + c.un.s fcc0, fa0, fa1 + li rRESULT0, 0 + li rRESULT1, 0 + bc1t .Lop_float_to_long_set_vreg +#endif + + JAL(__fixsfdi) + + b .Lop_float_to_long_set_vreg + +.LLONG_TO_max: + .word 0x5f000000 + +.LLONG_TO_min: + .word 0xdf000000 + +/* continuation for op_double_to_int */ + +d2i_doconv: +#ifdef MIPS32REVGE6 + la t0, .LDOUBLE_TO_INT_max + LOAD64_F(fa1, fa1f, t0) + cmp.ule.d ft2, fa1, fa0 + l.s fv0, .LDOUBLE_TO_INT_maxret + bc1nez ft2, .Lop_double_to_int_set_vreg_f + + la t0, .LDOUBLE_TO_INT_min + LOAD64_F(fa1, fa1f, t0) + cmp.ule.d ft2, fa0, fa1 + l.s fv0, .LDOUBLE_TO_INT_minret + bc1nez ft2, .Lop_double_to_int_set_vreg_f + + mov.d fa1, fa0 + cmp.un.d ft2, fa0, fa1 + li.s fv0, 0 + bc1nez ft2, .Lop_double_to_int_set_vreg_f +#else + la t0, .LDOUBLE_TO_INT_max + LOAD64_F(fa1, fa1f, t0) + c.ole.d fcc0, fa1, fa0 + l.s fv0, .LDOUBLE_TO_INT_maxret + bc1t .Lop_double_to_int_set_vreg_f + + la t0, .LDOUBLE_TO_INT_min + LOAD64_F(fa1, fa1f, t0) + c.ole.d fcc0, fa0, fa1 + l.s fv0, .LDOUBLE_TO_INT_minret + bc1t .Lop_double_to_int_set_vreg_f + + mov.d fa1, fa0 + c.un.d fcc0, fa0, fa1 + li.s fv0, 0 + bc1t .Lop_double_to_int_set_vreg_f +#endif + + trunc.w.d fv0, fa0 + b .Lop_double_to_int_set_vreg_f + +.LDOUBLE_TO_INT_max: + .dword 0x41dfffffffc00000 +.LDOUBLE_TO_INT_min: + .dword 0xc1e0000000000000 # minint, as a double (high word) +.LDOUBLE_TO_INT_maxret: + .word 0x7fffffff +.LDOUBLE_TO_INT_minret: + .word 0x80000000 + +/* continuation for op_double_to_long */ + +d2l_doconv: +#ifdef MIPS32REVGE6 + la t0, .LDOUBLE_TO_LONG_max + LOAD64_F(fa1, fa1f, t0) + cmp.ule.d ft2, fa1, fa0 + la t0, .LDOUBLE_TO_LONG_ret_max + LOAD64(rRESULT0, rRESULT1, t0) + bc1nez ft2, .Lop_double_to_long_set_vreg + + la t0, .LDOUBLE_TO_LONG_min + LOAD64_F(fa1, fa1f, t0) + cmp.ule.d ft2, fa0, fa1 + la t0, .LDOUBLE_TO_LONG_ret_min + LOAD64(rRESULT0, rRESULT1, t0) + bc1nez ft2, .Lop_double_to_long_set_vreg + + mov.d fa1, fa0 + cmp.un.d ft2, fa0, fa1 + li rRESULT0, 0 + li rRESULT1, 0 + bc1nez ft2, .Lop_double_to_long_set_vreg +#else + la t0, .LDOUBLE_TO_LONG_max + LOAD64_F(fa1, fa1f, t0) + c.ole.d fcc0, fa1, fa0 + la t0, .LDOUBLE_TO_LONG_ret_max + LOAD64(rRESULT0, rRESULT1, t0) + bc1t .Lop_double_to_long_set_vreg + + la t0, .LDOUBLE_TO_LONG_min + LOAD64_F(fa1, fa1f, t0) + c.ole.d fcc0, fa0, fa1 + la t0, .LDOUBLE_TO_LONG_ret_min + LOAD64(rRESULT0, rRESULT1, t0) + bc1t .Lop_double_to_long_set_vreg + + mov.d fa1, fa0 + c.un.d fcc0, fa0, fa1 + li rRESULT0, 0 + li rRESULT1, 0 + bc1t .Lop_double_to_long_set_vreg +#endif + JAL(__fixdfdi) + b .Lop_double_to_long_set_vreg + +.LDOUBLE_TO_LONG_max: + .dword 0x43e0000000000000 # maxlong, as a double (high word) +.LDOUBLE_TO_LONG_min: + .dword 0xc3e0000000000000 # minlong, as a double (high word) +.LDOUBLE_TO_LONG_ret_max: + .dword 0x7fffffffffffffff +.LDOUBLE_TO_LONG_ret_min: + .dword 0x8000000000000000 + +/* continuation for op_mul_long */ + +.Lop_mul_long_finish: + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG64(v0, v1, a0) # vAA::vAA+1 <- v0(low) :: v1(high) + GOTO_OPCODE(t0) # jump to next instruction + +/* continuation for op_shl_long */ + +.Lop_shl_long_finish: + SET_VREG64_GOTO(zero, v0, t2, t0) # vAA/vAA+1 <- rlo/rhi + +/* continuation for op_shr_long */ + +.Lop_shr_long_finish: + sra a3, a1, 31 # a3<- sign(ah) + SET_VREG64_GOTO(v1, a3, t3, t0) # vAA/VAA+1 <- rlo/rhi + +/* continuation for op_ushr_long */ + +.Lop_ushr_long_finish: + SET_VREG64_GOTO(v1, zero, rOBJ, t0) # vAA/vAA+1 <- rlo/rhi + +/* continuation for op_add_double */ + +.Lop_add_double_finish: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* continuation for op_sub_double */ + +.Lop_sub_double_finish: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* continuation for op_mul_double */ + +.Lop_mul_double_finish: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* continuation for op_div_double */ + +.Lop_div_double_finish: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* continuation for op_rem_double */ + +.Lop_rem_double_finish: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* continuation for op_shl_long_2addr */ + +.Lop_shl_long_2addr_finish: + SET_VREG64_GOTO(zero, v0, rOBJ, t0) # vAA/vAA+1 <- rlo/rhi + +/* continuation for op_shr_long_2addr */ + +.Lop_shr_long_2addr_finish: + sra a3, a1, 31 # a3<- sign(ah) + SET_VREG64_GOTO(v1, a3, t2, t0) # vAA/vAA+1 <- rlo/rhi + +/* continuation for op_ushr_long_2addr */ + +.Lop_ushr_long_2addr_finish: + SET_VREG64_GOTO(v1, zero, t3, t0) # vAA/vAA+1 <- rlo/rhi + + .size artMterpAsmSisterStart, .-artMterpAsmSisterStart + .global artMterpAsmSisterEnd +artMterpAsmSisterEnd: + + + .global artMterpAsmAltInstructionStart + .type artMterpAsmAltInstructionStart, %function + .text + +artMterpAsmAltInstructionStart = .L_ALT_op_nop +/* ------------------------------ */ + .balign 128 +.L_ALT_op_nop: /* 0x00 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (0 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move: /* 0x01 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (1 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_from16: /* 0x02 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (2 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_16: /* 0x03 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (3 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_wide: /* 0x04 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (4 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_wide_from16: /* 0x05 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (5 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_wide_16: /* 0x06 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (6 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_object: /* 0x07 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (7 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_object_from16: /* 0x08 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (8 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_object_16: /* 0x09 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (9 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_result: /* 0x0a */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (10 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_result_wide: /* 0x0b */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (11 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_result_object: /* 0x0c */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (12 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_exception: /* 0x0d */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (13 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_return_void: /* 0x0e */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (14 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_return: /* 0x0f */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (15 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_return_wide: /* 0x10 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (16 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_return_object: /* 0x11 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (17 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_4: /* 0x12 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (18 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_16: /* 0x13 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (19 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const: /* 0x14 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (20 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_high16: /* 0x15 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (21 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_wide_16: /* 0x16 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (22 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_wide_32: /* 0x17 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (23 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_wide: /* 0x18 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (24 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_wide_high16: /* 0x19 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (25 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_string: /* 0x1a */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (26 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_string_jumbo: /* 0x1b */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (27 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_class: /* 0x1c */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (28 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_monitor_enter: /* 0x1d */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (29 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_monitor_exit: /* 0x1e */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (30 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_check_cast: /* 0x1f */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (31 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_instance_of: /* 0x20 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (32 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_array_length: /* 0x21 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (33 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_new_instance: /* 0x22 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (34 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_new_array: /* 0x23 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (35 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_filled_new_array: /* 0x24 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (36 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_filled_new_array_range: /* 0x25 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (37 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_fill_array_data: /* 0x26 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (38 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_throw: /* 0x27 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (39 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_goto: /* 0x28 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (40 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_goto_16: /* 0x29 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (41 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_goto_32: /* 0x2a */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (42 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_packed_switch: /* 0x2b */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (43 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sparse_switch: /* 0x2c */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (44 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_cmpl_float: /* 0x2d */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (45 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_cmpg_float: /* 0x2e */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (46 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_cmpl_double: /* 0x2f */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (47 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_cmpg_double: /* 0x30 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (48 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_cmp_long: /* 0x31 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (49 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_eq: /* 0x32 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (50 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_ne: /* 0x33 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (51 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_lt: /* 0x34 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (52 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_ge: /* 0x35 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (53 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_gt: /* 0x36 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (54 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_le: /* 0x37 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (55 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_eqz: /* 0x38 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (56 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_nez: /* 0x39 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (57 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_ltz: /* 0x3a */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (58 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_gez: /* 0x3b */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (59 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_gtz: /* 0x3c */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (60 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_lez: /* 0x3d */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (61 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_3e: /* 0x3e */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (62 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_3f: /* 0x3f */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (63 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_40: /* 0x40 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (64 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_41: /* 0x41 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (65 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_42: /* 0x42 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (66 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_43: /* 0x43 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (67 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aget: /* 0x44 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (68 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aget_wide: /* 0x45 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (69 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aget_object: /* 0x46 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (70 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aget_boolean: /* 0x47 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (71 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aget_byte: /* 0x48 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (72 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aget_char: /* 0x49 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (73 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aget_short: /* 0x4a */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (74 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aput: /* 0x4b */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (75 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aput_wide: /* 0x4c */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (76 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aput_object: /* 0x4d */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (77 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aput_boolean: /* 0x4e */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (78 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aput_byte: /* 0x4f */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (79 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aput_char: /* 0x50 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (80 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aput_short: /* 0x51 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (81 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget: /* 0x52 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (82 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_wide: /* 0x53 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (83 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_object: /* 0x54 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (84 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_boolean: /* 0x55 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (85 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_byte: /* 0x56 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (86 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_char: /* 0x57 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (87 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_short: /* 0x58 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (88 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput: /* 0x59 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (89 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_wide: /* 0x5a */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (90 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_object: /* 0x5b */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (91 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_boolean: /* 0x5c */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (92 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_byte: /* 0x5d */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (93 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_char: /* 0x5e */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (94 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_short: /* 0x5f */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (95 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sget: /* 0x60 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (96 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sget_wide: /* 0x61 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (97 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sget_object: /* 0x62 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (98 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sget_boolean: /* 0x63 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (99 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sget_byte: /* 0x64 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (100 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sget_char: /* 0x65 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (101 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sget_short: /* 0x66 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (102 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sput: /* 0x67 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (103 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sput_wide: /* 0x68 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (104 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sput_object: /* 0x69 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (105 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sput_boolean: /* 0x6a */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (106 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sput_byte: /* 0x6b */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (107 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sput_char: /* 0x6c */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (108 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sput_short: /* 0x6d */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (109 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_virtual: /* 0x6e */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (110 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_super: /* 0x6f */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (111 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_direct: /* 0x70 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (112 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_static: /* 0x71 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (113 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_interface: /* 0x72 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (114 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_return_void_no_barrier: /* 0x73 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (115 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_virtual_range: /* 0x74 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (116 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_super_range: /* 0x75 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (117 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_direct_range: /* 0x76 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (118 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_static_range: /* 0x77 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (119 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_interface_range: /* 0x78 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (120 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_79: /* 0x79 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (121 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_7a: /* 0x7a */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (122 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_neg_int: /* 0x7b */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (123 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_not_int: /* 0x7c */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (124 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_neg_long: /* 0x7d */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (125 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_not_long: /* 0x7e */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (126 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_neg_float: /* 0x7f */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (127 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_neg_double: /* 0x80 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (128 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_int_to_long: /* 0x81 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (129 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_int_to_float: /* 0x82 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (130 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_int_to_double: /* 0x83 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (131 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_long_to_int: /* 0x84 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (132 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_long_to_float: /* 0x85 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (133 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_long_to_double: /* 0x86 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (134 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_float_to_int: /* 0x87 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (135 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_float_to_long: /* 0x88 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (136 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_float_to_double: /* 0x89 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (137 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_double_to_int: /* 0x8a */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (138 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_double_to_long: /* 0x8b */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (139 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_double_to_float: /* 0x8c */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (140 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_int_to_byte: /* 0x8d */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (141 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_int_to_char: /* 0x8e */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (142 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_int_to_short: /* 0x8f */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (143 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_int: /* 0x90 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (144 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sub_int: /* 0x91 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (145 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_int: /* 0x92 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (146 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_int: /* 0x93 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (147 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_int: /* 0x94 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (148 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_and_int: /* 0x95 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (149 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_or_int: /* 0x96 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (150 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_xor_int: /* 0x97 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (151 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shl_int: /* 0x98 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (152 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shr_int: /* 0x99 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (153 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_ushr_int: /* 0x9a */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (154 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_long: /* 0x9b */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (155 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sub_long: /* 0x9c */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (156 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_long: /* 0x9d */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (157 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_long: /* 0x9e */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (158 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_long: /* 0x9f */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (159 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_and_long: /* 0xa0 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (160 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_or_long: /* 0xa1 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (161 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_xor_long: /* 0xa2 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (162 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shl_long: /* 0xa3 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (163 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shr_long: /* 0xa4 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (164 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_ushr_long: /* 0xa5 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (165 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_float: /* 0xa6 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (166 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sub_float: /* 0xa7 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (167 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_float: /* 0xa8 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (168 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_float: /* 0xa9 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (169 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_float: /* 0xaa */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (170 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_double: /* 0xab */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (171 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sub_double: /* 0xac */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (172 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_double: /* 0xad */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (173 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_double: /* 0xae */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (174 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_double: /* 0xaf */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (175 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_int_2addr: /* 0xb0 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (176 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sub_int_2addr: /* 0xb1 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (177 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_int_2addr: /* 0xb2 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (178 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_int_2addr: /* 0xb3 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (179 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_int_2addr: /* 0xb4 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (180 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_and_int_2addr: /* 0xb5 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (181 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_or_int_2addr: /* 0xb6 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (182 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_xor_int_2addr: /* 0xb7 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (183 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shl_int_2addr: /* 0xb8 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (184 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shr_int_2addr: /* 0xb9 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (185 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_ushr_int_2addr: /* 0xba */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (186 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_long_2addr: /* 0xbb */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (187 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sub_long_2addr: /* 0xbc */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (188 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_long_2addr: /* 0xbd */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (189 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_long_2addr: /* 0xbe */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (190 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_long_2addr: /* 0xbf */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (191 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_and_long_2addr: /* 0xc0 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (192 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_or_long_2addr: /* 0xc1 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (193 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_xor_long_2addr: /* 0xc2 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (194 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shl_long_2addr: /* 0xc3 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (195 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shr_long_2addr: /* 0xc4 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (196 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_ushr_long_2addr: /* 0xc5 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (197 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_float_2addr: /* 0xc6 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (198 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sub_float_2addr: /* 0xc7 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (199 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_float_2addr: /* 0xc8 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (200 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_float_2addr: /* 0xc9 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (201 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_float_2addr: /* 0xca */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (202 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_double_2addr: /* 0xcb */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (203 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sub_double_2addr: /* 0xcc */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (204 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_double_2addr: /* 0xcd */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (205 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_double_2addr: /* 0xce */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (206 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_double_2addr: /* 0xcf */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (207 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_int_lit16: /* 0xd0 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (208 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rsub_int: /* 0xd1 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (209 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_int_lit16: /* 0xd2 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (210 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_int_lit16: /* 0xd3 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (211 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_int_lit16: /* 0xd4 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (212 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_and_int_lit16: /* 0xd5 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (213 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_or_int_lit16: /* 0xd6 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (214 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_xor_int_lit16: /* 0xd7 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (215 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_int_lit8: /* 0xd8 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (216 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rsub_int_lit8: /* 0xd9 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (217 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_int_lit8: /* 0xda */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (218 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_int_lit8: /* 0xdb */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (219 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_int_lit8: /* 0xdc */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (220 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_and_int_lit8: /* 0xdd */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (221 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_or_int_lit8: /* 0xde */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (222 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_xor_int_lit8: /* 0xdf */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (223 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shl_int_lit8: /* 0xe0 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (224 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shr_int_lit8: /* 0xe1 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (225 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_ushr_int_lit8: /* 0xe2 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (226 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_quick: /* 0xe3 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (227 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_wide_quick: /* 0xe4 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (228 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_object_quick: /* 0xe5 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (229 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_quick: /* 0xe6 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (230 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_wide_quick: /* 0xe7 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (231 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_object_quick: /* 0xe8 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (232 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_virtual_quick: /* 0xe9 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (233 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_virtual_range_quick: /* 0xea */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (234 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_boolean_quick: /* 0xeb */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (235 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_byte_quick: /* 0xec */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (236 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_char_quick: /* 0xed */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (237 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_short_quick: /* 0xee */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (238 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_boolean_quick: /* 0xef */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (239 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_byte_quick: /* 0xf0 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (240 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_char_quick: /* 0xf1 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (241 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_short_quick: /* 0xf2 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (242 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_lambda: /* 0xf3 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (243 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_f4: /* 0xf4 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (244 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_capture_variable: /* 0xf5 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (245 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_create_lambda: /* 0xf6 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (246 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_liberate_variable: /* 0xf7 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (247 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_box_lambda: /* 0xf8 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (248 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unbox_lambda: /* 0xf9 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (249 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_fa: /* 0xfa */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (250 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_fb: /* 0xfb */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (251 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_fc: /* 0xfc */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (252 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_fd: /* 0xfd */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (253 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_fe: /* 0xfe */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (254 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_ff: /* 0xff */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (255 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + + .balign 128 + .size artMterpAsmAltInstructionStart, .-artMterpAsmAltInstructionStart + .global artMterpAsmAltInstructionEnd +artMterpAsmAltInstructionEnd: +/* File: mips/footer.S */ +/* + * =========================================================================== + * Common subroutines and data + * =========================================================================== + */ + + .text + .align 2 + +/* + * We've detected a condition that will result in an exception, but the exception + * has not yet been thrown. Just bail out to the reference interpreter to deal with it. + * TUNING: for consistency, we may want to just go ahead and handle these here. + */ +common_errDivideByZero: + EXPORT_PC() +#if MTERP_LOGGING + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + JAL(MterpLogDivideByZeroException) +#endif + b MterpCommonFallback + +common_errArrayIndex: + EXPORT_PC() +#if MTERP_LOGGING + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + JAL(MterpLogArrayIndexException) +#endif + b MterpCommonFallback + +common_errNegativeArraySize: + EXPORT_PC() +#if MTERP_LOGGING + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + JAL(MterpLogNegativeArraySizeException) +#endif + b MterpCommonFallback + +common_errNoSuchMethod: + EXPORT_PC() +#if MTERP_LOGGING + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + JAL(MterpLogNoSuchMethodException) +#endif + b MterpCommonFallback + +common_errNullObject: + EXPORT_PC() +#if MTERP_LOGGING + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + JAL(MterpLogNullObjectException) +#endif + b MterpCommonFallback + +common_exceptionThrown: + EXPORT_PC() +#if MTERP_LOGGING + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + JAL(MterpLogExceptionThrownException) +#endif + b MterpCommonFallback + +MterpSuspendFallback: + EXPORT_PC() +#if MTERP_LOGGING + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + lw a2, THREAD_FLAGS_OFFSET(rSELF) + JAL(MterpLogSuspendFallback) +#endif + b MterpCommonFallback + +/* + * If we're here, something is out of the ordinary. If there is a pending + * exception, handle it. Otherwise, roll back and retry with the reference + * interpreter. + */ +MterpPossibleException: + lw a0, THREAD_EXCEPTION_OFFSET(rSELF) + beqz a0, MterpFallback # If exception, fall back to reference interpreter. + /* intentional fallthrough - handle pending exception. */ +/* + * On return from a runtime helper routine, we've found a pending exception. + * Can we handle it here - or need to bail out to caller? + * + */ +MterpException: + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + JAL(MterpHandleException) # (self, shadow_frame) + beqz v0, MterpExceptionReturn # no local catch, back to caller. + lw a0, OFF_FP_CODE_ITEM(rFP) + lw a1, OFF_FP_DEX_PC(rFP) + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) + addu rPC, a0, CODEITEM_INSNS_OFFSET + sll a1, a1, 1 + addu rPC, rPC, a1 # generate new dex_pc_ptr + /* Do we need to switch interpreters? */ + JAL(MterpShouldSwitchInterpreters) + bnez v0, MterpFallback + /* resume execution at catch block */ + EXPORT_PC() + FETCH_INST() + GET_INST_OPCODE(t0) + GOTO_OPCODE(t0) + /* NOTE: no fallthrough */ + +/* + * Check for suspend check request. Assumes rINST already loaded, rPC advanced and + * still needs to get the opcode and branch to it, and flags are in lr. + */ +MterpCheckSuspendAndContinue: + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh rIBASE + and ra, (THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + bnez ra, 1f + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction +1: + EXPORT_PC() + move a0, rSELF + JAL(MterpSuspendCheck) # (self) + bnez v0, MterpFallback + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* + * On-stack replacement has happened, and now we've returned from the compiled method. + */ +MterpOnStackReplacement: +#if MTERP_LOGGING + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + JAL(MterpLogOSR) +#endif + li v0, 1 # Signal normal return + b MterpDone + +/* + * Bail out to reference interpreter. + */ +MterpFallback: + EXPORT_PC() +#if MTERP_LOGGING + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + JAL(MterpLogFallback) +#endif +MterpCommonFallback: + move v0, zero # signal retry with reference interpreter. + b MterpDone +/* + * We pushed some registers on the stack in ExecuteMterpImpl, then saved + * SP and LR. Here we restore SP, restore the registers, and then restore + * LR to PC. + * + * On entry: + * uint32_t* rFP (should still be live, pointer to base of vregs) + */ +MterpExceptionReturn: + li v0, 1 # signal return to caller. + b MterpDone +MterpReturn: + lw a2, OFF_FP_RESULT_REGISTER(rFP) + sw v0, 0(a2) + sw v1, 4(a2) + li v0, 1 # signal return to caller. +MterpDone: +/* Restore from the stack and return. Frame size = STACK_SIZE */ + STACK_LOAD_FULL() + jalr zero, ra + + .end ExecuteMterpImpl + diff --git a/runtime/interpreter/mterp/rebuild.sh b/runtime/interpreter/mterp/rebuild.sh index ac8794581c..2b5f339869 100755 --- a/runtime/interpreter/mterp/rebuild.sh +++ b/runtime/interpreter/mterp/rebuild.sh @@ -21,4 +21,4 @@ set -e # for arch in arm x86 mips arm64 x86_64 mips64; do TARGET_ARCH_EXT=$arch make -f Makefile_mterp; done -for arch in arm x86 arm64 ; do TARGET_ARCH_EXT=$arch make -f Makefile_mterp; done +for arch in arm x86 mips arm64 ; do TARGET_ARCH_EXT=$arch make -f Makefile_mterp; done -- GitLab From c4a575f58007036ff3408b15c2ec48108add20f3 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Fri, 26 Feb 2016 08:56:45 +0000 Subject: [PATCH 055/204] Revert "Fast ART x86_64 interpretator" Fails gcstress configuration. This reverts commit c669beb798e273dd3d44cfa6a7a95ff90eba7209. Change-Id: Ifac92471f91a116fc271d0dde8356fbbb0a08048 --- runtime/Android.mk | 3 +- runtime/interpreter/interpreter.cc | 4 +- runtime/interpreter/mterp/config_x86_64 | 516 +- runtime/interpreter/mterp/out/mterp_x86_64.S | 11955 ---------------- runtime/interpreter/mterp/rebuild.sh | 2 +- runtime/interpreter/mterp/x86_64/alt_stub.S | 17 - runtime/interpreter/mterp/x86_64/bincmp.S | 28 - runtime/interpreter/mterp/x86_64/bindiv.S | 34 - .../interpreter/mterp/x86_64/bindiv2addr.S | 35 - .../interpreter/mterp/x86_64/bindivLit16.S | 27 - runtime/interpreter/mterp/x86_64/bindivLit8.S | 25 - runtime/interpreter/mterp/x86_64/binop.S | 17 - runtime/interpreter/mterp/x86_64/binop1.S | 19 - runtime/interpreter/mterp/x86_64/binop2addr.S | 19 - runtime/interpreter/mterp/x86_64/binopLit16.S | 19 - runtime/interpreter/mterp/x86_64/binopLit8.S | 18 - runtime/interpreter/mterp/x86_64/binopWide.S | 10 - .../interpreter/mterp/x86_64/binopWide2addr.S | 11 - runtime/interpreter/mterp/x86_64/cvtfp_int.S | 27 - runtime/interpreter/mterp/x86_64/entry.S | 70 - runtime/interpreter/mterp/x86_64/fallback.S | 3 - runtime/interpreter/mterp/x86_64/footer.S | 166 - runtime/interpreter/mterp/x86_64/fpcmp.S | 35 - runtime/interpreter/mterp/x86_64/fpcvt.S | 17 - runtime/interpreter/mterp/x86_64/header.S | 289 - runtime/interpreter/mterp/x86_64/invoke.S | 17 - .../interpreter/mterp/x86_64/op_add_double.S | 1 - .../mterp/x86_64/op_add_double_2addr.S | 1 - .../interpreter/mterp/x86_64/op_add_float.S | 1 - .../mterp/x86_64/op_add_float_2addr.S | 1 - runtime/interpreter/mterp/x86_64/op_add_int.S | 1 - .../mterp/x86_64/op_add_int_2addr.S | 1 - .../mterp/x86_64/op_add_int_lit16.S | 1 - .../mterp/x86_64/op_add_int_lit8.S | 1 - .../interpreter/mterp/x86_64/op_add_long.S | 1 - .../mterp/x86_64/op_add_long_2addr.S | 1 - runtime/interpreter/mterp/x86_64/op_aget.S | 24 - .../mterp/x86_64/op_aget_boolean.S | 1 - .../interpreter/mterp/x86_64/op_aget_byte.S | 1 - .../interpreter/mterp/x86_64/op_aget_char.S | 1 - .../interpreter/mterp/x86_64/op_aget_object.S | 16 - .../interpreter/mterp/x86_64/op_aget_short.S | 1 - .../interpreter/mterp/x86_64/op_aget_wide.S | 1 - runtime/interpreter/mterp/x86_64/op_and_int.S | 1 - .../mterp/x86_64/op_and_int_2addr.S | 1 - .../mterp/x86_64/op_and_int_lit16.S | 1 - .../mterp/x86_64/op_and_int_lit8.S | 1 - .../interpreter/mterp/x86_64/op_and_long.S | 1 - .../mterp/x86_64/op_and_long_2addr.S | 1 - runtime/interpreter/mterp/x86_64/op_aput.S | 23 - .../mterp/x86_64/op_aput_boolean.S | 1 - .../interpreter/mterp/x86_64/op_aput_byte.S | 1 - .../interpreter/mterp/x86_64/op_aput_char.S | 1 - .../interpreter/mterp/x86_64/op_aput_object.S | 13 - .../interpreter/mterp/x86_64/op_aput_short.S | 1 - .../interpreter/mterp/x86_64/op_aput_wide.S | 1 - .../mterp/x86_64/op_array_length.S | 12 - .../interpreter/mterp/x86_64/op_check_cast.S | 13 - .../interpreter/mterp/x86_64/op_cmp_long.S | 17 - .../interpreter/mterp/x86_64/op_cmpg_double.S | 1 - .../interpreter/mterp/x86_64/op_cmpg_float.S | 1 - .../interpreter/mterp/x86_64/op_cmpl_double.S | 1 - .../interpreter/mterp/x86_64/op_cmpl_float.S | 1 - runtime/interpreter/mterp/x86_64/op_const.S | 4 - .../interpreter/mterp/x86_64/op_const_16.S | 4 - runtime/interpreter/mterp/x86_64/op_const_4.S | 7 - .../interpreter/mterp/x86_64/op_const_class.S | 10 - .../mterp/x86_64/op_const_high16.S | 5 - .../mterp/x86_64/op_const_string.S | 10 - .../mterp/x86_64/op_const_string_jumbo.S | 10 - .../interpreter/mterp/x86_64/op_const_wide.S | 4 - .../mterp/x86_64/op_const_wide_16.S | 4 - .../mterp/x86_64/op_const_wide_32.S | 4 - .../mterp/x86_64/op_const_wide_high16.S | 5 - .../interpreter/mterp/x86_64/op_div_double.S | 1 - .../mterp/x86_64/op_div_double_2addr.S | 1 - .../interpreter/mterp/x86_64/op_div_float.S | 1 - .../mterp/x86_64/op_div_float_2addr.S | 1 - runtime/interpreter/mterp/x86_64/op_div_int.S | 1 - .../mterp/x86_64/op_div_int_2addr.S | 1 - .../mterp/x86_64/op_div_int_lit16.S | 1 - .../mterp/x86_64/op_div_int_lit8.S | 1 - .../interpreter/mterp/x86_64/op_div_long.S | 1 - .../mterp/x86_64/op_div_long_2addr.S | 1 - .../mterp/x86_64/op_double_to_float.S | 1 - .../mterp/x86_64/op_double_to_int.S | 1 - .../mterp/x86_64/op_double_to_long.S | 1 - .../mterp/x86_64/op_fill_array_data.S | 9 - .../mterp/x86_64/op_filled_new_array.S | 17 - .../mterp/x86_64/op_filled_new_array_range.S | 1 - .../mterp/x86_64/op_float_to_double.S | 1 - .../mterp/x86_64/op_float_to_int.S | 1 - .../mterp/x86_64/op_float_to_long.S | 1 - runtime/interpreter/mterp/x86_64/op_goto.S | 19 - runtime/interpreter/mterp/x86_64/op_goto_16.S | 19 - runtime/interpreter/mterp/x86_64/op_goto_32.S | 22 - runtime/interpreter/mterp/x86_64/op_if_eq.S | 1 - runtime/interpreter/mterp/x86_64/op_if_eqz.S | 1 - runtime/interpreter/mterp/x86_64/op_if_ge.S | 1 - runtime/interpreter/mterp/x86_64/op_if_gez.S | 1 - runtime/interpreter/mterp/x86_64/op_if_gt.S | 1 - runtime/interpreter/mterp/x86_64/op_if_gtz.S | 1 - runtime/interpreter/mterp/x86_64/op_if_le.S | 1 - runtime/interpreter/mterp/x86_64/op_if_lez.S | 1 - runtime/interpreter/mterp/x86_64/op_if_lt.S | 1 - runtime/interpreter/mterp/x86_64/op_if_ltz.S | 1 - runtime/interpreter/mterp/x86_64/op_if_ne.S | 1 - runtime/interpreter/mterp/x86_64/op_if_nez.S | 1 - runtime/interpreter/mterp/x86_64/op_iget.S | 27 - .../mterp/x86_64/op_iget_boolean.S | 1 - .../mterp/x86_64/op_iget_boolean_quick.S | 1 - .../interpreter/mterp/x86_64/op_iget_byte.S | 1 - .../mterp/x86_64/op_iget_byte_quick.S | 1 - .../interpreter/mterp/x86_64/op_iget_char.S | 1 - .../mterp/x86_64/op_iget_char_quick.S | 1 - .../interpreter/mterp/x86_64/op_iget_object.S | 1 - .../mterp/x86_64/op_iget_object_quick.S | 14 - .../interpreter/mterp/x86_64/op_iget_quick.S | 18 - .../interpreter/mterp/x86_64/op_iget_short.S | 1 - .../mterp/x86_64/op_iget_short_quick.S | 1 - .../interpreter/mterp/x86_64/op_iget_wide.S | 1 - .../mterp/x86_64/op_iget_wide_quick.S | 1 - .../interpreter/mterp/x86_64/op_instance_of.S | 21 - .../interpreter/mterp/x86_64/op_int_to_byte.S | 1 - .../interpreter/mterp/x86_64/op_int_to_char.S | 1 - .../mterp/x86_64/op_int_to_double.S | 1 - .../mterp/x86_64/op_int_to_float.S | 1 - .../interpreter/mterp/x86_64/op_int_to_long.S | 8 - .../mterp/x86_64/op_int_to_short.S | 1 - .../mterp/x86_64/op_invoke_direct.S | 1 - .../mterp/x86_64/op_invoke_direct_range.S | 1 - .../mterp/x86_64/op_invoke_interface.S | 8 - .../mterp/x86_64/op_invoke_interface_range.S | 1 - .../mterp/x86_64/op_invoke_static.S | 2 - .../mterp/x86_64/op_invoke_static_range.S | 1 - .../mterp/x86_64/op_invoke_super.S | 8 - .../mterp/x86_64/op_invoke_super_range.S | 1 - .../mterp/x86_64/op_invoke_virtual.S | 8 - .../mterp/x86_64/op_invoke_virtual_quick.S | 1 - .../mterp/x86_64/op_invoke_virtual_range.S | 1 - .../x86_64/op_invoke_virtual_range_quick.S | 1 - runtime/interpreter/mterp/x86_64/op_iput.S | 20 - .../mterp/x86_64/op_iput_boolean.S | 1 - .../mterp/x86_64/op_iput_boolean_quick.S | 1 - .../interpreter/mterp/x86_64/op_iput_byte.S | 1 - .../mterp/x86_64/op_iput_byte_quick.S | 1 - .../interpreter/mterp/x86_64/op_iput_char.S | 1 - .../mterp/x86_64/op_iput_char_quick.S | 1 - .../interpreter/mterp/x86_64/op_iput_object.S | 10 - .../mterp/x86_64/op_iput_object_quick.S | 9 - .../interpreter/mterp/x86_64/op_iput_quick.S | 13 - .../interpreter/mterp/x86_64/op_iput_short.S | 1 - .../mterp/x86_64/op_iput_short_quick.S | 1 - .../interpreter/mterp/x86_64/op_iput_wide.S | 14 - .../mterp/x86_64/op_iput_wide_quick.S | 12 - .../mterp/x86_64/op_long_to_double.S | 1 - .../mterp/x86_64/op_long_to_float.S | 1 - .../interpreter/mterp/x86_64/op_long_to_int.S | 2 - .../mterp/x86_64/op_monitor_enter.S | 11 - .../mterp/x86_64/op_monitor_exit.S | 15 - runtime/interpreter/mterp/x86_64/op_move.S | 13 - runtime/interpreter/mterp/x86_64/op_move_16.S | 12 - .../mterp/x86_64/op_move_exception.S | 5 - .../interpreter/mterp/x86_64/op_move_from16.S | 11 - .../interpreter/mterp/x86_64/op_move_object.S | 1 - .../mterp/x86_64/op_move_object_16.S | 1 - .../mterp/x86_64/op_move_object_from16.S | 1 - .../interpreter/mterp/x86_64/op_move_result.S | 11 - .../mterp/x86_64/op_move_result_object.S | 1 - .../mterp/x86_64/op_move_result_wide.S | 5 - .../interpreter/mterp/x86_64/op_move_wide.S | 8 - .../mterp/x86_64/op_move_wide_16.S | 7 - .../mterp/x86_64/op_move_wide_from16.S | 6 - .../interpreter/mterp/x86_64/op_mul_double.S | 1 - .../mterp/x86_64/op_mul_double_2addr.S | 1 - .../interpreter/mterp/x86_64/op_mul_float.S | 1 - .../mterp/x86_64/op_mul_float_2addr.S | 1 - runtime/interpreter/mterp/x86_64/op_mul_int.S | 1 - .../mterp/x86_64/op_mul_int_2addr.S | 8 - .../mterp/x86_64/op_mul_int_lit16.S | 1 - .../mterp/x86_64/op_mul_int_lit8.S | 1 - .../interpreter/mterp/x86_64/op_mul_long.S | 1 - .../mterp/x86_64/op_mul_long_2addr.S | 8 - .../interpreter/mterp/x86_64/op_neg_double.S | 1 - .../interpreter/mterp/x86_64/op_neg_float.S | 1 - runtime/interpreter/mterp/x86_64/op_neg_int.S | 1 - .../interpreter/mterp/x86_64/op_neg_long.S | 1 - .../interpreter/mterp/x86_64/op_new_array.S | 18 - .../mterp/x86_64/op_new_instance.S | 13 - runtime/interpreter/mterp/x86_64/op_nop.S | 1 - runtime/interpreter/mterp/x86_64/op_not_int.S | 1 - .../interpreter/mterp/x86_64/op_not_long.S | 1 - runtime/interpreter/mterp/x86_64/op_or_int.S | 1 - .../mterp/x86_64/op_or_int_2addr.S | 1 - .../mterp/x86_64/op_or_int_lit16.S | 1 - .../interpreter/mterp/x86_64/op_or_int_lit8.S | 1 - runtime/interpreter/mterp/x86_64/op_or_long.S | 1 - .../mterp/x86_64/op_or_long_2addr.S | 1 - .../mterp/x86_64/op_packed_switch.S | 27 - .../interpreter/mterp/x86_64/op_rem_double.S | 14 - .../mterp/x86_64/op_rem_double_2addr.S | 15 - .../interpreter/mterp/x86_64/op_rem_float.S | 14 - .../mterp/x86_64/op_rem_float_2addr.S | 15 - runtime/interpreter/mterp/x86_64/op_rem_int.S | 1 - .../mterp/x86_64/op_rem_int_2addr.S | 1 - .../mterp/x86_64/op_rem_int_lit16.S | 1 - .../mterp/x86_64/op_rem_int_lit8.S | 1 - .../interpreter/mterp/x86_64/op_rem_long.S | 1 - .../mterp/x86_64/op_rem_long_2addr.S | 1 - runtime/interpreter/mterp/x86_64/op_return.S | 15 - .../mterp/x86_64/op_return_object.S | 1 - .../interpreter/mterp/x86_64/op_return_void.S | 9 - .../mterp/x86_64/op_return_void_no_barrier.S | 7 - .../interpreter/mterp/x86_64/op_return_wide.S | 13 - .../interpreter/mterp/x86_64/op_rsub_int.S | 2 - .../mterp/x86_64/op_rsub_int_lit8.S | 1 - runtime/interpreter/mterp/x86_64/op_sget.S | 25 - .../mterp/x86_64/op_sget_boolean.S | 1 - .../interpreter/mterp/x86_64/op_sget_byte.S | 1 - .../interpreter/mterp/x86_64/op_sget_char.S | 1 - .../interpreter/mterp/x86_64/op_sget_object.S | 1 - .../interpreter/mterp/x86_64/op_sget_short.S | 1 - .../interpreter/mterp/x86_64/op_sget_wide.S | 1 - runtime/interpreter/mterp/x86_64/op_shl_int.S | 1 - .../mterp/x86_64/op_shl_int_2addr.S | 1 - .../mterp/x86_64/op_shl_int_lit8.S | 1 - .../interpreter/mterp/x86_64/op_shl_long.S | 1 - .../mterp/x86_64/op_shl_long_2addr.S | 1 - runtime/interpreter/mterp/x86_64/op_shr_int.S | 1 - .../mterp/x86_64/op_shr_int_2addr.S | 1 - .../mterp/x86_64/op_shr_int_lit8.S | 1 - .../interpreter/mterp/x86_64/op_shr_long.S | 1 - .../mterp/x86_64/op_shr_long_2addr.S | 1 - .../mterp/x86_64/op_sparse_switch.S | 1 - runtime/interpreter/mterp/x86_64/op_sput.S | 17 - .../mterp/x86_64/op_sput_boolean.S | 1 - .../interpreter/mterp/x86_64/op_sput_byte.S | 1 - .../interpreter/mterp/x86_64/op_sput_char.S | 1 - .../interpreter/mterp/x86_64/op_sput_object.S | 10 - .../interpreter/mterp/x86_64/op_sput_short.S | 1 - .../interpreter/mterp/x86_64/op_sput_wide.S | 15 - .../interpreter/mterp/x86_64/op_sub_double.S | 1 - .../mterp/x86_64/op_sub_double_2addr.S | 1 - .../interpreter/mterp/x86_64/op_sub_float.S | 1 - .../mterp/x86_64/op_sub_float_2addr.S | 1 - runtime/interpreter/mterp/x86_64/op_sub_int.S | 1 - .../mterp/x86_64/op_sub_int_2addr.S | 1 - .../interpreter/mterp/x86_64/op_sub_long.S | 1 - .../mterp/x86_64/op_sub_long_2addr.S | 1 - runtime/interpreter/mterp/x86_64/op_throw.S | 10 - .../interpreter/mterp/x86_64/op_unused_3e.S | 1 - .../interpreter/mterp/x86_64/op_unused_3f.S | 1 - .../interpreter/mterp/x86_64/op_unused_40.S | 1 - .../interpreter/mterp/x86_64/op_unused_41.S | 1 - .../interpreter/mterp/x86_64/op_unused_42.S | 1 - .../interpreter/mterp/x86_64/op_unused_43.S | 1 - .../interpreter/mterp/x86_64/op_unused_79.S | 1 - .../interpreter/mterp/x86_64/op_unused_7a.S | 1 - .../interpreter/mterp/x86_64/op_unused_f4.S | 1 - .../interpreter/mterp/x86_64/op_unused_fa.S | 1 - .../interpreter/mterp/x86_64/op_unused_fb.S | 1 - .../interpreter/mterp/x86_64/op_unused_fc.S | 1 - .../interpreter/mterp/x86_64/op_unused_fd.S | 1 - .../interpreter/mterp/x86_64/op_unused_fe.S | 1 - .../interpreter/mterp/x86_64/op_unused_ff.S | 1 - .../interpreter/mterp/x86_64/op_ushr_int.S | 1 - .../mterp/x86_64/op_ushr_int_2addr.S | 1 - .../mterp/x86_64/op_ushr_int_lit8.S | 1 - .../interpreter/mterp/x86_64/op_ushr_long.S | 1 - .../mterp/x86_64/op_ushr_long_2addr.S | 1 - runtime/interpreter/mterp/x86_64/op_xor_int.S | 1 - .../mterp/x86_64/op_xor_int_2addr.S | 1 - .../mterp/x86_64/op_xor_int_lit16.S | 1 - .../mterp/x86_64/op_xor_int_lit8.S | 1 - .../interpreter/mterp/x86_64/op_xor_long.S | 1 - .../mterp/x86_64/op_xor_long_2addr.S | 1 - runtime/interpreter/mterp/x86_64/shop2addr.S | 19 - runtime/interpreter/mterp/x86_64/sseBinop.S | 9 - .../interpreter/mterp/x86_64/sseBinop2Addr.S | 10 - runtime/interpreter/mterp/x86_64/unop.S | 22 - runtime/interpreter/mterp/x86_64/unused.S | 4 - runtime/interpreter/mterp/x86_64/zcmp.S | 24 - 282 files changed, 260 insertions(+), 14216 deletions(-) delete mode 100644 runtime/interpreter/mterp/out/mterp_x86_64.S delete mode 100644 runtime/interpreter/mterp/x86_64/alt_stub.S delete mode 100644 runtime/interpreter/mterp/x86_64/bincmp.S delete mode 100644 runtime/interpreter/mterp/x86_64/bindiv.S delete mode 100644 runtime/interpreter/mterp/x86_64/bindiv2addr.S delete mode 100644 runtime/interpreter/mterp/x86_64/bindivLit16.S delete mode 100644 runtime/interpreter/mterp/x86_64/bindivLit8.S delete mode 100644 runtime/interpreter/mterp/x86_64/binop.S delete mode 100644 runtime/interpreter/mterp/x86_64/binop1.S delete mode 100644 runtime/interpreter/mterp/x86_64/binop2addr.S delete mode 100644 runtime/interpreter/mterp/x86_64/binopLit16.S delete mode 100644 runtime/interpreter/mterp/x86_64/binopLit8.S delete mode 100644 runtime/interpreter/mterp/x86_64/binopWide.S delete mode 100644 runtime/interpreter/mterp/x86_64/binopWide2addr.S delete mode 100644 runtime/interpreter/mterp/x86_64/cvtfp_int.S delete mode 100644 runtime/interpreter/mterp/x86_64/entry.S delete mode 100644 runtime/interpreter/mterp/x86_64/fallback.S delete mode 100644 runtime/interpreter/mterp/x86_64/footer.S delete mode 100644 runtime/interpreter/mterp/x86_64/fpcmp.S delete mode 100644 runtime/interpreter/mterp/x86_64/fpcvt.S delete mode 100644 runtime/interpreter/mterp/x86_64/header.S delete mode 100644 runtime/interpreter/mterp/x86_64/invoke.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_add_double.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_add_double_2addr.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_add_float.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_add_float_2addr.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_add_int.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_add_int_2addr.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_add_int_lit16.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_add_int_lit8.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_add_long.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_add_long_2addr.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_aget.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_aget_boolean.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_aget_byte.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_aget_char.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_aget_object.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_aget_short.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_aget_wide.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_and_int.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_and_int_2addr.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_and_int_lit16.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_and_int_lit8.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_and_long.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_and_long_2addr.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_aput.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_aput_boolean.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_aput_byte.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_aput_char.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_aput_object.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_aput_short.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_aput_wide.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_array_length.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_check_cast.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_cmp_long.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_cmpg_double.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_cmpg_float.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_cmpl_double.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_cmpl_float.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_const.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_const_16.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_const_4.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_const_class.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_const_high16.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_const_string.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_const_string_jumbo.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_const_wide.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_const_wide_16.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_const_wide_32.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_const_wide_high16.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_div_double.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_div_double_2addr.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_div_float.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_div_float_2addr.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_div_int.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_div_int_2addr.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_div_int_lit16.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_div_int_lit8.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_div_long.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_div_long_2addr.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_double_to_float.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_double_to_int.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_double_to_long.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_fill_array_data.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_filled_new_array.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_filled_new_array_range.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_float_to_double.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_float_to_int.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_float_to_long.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_goto.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_goto_16.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_goto_32.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_if_eq.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_if_eqz.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_if_ge.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_if_gez.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_if_gt.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_if_gtz.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_if_le.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_if_lez.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_if_lt.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_if_ltz.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_if_ne.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_if_nez.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_iget.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_iget_boolean.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_iget_boolean_quick.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_iget_byte.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_iget_byte_quick.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_iget_char.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_iget_char_quick.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_iget_object.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_iget_object_quick.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_iget_quick.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_iget_short.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_iget_short_quick.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_iget_wide.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_iget_wide_quick.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_instance_of.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_int_to_byte.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_int_to_char.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_int_to_double.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_int_to_float.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_int_to_long.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_int_to_short.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_invoke_direct.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_invoke_direct_range.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_invoke_interface.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_invoke_interface_range.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_invoke_static.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_invoke_static_range.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_invoke_super.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_invoke_super_range.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_invoke_virtual.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_invoke_virtual_quick.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_invoke_virtual_range.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_invoke_virtual_range_quick.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_iput.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_iput_boolean.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_iput_boolean_quick.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_iput_byte.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_iput_byte_quick.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_iput_char.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_iput_char_quick.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_iput_object.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_iput_object_quick.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_iput_quick.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_iput_short.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_iput_short_quick.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_iput_wide.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_iput_wide_quick.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_long_to_double.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_long_to_float.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_long_to_int.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_monitor_enter.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_monitor_exit.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_move.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_move_16.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_move_exception.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_move_from16.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_move_object.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_move_object_16.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_move_object_from16.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_move_result.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_move_result_object.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_move_result_wide.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_move_wide.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_move_wide_16.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_move_wide_from16.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_mul_double.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_mul_double_2addr.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_mul_float.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_mul_float_2addr.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_mul_int.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_mul_int_2addr.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_mul_int_lit16.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_mul_int_lit8.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_mul_long.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_mul_long_2addr.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_neg_double.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_neg_float.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_neg_int.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_neg_long.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_new_array.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_new_instance.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_nop.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_not_int.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_not_long.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_or_int.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_or_int_2addr.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_or_int_lit16.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_or_int_lit8.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_or_long.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_or_long_2addr.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_packed_switch.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_rem_double.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_rem_double_2addr.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_rem_float.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_rem_float_2addr.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_rem_int.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_rem_int_2addr.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_rem_int_lit16.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_rem_int_lit8.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_rem_long.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_rem_long_2addr.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_return.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_return_object.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_return_void.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_return_void_no_barrier.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_return_wide.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_rsub_int.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_rsub_int_lit8.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_sget.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_sget_boolean.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_sget_byte.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_sget_char.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_sget_object.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_sget_short.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_sget_wide.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_shl_int.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_shl_int_2addr.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_shl_int_lit8.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_shl_long.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_shl_long_2addr.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_shr_int.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_shr_int_2addr.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_shr_int_lit8.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_shr_long.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_shr_long_2addr.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_sparse_switch.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_sput.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_sput_boolean.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_sput_byte.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_sput_char.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_sput_object.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_sput_short.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_sput_wide.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_sub_double.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_sub_double_2addr.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_sub_float.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_sub_float_2addr.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_sub_int.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_sub_int_2addr.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_sub_long.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_sub_long_2addr.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_throw.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_unused_3e.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_unused_3f.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_unused_40.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_unused_41.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_unused_42.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_unused_43.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_unused_79.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_unused_7a.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_unused_f4.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_unused_fa.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_unused_fb.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_unused_fc.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_unused_fd.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_unused_fe.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_unused_ff.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_ushr_int.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_ushr_int_2addr.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_ushr_int_lit8.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_ushr_long.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_ushr_long_2addr.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_xor_int.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_xor_int_2addr.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_xor_int_lit16.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_xor_int_lit8.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_xor_long.S delete mode 100644 runtime/interpreter/mterp/x86_64/op_xor_long_2addr.S delete mode 100644 runtime/interpreter/mterp/x86_64/shop2addr.S delete mode 100644 runtime/interpreter/mterp/x86_64/sseBinop.S delete mode 100644 runtime/interpreter/mterp/x86_64/sseBinop2Addr.S delete mode 100644 runtime/interpreter/mterp/x86_64/unop.S delete mode 100644 runtime/interpreter/mterp/x86_64/unused.S delete mode 100644 runtime/interpreter/mterp/x86_64/zcmp.S diff --git a/runtime/Android.mk b/runtime/Android.mk index 624f7d78ab..e9f7add1af 100644 --- a/runtime/Android.mk +++ b/runtime/Android.mk @@ -291,8 +291,7 @@ LIBART_TARGET_SRC_FILES_x86 := \ # Note that the fault_handler_x86.cc is not a mistake. This file is # shared between the x86 and x86_64 architectures. LIBART_SRC_FILES_x86_64 := \ - interpreter/mterp/mterp.cc \ - interpreter/mterp/out/mterp_x86_64.S \ + interpreter/mterp/mterp_stub.cc \ arch/x86_64/context_x86_64.cc \ arch/x86_64/entrypoints_init_x86_64.cc \ arch/x86_64/jni_entrypoints_x86_64.S \ diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc index c77daa04bd..4fd3c78f44 100644 --- a/runtime/interpreter/interpreter.cc +++ b/runtime/interpreter/interpreter.cc @@ -241,7 +241,7 @@ static std::ostream& operator<<(std::ostream& os, const InterpreterImplKind& rhs } #if !defined(__clang__) -#if (defined(__arm__) || defined(__i386__) || defined(__aarch64__) || defined(__x86_64__)) +#if (defined(__arm__) || defined(__i386__) || defined(__aarch64__)) // TODO: remove when all targets implemented. static constexpr InterpreterImplKind kInterpreterImplKind = kMterpImplKind; #else @@ -249,7 +249,7 @@ static constexpr InterpreterImplKind kInterpreterImplKind = kComputedGotoImplKin #endif #else // Clang 3.4 fails to build the goto interpreter implementation. -#if (defined(__arm__) || defined(__i386__) || defined(__aarch64__) || defined(__x86_64__)) +#if (defined(__arm__) || defined(__i386__) || defined(__aarch64__)) static constexpr InterpreterImplKind kInterpreterImplKind = kMterpImplKind; #else static constexpr InterpreterImplKind kInterpreterImplKind = kSwitchImplKind; diff --git a/runtime/interpreter/mterp/config_x86_64 b/runtime/interpreter/mterp/config_x86_64 index 1d7eb038bb..a002dc2873 100644 --- a/runtime/interpreter/mterp/config_x86_64 +++ b/runtime/interpreter/mterp/config_x86_64 @@ -19,10 +19,6 @@ handler-style computed-goto handler-size 128 -function-type-format FUNCTION_TYPE(%s) -function-size-format SIZE(%s,%s) -global-name-format SYMBOL(%s) - # source for alternate entry stub asm-alt-stub x86_64/alt_stub.S @@ -40,262 +36,262 @@ op-start x86_64 # (override example:) op OP_SUB_FLOAT_2ADDR arm-vfp # (fallback example:) op OP_SUB_FLOAT_2ADDR FALLBACK - # op op_nop FALLBACK - # op op_move FALLBACK - # op op_move_from16 FALLBACK - # op op_move_16 FALLBACK - # op op_move_wide FALLBACK - # op op_move_wide_from16 FALLBACK - # op op_move_wide_16 FALLBACK - # op op_move_object FALLBACK - # op op_move_object_from16 FALLBACK - # op op_move_object_16 FALLBACK - # op op_move_result FALLBACK - # op op_move_result_wide FALLBACK - # op op_move_result_object FALLBACK - # op op_move_exception FALLBACK - # op op_return_void FALLBACK - # op op_return FALLBACK - # op op_return_wide FALLBACK - # op op_return_object FALLBACK - # op op_const_4 FALLBACK - # op op_const_16 FALLBACK - # op op_const FALLBACK - # op op_const_high16 FALLBACK - # op op_const_wide_16 FALLBACK - # op op_const_wide_32 FALLBACK - # op op_const_wide FALLBACK - # op op_const_wide_high16 FALLBACK - # op op_const_string FALLBACK - # op op_const_string_jumbo FALLBACK - # op op_const_class FALLBACK - # op op_monitor_enter FALLBACK - # op op_monitor_exit FALLBACK - # op op_check_cast FALLBACK - # op op_instance_of FALLBACK - # op op_array_length FALLBACK - # op op_new_instance FALLBACK - # op op_new_array FALLBACK - # op op_filled_new_array FALLBACK - # op op_filled_new_array_range FALLBACK - # op op_fill_array_data FALLBACK - # op op_throw FALLBACK - # op op_goto FALLBACK - # op op_goto_16 FALLBACK - # op op_goto_32 FALLBACK - # op op_packed_switch FALLBACK - # op op_sparse_switch FALLBACK - # op op_cmpl_float FALLBACK - # op op_cmpg_float FALLBACK - # op op_cmpl_double FALLBACK - # op op_cmpg_double FALLBACK - # op op_cmp_long FALLBACK - # op op_if_eq FALLBACK - # op op_if_ne FALLBACK - # op op_if_lt FALLBACK - # op op_if_ge FALLBACK - # op op_if_gt FALLBACK - # op op_if_le FALLBACK - # op op_if_eqz FALLBACK - # op op_if_nez FALLBACK - # op op_if_ltz FALLBACK - # op op_if_gez FALLBACK - # op op_if_gtz FALLBACK - # op op_if_lez FALLBACK - # op op_unused_3e FALLBACK - # op op_unused_3f FALLBACK - # op op_unused_40 FALLBACK - # op op_unused_41 FALLBACK - # op op_unused_42 FALLBACK - # op op_unused_43 FALLBACK - # op op_aget FALLBACK - # op op_aget_wide FALLBACK - # op op_aget_object FALLBACK - # op op_aget_boolean FALLBACK - # op op_aget_byte FALLBACK - # op op_aget_char FALLBACK - # op op_aget_short FALLBACK - # op op_aput FALLBACK - # op op_aput_wide FALLBACK - # op op_aput_object FALLBACK - # op op_aput_boolean FALLBACK - # op op_aput_byte FALLBACK - # op op_aput_char FALLBACK - # op op_aput_short FALLBACK - # op op_iget FALLBACK - # op op_iget_wide FALLBACK - # op op_iget_object FALLBACK - # op op_iget_boolean FALLBACK - # op op_iget_byte FALLBACK - # op op_iget_char FALLBACK - # op op_iget_short FALLBACK - # op op_iput FALLBACK - # op op_iput_wide FALLBACK - # op op_iput_object FALLBACK - # op op_iput_boolean FALLBACK - # op op_iput_byte FALLBACK - # op op_iput_char FALLBACK - # op op_iput_short FALLBACK - # op op_sget FALLBACK - # op op_sget_wide FALLBACK - # op op_sget_object FALLBACK - # op op_sget_boolean FALLBACK - # op op_sget_byte FALLBACK - # op op_sget_char FALLBACK - # op op_sget_short FALLBACK - # op op_sput FALLBACK - # op op_sput_wide FALLBACK - # op op_sput_object FALLBACK - # op op_sput_boolean FALLBACK - # op op_sput_byte FALLBACK - # op op_sput_char FALLBACK - # op op_sput_short FALLBACK - # op op_invoke_virtual FALLBACK - # op op_invoke_super FALLBACK - # op op_invoke_direct FALLBACK - # op op_invoke_static FALLBACK - # op op_invoke_interface FALLBACK - # op op_return_void_no_barrier FALLBACK - # op op_invoke_virtual_range FALLBACK - # op op_invoke_super_range FALLBACK - # op op_invoke_direct_range FALLBACK - # op op_invoke_static_range FALLBACK - # op op_invoke_interface_range FALLBACK - # op op_unused_79 FALLBACK - # op op_unused_7a FALLBACK - # op op_neg_int FALLBACK - # op op_not_int FALLBACK - # op op_neg_long FALLBACK - # op op_not_long FALLBACK - # op op_neg_float FALLBACK - # op op_neg_double FALLBACK - # op op_int_to_long FALLBACK - # op op_int_to_float FALLBACK - # op op_int_to_double FALLBACK - # op op_long_to_int FALLBACK - # op op_long_to_float FALLBACK - # op op_long_to_double FALLBACK - # op op_float_to_int FALLBACK - # op op_float_to_long FALLBACK - # op op_float_to_double FALLBACK - # op op_double_to_int FALLBACK - # op op_double_to_long FALLBACK - # op op_double_to_float FALLBACK - # op op_int_to_byte FALLBACK - # op op_int_to_char FALLBACK - # op op_int_to_short FALLBACK - # op op_add_int FALLBACK - # op op_sub_int FALLBACK - # op op_mul_int FALLBACK - # op op_div_int FALLBACK - # op op_rem_int FALLBACK - # op op_and_int FALLBACK - # op op_or_int FALLBACK - # op op_xor_int FALLBACK - # op op_shl_int FALLBACK - # op op_shr_int FALLBACK - # op op_ushr_int FALLBACK - # op op_add_long FALLBACK - # op op_sub_long FALLBACK - # op op_mul_long FALLBACK - # op op_div_long FALLBACK - # op op_rem_long FALLBACK - # op op_and_long FALLBACK - # op op_or_long FALLBACK - # op op_xor_long FALLBACK - # op op_shl_long FALLBACK - # op op_shr_long FALLBACK - # op op_ushr_long FALLBACK - # op op_add_float FALLBACK - # op op_sub_float FALLBACK - # op op_mul_float FALLBACK - # op op_div_float FALLBACK - # op op_rem_float FALLBACK - # op op_add_double FALLBACK - # op op_sub_double FALLBACK - # op op_mul_double FALLBACK - # op op_div_double FALLBACK - # op op_rem_double FALLBACK - # op op_add_int_2addr FALLBACK - # op op_sub_int_2addr FALLBACK - # op op_mul_int_2addr FALLBACK - # op op_div_int_2addr FALLBACK - # op op_rem_int_2addr FALLBACK - # op op_and_int_2addr FALLBACK - # op op_or_int_2addr FALLBACK - # op op_xor_int_2addr FALLBACK - # op op_shl_int_2addr FALLBACK - # op op_shr_int_2addr FALLBACK - # op op_ushr_int_2addr FALLBACK - # op op_add_long_2addr FALLBACK - # op op_sub_long_2addr FALLBACK - # op op_mul_long_2addr FALLBACK - # op op_div_long_2addr FALLBACK - # op op_rem_long_2addr FALLBACK - # op op_and_long_2addr FALLBACK - # op op_or_long_2addr FALLBACK - # op op_xor_long_2addr FALLBACK - # op op_shl_long_2addr FALLBACK - # op op_shr_long_2addr FALLBACK - # op op_ushr_long_2addr FALLBACK - # op op_add_float_2addr FALLBACK - # op op_sub_float_2addr FALLBACK - # op op_mul_float_2addr FALLBACK - # op op_div_float_2addr FALLBACK - # op op_rem_float_2addr FALLBACK - # op op_add_double_2addr FALLBACK - # op op_sub_double_2addr FALLBACK - # op op_mul_double_2addr FALLBACK - # op op_div_double_2addr FALLBACK - # op op_rem_double_2addr FALLBACK - # op op_add_int_lit16 FALLBACK - # op op_rsub_int FALLBACK - # op op_mul_int_lit16 FALLBACK - # op op_div_int_lit16 FALLBACK - # op op_rem_int_lit16 FALLBACK - # op op_and_int_lit16 FALLBACK - # op op_or_int_lit16 FALLBACK - # op op_xor_int_lit16 FALLBACK - # op op_add_int_lit8 FALLBACK - # op op_rsub_int_lit8 FALLBACK - # op op_mul_int_lit8 FALLBACK - # op op_div_int_lit8 FALLBACK - # op op_rem_int_lit8 FALLBACK - # op op_and_int_lit8 FALLBACK - # op op_or_int_lit8 FALLBACK - # op op_xor_int_lit8 FALLBACK - # op op_shl_int_lit8 FALLBACK - # op op_shr_int_lit8 FALLBACK - # op op_ushr_int_lit8 FALLBACK - # op op_iget_quick FALLBACK - # op op_iget_wide_quick FALLBACK - # op op_iget_object_quick FALLBACK - # op op_iput_quick FALLBACK - # op op_iput_wide_quick FALLBACK - # op op_iput_object_quick FALLBACK - # op op_invoke_virtual_quick FALLBACK - # op op_invoke_virtual_range_quick FALLBACK - # op op_iput_boolean_quick FALLBACK - # op op_iput_byte_quick FALLBACK - # op op_iput_char_quick FALLBACK - # op op_iput_short_quick FALLBACK - # op op_iget_boolean_quick FALLBACK - # op op_iget_byte_quick FALLBACK - # op op_iget_char_quick FALLBACK - # op op_iget_short_quick FALLBACK - op op_invoke_lambda FALLBACK - # op op_unused_f4 FALLBACK - op op_capture_variable FALLBACK - op op_create_lambda FALLBACK - op op_liberate_variable FALLBACK - op op_box_lambda FALLBACK - op op_unbox_lambda FALLBACK - # op op_unused_fa FALLBACK - # op op_unused_fb FALLBACK - # op op_unused_fc FALLBACK - # op op_unused_fd FALLBACK - # op op_unused_fe FALLBACK - # op op_unused_ff FALLBACK + op op_nop FALLBACK + op op_move FALLBACK + op op_move_from16 FALLBACK + op op_move_16 FALLBACK + op op_move_wide FALLBACK + op op_move_wide_from16 FALLBACK + op op_move_wide_16 FALLBACK + op op_move_object FALLBACK + op op_move_object_from16 FALLBACK + op op_move_object_16 FALLBACK + op op_move_result FALLBACK + op op_move_result_wide FALLBACK + op op_move_result_object FALLBACK + op op_move_exception FALLBACK + op op_return_void FALLBACK + op op_return FALLBACK + op op_return_wide FALLBACK + op op_return_object FALLBACK + op op_const_4 FALLBACK + op op_const_16 FALLBACK + op op_const FALLBACK + op op_const_high16 FALLBACK + op op_const_wide_16 FALLBACK + op op_const_wide_32 FALLBACK + op op_const_wide FALLBACK + op op_const_wide_high16 FALLBACK + op op_const_string FALLBACK + op op_const_string_jumbo FALLBACK + op op_const_class FALLBACK + op op_monitor_enter FALLBACK + op op_monitor_exit FALLBACK + op op_check_cast FALLBACK + op op_instance_of FALLBACK + op op_array_length FALLBACK + op op_new_instance FALLBACK + op op_new_array FALLBACK + op op_filled_new_array FALLBACK + op op_filled_new_array_range FALLBACK + op op_fill_array_data FALLBACK + op op_throw FALLBACK + op op_goto FALLBACK + op op_goto_16 FALLBACK + op op_goto_32 FALLBACK + op op_packed_switch FALLBACK + op op_sparse_switch FALLBACK + op op_cmpl_float FALLBACK + op op_cmpg_float FALLBACK + op op_cmpl_double FALLBACK + op op_cmpg_double FALLBACK + op op_cmp_long FALLBACK + op op_if_eq FALLBACK + op op_if_ne FALLBACK + op op_if_lt FALLBACK + op op_if_ge FALLBACK + op op_if_gt FALLBACK + op op_if_le FALLBACK + op op_if_eqz FALLBACK + op op_if_nez FALLBACK + op op_if_ltz FALLBACK + op op_if_gez FALLBACK + op op_if_gtz FALLBACK + op op_if_lez FALLBACK + op_unused_3e FALLBACK + op_unused_3f FALLBACK + op_unused_40 FALLBACK + op_unused_41 FALLBACK + op_unused_42 FALLBACK + op_unused_43 FALLBACK + op op_aget FALLBACK + op op_aget_wide FALLBACK + op op_aget_object FALLBACK + op op_aget_boolean FALLBACK + op op_aget_byte FALLBACK + op op_aget_char FALLBACK + op op_aget_short FALLBACK + op op_aput FALLBACK + op op_aput_wide FALLBACK + op op_aput_object FALLBACK + op op_aput_boolean FALLBACK + op op_aput_byte FALLBACK + op op_aput_char FALLBACK + op op_aput_short FALLBACK + op op_iget FALLBACK + op op_iget_wide FALLBACK + op op_iget_object FALLBACK + op op_iget_boolean FALLBACK + op op_iget_byte FALLBACK + op op_iget_char FALLBACK + op op_iget_short FALLBACK + op op_iput FALLBACK + op op_iput_wide FALLBACK + op op_iput_object FALLBACK + op op_iput_boolean FALLBACK + op op_iput_byte FALLBACK + op op_iput_char FALLBACK + op op_iput_short FALLBACK + op op_sget FALLBACK + op op_sget_wide FALLBACK + op op_sget_object FALLBACK + op op_sget_boolean FALLBACK + op op_sget_byte FALLBACK + op op_sget_char FALLBACK + op op_sget_short FALLBACK + op op_sput FALLBACK + op op_sput_wide FALLBACK + op op_sput_object FALLBACK + op op_sput_boolean FALLBACK + op op_sput_byte FALLBACK + op op_sput_char FALLBACK + op op_sput_short FALLBACK + op op_invoke_virtual FALLBACK + op op_invoke_super FALLBACK + op op_invoke_direct FALLBACK + op op_invoke_static FALLBACK + op op_invoke_interface FALLBACK + op op_return_void_no_barrier FALLBACK + op op_invoke_virtual_range FALLBACK + op op_invoke_super_range FALLBACK + op op_invoke_direct_range FALLBACK + op op_invoke_static_range FALLBACK + op op_invoke_interface_range FALLBACK + op_unused_79 FALLBACK + op_unused_7a FALLBACK + op op_neg_int FALLBACK + op op_not_int FALLBACK + op op_neg_long FALLBACK + op op_not_long FALLBACK + op op_neg_float FALLBACK + op op_neg_double FALLBACK + op op_int_to_long FALLBACK + op op_int_to_float FALLBACK + op op_int_to_double FALLBACK + op op_long_to_int FALLBACK + op op_long_to_float FALLBACK + op op_long_to_double FALLBACK + op op_float_to_int FALLBACK + op op_float_to_long FALLBACK + op op_float_to_double FALLBACK + op op_double_to_int FALLBACK + op op_double_to_long FALLBACK + op op_double_to_float FALLBACK + op op_int_to_byte FALLBACK + op op_int_to_char FALLBACK + op op_int_to_short FALLBACK + op op_add_int FALLBACK + op op_sub_int FALLBACK + op op_mul_int FALLBACK + op op_div_int FALLBACK + op op_rem_int FALLBACK + op op_and_int FALLBACK + op op_or_int FALLBACK + op op_xor_int FALLBACK + op op_shl_int FALLBACK + op op_shr_int FALLBACK + op op_ushr_int FALLBACK + op op_add_long FALLBACK + op op_sub_long FALLBACK + op op_mul_long FALLBACK + op op_div_long FALLBACK + op op_rem_long FALLBACK + op op_and_long FALLBACK + op op_or_long FALLBACK + op op_xor_long FALLBACK + op op_shl_long FALLBACK + op op_shr_long FALLBACK + op op_ushr_long FALLBACK + op op_add_float FALLBACK + op op_sub_float FALLBACK + op op_mul_float FALLBACK + op op_div_float FALLBACK + op op_rem_float FALLBACK + op op_add_double FALLBACK + op op_sub_double FALLBACK + op op_mul_double FALLBACK + op op_div_double FALLBACK + op op_rem_double FALLBACK + op op_add_int_2addr FALLBACK + op op_sub_int_2addr FALLBACK + op op_mul_int_2addr FALLBACK + op op_div_int_2addr FALLBACK + op op_rem_int_2addr FALLBACK + op op_and_int_2addr FALLBACK + op op_or_int_2addr FALLBACK + op op_xor_int_2addr FALLBACK + op op_shl_int_2addr FALLBACK + op op_shr_int_2addr FALLBACK + op op_ushr_int_2addr FALLBACK + op op_add_long_2addr FALLBACK + op op_sub_long_2addr FALLBACK + op op_mul_long_2addr FALLBACK + op op_div_long_2addr FALLBACK + op op_rem_long_2addr FALLBACK + op op_and_long_2addr FALLBACK + op op_or_long_2addr FALLBACK + op op_xor_long_2addr FALLBACK + op op_shl_long_2addr FALLBACK + op op_shr_long_2addr FALLBACK + op op_ushr_long_2addr FALLBACK + op op_add_float_2addr FALLBACK + op op_sub_float_2addr FALLBACK + op op_mul_float_2addr FALLBACK + op op_div_float_2addr FALLBACK + op op_rem_float_2addr FALLBACK + op op_add_double_2addr FALLBACK + op op_sub_double_2addr FALLBACK + op op_mul_double_2addr FALLBACK + op op_div_double_2addr FALLBACK + op op_rem_double_2addr FALLBACK + op op_add_int_lit16 FALLBACK + op op_rsub_int FALLBACK + op op_mul_int_lit16 FALLBACK + op op_div_int_lit16 FALLBACK + op op_rem_int_lit16 FALLBACK + op op_and_int_lit16 FALLBACK + op op_or_int_lit16 FALLBACK + op op_xor_int_lit16 FALLBACK + op op_add_int_lit8 FALLBACK + op op_rsub_int_lit8 FALLBACK + op op_mul_int_lit8 FALLBACK + op op_div_int_lit8 FALLBACK + op op_rem_int_lit8 FALLBACK + op op_and_int_lit8 FALLBACK + op op_or_int_lit8 FALLBACK + op op_xor_int_lit8 FALLBACK + op op_shl_int_lit8 FALLBACK + op op_shr_int_lit8 FALLBACK + op op_ushr_int_lit8 FALLBACK + op op_iget_quick FALLBACK + op op_iget_wide_quick FALLBACK + op op_iget_object_quick FALLBACK + op op_iput_quick FALLBACK + op op_iput_wide_quick FALLBACK + op op_iput_object_quick FALLBACK + op op_invoke_virtual_quick FALLBACK + op op_invoke_virtual_range_quick FALLBACK + op op_iput_boolean_quick FALLBACK + op op_iput_byte_quick FALLBACK + op op_iput_char_quick FALLBACK + op op_iput_short_quick FALLBACK + op op_iget_boolean_quick FALLBACK + op op_iget_byte_quick FALLBACK + op op_iget_char_quick FALLBACK + op op_iget_short_quick FALLBACK + op_unused_f3 FALLBACK + op_unused_f4 FALLBACK + op_unused_f5 FALLBACK + op_unused_f6 FALLBACK + op_unused_f7 FALLBACK + op_unused_f8 FALLBACK + op_unused_f9 FALLBACK + op_unused_fa FALLBACK + op_unused_fb FALLBACK + op_unused_fc FALLBACK + op_unused_fd FALLBACK + op_unused_fe FALLBACK + op_unused_ff FALLBACK op-end # common subroutines for asm diff --git a/runtime/interpreter/mterp/out/mterp_x86_64.S b/runtime/interpreter/mterp/out/mterp_x86_64.S deleted file mode 100644 index 70f47fe837..0000000000 --- a/runtime/interpreter/mterp/out/mterp_x86_64.S +++ /dev/null @@ -1,11955 +0,0 @@ -/* - * This file was generated automatically by gen-mterp.py for 'x86_64'. - * - * --> DO NOT EDIT <-- - */ - -/* File: x86_64/header.S */ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - Art assembly interpreter notes: - - First validate assembly code by implementing ExecuteXXXImpl() style body (doesn't - handle invoke, allows higher-level code to create frame & shadow frame. - - Once that's working, support direct entry code & eliminate shadow frame (and - excess locals allocation. - - Some (hopefully) temporary ugliness. We'll treat rFP as pointing to the - base of the vreg array within the shadow frame. Access the other fields, - dex_pc_, method_ and number_of_vregs_ via negative offsets. For now, we'll continue - the shadow frame mechanism of double-storing object references - via rFP & - number_of_vregs_. - - */ - -/* -x86_64 ABI general notes: - -Caller save set: - rax, rdx, rcx, rsi, rdi, r8-r11, st(0)-st(7) -Callee save set: - rbx, rbp, r12-r15 -Return regs: - 32-bit in eax - 64-bit in rax - fp on xmm0 - -First 8 fp parameters came in xmm0-xmm7. -First 6 non-fp parameters came in rdi, rsi, rdx, rcx, r8, r9. -Other parameters passed on stack, pushed right-to-left. On entry to target, first -param is at 8(%esp). Traditional entry code is: - -Stack must be 16-byte aligned to support SSE in native code. - -If we're not doing variable stack allocation (alloca), the frame pointer can be -eliminated and all arg references adjusted to be esp relative. -*/ - -/* -Mterp and x86_64 notes: - -Some key interpreter variables will be assigned to registers. - - nick reg purpose - rSELF rbp pointer to ThreadSelf. - rPC r12 interpreted program counter, used for fetching instructions - rFP r13 interpreted frame pointer, used for accessing locals and args - rINSTw bx first 16-bit code of current instruction - rINSTbl bl opcode portion of instruction word - rINSTbh bh high byte of inst word, usually contains src/tgt reg names - rIBASE r14 base of instruction handler table - rREFS r15 base of object references in shadow frame. - -Notes: - o High order 16 bits of ebx must be zero on entry to handler - o rPC, rFP, rINSTw/rINSTbl valid on handler entry and exit - o eax and ecx are scratch, rINSTw/ebx sometimes scratch - -Macros are provided for common operations. Each macro MUST emit only -one instruction to make instruction-counting easier. They MUST NOT alter -unspecified registers or condition codes. -*/ - -/* - * This is a #include, not a %include, because we want the C pre-processor - * to expand the macros into assembler assignment statements. - */ -#include "asm_support.h" - -/* - * Handle mac compiler specific - */ -#if defined(__APPLE__) - #define MACRO_LITERAL(value) $(value) - #define FUNCTION_TYPE(name) - #define SIZE(start,end) - // Mac OS' symbols have an _ prefix. - #define SYMBOL(name) _ ## name -#else - #define MACRO_LITERAL(value) $value - #define FUNCTION_TYPE(name) .type name, @function - #define SIZE(start,end) .size start, .-end - #define SYMBOL(name) name -#endif - -/* Frame size must be 16-byte aligned. - * Remember about 8 bytes for return address - */ -#define FRAME_SIZE 56 - -/* Frame diagram while executing ExecuteMterpImpl, high to low addresses */ -#define IN_ARG3 %rcx -#define IN_ARG2 %rdx -#define IN_ARG1 %rsi -#define IN_ARG0 %rdi -#define CALLER_RP (FRAME_SIZE + 0) -/* Spill offsets relative to %esp */ -#define RBX_SPILL (FRAME_SIZE - 8) -#define RBP_SPILL (FRAME_SIZE - 16) -#define R12_SPILL (FRAME_SIZE - 24) -#define R13_SPILL (FRAME_SIZE - 32) -#define R14_SPILL (FRAME_SIZE - 40) -#define R15_SPILL (FRAME_SIZE - 48) -/* Out Arg offsets, relative to %esp */ -#define OUT_ARG3 %rcx -#define OUT_ARG2 %rdx -#define OUT_ARG1 %rsi -#define OUT_ARG0 %rdi -#define OUT_32_ARG3 %ecx -#define OUT_32_ARG2 %edx -#define OUT_32_ARG1 %esi -#define OUT_32_ARG0 %edi -#define OUT_FP_ARG1 %xmm1 -#define OUT_FP_ARG0 %xmm0 - -/* During bringup, we'll use the shadow frame model instead of rFP */ -/* single-purpose registers, given names for clarity */ -#define rSELF %rbp -#define rPC %r12 -#define rFP %r13 -#define rINST %ebx -#define rINSTq %rbx -#define rINSTw %bx -#define rINSTbh %bh -#define rINSTbl %bl -#define rIBASE %r14 -#define rREFS %r15 - -/* - * Instead of holding a pointer to the shadow frame, we keep rFP at the base of the vregs. So, - * to access other shadow frame fields, we need to use a backwards offset. Define those here. - */ -#define OFF_FP(a) (a - SHADOWFRAME_VREGS_OFFSET) -#define OFF_FP_NUMBER_OF_VREGS OFF_FP(SHADOWFRAME_NUMBER_OF_VREGS_OFFSET) -#define OFF_FP_DEX_PC OFF_FP(SHADOWFRAME_DEX_PC_OFFSET) -#define OFF_FP_LINK OFF_FP(SHADOWFRAME_LINK_OFFSET) -#define OFF_FP_METHOD OFF_FP(SHADOWFRAME_METHOD_OFFSET) -#define OFF_FP_RESULT_REGISTER OFF_FP(SHADOWFRAME_RESULT_REGISTER_OFFSET) -#define OFF_FP_DEX_PC_PTR OFF_FP(SHADOWFRAME_DEX_PC_PTR_OFFSET) -#define OFF_FP_CODE_ITEM OFF_FP(SHADOWFRAME_CODE_ITEM_OFFSET) -#define OFF_FP_SHADOWFRAME (-SHADOWFRAME_VREGS_OFFSET) - -/* - * - * The reference interpreter performs explicit suspect checks, which is somewhat wasteful. - * Dalvik's interpreter folded suspend checks into the jump table mechanism, and eventually - * mterp should do so as well. - */ -#define MTERP_SUSPEND 0 - -/* - * "export" the PC to dex_pc field in the shadow frame, f/b/o future exception objects. Must - * be done *before* something throws. - * - * It's okay to do this more than once. - * - * NOTE: the fast interpreter keeps track of dex pc as a direct pointer to the mapped - * dex byte codes. However, the rest of the runtime expects dex pc to be an instruction - * offset into the code_items_[] array. For effiency, we will "export" the - * current dex pc as a direct pointer using the EXPORT_PC macro, and rely on GetDexPC - * to convert to a dex pc when needed. - */ -.macro EXPORT_PC - movq rPC, OFF_FP_DEX_PC_PTR(rFP) -.endm - -/* - * Refresh handler table. - * IBase handles uses the caller save register so we must restore it after each call. - * Also it is used as a result of some 64-bit operations (like imul) and we should - * restore it in such cases also. - * - */ -.macro REFRESH_IBASE - movq THREAD_CURRENT_IBASE_OFFSET(rSELF), rIBASE -.endm - -/* - * Refresh rINST. - * At enter to handler rINST does not contain the opcode number. - * However some utilities require the full value, so this macro - * restores the opcode number. - */ -.macro REFRESH_INST _opnum - movb rINSTbl, rINSTbh - movb $\_opnum, rINSTbl -.endm - -/* - * Fetch the next instruction from rPC into rINSTw. Does not advance rPC. - */ -.macro FETCH_INST - movzwq (rPC), rINSTq -.endm - -/* - * Remove opcode from rINST, compute the address of handler and jump to it. - */ -.macro GOTO_NEXT - movzx rINSTbl,%eax - movzbl rINSTbh,rINST - shll MACRO_LITERAL(7), %eax - addq rIBASE, %rax - jmp *%rax -.endm - -/* - * Advance rPC by instruction count. - */ -.macro ADVANCE_PC _count - leaq 2*\_count(rPC), rPC -.endm - -/* - * Advance rPC by instruction count, fetch instruction and jump to handler. - */ -.macro ADVANCE_PC_FETCH_AND_GOTO_NEXT _count - ADVANCE_PC \_count - FETCH_INST - GOTO_NEXT -.endm - -/* - * Get/set the 32-bit value from a Dalvik register. - */ -#define VREG_ADDRESS(_vreg) (rFP,_vreg,4) -#define VREG_REF_ADDRESS(_vreg) (rREFS,_vreg,4) - -.macro GET_VREG _reg _vreg - movl (rFP,\_vreg,4), \_reg -.endm - -/* Read wide value. */ -.macro GET_WIDE_VREG _reg _vreg - movq (rFP,\_vreg,4), \_reg -.endm - -.macro SET_VREG _reg _vreg - movl \_reg, (rFP,\_vreg,4) - movl MACRO_LITERAL(0), (rREFS,\_vreg,4) -.endm - -/* Write wide value. reg is clobbered. */ -.macro SET_WIDE_VREG _reg _vreg - movq \_reg, (rFP,\_vreg,4) - xorq \_reg, \_reg - movq \_reg, (rREFS,\_vreg,4) -.endm - -.macro SET_VREG_OBJECT _reg _vreg - movl \_reg, (rFP,\_vreg,4) - movl \_reg, (rREFS,\_vreg,4) -.endm - -.macro GET_VREG_HIGH _reg _vreg - movl 4(rFP,\_vreg,4), \_reg -.endm - -.macro SET_VREG_HIGH _reg _vreg - movl \_reg, 4(rFP,\_vreg,4) - movl MACRO_LITERAL(0), 4(rREFS,\_vreg,4) -.endm - -.macro CLEAR_REF _vreg - movl MACRO_LITERAL(0), (rREFS,\_vreg,4) -.endm - -.macro CLEAR_WIDE_REF _vreg - movl MACRO_LITERAL(0), (rREFS,\_vreg,4) - movl MACRO_LITERAL(0), 4(rREFS,\_vreg,4) -.endm - -/* File: x86_64/entry.S */ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* - * Interpreter entry point. - */ - - .text - .global SYMBOL(ExecuteMterpImpl) - FUNCTION_TYPE(ExecuteMterpImpl) - -/* - * On entry: - * 0 Thread* self - * 1 code_item - * 2 ShadowFrame - * 3 JValue* result_register - * - */ - -SYMBOL(ExecuteMterpImpl): - .cfi_startproc - - /* Allocate frame */ - subq $FRAME_SIZE, %rsp - .cfi_adjust_cfa_offset FRAME_SIZE - - /* Spill callee save regs */ - movq %rbx, RBX_SPILL(%rsp) - movq %rbp, RBP_SPILL(%rsp) - movq %r12, R12_SPILL(%rsp) - movq %r13, R13_SPILL(%rsp) - movq %r14, R14_SPILL(%rsp) - movq %r15, R15_SPILL(%rsp) - - /* Remember the return register */ - movq IN_ARG3, SHADOWFRAME_RESULT_REGISTER_OFFSET(IN_ARG2) - - /* Remember the code_item */ - movq IN_ARG1, SHADOWFRAME_CODE_ITEM_OFFSET(IN_ARG2) - - /* set up "named" registers */ - movl SHADOWFRAME_NUMBER_OF_VREGS_OFFSET(IN_ARG2), %eax - leaq SHADOWFRAME_VREGS_OFFSET(IN_ARG2), rFP - leaq (rFP, %rax, 4), rREFS - movl SHADOWFRAME_DEX_PC_OFFSET(IN_ARG2), %eax - leaq CODEITEM_INSNS_OFFSET(IN_ARG1), rPC - leaq (rPC, %rax, 2), rPC - EXPORT_PC - - /* Starting ibase */ - movq IN_ARG0, rSELF - REFRESH_IBASE - - /* start executing the instruction at rPC */ - FETCH_INST - GOTO_NEXT - /* NOTE: no fallthrough */ - - - .global SYMBOL(artMterpAsmInstructionStart) - FUNCTION_TYPE(SYMBOL(artMterpAsmInstructionStart)) -SYMBOL(artMterpAsmInstructionStart) = .L_op_nop - .text - -/* ------------------------------ */ - .balign 128 -.L_op_nop: /* 0x00 */ -/* File: x86_64/op_nop.S */ - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 - -/* ------------------------------ */ - .balign 128 -.L_op_move: /* 0x01 */ -/* File: x86_64/op_move.S */ - /* for move, move-object, long-to-int */ - /* op vA, vB */ - movl rINST, %eax # eax <- BA - andb $0xf, %al # eax <- A - shrl $4, rINST # rINST <- B - GET_VREG %edx, rINSTq - .if 0 - SET_VREG_OBJECT %edx, %rax # fp[A] <- fp[B] - .else - SET_VREG %edx, %rax # fp[A] <- fp[B] - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 - -/* ------------------------------ */ - .balign 128 -.L_op_move_from16: /* 0x02 */ -/* File: x86_64/op_move_from16.S */ - /* for: move/from16, move-object/from16 */ - /* op vAA, vBBBB */ - movzwq 2(rPC), %rax # eax <- BBBB - GET_VREG %edx, %rax # edx <- fp[BBBB] - .if 0 - SET_VREG_OBJECT %edx, rINSTq # fp[A] <- fp[B] - .else - SET_VREG %edx, rINSTq # fp[A] <- fp[B] - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - -/* ------------------------------ */ - .balign 128 -.L_op_move_16: /* 0x03 */ -/* File: x86_64/op_move_16.S */ - /* for: move/16, move-object/16 */ - /* op vAAAA, vBBBB */ - movzwq 4(rPC), %rcx # ecx <- BBBB - movzwq 2(rPC), %rax # eax <- AAAA - GET_VREG %edx, %rcx - .if 0 - SET_VREG_OBJECT %edx, %rax # fp[A] <- fp[B] - .else - SET_VREG %edx, %rax # fp[A] <- fp[B] - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 - -/* ------------------------------ */ - .balign 128 -.L_op_move_wide: /* 0x04 */ -/* File: x86_64/op_move_wide.S */ - /* move-wide vA, vB */ - /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */ - movl rINST, %ecx # ecx <- BA - sarl $4, rINST # rINST <- B - andb $0xf, %cl # ecx <- A - GET_WIDE_VREG %rdx, rINSTq # rdx <- v[B] - SET_WIDE_VREG %rdx, %rcx # v[A] <- rdx - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 - -/* ------------------------------ */ - .balign 128 -.L_op_move_wide_from16: /* 0x05 */ -/* File: x86_64/op_move_wide_from16.S */ - /* move-wide/from16 vAA, vBBBB */ - /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */ - movzwl 2(rPC), %ecx # ecx <- BBBB - GET_WIDE_VREG %rdx, %rcx # rdx <- v[B] - SET_WIDE_VREG %rdx, rINSTq # v[A] <- rdx - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - -/* ------------------------------ */ - .balign 128 -.L_op_move_wide_16: /* 0x06 */ -/* File: x86_64/op_move_wide_16.S */ - /* move-wide/16 vAAAA, vBBBB */ - /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */ - movzwq 4(rPC), %rcx # ecx<- BBBB - movzwq 2(rPC), %rax # eax<- AAAA - GET_WIDE_VREG %rdx, %rcx # rdx <- v[B] - SET_WIDE_VREG %rdx, %rax # v[A] <- rdx - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 - -/* ------------------------------ */ - .balign 128 -.L_op_move_object: /* 0x07 */ -/* File: x86_64/op_move_object.S */ -/* File: x86_64/op_move.S */ - /* for move, move-object, long-to-int */ - /* op vA, vB */ - movl rINST, %eax # eax <- BA - andb $0xf, %al # eax <- A - shrl $4, rINST # rINST <- B - GET_VREG %edx, rINSTq - .if 1 - SET_VREG_OBJECT %edx, %rax # fp[A] <- fp[B] - .else - SET_VREG %edx, %rax # fp[A] <- fp[B] - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 - - -/* ------------------------------ */ - .balign 128 -.L_op_move_object_from16: /* 0x08 */ -/* File: x86_64/op_move_object_from16.S */ -/* File: x86_64/op_move_from16.S */ - /* for: move/from16, move-object/from16 */ - /* op vAA, vBBBB */ - movzwq 2(rPC), %rax # eax <- BBBB - GET_VREG %edx, %rax # edx <- fp[BBBB] - .if 1 - SET_VREG_OBJECT %edx, rINSTq # fp[A] <- fp[B] - .else - SET_VREG %edx, rINSTq # fp[A] <- fp[B] - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_move_object_16: /* 0x09 */ -/* File: x86_64/op_move_object_16.S */ -/* File: x86_64/op_move_16.S */ - /* for: move/16, move-object/16 */ - /* op vAAAA, vBBBB */ - movzwq 4(rPC), %rcx # ecx <- BBBB - movzwq 2(rPC), %rax # eax <- AAAA - GET_VREG %edx, %rcx - .if 1 - SET_VREG_OBJECT %edx, %rax # fp[A] <- fp[B] - .else - SET_VREG %edx, %rax # fp[A] <- fp[B] - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 - - -/* ------------------------------ */ - .balign 128 -.L_op_move_result: /* 0x0a */ -/* File: x86_64/op_move_result.S */ - /* for: move-result, move-result-object */ - /* op vAA */ - movq OFF_FP_RESULT_REGISTER(rFP), %rax # get pointer to result JType. - movl (%rax), %eax # r0 <- result.i. - .if 0 - SET_VREG_OBJECT %eax, rINSTq # fp[A] <- fp[B] - .else - SET_VREG %eax, rINSTq # fp[A] <- fp[B] - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 - -/* ------------------------------ */ - .balign 128 -.L_op_move_result_wide: /* 0x0b */ -/* File: x86_64/op_move_result_wide.S */ - /* move-result-wide vAA */ - movq OFF_FP_RESULT_REGISTER(rFP), %rax # get pointer to result JType. - movq (%rax), %rdx # Get wide - SET_WIDE_VREG %rdx, rINSTq # v[AA] <- rdx - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 - -/* ------------------------------ */ - .balign 128 -.L_op_move_result_object: /* 0x0c */ -/* File: x86_64/op_move_result_object.S */ -/* File: x86_64/op_move_result.S */ - /* for: move-result, move-result-object */ - /* op vAA */ - movq OFF_FP_RESULT_REGISTER(rFP), %rax # get pointer to result JType. - movl (%rax), %eax # r0 <- result.i. - .if 1 - SET_VREG_OBJECT %eax, rINSTq # fp[A] <- fp[B] - .else - SET_VREG %eax, rINSTq # fp[A] <- fp[B] - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 - - -/* ------------------------------ */ - .balign 128 -.L_op_move_exception: /* 0x0d */ -/* File: x86_64/op_move_exception.S */ - /* move-exception vAA */ - movl THREAD_EXCEPTION_OFFSET(rSELF), %eax - SET_VREG_OBJECT %eax, rINSTq # fp[AA] <- exception object - movl $0, THREAD_EXCEPTION_OFFSET(rSELF) - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 - -/* ------------------------------ */ - .balign 128 -.L_op_return_void: /* 0x0e */ -/* File: x86_64/op_return_void.S */ - .extern MterpThreadFenceForConstructor - call SYMBOL(MterpThreadFenceForConstructor) - testl $(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(rSELF) - jz 1f - movq rSELF, OUT_ARG0 - call SYMBOL(MterpSuspendCheck) -1: - xorq %rax, %rax - jmp MterpReturn - -/* ------------------------------ */ - .balign 128 -.L_op_return: /* 0x0f */ -/* File: x86_64/op_return.S */ -/* - * Return a 32-bit value. - * - * for: return, return-object - */ - /* op vAA */ - .extern MterpThreadFenceForConstructor - call SYMBOL(MterpThreadFenceForConstructor) - testl $(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(rSELF) - jz 1f - movq rSELF, OUT_ARG0 - call SYMBOL(MterpSuspendCheck) -1: - GET_VREG %eax, rINSTq # eax <- vAA - jmp MterpReturn - -/* ------------------------------ */ - .balign 128 -.L_op_return_wide: /* 0x10 */ -/* File: x86_64/op_return_wide.S */ -/* - * Return a 64-bit value. - */ - /* return-wide vAA */ - .extern MterpThreadFenceForConstructor - call SYMBOL(MterpThreadFenceForConstructor) - testl $(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(rSELF) - jz 1f - movq rSELF, OUT_ARG0 - call SYMBOL(MterpSuspendCheck) -1: - GET_WIDE_VREG %rax, rINSTq # eax <- v[AA] - jmp MterpReturn - -/* ------------------------------ */ - .balign 128 -.L_op_return_object: /* 0x11 */ -/* File: x86_64/op_return_object.S */ -/* File: x86_64/op_return.S */ -/* - * Return a 32-bit value. - * - * for: return, return-object - */ - /* op vAA */ - .extern MterpThreadFenceForConstructor - call SYMBOL(MterpThreadFenceForConstructor) - testl $(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(rSELF) - jz 1f - movq rSELF, OUT_ARG0 - call SYMBOL(MterpSuspendCheck) -1: - GET_VREG %eax, rINSTq # eax <- vAA - jmp MterpReturn - - -/* ------------------------------ */ - .balign 128 -.L_op_const_4: /* 0x12 */ -/* File: x86_64/op_const_4.S */ - /* const/4 vA, #+B */ - movsbl rINSTbl, %eax # eax <-ssssssBx - movl $0xf, rINST - andl %eax, rINST # rINST <- A - sarl $4, %eax - SET_VREG %eax, rINSTq - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 - -/* ------------------------------ */ - .balign 128 -.L_op_const_16: /* 0x13 */ -/* File: x86_64/op_const_16.S */ - /* const/16 vAA, #+BBBB */ - movswl 2(rPC), %ecx # ecx <- ssssBBBB - SET_VREG %ecx, rINSTq # vAA <- ssssBBBB - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - -/* ------------------------------ */ - .balign 128 -.L_op_const: /* 0x14 */ -/* File: x86_64/op_const.S */ - /* const vAA, #+BBBBbbbb */ - movl 2(rPC), %eax # grab all 32 bits at once - SET_VREG %eax, rINSTq # vAA<- eax - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 - -/* ------------------------------ */ - .balign 128 -.L_op_const_high16: /* 0x15 */ -/* File: x86_64/op_const_high16.S */ - /* const/high16 vAA, #+BBBB0000 */ - movzwl 2(rPC), %eax # eax <- 0000BBBB - sall $16, %eax # eax <- BBBB0000 - SET_VREG %eax, rINSTq # vAA <- eax - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - -/* ------------------------------ */ - .balign 128 -.L_op_const_wide_16: /* 0x16 */ -/* File: x86_64/op_const_wide_16.S */ - /* const-wide/16 vAA, #+BBBB */ - movswq 2(rPC), %rax # rax <- ssssBBBB - SET_WIDE_VREG %rax, rINSTq # store - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - -/* ------------------------------ */ - .balign 128 -.L_op_const_wide_32: /* 0x17 */ -/* File: x86_64/op_const_wide_32.S */ - /* const-wide/32 vAA, #+BBBBbbbb */ - movslq 2(rPC), %rax # eax <- ssssssssBBBBbbbb - SET_WIDE_VREG %rax, rINSTq # store - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 - -/* ------------------------------ */ - .balign 128 -.L_op_const_wide: /* 0x18 */ -/* File: x86_64/op_const_wide.S */ - /* const-wide vAA, #+HHHHhhhhBBBBbbbb */ - movq 2(rPC), %rax # rax <- HHHHhhhhBBBBbbbb - SET_WIDE_VREG %rax, rINSTq - ADVANCE_PC_FETCH_AND_GOTO_NEXT 5 - -/* ------------------------------ */ - .balign 128 -.L_op_const_wide_high16: /* 0x19 */ -/* File: x86_64/op_const_wide_high16.S */ - /* const-wide/high16 vAA, #+BBBB000000000000 */ - movzwq 2(rPC), %rax # eax <- 0000BBBB - salq $48, %rax # eax <- BBBB0000 - SET_WIDE_VREG %rax, rINSTq # v[AA+0] <- eax - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - -/* ------------------------------ */ - .balign 128 -.L_op_const_string: /* 0x1a */ -/* File: x86_64/op_const_string.S */ - /* const/string vAA, String@BBBB */ - EXPORT_PC - movzwq 2(rPC), OUT_ARG0 # OUT_ARG0 <- BBBB - movq rINSTq, OUT_ARG1 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG2 - movq rSELF, OUT_ARG3 - call SYMBOL(MterpConstString) # (index, tgt_reg, shadow_frame, self) - testb %al, %al - jnz MterpPossibleException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - -/* ------------------------------ */ - .balign 128 -.L_op_const_string_jumbo: /* 0x1b */ -/* File: x86_64/op_const_string_jumbo.S */ - /* const/string vAA, String@BBBBBBBB */ - EXPORT_PC - movl 2(rPC), OUT_32_ARG0 # OUT_32_ARG0 <- BBBB - movq rINSTq, OUT_ARG1 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG2 - movq rSELF, OUT_ARG3 - call SYMBOL(MterpConstString) # (index, tgt_reg, shadow_frame, self) - testb %al, %al - jnz MterpPossibleException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 - -/* ------------------------------ */ - .balign 128 -.L_op_const_class: /* 0x1c */ -/* File: x86_64/op_const_class.S */ - /* const/class vAA, Class@BBBB */ - EXPORT_PC - movzwq 2(rPC), OUT_ARG0 # eax <- OUT_ARG0 - movq rINSTq, OUT_ARG1 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG2 - movq rSELF, OUT_ARG3 - call SYMBOL(MterpConstClass) # (index, tgt_reg, shadow_frame, self) - testb %al, %al - jnz MterpPossibleException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - -/* ------------------------------ */ - .balign 128 -.L_op_monitor_enter: /* 0x1d */ -/* File: x86_64/op_monitor_enter.S */ -/* - * Synchronize on an object. - */ - /* monitor-enter vAA */ - EXPORT_PC - GET_VREG OUT_32_ARG0, rINSTq - movq rSELF, OUT_ARG1 - call SYMBOL(artLockObjectFromCode) # (object, self) - testq %rax, %rax - jnz MterpException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 - -/* ------------------------------ */ - .balign 128 -.L_op_monitor_exit: /* 0x1e */ -/* File: x86_64/op_monitor_exit.S */ -/* - * Unlock an object. - * - * Exceptions that occur when unlocking a monitor need to appear as - * if they happened at the following instruction. See the Dalvik - * instruction spec. - */ - /* monitor-exit vAA */ - EXPORT_PC - GET_VREG OUT_32_ARG0, rINSTq - movq rSELF, OUT_ARG1 - call SYMBOL(artUnlockObjectFromCode) # (object, self) - testq %rax, %rax - jnz MterpException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 - -/* ------------------------------ */ - .balign 128 -.L_op_check_cast: /* 0x1f */ -/* File: x86_64/op_check_cast.S */ -/* - * Check to see if a cast from one class to another is allowed. - */ - /* check-cast vAA, class@BBBB */ - EXPORT_PC - movzwq 2(rPC), OUT_ARG0 # OUT_ARG0 <- BBBB - leaq VREG_ADDRESS(rINSTq), OUT_ARG1 - movq OFF_FP_METHOD(rFP), OUT_ARG2 - movq rSELF, OUT_ARG3 - call SYMBOL(MterpCheckCast) # (index, &obj, method, self) - testb %al, %al - jnz MterpPossibleException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - -/* ------------------------------ */ - .balign 128 -.L_op_instance_of: /* 0x20 */ -/* File: x86_64/op_instance_of.S */ -/* - * Check to see if an object reference is an instance of a class. - * - * Most common situation is a non-null object, being compared against - * an already-resolved class. - */ - /* instance-of vA, vB, class@CCCC */ - EXPORT_PC - movzwl 2(rPC), OUT_32_ARG0 # OUT_32_ARG0 <- CCCC - movl rINST, %eax # eax <- BA - sarl $4, %eax # eax <- B - leaq VREG_ADDRESS(%rax), OUT_ARG1 # Get object address - movq OFF_FP_METHOD(rFP), OUT_ARG2 - movq rSELF, OUT_ARG3 - call SYMBOL(MterpInstanceOf) # (index, &obj, method, self) - movsbl %al, %eax - cmpq $0, THREAD_EXCEPTION_OFFSET(rSELF) - jnz MterpException - andb $0xf, rINSTbl # rINSTbl <- A - SET_VREG %eax, rINSTq - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - -/* ------------------------------ */ - .balign 128 -.L_op_array_length: /* 0x21 */ -/* File: x86_64/op_array_length.S */ -/* - * Return the length of an array. - */ - movl rINST, %eax # eax <- BA - sarl $4, rINST # rINST <- B - GET_VREG %ecx, rINSTq # ecx <- vB (object ref) - testl %ecx, %ecx # is null? - je common_errNullObject - andb $0xf, %al # eax <- A - movl MIRROR_ARRAY_LENGTH_OFFSET(%rcx), rINST - SET_VREG rINST, %rax - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 - -/* ------------------------------ */ - .balign 128 -.L_op_new_instance: /* 0x22 */ -/* File: x86_64/op_new_instance.S */ -/* - * Create a new instance of a class. - */ - /* new-instance vAA, class@BBBB */ - EXPORT_PC - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG0 - movq rSELF, OUT_ARG1 - REFRESH_INST 34 - movq rINSTq, OUT_ARG2 - call SYMBOL(MterpNewInstance) - testb %al, %al # 0 means an exception is thrown - jz MterpPossibleException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - -/* ------------------------------ */ - .balign 128 -.L_op_new_array: /* 0x23 */ -/* File: x86_64/op_new_array.S */ -/* - * Allocate an array of objects, specified with the array class - * and a count. - * - * The verifier guarantees that this is an array class, so we don't - * check for it here. - */ - /* new-array vA, vB, class@CCCC */ - EXPORT_PC - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG0 - movq rPC, OUT_ARG1 - REFRESH_INST 35 - movq rINSTq, OUT_ARG2 - movq rSELF, OUT_ARG3 - call SYMBOL(MterpNewArray) - testb %al, %al # 0 means an exception is thrown - jz MterpPossibleException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - -/* ------------------------------ */ - .balign 128 -.L_op_filled_new_array: /* 0x24 */ -/* File: x86_64/op_filled_new_array.S */ -/* - * Create a new array with elements filled from registers. - * - * for: filled-new-array, filled-new-array/range - */ - /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ - /* op {vCCCC..v(CCCC+AA-1)}, type@BBBB */ - .extern MterpFilledNewArray - EXPORT_PC - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG0 - movq rPC, OUT_ARG1 - movq rSELF, OUT_ARG2 - call SYMBOL(MterpFilledNewArray) - testb %al, %al # 0 means an exception is thrown - jz MterpPossibleException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 - -/* ------------------------------ */ - .balign 128 -.L_op_filled_new_array_range: /* 0x25 */ -/* File: x86_64/op_filled_new_array_range.S */ -/* File: x86_64/op_filled_new_array.S */ -/* - * Create a new array with elements filled from registers. - * - * for: filled-new-array, filled-new-array/range - */ - /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ - /* op {vCCCC..v(CCCC+AA-1)}, type@BBBB */ - .extern MterpFilledNewArrayRange - EXPORT_PC - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG0 - movq rPC, OUT_ARG1 - movq rSELF, OUT_ARG2 - call SYMBOL(MterpFilledNewArrayRange) - testb %al, %al # 0 means an exception is thrown - jz MterpPossibleException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 - - -/* ------------------------------ */ - .balign 128 -.L_op_fill_array_data: /* 0x26 */ -/* File: x86_64/op_fill_array_data.S */ - /* fill-array-data vAA, +BBBBBBBB */ - EXPORT_PC - movl 2(rPC), %ecx # ecx <- BBBBbbbb - leaq (rPC,%rcx,2), OUT_ARG1 # OUT_ARG1 <- PC + BBBBbbbb*2 - GET_VREG OUT_32_ARG0, rINSTq # OUT_ARG0 <- vAA (array object) - call SYMBOL(MterpFillArrayData) # (obj, payload) - testb %al, %al # 0 means an exception is thrown - jz MterpPossibleException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 - -/* ------------------------------ */ - .balign 128 -.L_op_throw: /* 0x27 */ -/* File: x86_64/op_throw.S */ -/* - * Throw an exception object in the current thread. - */ - /* throw vAA */ - EXPORT_PC - GET_VREG %eax, rINSTq # eax<- vAA (exception object) - testb %al, %al - jz common_errNullObject - movq %rax, THREAD_EXCEPTION_OFFSET(rSELF) - jmp MterpException - -/* ------------------------------ */ - .balign 128 -.L_op_goto: /* 0x28 */ -/* File: x86_64/op_goto.S */ -/* - * Unconditional branch, 8-bit offset. - * - * The branch distance is a signed code-unit offset, which we need to - * double to get a byte offset. - */ - /* goto +AA */ - movsbq rINSTbl, %rax # rax <- ssssssAA - addq %rax, %rax # rax <- AA * 2 - leaq (rPC, %rax), rPC - FETCH_INST - jg 1f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -1: - GOTO_NEXT - -/* ------------------------------ */ - .balign 128 -.L_op_goto_16: /* 0x29 */ -/* File: x86_64/op_goto_16.S */ -/* - * Unconditional branch, 16-bit offset. - * - * The branch distance is a signed code-unit offset, which we need to - * double to get a byte offset. - */ - /* goto/16 +AAAA */ - movswq 2(rPC), %rax # rax <- ssssAAAA - addq %rax, %rax # rax <- AA * 2 - leaq (rPC, %rax), rPC - FETCH_INST - jg 1f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -1: - GOTO_NEXT - -/* ------------------------------ */ - .balign 128 -.L_op_goto_32: /* 0x2a */ -/* File: x86_64/op_goto_32.S */ -/* - * Unconditional branch, 32-bit offset. - * - * The branch distance is a signed code-unit offset, which we need to - * double to get a byte offset. - * - * Because we need the SF bit set, we'll use an adds - * to convert from Dalvik offset to byte offset. - */ - /* goto/32 +AAAAAAAA */ - movslq 2(rPC), %rax # rax <- AAAAAAAA - addq %rax, %rax # rax <- AA * 2 - leaq (rPC, %rax), rPC - FETCH_INST - jg 1f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -1: - GOTO_NEXT - -/* ------------------------------ */ - .balign 128 -.L_op_packed_switch: /* 0x2b */ -/* File: x86_64/op_packed_switch.S */ -/* - * Handle a packed-switch or sparse-switch instruction. In both cases - * we decode it and hand it off to a helper function. - * - * We don't really expect backward branches in a switch statement, but - * they're perfectly legal, so we check for them here. - * - * for: packed-switch, sparse-switch - */ - /* op vAA, +BBBB */ - movslq 2(rPC), OUT_ARG0 # rcx <- BBBBbbbb - leaq (rPC,OUT_ARG0,2), OUT_ARG0 # rcx <- PC + BBBBbbbb*2 - GET_VREG OUT_32_ARG1, rINSTq # eax <- vAA - call SYMBOL(MterpDoPackedSwitch) - addl %eax, %eax - movslq %eax, %rax - leaq (rPC, %rax), rPC - FETCH_INST - jg 1f -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -1: - GOTO_NEXT - -/* ------------------------------ */ - .balign 128 -.L_op_sparse_switch: /* 0x2c */ -/* File: x86_64/op_sparse_switch.S */ -/* File: x86_64/op_packed_switch.S */ -/* - * Handle a packed-switch or sparse-switch instruction. In both cases - * we decode it and hand it off to a helper function. - * - * We don't really expect backward branches in a switch statement, but - * they're perfectly legal, so we check for them here. - * - * for: packed-switch, sparse-switch - */ - /* op vAA, +BBBB */ - movslq 2(rPC), OUT_ARG0 # rcx <- BBBBbbbb - leaq (rPC,OUT_ARG0,2), OUT_ARG0 # rcx <- PC + BBBBbbbb*2 - GET_VREG OUT_32_ARG1, rINSTq # eax <- vAA - call SYMBOL(MterpDoSparseSwitch) - addl %eax, %eax - movslq %eax, %rax - leaq (rPC, %rax), rPC - FETCH_INST - jg 1f -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -1: - GOTO_NEXT - - -/* ------------------------------ */ - .balign 128 -.L_op_cmpl_float: /* 0x2d */ -/* File: x86_64/op_cmpl_float.S */ -/* File: x86_64/fpcmp.S */ -/* - * Compare two floating-point values. Puts 0, 1, or -1 into the - * destination register based on the results of the comparison. - * - * int compare(x, y) { - * if (x == y) { - * return 0; - * } else if (x < y) { - * return -1; - * } else if (x > y) { - * return 1; - * } else { - * return nanval ? 1 : -1; - * } - * } - */ - /* op vAA, vBB, vCC */ - movzbq 3(rPC), %rcx # ecx<- CC - movzbq 2(rPC), %rax # eax<- BB - movss VREG_ADDRESS(%rax), %xmm0 - xor %eax, %eax - ucomiss VREG_ADDRESS(%rcx), %xmm0 - jp .Lop_cmpl_float_nan_is_neg - je .Lop_cmpl_float_finish - jb .Lop_cmpl_float_less -.Lop_cmpl_float_nan_is_pos: - addb $1, %al - jmp .Lop_cmpl_float_finish -.Lop_cmpl_float_nan_is_neg: -.Lop_cmpl_float_less: - movl $-1, %eax -.Lop_cmpl_float_finish: - SET_VREG %eax, rINSTq - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_cmpg_float: /* 0x2e */ -/* File: x86_64/op_cmpg_float.S */ -/* File: x86_64/fpcmp.S */ -/* - * Compare two floating-point values. Puts 0, 1, or -1 into the - * destination register based on the results of the comparison. - * - * int compare(x, y) { - * if (x == y) { - * return 0; - * } else if (x < y) { - * return -1; - * } else if (x > y) { - * return 1; - * } else { - * return nanval ? 1 : -1; - * } - * } - */ - /* op vAA, vBB, vCC */ - movzbq 3(rPC), %rcx # ecx<- CC - movzbq 2(rPC), %rax # eax<- BB - movss VREG_ADDRESS(%rax), %xmm0 - xor %eax, %eax - ucomiss VREG_ADDRESS(%rcx), %xmm0 - jp .Lop_cmpg_float_nan_is_pos - je .Lop_cmpg_float_finish - jb .Lop_cmpg_float_less -.Lop_cmpg_float_nan_is_pos: - addb $1, %al - jmp .Lop_cmpg_float_finish -.Lop_cmpg_float_nan_is_neg: -.Lop_cmpg_float_less: - movl $-1, %eax -.Lop_cmpg_float_finish: - SET_VREG %eax, rINSTq - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_cmpl_double: /* 0x2f */ -/* File: x86_64/op_cmpl_double.S */ -/* File: x86_64/fpcmp.S */ -/* - * Compare two floating-point values. Puts 0, 1, or -1 into the - * destination register based on the results of the comparison. - * - * int compare(x, y) { - * if (x == y) { - * return 0; - * } else if (x < y) { - * return -1; - * } else if (x > y) { - * return 1; - * } else { - * return nanval ? 1 : -1; - * } - * } - */ - /* op vAA, vBB, vCC */ - movzbq 3(rPC), %rcx # ecx<- CC - movzbq 2(rPC), %rax # eax<- BB - movsd VREG_ADDRESS(%rax), %xmm0 - xor %eax, %eax - ucomisd VREG_ADDRESS(%rcx), %xmm0 - jp .Lop_cmpl_double_nan_is_neg - je .Lop_cmpl_double_finish - jb .Lop_cmpl_double_less -.Lop_cmpl_double_nan_is_pos: - addb $1, %al - jmp .Lop_cmpl_double_finish -.Lop_cmpl_double_nan_is_neg: -.Lop_cmpl_double_less: - movl $-1, %eax -.Lop_cmpl_double_finish: - SET_VREG %eax, rINSTq - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_cmpg_double: /* 0x30 */ -/* File: x86_64/op_cmpg_double.S */ -/* File: x86_64/fpcmp.S */ -/* - * Compare two floating-point values. Puts 0, 1, or -1 into the - * destination register based on the results of the comparison. - * - * int compare(x, y) { - * if (x == y) { - * return 0; - * } else if (x < y) { - * return -1; - * } else if (x > y) { - * return 1; - * } else { - * return nanval ? 1 : -1; - * } - * } - */ - /* op vAA, vBB, vCC */ - movzbq 3(rPC), %rcx # ecx<- CC - movzbq 2(rPC), %rax # eax<- BB - movsd VREG_ADDRESS(%rax), %xmm0 - xor %eax, %eax - ucomisd VREG_ADDRESS(%rcx), %xmm0 - jp .Lop_cmpg_double_nan_is_pos - je .Lop_cmpg_double_finish - jb .Lop_cmpg_double_less -.Lop_cmpg_double_nan_is_pos: - addb $1, %al - jmp .Lop_cmpg_double_finish -.Lop_cmpg_double_nan_is_neg: -.Lop_cmpg_double_less: - movl $-1, %eax -.Lop_cmpg_double_finish: - SET_VREG %eax, rINSTq - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_cmp_long: /* 0x31 */ -/* File: x86_64/op_cmp_long.S */ -/* - * Compare two 64-bit values. Puts 0, 1, or -1 into the destination - * register based on the results of the comparison. - */ - /* cmp-long vAA, vBB, vCC */ - movzbq 2(rPC), %rdx # edx <- BB - movzbq 3(rPC), %rcx # ecx <- CC - GET_WIDE_VREG %rdx, %rdx # rdx <- v[BB] - xorl %eax, %eax - xorl %edi, %edi - addb $1, %al - movl $-1, %esi - cmpq VREG_ADDRESS(%rcx), %rdx - cmovl %esi, %edi - cmovg %eax, %edi - SET_VREG %edi, rINSTq - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - -/* ------------------------------ */ - .balign 128 -.L_op_if_eq: /* 0x32 */ -/* File: x86_64/op_if_eq.S */ -/* File: x86_64/bincmp.S */ -/* - * Generic two-operand compare-and-branch operation. Provide a "revcmp" - * fragment that specifies the *reverse* comparison to perform, e.g. - * for "if-le" you would use "gt". - * - * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le - */ - /* if-cmp vA, vB, +CCCC */ - movl rINST, %ecx # rcx <- A+ - sarl $4, rINST # rINST <- B - andb $0xf, %cl # rcx <- A - GET_VREG %eax, %rcx # eax <- vA - cmpl VREG_ADDRESS(rINSTq), %eax # compare (vA, vB) - movl $2, %eax # assume not taken - jne 1f - movswq 2(rPC),%rax # Get signed branch offset -1: - addq %rax, %rax # rax <- AA * 2 - leaq (rPC, %rax), rPC - FETCH_INST - jg 2f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -2: - GOTO_NEXT - - -/* ------------------------------ */ - .balign 128 -.L_op_if_ne: /* 0x33 */ -/* File: x86_64/op_if_ne.S */ -/* File: x86_64/bincmp.S */ -/* - * Generic two-operand compare-and-branch operation. Provide a "revcmp" - * fragment that specifies the *reverse* comparison to perform, e.g. - * for "if-le" you would use "gt". - * - * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le - */ - /* if-cmp vA, vB, +CCCC */ - movl rINST, %ecx # rcx <- A+ - sarl $4, rINST # rINST <- B - andb $0xf, %cl # rcx <- A - GET_VREG %eax, %rcx # eax <- vA - cmpl VREG_ADDRESS(rINSTq), %eax # compare (vA, vB) - movl $2, %eax # assume not taken - je 1f - movswq 2(rPC),%rax # Get signed branch offset -1: - addq %rax, %rax # rax <- AA * 2 - leaq (rPC, %rax), rPC - FETCH_INST - jg 2f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -2: - GOTO_NEXT - - -/* ------------------------------ */ - .balign 128 -.L_op_if_lt: /* 0x34 */ -/* File: x86_64/op_if_lt.S */ -/* File: x86_64/bincmp.S */ -/* - * Generic two-operand compare-and-branch operation. Provide a "revcmp" - * fragment that specifies the *reverse* comparison to perform, e.g. - * for "if-le" you would use "gt". - * - * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le - */ - /* if-cmp vA, vB, +CCCC */ - movl rINST, %ecx # rcx <- A+ - sarl $4, rINST # rINST <- B - andb $0xf, %cl # rcx <- A - GET_VREG %eax, %rcx # eax <- vA - cmpl VREG_ADDRESS(rINSTq), %eax # compare (vA, vB) - movl $2, %eax # assume not taken - jge 1f - movswq 2(rPC),%rax # Get signed branch offset -1: - addq %rax, %rax # rax <- AA * 2 - leaq (rPC, %rax), rPC - FETCH_INST - jg 2f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -2: - GOTO_NEXT - - -/* ------------------------------ */ - .balign 128 -.L_op_if_ge: /* 0x35 */ -/* File: x86_64/op_if_ge.S */ -/* File: x86_64/bincmp.S */ -/* - * Generic two-operand compare-and-branch operation. Provide a "revcmp" - * fragment that specifies the *reverse* comparison to perform, e.g. - * for "if-le" you would use "gt". - * - * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le - */ - /* if-cmp vA, vB, +CCCC */ - movl rINST, %ecx # rcx <- A+ - sarl $4, rINST # rINST <- B - andb $0xf, %cl # rcx <- A - GET_VREG %eax, %rcx # eax <- vA - cmpl VREG_ADDRESS(rINSTq), %eax # compare (vA, vB) - movl $2, %eax # assume not taken - jl 1f - movswq 2(rPC),%rax # Get signed branch offset -1: - addq %rax, %rax # rax <- AA * 2 - leaq (rPC, %rax), rPC - FETCH_INST - jg 2f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -2: - GOTO_NEXT - - -/* ------------------------------ */ - .balign 128 -.L_op_if_gt: /* 0x36 */ -/* File: x86_64/op_if_gt.S */ -/* File: x86_64/bincmp.S */ -/* - * Generic two-operand compare-and-branch operation. Provide a "revcmp" - * fragment that specifies the *reverse* comparison to perform, e.g. - * for "if-le" you would use "gt". - * - * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le - */ - /* if-cmp vA, vB, +CCCC */ - movl rINST, %ecx # rcx <- A+ - sarl $4, rINST # rINST <- B - andb $0xf, %cl # rcx <- A - GET_VREG %eax, %rcx # eax <- vA - cmpl VREG_ADDRESS(rINSTq), %eax # compare (vA, vB) - movl $2, %eax # assume not taken - jle 1f - movswq 2(rPC),%rax # Get signed branch offset -1: - addq %rax, %rax # rax <- AA * 2 - leaq (rPC, %rax), rPC - FETCH_INST - jg 2f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -2: - GOTO_NEXT - - -/* ------------------------------ */ - .balign 128 -.L_op_if_le: /* 0x37 */ -/* File: x86_64/op_if_le.S */ -/* File: x86_64/bincmp.S */ -/* - * Generic two-operand compare-and-branch operation. Provide a "revcmp" - * fragment that specifies the *reverse* comparison to perform, e.g. - * for "if-le" you would use "gt". - * - * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le - */ - /* if-cmp vA, vB, +CCCC */ - movl rINST, %ecx # rcx <- A+ - sarl $4, rINST # rINST <- B - andb $0xf, %cl # rcx <- A - GET_VREG %eax, %rcx # eax <- vA - cmpl VREG_ADDRESS(rINSTq), %eax # compare (vA, vB) - movl $2, %eax # assume not taken - jg 1f - movswq 2(rPC),%rax # Get signed branch offset -1: - addq %rax, %rax # rax <- AA * 2 - leaq (rPC, %rax), rPC - FETCH_INST - jg 2f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -2: - GOTO_NEXT - - -/* ------------------------------ */ - .balign 128 -.L_op_if_eqz: /* 0x38 */ -/* File: x86_64/op_if_eqz.S */ -/* File: x86_64/zcmp.S */ -/* - * Generic one-operand compare-and-branch operation. Provide a "revcmp" - * fragment that specifies the *reverse* comparison to perform, e.g. - * for "if-le" you would use "gt". - * - * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez - */ - /* if-cmp vAA, +BBBB */ - cmpl $0, VREG_ADDRESS(rINSTq) # compare (vA, 0) - movl $2, %eax # assume branch not taken - jne 1f - movswq 2(rPC),%rax # fetch signed displacement -1: - addq %rax, %rax # eax <- AA * 2 - leaq (rPC, %rax), rPC - FETCH_INST - jg 2f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -2: - GOTO_NEXT - - -/* ------------------------------ */ - .balign 128 -.L_op_if_nez: /* 0x39 */ -/* File: x86_64/op_if_nez.S */ -/* File: x86_64/zcmp.S */ -/* - * Generic one-operand compare-and-branch operation. Provide a "revcmp" - * fragment that specifies the *reverse* comparison to perform, e.g. - * for "if-le" you would use "gt". - * - * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez - */ - /* if-cmp vAA, +BBBB */ - cmpl $0, VREG_ADDRESS(rINSTq) # compare (vA, 0) - movl $2, %eax # assume branch not taken - je 1f - movswq 2(rPC),%rax # fetch signed displacement -1: - addq %rax, %rax # eax <- AA * 2 - leaq (rPC, %rax), rPC - FETCH_INST - jg 2f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -2: - GOTO_NEXT - - -/* ------------------------------ */ - .balign 128 -.L_op_if_ltz: /* 0x3a */ -/* File: x86_64/op_if_ltz.S */ -/* File: x86_64/zcmp.S */ -/* - * Generic one-operand compare-and-branch operation. Provide a "revcmp" - * fragment that specifies the *reverse* comparison to perform, e.g. - * for "if-le" you would use "gt". - * - * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez - */ - /* if-cmp vAA, +BBBB */ - cmpl $0, VREG_ADDRESS(rINSTq) # compare (vA, 0) - movl $2, %eax # assume branch not taken - jge 1f - movswq 2(rPC),%rax # fetch signed displacement -1: - addq %rax, %rax # eax <- AA * 2 - leaq (rPC, %rax), rPC - FETCH_INST - jg 2f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -2: - GOTO_NEXT - - -/* ------------------------------ */ - .balign 128 -.L_op_if_gez: /* 0x3b */ -/* File: x86_64/op_if_gez.S */ -/* File: x86_64/zcmp.S */ -/* - * Generic one-operand compare-and-branch operation. Provide a "revcmp" - * fragment that specifies the *reverse* comparison to perform, e.g. - * for "if-le" you would use "gt". - * - * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez - */ - /* if-cmp vAA, +BBBB */ - cmpl $0, VREG_ADDRESS(rINSTq) # compare (vA, 0) - movl $2, %eax # assume branch not taken - jl 1f - movswq 2(rPC),%rax # fetch signed displacement -1: - addq %rax, %rax # eax <- AA * 2 - leaq (rPC, %rax), rPC - FETCH_INST - jg 2f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -2: - GOTO_NEXT - - -/* ------------------------------ */ - .balign 128 -.L_op_if_gtz: /* 0x3c */ -/* File: x86_64/op_if_gtz.S */ -/* File: x86_64/zcmp.S */ -/* - * Generic one-operand compare-and-branch operation. Provide a "revcmp" - * fragment that specifies the *reverse* comparison to perform, e.g. - * for "if-le" you would use "gt". - * - * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez - */ - /* if-cmp vAA, +BBBB */ - cmpl $0, VREG_ADDRESS(rINSTq) # compare (vA, 0) - movl $2, %eax # assume branch not taken - jle 1f - movswq 2(rPC),%rax # fetch signed displacement -1: - addq %rax, %rax # eax <- AA * 2 - leaq (rPC, %rax), rPC - FETCH_INST - jg 2f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -2: - GOTO_NEXT - - -/* ------------------------------ */ - .balign 128 -.L_op_if_lez: /* 0x3d */ -/* File: x86_64/op_if_lez.S */ -/* File: x86_64/zcmp.S */ -/* - * Generic one-operand compare-and-branch operation. Provide a "revcmp" - * fragment that specifies the *reverse* comparison to perform, e.g. - * for "if-le" you would use "gt". - * - * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez - */ - /* if-cmp vAA, +BBBB */ - cmpl $0, VREG_ADDRESS(rINSTq) # compare (vA, 0) - movl $2, %eax # assume branch not taken - jg 1f - movswq 2(rPC),%rax # fetch signed displacement -1: - addq %rax, %rax # eax <- AA * 2 - leaq (rPC, %rax), rPC - FETCH_INST - jg 2f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -2: - GOTO_NEXT - - -/* ------------------------------ */ - .balign 128 -.L_op_unused_3e: /* 0x3e */ -/* File: x86_64/op_unused_3e.S */ -/* File: x86_64/unused.S */ -/* - * Bail to reference interpreter to throw. - */ - jmp MterpFallback - - -/* ------------------------------ */ - .balign 128 -.L_op_unused_3f: /* 0x3f */ -/* File: x86_64/op_unused_3f.S */ -/* File: x86_64/unused.S */ -/* - * Bail to reference interpreter to throw. - */ - jmp MterpFallback - - -/* ------------------------------ */ - .balign 128 -.L_op_unused_40: /* 0x40 */ -/* File: x86_64/op_unused_40.S */ -/* File: x86_64/unused.S */ -/* - * Bail to reference interpreter to throw. - */ - jmp MterpFallback - - -/* ------------------------------ */ - .balign 128 -.L_op_unused_41: /* 0x41 */ -/* File: x86_64/op_unused_41.S */ -/* File: x86_64/unused.S */ -/* - * Bail to reference interpreter to throw. - */ - jmp MterpFallback - - -/* ------------------------------ */ - .balign 128 -.L_op_unused_42: /* 0x42 */ -/* File: x86_64/op_unused_42.S */ -/* File: x86_64/unused.S */ -/* - * Bail to reference interpreter to throw. - */ - jmp MterpFallback - - -/* ------------------------------ */ - .balign 128 -.L_op_unused_43: /* 0x43 */ -/* File: x86_64/op_unused_43.S */ -/* File: x86_64/unused.S */ -/* - * Bail to reference interpreter to throw. - */ - jmp MterpFallback - - -/* ------------------------------ */ - .balign 128 -.L_op_aget: /* 0x44 */ -/* File: x86_64/op_aget.S */ -/* - * Array get, 32 bits or less. vAA <- vBB[vCC]. - * - * for: aget, aget-boolean, aget-byte, aget-char, aget-short, aget-wide - * - */ - /* op vAA, vBB, vCC */ - movzbq 2(rPC), %rax # eax <- BB - movzbq 3(rPC), %rcx # ecx <- CC - GET_VREG %eax, %rax # eax <- vBB (array object) - GET_VREG %ecx, %rcx # ecx <- vCC (requested index) - testl %eax, %eax # null array object? - je common_errNullObject # bail if so - cmpl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ecx - jae common_errArrayIndex # index >= length, bail. - .if 0 - movq MIRROR_INT_ARRAY_DATA_OFFSET(%rax,%rcx,8), %rax - SET_WIDE_VREG %rax, rINSTq - .else - movl MIRROR_INT_ARRAY_DATA_OFFSET(%rax,%rcx,4), %eax - SET_VREG %eax, rINSTq - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - -/* ------------------------------ */ - .balign 128 -.L_op_aget_wide: /* 0x45 */ -/* File: x86_64/op_aget_wide.S */ -/* File: x86_64/op_aget.S */ -/* - * Array get, 32 bits or less. vAA <- vBB[vCC]. - * - * for: aget, aget-boolean, aget-byte, aget-char, aget-short, aget-wide - * - */ - /* op vAA, vBB, vCC */ - movzbq 2(rPC), %rax # eax <- BB - movzbq 3(rPC), %rcx # ecx <- CC - GET_VREG %eax, %rax # eax <- vBB (array object) - GET_VREG %ecx, %rcx # ecx <- vCC (requested index) - testl %eax, %eax # null array object? - je common_errNullObject # bail if so - cmpl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ecx - jae common_errArrayIndex # index >= length, bail. - .if 1 - movq MIRROR_WIDE_ARRAY_DATA_OFFSET(%rax,%rcx,8), %rax - SET_WIDE_VREG %rax, rINSTq - .else - movq MIRROR_WIDE_ARRAY_DATA_OFFSET(%rax,%rcx,8), %eax - SET_VREG %eax, rINSTq - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_aget_object: /* 0x46 */ -/* File: x86_64/op_aget_object.S */ -/* - * Array object get. vAA <- vBB[vCC]. - * - * for: aget-object - */ - /* op vAA, vBB, vCC */ - movzbq 2(rPC), %rax # rax <- BB - movzbq 3(rPC), %rcx # rcx <- CC - GET_VREG OUT_32_ARG0, %rax # eax <- vBB (array object) - GET_VREG OUT_32_ARG1, %rcx # ecx <- vCC (requested index) - EXPORT_PC - call SYMBOL(artAGetObjectFromMterp) # (array, index) - cmpq $0, THREAD_EXCEPTION_OFFSET(rSELF) - jnz MterpException - SET_VREG_OBJECT %eax, rINSTq - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - -/* ------------------------------ */ - .balign 128 -.L_op_aget_boolean: /* 0x47 */ -/* File: x86_64/op_aget_boolean.S */ -/* File: x86_64/op_aget.S */ -/* - * Array get, 32 bits or less. vAA <- vBB[vCC]. - * - * for: aget, aget-boolean, aget-byte, aget-char, aget-short, aget-wide - * - */ - /* op vAA, vBB, vCC */ - movzbq 2(rPC), %rax # eax <- BB - movzbq 3(rPC), %rcx # ecx <- CC - GET_VREG %eax, %rax # eax <- vBB (array object) - GET_VREG %ecx, %rcx # ecx <- vCC (requested index) - testl %eax, %eax # null array object? - je common_errNullObject # bail if so - cmpl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ecx - jae common_errArrayIndex # index >= length, bail. - .if 0 - movq MIRROR_BOOLEAN_ARRAY_DATA_OFFSET(%rax,%rcx,8), %rax - SET_WIDE_VREG %rax, rINSTq - .else - movzbl MIRROR_BOOLEAN_ARRAY_DATA_OFFSET(%rax,%rcx,1), %eax - SET_VREG %eax, rINSTq - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_aget_byte: /* 0x48 */ -/* File: x86_64/op_aget_byte.S */ -/* File: x86_64/op_aget.S */ -/* - * Array get, 32 bits or less. vAA <- vBB[vCC]. - * - * for: aget, aget-boolean, aget-byte, aget-char, aget-short, aget-wide - * - */ - /* op vAA, vBB, vCC */ - movzbq 2(rPC), %rax # eax <- BB - movzbq 3(rPC), %rcx # ecx <- CC - GET_VREG %eax, %rax # eax <- vBB (array object) - GET_VREG %ecx, %rcx # ecx <- vCC (requested index) - testl %eax, %eax # null array object? - je common_errNullObject # bail if so - cmpl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ecx - jae common_errArrayIndex # index >= length, bail. - .if 0 - movq MIRROR_BYTE_ARRAY_DATA_OFFSET(%rax,%rcx,8), %rax - SET_WIDE_VREG %rax, rINSTq - .else - movsbl MIRROR_BYTE_ARRAY_DATA_OFFSET(%rax,%rcx,1), %eax - SET_VREG %eax, rINSTq - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_aget_char: /* 0x49 */ -/* File: x86_64/op_aget_char.S */ -/* File: x86_64/op_aget.S */ -/* - * Array get, 32 bits or less. vAA <- vBB[vCC]. - * - * for: aget, aget-boolean, aget-byte, aget-char, aget-short, aget-wide - * - */ - /* op vAA, vBB, vCC */ - movzbq 2(rPC), %rax # eax <- BB - movzbq 3(rPC), %rcx # ecx <- CC - GET_VREG %eax, %rax # eax <- vBB (array object) - GET_VREG %ecx, %rcx # ecx <- vCC (requested index) - testl %eax, %eax # null array object? - je common_errNullObject # bail if so - cmpl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ecx - jae common_errArrayIndex # index >= length, bail. - .if 0 - movq MIRROR_CHAR_ARRAY_DATA_OFFSET(%rax,%rcx,8), %rax - SET_WIDE_VREG %rax, rINSTq - .else - movzwl MIRROR_CHAR_ARRAY_DATA_OFFSET(%rax,%rcx,2), %eax - SET_VREG %eax, rINSTq - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_aget_short: /* 0x4a */ -/* File: x86_64/op_aget_short.S */ -/* File: x86_64/op_aget.S */ -/* - * Array get, 32 bits or less. vAA <- vBB[vCC]. - * - * for: aget, aget-boolean, aget-byte, aget-char, aget-short, aget-wide - * - */ - /* op vAA, vBB, vCC */ - movzbq 2(rPC), %rax # eax <- BB - movzbq 3(rPC), %rcx # ecx <- CC - GET_VREG %eax, %rax # eax <- vBB (array object) - GET_VREG %ecx, %rcx # ecx <- vCC (requested index) - testl %eax, %eax # null array object? - je common_errNullObject # bail if so - cmpl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ecx - jae common_errArrayIndex # index >= length, bail. - .if 0 - movq MIRROR_SHORT_ARRAY_DATA_OFFSET(%rax,%rcx,8), %rax - SET_WIDE_VREG %rax, rINSTq - .else - movswl MIRROR_SHORT_ARRAY_DATA_OFFSET(%rax,%rcx,2), %eax - SET_VREG %eax, rINSTq - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_aput: /* 0x4b */ -/* File: x86_64/op_aput.S */ -/* - * Array put, 32 bits or less. vBB[vCC] <- vAA. - * - * for: aput, aput-boolean, aput-byte, aput-char, aput-short, aput-wide - * - */ - /* op vAA, vBB, vCC */ - movzbq 2(rPC), %rax # rax <- BB - movzbq 3(rPC), %rcx # rcx <- CC - GET_VREG %eax, %rax # eax <- vBB (array object) - GET_VREG %ecx, %rcx # ecx <- vCC (requested index) - testl %eax, %eax # null array object? - je common_errNullObject # bail if so - cmpl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ecx - jae common_errArrayIndex # index >= length, bail. - .if 0 - GET_WIDE_VREG rINSTq, rINSTq - .else - GET_VREG rINST, rINSTq - .endif - movl rINST, MIRROR_INT_ARRAY_DATA_OFFSET(%rax,%rcx,4) - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - -/* ------------------------------ */ - .balign 128 -.L_op_aput_wide: /* 0x4c */ -/* File: x86_64/op_aput_wide.S */ -/* File: x86_64/op_aput.S */ -/* - * Array put, 32 bits or less. vBB[vCC] <- vAA. - * - * for: aput, aput-boolean, aput-byte, aput-char, aput-short, aput-wide - * - */ - /* op vAA, vBB, vCC */ - movzbq 2(rPC), %rax # rax <- BB - movzbq 3(rPC), %rcx # rcx <- CC - GET_VREG %eax, %rax # eax <- vBB (array object) - GET_VREG %ecx, %rcx # ecx <- vCC (requested index) - testl %eax, %eax # null array object? - je common_errNullObject # bail if so - cmpl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ecx - jae common_errArrayIndex # index >= length, bail. - .if 1 - GET_WIDE_VREG rINSTq, rINSTq - .else - GET_VREG rINST, rINSTq - .endif - movq rINSTq, MIRROR_WIDE_ARRAY_DATA_OFFSET(%rax,%rcx,8) - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_aput_object: /* 0x4d */ -/* File: x86_64/op_aput_object.S */ -/* - * Store an object into an array. vBB[vCC] <- vAA. - */ - /* op vAA, vBB, vCC */ - EXPORT_PC - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG0 - movq rPC, OUT_ARG1 - REFRESH_INST 77 - movq rINSTq, OUT_ARG2 - call SYMBOL(MterpAputObject) # (array, index) - testb %al, %al - jz MterpPossibleException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - -/* ------------------------------ */ - .balign 128 -.L_op_aput_boolean: /* 0x4e */ -/* File: x86_64/op_aput_boolean.S */ -/* File: x86_64/op_aput.S */ -/* - * Array put, 32 bits or less. vBB[vCC] <- vAA. - * - * for: aput, aput-boolean, aput-byte, aput-char, aput-short, aput-wide - * - */ - /* op vAA, vBB, vCC */ - movzbq 2(rPC), %rax # rax <- BB - movzbq 3(rPC), %rcx # rcx <- CC - GET_VREG %eax, %rax # eax <- vBB (array object) - GET_VREG %ecx, %rcx # ecx <- vCC (requested index) - testl %eax, %eax # null array object? - je common_errNullObject # bail if so - cmpl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ecx - jae common_errArrayIndex # index >= length, bail. - .if 0 - GET_WIDE_VREG rINSTq, rINSTq - .else - GET_VREG rINST, rINSTq - .endif - movb rINSTbl, MIRROR_BOOLEAN_ARRAY_DATA_OFFSET(%rax,%rcx,1) - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_aput_byte: /* 0x4f */ -/* File: x86_64/op_aput_byte.S */ -/* File: x86_64/op_aput.S */ -/* - * Array put, 32 bits or less. vBB[vCC] <- vAA. - * - * for: aput, aput-boolean, aput-byte, aput-char, aput-short, aput-wide - * - */ - /* op vAA, vBB, vCC */ - movzbq 2(rPC), %rax # rax <- BB - movzbq 3(rPC), %rcx # rcx <- CC - GET_VREG %eax, %rax # eax <- vBB (array object) - GET_VREG %ecx, %rcx # ecx <- vCC (requested index) - testl %eax, %eax # null array object? - je common_errNullObject # bail if so - cmpl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ecx - jae common_errArrayIndex # index >= length, bail. - .if 0 - GET_WIDE_VREG rINSTq, rINSTq - .else - GET_VREG rINST, rINSTq - .endif - movb rINSTbl, MIRROR_BYTE_ARRAY_DATA_OFFSET(%rax,%rcx,1) - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_aput_char: /* 0x50 */ -/* File: x86_64/op_aput_char.S */ -/* File: x86_64/op_aput.S */ -/* - * Array put, 32 bits or less. vBB[vCC] <- vAA. - * - * for: aput, aput-boolean, aput-byte, aput-char, aput-short, aput-wide - * - */ - /* op vAA, vBB, vCC */ - movzbq 2(rPC), %rax # rax <- BB - movzbq 3(rPC), %rcx # rcx <- CC - GET_VREG %eax, %rax # eax <- vBB (array object) - GET_VREG %ecx, %rcx # ecx <- vCC (requested index) - testl %eax, %eax # null array object? - je common_errNullObject # bail if so - cmpl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ecx - jae common_errArrayIndex # index >= length, bail. - .if 0 - GET_WIDE_VREG rINSTq, rINSTq - .else - GET_VREG rINST, rINSTq - .endif - movw rINSTw, MIRROR_CHAR_ARRAY_DATA_OFFSET(%rax,%rcx,2) - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_aput_short: /* 0x51 */ -/* File: x86_64/op_aput_short.S */ -/* File: x86_64/op_aput.S */ -/* - * Array put, 32 bits or less. vBB[vCC] <- vAA. - * - * for: aput, aput-boolean, aput-byte, aput-char, aput-short, aput-wide - * - */ - /* op vAA, vBB, vCC */ - movzbq 2(rPC), %rax # rax <- BB - movzbq 3(rPC), %rcx # rcx <- CC - GET_VREG %eax, %rax # eax <- vBB (array object) - GET_VREG %ecx, %rcx # ecx <- vCC (requested index) - testl %eax, %eax # null array object? - je common_errNullObject # bail if so - cmpl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ecx - jae common_errArrayIndex # index >= length, bail. - .if 0 - GET_WIDE_VREG rINSTq, rINSTq - .else - GET_VREG rINST, rINSTq - .endif - movw rINSTw, MIRROR_SHORT_ARRAY_DATA_OFFSET(%rax,%rcx,2) - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_iget: /* 0x52 */ -/* File: x86_64/op_iget.S */ -/* - * General instance field get. - * - * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short, iget-wide - */ - EXPORT_PC - movzbq rINSTbl, %rcx # rcx <- BA - movzwl 2(rPC), OUT_32_ARG0 # eax <- field ref CCCC - sarl $4, %ecx # ecx <- B - GET_VREG OUT_32_ARG1, %rcx # the object pointer - movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer - movq rSELF, OUT_ARG3 - call SYMBOL(artGet32InstanceFromCode) - cmpq $0, THREAD_EXCEPTION_OFFSET(rSELF) - jnz MterpException # bail out - andb $0xf, rINSTbl # rINST <- A - .if 0 - SET_VREG_OBJECT %eax, rINSTq # fp[A] <-value - .else - .if 0 - SET_WIDE_VREG %rax, rINSTq # fp[A] <-value - .else - SET_VREG %eax, rINSTq # fp[A] <-value - .endif - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - -/* ------------------------------ */ - .balign 128 -.L_op_iget_wide: /* 0x53 */ -/* File: x86_64/op_iget_wide.S */ -/* File: x86_64/op_iget.S */ -/* - * General instance field get. - * - * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short, iget-wide - */ - EXPORT_PC - movzbq rINSTbl, %rcx # rcx <- BA - movzwl 2(rPC), OUT_32_ARG0 # eax <- field ref CCCC - sarl $4, %ecx # ecx <- B - GET_VREG OUT_32_ARG1, %rcx # the object pointer - movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer - movq rSELF, OUT_ARG3 - call SYMBOL(artGet64InstanceFromCode) - cmpq $0, THREAD_EXCEPTION_OFFSET(rSELF) - jnz MterpException # bail out - andb $0xf, rINSTbl # rINST <- A - .if 0 - SET_VREG_OBJECT %eax, rINSTq # fp[A] <-value - .else - .if 1 - SET_WIDE_VREG %rax, rINSTq # fp[A] <-value - .else - SET_VREG %eax, rINSTq # fp[A] <-value - .endif - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_iget_object: /* 0x54 */ -/* File: x86_64/op_iget_object.S */ -/* File: x86_64/op_iget.S */ -/* - * General instance field get. - * - * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short, iget-wide - */ - EXPORT_PC - movzbq rINSTbl, %rcx # rcx <- BA - movzwl 2(rPC), OUT_32_ARG0 # eax <- field ref CCCC - sarl $4, %ecx # ecx <- B - GET_VREG OUT_32_ARG1, %rcx # the object pointer - movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer - movq rSELF, OUT_ARG3 - call SYMBOL(artGetObjInstanceFromCode) - cmpq $0, THREAD_EXCEPTION_OFFSET(rSELF) - jnz MterpException # bail out - andb $0xf, rINSTbl # rINST <- A - .if 1 - SET_VREG_OBJECT %eax, rINSTq # fp[A] <-value - .else - .if 0 - SET_WIDE_VREG %rax, rINSTq # fp[A] <-value - .else - SET_VREG %eax, rINSTq # fp[A] <-value - .endif - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_iget_boolean: /* 0x55 */ -/* File: x86_64/op_iget_boolean.S */ -/* File: x86_64/op_iget.S */ -/* - * General instance field get. - * - * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short, iget-wide - */ - EXPORT_PC - movzbq rINSTbl, %rcx # rcx <- BA - movzwl 2(rPC), OUT_32_ARG0 # eax <- field ref CCCC - sarl $4, %ecx # ecx <- B - GET_VREG OUT_32_ARG1, %rcx # the object pointer - movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer - movq rSELF, OUT_ARG3 - call SYMBOL(artGetBooleanInstanceFromCode) - cmpq $0, THREAD_EXCEPTION_OFFSET(rSELF) - jnz MterpException # bail out - andb $0xf, rINSTbl # rINST <- A - .if 0 - SET_VREG_OBJECT %eax, rINSTq # fp[A] <-value - .else - .if 0 - SET_WIDE_VREG %rax, rINSTq # fp[A] <-value - .else - SET_VREG %eax, rINSTq # fp[A] <-value - .endif - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_iget_byte: /* 0x56 */ -/* File: x86_64/op_iget_byte.S */ -/* File: x86_64/op_iget.S */ -/* - * General instance field get. - * - * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short, iget-wide - */ - EXPORT_PC - movzbq rINSTbl, %rcx # rcx <- BA - movzwl 2(rPC), OUT_32_ARG0 # eax <- field ref CCCC - sarl $4, %ecx # ecx <- B - GET_VREG OUT_32_ARG1, %rcx # the object pointer - movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer - movq rSELF, OUT_ARG3 - call SYMBOL(artGetByteInstanceFromCode) - cmpq $0, THREAD_EXCEPTION_OFFSET(rSELF) - jnz MterpException # bail out - andb $0xf, rINSTbl # rINST <- A - .if 0 - SET_VREG_OBJECT %eax, rINSTq # fp[A] <-value - .else - .if 0 - SET_WIDE_VREG %rax, rINSTq # fp[A] <-value - .else - SET_VREG %eax, rINSTq # fp[A] <-value - .endif - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_iget_char: /* 0x57 */ -/* File: x86_64/op_iget_char.S */ -/* File: x86_64/op_iget.S */ -/* - * General instance field get. - * - * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short, iget-wide - */ - EXPORT_PC - movzbq rINSTbl, %rcx # rcx <- BA - movzwl 2(rPC), OUT_32_ARG0 # eax <- field ref CCCC - sarl $4, %ecx # ecx <- B - GET_VREG OUT_32_ARG1, %rcx # the object pointer - movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer - movq rSELF, OUT_ARG3 - call SYMBOL(artGetCharInstanceFromCode) - cmpq $0, THREAD_EXCEPTION_OFFSET(rSELF) - jnz MterpException # bail out - andb $0xf, rINSTbl # rINST <- A - .if 0 - SET_VREG_OBJECT %eax, rINSTq # fp[A] <-value - .else - .if 0 - SET_WIDE_VREG %rax, rINSTq # fp[A] <-value - .else - SET_VREG %eax, rINSTq # fp[A] <-value - .endif - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_iget_short: /* 0x58 */ -/* File: x86_64/op_iget_short.S */ -/* File: x86_64/op_iget.S */ -/* - * General instance field get. - * - * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short, iget-wide - */ - EXPORT_PC - movzbq rINSTbl, %rcx # rcx <- BA - movzwl 2(rPC), OUT_32_ARG0 # eax <- field ref CCCC - sarl $4, %ecx # ecx <- B - GET_VREG OUT_32_ARG1, %rcx # the object pointer - movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer - movq rSELF, OUT_ARG3 - call SYMBOL(artGetShortInstanceFromCode) - cmpq $0, THREAD_EXCEPTION_OFFSET(rSELF) - jnz MterpException # bail out - andb $0xf, rINSTbl # rINST <- A - .if 0 - SET_VREG_OBJECT %eax, rINSTq # fp[A] <-value - .else - .if 0 - SET_WIDE_VREG %rax, rINSTq # fp[A] <-value - .else - SET_VREG %eax, rINSTq # fp[A] <-value - .endif - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_iput: /* 0x59 */ -/* File: x86_64/op_iput.S */ -/* - * General 32-bit instance field put. - * - * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short - */ - /* op vA, vB, field@CCCC */ - .extern artSet32InstanceFromMterp - EXPORT_PC - movzwl 2(rPC), OUT_32_ARG0 # field ref <- 0000CCCC - movzbq rINSTbl, %rcx # rcx<- BA - sarl $4, %ecx # ecx<- B - GET_VREG OUT_32_ARG1, %rcx # the object pointer - andb $0xf, rINSTbl # rINST<- A - GET_VREG OUT_32_ARG2, rINSTq # fp[A] - movq OFF_FP_METHOD(rFP), OUT_ARG3 # referrer - call SYMBOL(artSet32InstanceFromMterp) - testb %al, %al - jnz MterpPossibleException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - -/* ------------------------------ */ - .balign 128 -.L_op_iput_wide: /* 0x5a */ -/* File: x86_64/op_iput_wide.S */ - /* iput-wide vA, vB, field@CCCC */ - .extern artSet64InstanceFromMterp - EXPORT_PC - movzwq 2(rPC), OUT_ARG0 # field ref CCCC - movzbq rINSTbl, %rcx # rcx <- BA - sarl $4, %ecx # ecx <- B - GET_VREG OUT_32_ARG1, %rcx # the object pointer - andb $0xf, rINSTbl # rINST <- A - leaq VREG_ADDRESS(rINSTq), OUT_ARG2 # &fp[A] - movq OFF_FP_METHOD(rFP), OUT_ARG3 # referrer - call SYMBOL(artSet64InstanceFromMterp) - testb %al, %al - jnz MterpPossibleException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - -/* ------------------------------ */ - .balign 128 -.L_op_iput_object: /* 0x5b */ -/* File: x86_64/op_iput_object.S */ - EXPORT_PC - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG0 - movq rPC, OUT_ARG1 - REFRESH_INST 91 - movl rINST, OUT_32_ARG2 - movq rSELF, OUT_ARG3 - call SYMBOL(MterpIputObject) - testb %al, %al - jz MterpException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - -/* ------------------------------ */ - .balign 128 -.L_op_iput_boolean: /* 0x5c */ -/* File: x86_64/op_iput_boolean.S */ -/* File: x86_64/op_iput.S */ -/* - * General 32-bit instance field put. - * - * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short - */ - /* op vA, vB, field@CCCC */ - .extern artSet8InstanceFromMterp - EXPORT_PC - movzwl 2(rPC), OUT_32_ARG0 # field ref <- 0000CCCC - movzbq rINSTbl, %rcx # rcx<- BA - sarl $4, %ecx # ecx<- B - GET_VREG OUT_32_ARG1, %rcx # the object pointer - andb $0xf, rINSTbl # rINST<- A - GET_VREG OUT_32_ARG2, rINSTq # fp[A] - movq OFF_FP_METHOD(rFP), OUT_ARG3 # referrer - call SYMBOL(artSet8InstanceFromMterp) - testb %al, %al - jnz MterpPossibleException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_iput_byte: /* 0x5d */ -/* File: x86_64/op_iput_byte.S */ -/* File: x86_64/op_iput.S */ -/* - * General 32-bit instance field put. - * - * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short - */ - /* op vA, vB, field@CCCC */ - .extern artSet8InstanceFromMterp - EXPORT_PC - movzwl 2(rPC), OUT_32_ARG0 # field ref <- 0000CCCC - movzbq rINSTbl, %rcx # rcx<- BA - sarl $4, %ecx # ecx<- B - GET_VREG OUT_32_ARG1, %rcx # the object pointer - andb $0xf, rINSTbl # rINST<- A - GET_VREG OUT_32_ARG2, rINSTq # fp[A] - movq OFF_FP_METHOD(rFP), OUT_ARG3 # referrer - call SYMBOL(artSet8InstanceFromMterp) - testb %al, %al - jnz MterpPossibleException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_iput_char: /* 0x5e */ -/* File: x86_64/op_iput_char.S */ -/* File: x86_64/op_iput.S */ -/* - * General 32-bit instance field put. - * - * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short - */ - /* op vA, vB, field@CCCC */ - .extern artSet16InstanceFromMterp - EXPORT_PC - movzwl 2(rPC), OUT_32_ARG0 # field ref <- 0000CCCC - movzbq rINSTbl, %rcx # rcx<- BA - sarl $4, %ecx # ecx<- B - GET_VREG OUT_32_ARG1, %rcx # the object pointer - andb $0xf, rINSTbl # rINST<- A - GET_VREG OUT_32_ARG2, rINSTq # fp[A] - movq OFF_FP_METHOD(rFP), OUT_ARG3 # referrer - call SYMBOL(artSet16InstanceFromMterp) - testb %al, %al - jnz MterpPossibleException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_iput_short: /* 0x5f */ -/* File: x86_64/op_iput_short.S */ -/* File: x86_64/op_iput.S */ -/* - * General 32-bit instance field put. - * - * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short - */ - /* op vA, vB, field@CCCC */ - .extern artSet16InstanceFromMterp - EXPORT_PC - movzwl 2(rPC), OUT_32_ARG0 # field ref <- 0000CCCC - movzbq rINSTbl, %rcx # rcx<- BA - sarl $4, %ecx # ecx<- B - GET_VREG OUT_32_ARG1, %rcx # the object pointer - andb $0xf, rINSTbl # rINST<- A - GET_VREG OUT_32_ARG2, rINSTq # fp[A] - movq OFF_FP_METHOD(rFP), OUT_ARG3 # referrer - call SYMBOL(artSet16InstanceFromMterp) - testb %al, %al - jnz MterpPossibleException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_sget: /* 0x60 */ -/* File: x86_64/op_sget.S */ -/* - * General SGET handler wrapper. - * - * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short, sget-wide - */ - /* op vAA, field@BBBB */ - .extern artGet32StaticFromCode - EXPORT_PC - movzwq 2(rPC), OUT_ARG0 # field ref CCCC - movq OFF_FP_METHOD(rFP), OUT_ARG1 # referrer - movq rSELF, OUT_ARG2 # self - call SYMBOL(artGet32StaticFromCode) - cmpl $0, THREAD_EXCEPTION_OFFSET(rSELF) - jnz MterpException - .if 0 - SET_VREG_OBJECT %eax, rINSTq # fp[A] <- value - .else - .if 0 - SET_WIDE_VREG %rax, rINSTq # fp[A] <- value - .else - SET_VREG %eax, rINSTq # fp[A] <- value - .endif - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - -/* ------------------------------ */ - .balign 128 -.L_op_sget_wide: /* 0x61 */ -/* File: x86_64/op_sget_wide.S */ -/* File: x86_64/op_sget.S */ -/* - * General SGET handler wrapper. - * - * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short, sget-wide - */ - /* op vAA, field@BBBB */ - .extern artGet64StaticFromCode - EXPORT_PC - movzwq 2(rPC), OUT_ARG0 # field ref CCCC - movq OFF_FP_METHOD(rFP), OUT_ARG1 # referrer - movq rSELF, OUT_ARG2 # self - call SYMBOL(artGet64StaticFromCode) - cmpl $0, THREAD_EXCEPTION_OFFSET(rSELF) - jnz MterpException - .if 0 - SET_VREG_OBJECT %eax, rINSTq # fp[A] <- value - .else - .if 1 - SET_WIDE_VREG %rax, rINSTq # fp[A] <- value - .else - SET_VREG %eax, rINSTq # fp[A] <- value - .endif - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_sget_object: /* 0x62 */ -/* File: x86_64/op_sget_object.S */ -/* File: x86_64/op_sget.S */ -/* - * General SGET handler wrapper. - * - * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short, sget-wide - */ - /* op vAA, field@BBBB */ - .extern artGetObjStaticFromCode - EXPORT_PC - movzwq 2(rPC), OUT_ARG0 # field ref CCCC - movq OFF_FP_METHOD(rFP), OUT_ARG1 # referrer - movq rSELF, OUT_ARG2 # self - call SYMBOL(artGetObjStaticFromCode) - cmpl $0, THREAD_EXCEPTION_OFFSET(rSELF) - jnz MterpException - .if 1 - SET_VREG_OBJECT %eax, rINSTq # fp[A] <- value - .else - .if 0 - SET_WIDE_VREG %rax, rINSTq # fp[A] <- value - .else - SET_VREG %eax, rINSTq # fp[A] <- value - .endif - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_sget_boolean: /* 0x63 */ -/* File: x86_64/op_sget_boolean.S */ -/* File: x86_64/op_sget.S */ -/* - * General SGET handler wrapper. - * - * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short, sget-wide - */ - /* op vAA, field@BBBB */ - .extern artGetBooleanStaticFromCode - EXPORT_PC - movzwq 2(rPC), OUT_ARG0 # field ref CCCC - movq OFF_FP_METHOD(rFP), OUT_ARG1 # referrer - movq rSELF, OUT_ARG2 # self - call SYMBOL(artGetBooleanStaticFromCode) - cmpl $0, THREAD_EXCEPTION_OFFSET(rSELF) - jnz MterpException - .if 0 - SET_VREG_OBJECT %eax, rINSTq # fp[A] <- value - .else - .if 0 - SET_WIDE_VREG %rax, rINSTq # fp[A] <- value - .else - SET_VREG %eax, rINSTq # fp[A] <- value - .endif - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_sget_byte: /* 0x64 */ -/* File: x86_64/op_sget_byte.S */ -/* File: x86_64/op_sget.S */ -/* - * General SGET handler wrapper. - * - * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short, sget-wide - */ - /* op vAA, field@BBBB */ - .extern artGetByteStaticFromCode - EXPORT_PC - movzwq 2(rPC), OUT_ARG0 # field ref CCCC - movq OFF_FP_METHOD(rFP), OUT_ARG1 # referrer - movq rSELF, OUT_ARG2 # self - call SYMBOL(artGetByteStaticFromCode) - cmpl $0, THREAD_EXCEPTION_OFFSET(rSELF) - jnz MterpException - .if 0 - SET_VREG_OBJECT %eax, rINSTq # fp[A] <- value - .else - .if 0 - SET_WIDE_VREG %rax, rINSTq # fp[A] <- value - .else - SET_VREG %eax, rINSTq # fp[A] <- value - .endif - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_sget_char: /* 0x65 */ -/* File: x86_64/op_sget_char.S */ -/* File: x86_64/op_sget.S */ -/* - * General SGET handler wrapper. - * - * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short, sget-wide - */ - /* op vAA, field@BBBB */ - .extern artGetCharStaticFromCode - EXPORT_PC - movzwq 2(rPC), OUT_ARG0 # field ref CCCC - movq OFF_FP_METHOD(rFP), OUT_ARG1 # referrer - movq rSELF, OUT_ARG2 # self - call SYMBOL(artGetCharStaticFromCode) - cmpl $0, THREAD_EXCEPTION_OFFSET(rSELF) - jnz MterpException - .if 0 - SET_VREG_OBJECT %eax, rINSTq # fp[A] <- value - .else - .if 0 - SET_WIDE_VREG %rax, rINSTq # fp[A] <- value - .else - SET_VREG %eax, rINSTq # fp[A] <- value - .endif - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_sget_short: /* 0x66 */ -/* File: x86_64/op_sget_short.S */ -/* File: x86_64/op_sget.S */ -/* - * General SGET handler wrapper. - * - * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short, sget-wide - */ - /* op vAA, field@BBBB */ - .extern artGetShortStaticFromCode - EXPORT_PC - movzwq 2(rPC), OUT_ARG0 # field ref CCCC - movq OFF_FP_METHOD(rFP), OUT_ARG1 # referrer - movq rSELF, OUT_ARG2 # self - call SYMBOL(artGetShortStaticFromCode) - cmpl $0, THREAD_EXCEPTION_OFFSET(rSELF) - jnz MterpException - .if 0 - SET_VREG_OBJECT %eax, rINSTq # fp[A] <- value - .else - .if 0 - SET_WIDE_VREG %rax, rINSTq # fp[A] <- value - .else - SET_VREG %eax, rINSTq # fp[A] <- value - .endif - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_sput: /* 0x67 */ -/* File: x86_64/op_sput.S */ -/* - * General SPUT handler wrapper. - * - * for: sput, sput-boolean, sput-byte, sput-char, sput-short - */ - /* op vAA, field@BBBB */ - .extern artSet32StaticFromCode - EXPORT_PC - movzwq 2(rPC), OUT_ARG0 # field ref BBBB - GET_VREG OUT_32_ARG1, rINSTq # fp[AA] - movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer - movq rSELF, OUT_ARG3 # self - call SYMBOL(artSet32StaticFromCode) - testb %al, %al - jnz MterpException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - -/* ------------------------------ */ - .balign 128 -.L_op_sput_wide: /* 0x68 */ -/* File: x86_64/op_sput_wide.S */ -/* - * SPUT_WIDE handler wrapper. - * - */ - /* sput-wide vAA, field@BBBB */ - .extern artSet64IndirectStaticFromMterp - EXPORT_PC - movzwq 2(rPC), OUT_ARG0 # field ref BBBB - movq OFF_FP_METHOD(rFP), OUT_ARG1 # referrer - leaq VREG_ADDRESS(rINSTq), OUT_ARG2 # &fp[AA] - movq rSELF, OUT_ARG3 # self - call SYMBOL(artSet64IndirectStaticFromMterp) - testb %al, %al - jnz MterpException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - -/* ------------------------------ */ - .balign 128 -.L_op_sput_object: /* 0x69 */ -/* File: x86_64/op_sput_object.S */ - EXPORT_PC - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG0 - movq rPC, OUT_ARG1 - REFRESH_INST 105 - movq rINSTq, OUT_ARG2 - movq rSELF, OUT_ARG3 - call SYMBOL(MterpSputObject) - testb %al, %al - jz MterpException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - -/* ------------------------------ */ - .balign 128 -.L_op_sput_boolean: /* 0x6a */ -/* File: x86_64/op_sput_boolean.S */ -/* File: x86_64/op_sput.S */ -/* - * General SPUT handler wrapper. - * - * for: sput, sput-boolean, sput-byte, sput-char, sput-short - */ - /* op vAA, field@BBBB */ - .extern artSet8StaticFromCode - EXPORT_PC - movzwq 2(rPC), OUT_ARG0 # field ref BBBB - GET_VREG OUT_32_ARG1, rINSTq # fp[AA] - movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer - movq rSELF, OUT_ARG3 # self - call SYMBOL(artSet8StaticFromCode) - testb %al, %al - jnz MterpException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_sput_byte: /* 0x6b */ -/* File: x86_64/op_sput_byte.S */ -/* File: x86_64/op_sput.S */ -/* - * General SPUT handler wrapper. - * - * for: sput, sput-boolean, sput-byte, sput-char, sput-short - */ - /* op vAA, field@BBBB */ - .extern artSet8StaticFromCode - EXPORT_PC - movzwq 2(rPC), OUT_ARG0 # field ref BBBB - GET_VREG OUT_32_ARG1, rINSTq # fp[AA] - movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer - movq rSELF, OUT_ARG3 # self - call SYMBOL(artSet8StaticFromCode) - testb %al, %al - jnz MterpException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_sput_char: /* 0x6c */ -/* File: x86_64/op_sput_char.S */ -/* File: x86_64/op_sput.S */ -/* - * General SPUT handler wrapper. - * - * for: sput, sput-boolean, sput-byte, sput-char, sput-short - */ - /* op vAA, field@BBBB */ - .extern artSet16StaticFromCode - EXPORT_PC - movzwq 2(rPC), OUT_ARG0 # field ref BBBB - GET_VREG OUT_32_ARG1, rINSTq # fp[AA] - movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer - movq rSELF, OUT_ARG3 # self - call SYMBOL(artSet16StaticFromCode) - testb %al, %al - jnz MterpException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_sput_short: /* 0x6d */ -/* File: x86_64/op_sput_short.S */ -/* File: x86_64/op_sput.S */ -/* - * General SPUT handler wrapper. - * - * for: sput, sput-boolean, sput-byte, sput-char, sput-short - */ - /* op vAA, field@BBBB */ - .extern artSet16StaticFromCode - EXPORT_PC - movzwq 2(rPC), OUT_ARG0 # field ref BBBB - GET_VREG OUT_32_ARG1, rINSTq # fp[AA] - movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer - movq rSELF, OUT_ARG3 # self - call SYMBOL(artSet16StaticFromCode) - testb %al, %al - jnz MterpException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_invoke_virtual: /* 0x6e */ -/* File: x86_64/op_invoke_virtual.S */ -/* File: x86_64/invoke.S */ -/* - * Generic invoke handler wrapper. - */ - /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ - /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ - .extern MterpInvokeVirtual - EXPORT_PC - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - movq rPC, OUT_ARG2 - REFRESH_INST 110 - movl rINST, OUT_32_ARG3 - call SYMBOL(MterpInvokeVirtual) - testb %al, %al - jz MterpException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 - -/* - * Handle a virtual method call. - * - * for: invoke-virtual, invoke-virtual/range - */ - /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ - /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ - -/* ------------------------------ */ - .balign 128 -.L_op_invoke_super: /* 0x6f */ -/* File: x86_64/op_invoke_super.S */ -/* File: x86_64/invoke.S */ -/* - * Generic invoke handler wrapper. - */ - /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ - /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ - .extern MterpInvokeSuper - EXPORT_PC - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - movq rPC, OUT_ARG2 - REFRESH_INST 111 - movl rINST, OUT_32_ARG3 - call SYMBOL(MterpInvokeSuper) - testb %al, %al - jz MterpException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 - -/* - * Handle a "super" method call. - * - * for: invoke-super, invoke-super/range - */ - /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ - /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ - -/* ------------------------------ */ - .balign 128 -.L_op_invoke_direct: /* 0x70 */ -/* File: x86_64/op_invoke_direct.S */ -/* File: x86_64/invoke.S */ -/* - * Generic invoke handler wrapper. - */ - /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ - /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ - .extern MterpInvokeDirect - EXPORT_PC - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - movq rPC, OUT_ARG2 - REFRESH_INST 112 - movl rINST, OUT_32_ARG3 - call SYMBOL(MterpInvokeDirect) - testb %al, %al - jz MterpException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 - - -/* ------------------------------ */ - .balign 128 -.L_op_invoke_static: /* 0x71 */ -/* File: x86_64/op_invoke_static.S */ -/* File: x86_64/invoke.S */ -/* - * Generic invoke handler wrapper. - */ - /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ - /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ - .extern MterpInvokeStatic - EXPORT_PC - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - movq rPC, OUT_ARG2 - REFRESH_INST 113 - movl rINST, OUT_32_ARG3 - call SYMBOL(MterpInvokeStatic) - testb %al, %al - jz MterpException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 - - - -/* ------------------------------ */ - .balign 128 -.L_op_invoke_interface: /* 0x72 */ -/* File: x86_64/op_invoke_interface.S */ -/* File: x86_64/invoke.S */ -/* - * Generic invoke handler wrapper. - */ - /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ - /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ - .extern MterpInvokeInterface - EXPORT_PC - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - movq rPC, OUT_ARG2 - REFRESH_INST 114 - movl rINST, OUT_32_ARG3 - call SYMBOL(MterpInvokeInterface) - testb %al, %al - jz MterpException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 - -/* - * Handle an interface method call. - * - * for: invoke-interface, invoke-interface/range - */ - /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ - /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ - -/* ------------------------------ */ - .balign 128 -.L_op_return_void_no_barrier: /* 0x73 */ -/* File: x86_64/op_return_void_no_barrier.S */ - testl $(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(rSELF) - jz 1f - movq rSELF, OUT_ARG0 - call SYMBOL(MterpSuspendCheck) -1: - xorq %rax, %rax - jmp MterpReturn - -/* ------------------------------ */ - .balign 128 -.L_op_invoke_virtual_range: /* 0x74 */ -/* File: x86_64/op_invoke_virtual_range.S */ -/* File: x86_64/invoke.S */ -/* - * Generic invoke handler wrapper. - */ - /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ - /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ - .extern MterpInvokeVirtualRange - EXPORT_PC - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - movq rPC, OUT_ARG2 - REFRESH_INST 116 - movl rINST, OUT_32_ARG3 - call SYMBOL(MterpInvokeVirtualRange) - testb %al, %al - jz MterpException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 - - -/* ------------------------------ */ - .balign 128 -.L_op_invoke_super_range: /* 0x75 */ -/* File: x86_64/op_invoke_super_range.S */ -/* File: x86_64/invoke.S */ -/* - * Generic invoke handler wrapper. - */ - /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ - /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ - .extern MterpInvokeSuperRange - EXPORT_PC - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - movq rPC, OUT_ARG2 - REFRESH_INST 117 - movl rINST, OUT_32_ARG3 - call SYMBOL(MterpInvokeSuperRange) - testb %al, %al - jz MterpException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 - - -/* ------------------------------ */ - .balign 128 -.L_op_invoke_direct_range: /* 0x76 */ -/* File: x86_64/op_invoke_direct_range.S */ -/* File: x86_64/invoke.S */ -/* - * Generic invoke handler wrapper. - */ - /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ - /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ - .extern MterpInvokeDirectRange - EXPORT_PC - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - movq rPC, OUT_ARG2 - REFRESH_INST 118 - movl rINST, OUT_32_ARG3 - call SYMBOL(MterpInvokeDirectRange) - testb %al, %al - jz MterpException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 - - -/* ------------------------------ */ - .balign 128 -.L_op_invoke_static_range: /* 0x77 */ -/* File: x86_64/op_invoke_static_range.S */ -/* File: x86_64/invoke.S */ -/* - * Generic invoke handler wrapper. - */ - /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ - /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ - .extern MterpInvokeStaticRange - EXPORT_PC - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - movq rPC, OUT_ARG2 - REFRESH_INST 119 - movl rINST, OUT_32_ARG3 - call SYMBOL(MterpInvokeStaticRange) - testb %al, %al - jz MterpException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 - - -/* ------------------------------ */ - .balign 128 -.L_op_invoke_interface_range: /* 0x78 */ -/* File: x86_64/op_invoke_interface_range.S */ -/* File: x86_64/invoke.S */ -/* - * Generic invoke handler wrapper. - */ - /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ - /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ - .extern MterpInvokeInterfaceRange - EXPORT_PC - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - movq rPC, OUT_ARG2 - REFRESH_INST 120 - movl rINST, OUT_32_ARG3 - call SYMBOL(MterpInvokeInterfaceRange) - testb %al, %al - jz MterpException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 - - -/* ------------------------------ */ - .balign 128 -.L_op_unused_79: /* 0x79 */ -/* File: x86_64/op_unused_79.S */ -/* File: x86_64/unused.S */ -/* - * Bail to reference interpreter to throw. - */ - jmp MterpFallback - - -/* ------------------------------ */ - .balign 128 -.L_op_unused_7a: /* 0x7a */ -/* File: x86_64/op_unused_7a.S */ -/* File: x86_64/unused.S */ -/* - * Bail to reference interpreter to throw. - */ - jmp MterpFallback - - -/* ------------------------------ */ - .balign 128 -.L_op_neg_int: /* 0x7b */ -/* File: x86_64/op_neg_int.S */ -/* File: x86_64/unop.S */ -/* - * Generic 32/64-bit unary operation. Provide an "instr" line that - * specifies an instruction that performs "result = op eax". - */ - /* unop vA, vB */ - movl rINST, %ecx # rcx <- A+ - sarl $4,rINST # rINST <- B - .if 0 - GET_WIDE_VREG %rax, rINSTq # rax <- vB - .else - GET_VREG %eax, rINSTq # eax <- vB - .endif - andb $0xf,%cl # ecx <- A - - negl %eax - .if 0 - SET_WIDE_VREG %rax, %rcx - .else - SET_VREG %eax, %rcx - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 - - -/* ------------------------------ */ - .balign 128 -.L_op_not_int: /* 0x7c */ -/* File: x86_64/op_not_int.S */ -/* File: x86_64/unop.S */ -/* - * Generic 32/64-bit unary operation. Provide an "instr" line that - * specifies an instruction that performs "result = op eax". - */ - /* unop vA, vB */ - movl rINST, %ecx # rcx <- A+ - sarl $4,rINST # rINST <- B - .if 0 - GET_WIDE_VREG %rax, rINSTq # rax <- vB - .else - GET_VREG %eax, rINSTq # eax <- vB - .endif - andb $0xf,%cl # ecx <- A - - notl %eax - .if 0 - SET_WIDE_VREG %rax, %rcx - .else - SET_VREG %eax, %rcx - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 - - -/* ------------------------------ */ - .balign 128 -.L_op_neg_long: /* 0x7d */ -/* File: x86_64/op_neg_long.S */ -/* File: x86_64/unop.S */ -/* - * Generic 32/64-bit unary operation. Provide an "instr" line that - * specifies an instruction that performs "result = op eax". - */ - /* unop vA, vB */ - movl rINST, %ecx # rcx <- A+ - sarl $4,rINST # rINST <- B - .if 1 - GET_WIDE_VREG %rax, rINSTq # rax <- vB - .else - GET_VREG %eax, rINSTq # eax <- vB - .endif - andb $0xf,%cl # ecx <- A - - negq %rax - .if 1 - SET_WIDE_VREG %rax, %rcx - .else - SET_VREG %eax, %rcx - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 - - -/* ------------------------------ */ - .balign 128 -.L_op_not_long: /* 0x7e */ -/* File: x86_64/op_not_long.S */ -/* File: x86_64/unop.S */ -/* - * Generic 32/64-bit unary operation. Provide an "instr" line that - * specifies an instruction that performs "result = op eax". - */ - /* unop vA, vB */ - movl rINST, %ecx # rcx <- A+ - sarl $4,rINST # rINST <- B - .if 1 - GET_WIDE_VREG %rax, rINSTq # rax <- vB - .else - GET_VREG %eax, rINSTq # eax <- vB - .endif - andb $0xf,%cl # ecx <- A - - notq %rax - .if 1 - SET_WIDE_VREG %rax, %rcx - .else - SET_VREG %eax, %rcx - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 - - -/* ------------------------------ */ - .balign 128 -.L_op_neg_float: /* 0x7f */ -/* File: x86_64/op_neg_float.S */ -/* File: x86_64/unop.S */ -/* - * Generic 32/64-bit unary operation. Provide an "instr" line that - * specifies an instruction that performs "result = op eax". - */ - /* unop vA, vB */ - movl rINST, %ecx # rcx <- A+ - sarl $4,rINST # rINST <- B - .if 0 - GET_WIDE_VREG %rax, rINSTq # rax <- vB - .else - GET_VREG %eax, rINSTq # eax <- vB - .endif - andb $0xf,%cl # ecx <- A - - xorl $0x80000000, %eax - .if 0 - SET_WIDE_VREG %rax, %rcx - .else - SET_VREG %eax, %rcx - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 - - -/* ------------------------------ */ - .balign 128 -.L_op_neg_double: /* 0x80 */ -/* File: x86_64/op_neg_double.S */ -/* File: x86_64/unop.S */ -/* - * Generic 32/64-bit unary operation. Provide an "instr" line that - * specifies an instruction that performs "result = op eax". - */ - /* unop vA, vB */ - movl rINST, %ecx # rcx <- A+ - sarl $4,rINST # rINST <- B - .if 1 - GET_WIDE_VREG %rax, rINSTq # rax <- vB - .else - GET_VREG %eax, rINSTq # eax <- vB - .endif - andb $0xf,%cl # ecx <- A - movq $0x8000000000000000, %rsi - xorq %rsi, %rax - .if 1 - SET_WIDE_VREG %rax, %rcx - .else - SET_VREG %eax, %rcx - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 - - -/* ------------------------------ */ - .balign 128 -.L_op_int_to_long: /* 0x81 */ -/* File: x86_64/op_int_to_long.S */ - /* int to long vA, vB */ - movzbq rINSTbl, %rax # rax <- +A - sarl $4, %eax # eax <- B - andb $0xf, rINSTbl # rINST <- A - movslq VREG_ADDRESS(%rax), %rax - SET_WIDE_VREG %rax, rINSTq # v[A] <- %rax - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 - - -/* ------------------------------ */ - .balign 128 -.L_op_int_to_float: /* 0x82 */ -/* File: x86_64/op_int_to_float.S */ -/* File: x86_64/fpcvt.S */ -/* - * Generic 32-bit FP conversion operation. - */ - /* unop vA, vB */ - movl rINST, %ecx # rcx <- A+ - sarl $4, rINST # rINST <- B - andb $0xf, %cl # ecx <- A - cvtsi2ssl VREG_ADDRESS(rINSTq), %xmm0 - .if 0 - movsd %xmm0, VREG_ADDRESS(%rcx) - CLEAR_WIDE_REF %rcx - .else - movss %xmm0, VREG_ADDRESS(%rcx) - CLEAR_REF %rcx - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 - - -/* ------------------------------ */ - .balign 128 -.L_op_int_to_double: /* 0x83 */ -/* File: x86_64/op_int_to_double.S */ -/* File: x86_64/fpcvt.S */ -/* - * Generic 32-bit FP conversion operation. - */ - /* unop vA, vB */ - movl rINST, %ecx # rcx <- A+ - sarl $4, rINST # rINST <- B - andb $0xf, %cl # ecx <- A - cvtsi2sdl VREG_ADDRESS(rINSTq), %xmm0 - .if 1 - movsd %xmm0, VREG_ADDRESS(%rcx) - CLEAR_WIDE_REF %rcx - .else - movss %xmm0, VREG_ADDRESS(%rcx) - CLEAR_REF %rcx - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 - - -/* ------------------------------ */ - .balign 128 -.L_op_long_to_int: /* 0x84 */ -/* File: x86_64/op_long_to_int.S */ -/* we ignore the high word, making this equivalent to a 32-bit reg move */ -/* File: x86_64/op_move.S */ - /* for move, move-object, long-to-int */ - /* op vA, vB */ - movl rINST, %eax # eax <- BA - andb $0xf, %al # eax <- A - shrl $4, rINST # rINST <- B - GET_VREG %edx, rINSTq - .if 0 - SET_VREG_OBJECT %edx, %rax # fp[A] <- fp[B] - .else - SET_VREG %edx, %rax # fp[A] <- fp[B] - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 - - -/* ------------------------------ */ - .balign 128 -.L_op_long_to_float: /* 0x85 */ -/* File: x86_64/op_long_to_float.S */ -/* File: x86_64/fpcvt.S */ -/* - * Generic 32-bit FP conversion operation. - */ - /* unop vA, vB */ - movl rINST, %ecx # rcx <- A+ - sarl $4, rINST # rINST <- B - andb $0xf, %cl # ecx <- A - cvtsi2ssq VREG_ADDRESS(rINSTq), %xmm0 - .if 0 - movsd %xmm0, VREG_ADDRESS(%rcx) - CLEAR_WIDE_REF %rcx - .else - movss %xmm0, VREG_ADDRESS(%rcx) - CLEAR_REF %rcx - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 - - -/* ------------------------------ */ - .balign 128 -.L_op_long_to_double: /* 0x86 */ -/* File: x86_64/op_long_to_double.S */ -/* File: x86_64/fpcvt.S */ -/* - * Generic 32-bit FP conversion operation. - */ - /* unop vA, vB */ - movl rINST, %ecx # rcx <- A+ - sarl $4, rINST # rINST <- B - andb $0xf, %cl # ecx <- A - cvtsi2sdq VREG_ADDRESS(rINSTq), %xmm0 - .if 1 - movsd %xmm0, VREG_ADDRESS(%rcx) - CLEAR_WIDE_REF %rcx - .else - movss %xmm0, VREG_ADDRESS(%rcx) - CLEAR_REF %rcx - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 - - -/* ------------------------------ */ - .balign 128 -.L_op_float_to_int: /* 0x87 */ -/* File: x86_64/op_float_to_int.S */ -/* File: x86_64/cvtfp_int.S */ -/* On fp to int conversions, Java requires that - * if the result > maxint, it should be clamped to maxint. If it is less - * than minint, it should be clamped to minint. If it is a nan, the result - * should be zero. Further, the rounding mode is to truncate. - */ - /* float/double to int/long vA, vB */ - movl rINST, %ecx # rcx <- A+ - sarl $4, rINST # rINST <- B - andb $0xf, %cl # ecx <- A - movss VREG_ADDRESS(rINSTq), %xmm0 - movl $0x7fffffff, %eax - cvtsi2ssl %eax, %xmm1 - comiss %xmm1, %xmm0 - jae 1f - jp 2f - cvttss2sil %xmm0, %eax - jmp 1f -2: - xorl %eax, %eax -1: - .if 0 - SET_WIDE_VREG %eax, %rcx - .else - SET_VREG %eax, %rcx - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 - - -/* ------------------------------ */ - .balign 128 -.L_op_float_to_long: /* 0x88 */ -/* File: x86_64/op_float_to_long.S */ -/* File: x86_64/cvtfp_int.S */ -/* On fp to int conversions, Java requires that - * if the result > maxint, it should be clamped to maxint. If it is less - * than minint, it should be clamped to minint. If it is a nan, the result - * should be zero. Further, the rounding mode is to truncate. - */ - /* float/double to int/long vA, vB */ - movl rINST, %ecx # rcx <- A+ - sarl $4, rINST # rINST <- B - andb $0xf, %cl # ecx <- A - movss VREG_ADDRESS(rINSTq), %xmm0 - movq $0x7fffffffffffffff, %rax - cvtsi2ssq %rax, %xmm1 - comiss %xmm1, %xmm0 - jae 1f - jp 2f - cvttss2siq %xmm0, %rax - jmp 1f -2: - xorq %rax, %rax -1: - .if 1 - SET_WIDE_VREG %rax, %rcx - .else - SET_VREG %rax, %rcx - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 - - -/* ------------------------------ */ - .balign 128 -.L_op_float_to_double: /* 0x89 */ -/* File: x86_64/op_float_to_double.S */ -/* File: x86_64/fpcvt.S */ -/* - * Generic 32-bit FP conversion operation. - */ - /* unop vA, vB */ - movl rINST, %ecx # rcx <- A+ - sarl $4, rINST # rINST <- B - andb $0xf, %cl # ecx <- A - cvtss2sd VREG_ADDRESS(rINSTq), %xmm0 - .if 1 - movsd %xmm0, VREG_ADDRESS(%rcx) - CLEAR_WIDE_REF %rcx - .else - movss %xmm0, VREG_ADDRESS(%rcx) - CLEAR_REF %rcx - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 - - -/* ------------------------------ */ - .balign 128 -.L_op_double_to_int: /* 0x8a */ -/* File: x86_64/op_double_to_int.S */ -/* File: x86_64/cvtfp_int.S */ -/* On fp to int conversions, Java requires that - * if the result > maxint, it should be clamped to maxint. If it is less - * than minint, it should be clamped to minint. If it is a nan, the result - * should be zero. Further, the rounding mode is to truncate. - */ - /* float/double to int/long vA, vB */ - movl rINST, %ecx # rcx <- A+ - sarl $4, rINST # rINST <- B - andb $0xf, %cl # ecx <- A - movsd VREG_ADDRESS(rINSTq), %xmm0 - movl $0x7fffffff, %eax - cvtsi2sdl %eax, %xmm1 - comisd %xmm1, %xmm0 - jae 1f - jp 2f - cvttsd2sil %xmm0, %eax - jmp 1f -2: - xorl %eax, %eax -1: - .if 0 - SET_WIDE_VREG %eax, %rcx - .else - SET_VREG %eax, %rcx - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 - - -/* ------------------------------ */ - .balign 128 -.L_op_double_to_long: /* 0x8b */ -/* File: x86_64/op_double_to_long.S */ -/* File: x86_64/cvtfp_int.S */ -/* On fp to int conversions, Java requires that - * if the result > maxint, it should be clamped to maxint. If it is less - * than minint, it should be clamped to minint. If it is a nan, the result - * should be zero. Further, the rounding mode is to truncate. - */ - /* float/double to int/long vA, vB */ - movl rINST, %ecx # rcx <- A+ - sarl $4, rINST # rINST <- B - andb $0xf, %cl # ecx <- A - movsd VREG_ADDRESS(rINSTq), %xmm0 - movq $0x7fffffffffffffff, %rax - cvtsi2sdq %rax, %xmm1 - comisd %xmm1, %xmm0 - jae 1f - jp 2f - cvttsd2siq %xmm0, %rax - jmp 1f -2: - xorq %rax, %rax -1: - .if 1 - SET_WIDE_VREG %rax, %rcx - .else - SET_VREG %rax, %rcx - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 - - -/* ------------------------------ */ - .balign 128 -.L_op_double_to_float: /* 0x8c */ -/* File: x86_64/op_double_to_float.S */ -/* File: x86_64/fpcvt.S */ -/* - * Generic 32-bit FP conversion operation. - */ - /* unop vA, vB */ - movl rINST, %ecx # rcx <- A+ - sarl $4, rINST # rINST <- B - andb $0xf, %cl # ecx <- A - cvtsd2ss VREG_ADDRESS(rINSTq), %xmm0 - .if 0 - movsd %xmm0, VREG_ADDRESS(%rcx) - CLEAR_WIDE_REF %rcx - .else - movss %xmm0, VREG_ADDRESS(%rcx) - CLEAR_REF %rcx - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 - - -/* ------------------------------ */ - .balign 128 -.L_op_int_to_byte: /* 0x8d */ -/* File: x86_64/op_int_to_byte.S */ -/* File: x86_64/unop.S */ -/* - * Generic 32/64-bit unary operation. Provide an "instr" line that - * specifies an instruction that performs "result = op eax". - */ - /* unop vA, vB */ - movl rINST, %ecx # rcx <- A+ - sarl $4,rINST # rINST <- B - .if 0 - GET_WIDE_VREG %rax, rINSTq # rax <- vB - .else - GET_VREG %eax, rINSTq # eax <- vB - .endif - andb $0xf,%cl # ecx <- A - -movsbl %al, %eax - .if 0 - SET_WIDE_VREG %rax, %rcx - .else - SET_VREG %eax, %rcx - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 - - -/* ------------------------------ */ - .balign 128 -.L_op_int_to_char: /* 0x8e */ -/* File: x86_64/op_int_to_char.S */ -/* File: x86_64/unop.S */ -/* - * Generic 32/64-bit unary operation. Provide an "instr" line that - * specifies an instruction that performs "result = op eax". - */ - /* unop vA, vB */ - movl rINST, %ecx # rcx <- A+ - sarl $4,rINST # rINST <- B - .if 0 - GET_WIDE_VREG %rax, rINSTq # rax <- vB - .else - GET_VREG %eax, rINSTq # eax <- vB - .endif - andb $0xf,%cl # ecx <- A - -movzwl %ax,%eax - .if 0 - SET_WIDE_VREG %rax, %rcx - .else - SET_VREG %eax, %rcx - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 - - -/* ------------------------------ */ - .balign 128 -.L_op_int_to_short: /* 0x8f */ -/* File: x86_64/op_int_to_short.S */ -/* File: x86_64/unop.S */ -/* - * Generic 32/64-bit unary operation. Provide an "instr" line that - * specifies an instruction that performs "result = op eax". - */ - /* unop vA, vB */ - movl rINST, %ecx # rcx <- A+ - sarl $4,rINST # rINST <- B - .if 0 - GET_WIDE_VREG %rax, rINSTq # rax <- vB - .else - GET_VREG %eax, rINSTq # eax <- vB - .endif - andb $0xf,%cl # ecx <- A - -movswl %ax, %eax - .if 0 - SET_WIDE_VREG %rax, %rcx - .else - SET_VREG %eax, %rcx - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 - - -/* ------------------------------ */ - .balign 128 -.L_op_add_int: /* 0x90 */ -/* File: x86_64/op_add_int.S */ -/* File: x86_64/binop.S */ -/* - * Generic 32-bit binary operation. Provide an "instr" line that - * specifies an instruction that performs "result = eax op (rFP,%ecx,4)". - * This could be an x86 instruction or a function call. (If the result - * comes back in a register other than eax, you can override "result".) - * - * For: add-int, sub-int, and-int, or-int, - * xor-int, shl-int, shr-int, ushr-int - */ - /* binop vAA, vBB, vCC */ - movzbq 2(rPC), %rax # rax <- BB - movzbq 3(rPC), %rcx # rcx <- CC - GET_VREG %eax, %rax # eax <- vBB - addl (rFP,%rcx,4), %eax # ex: addl (rFP,%rcx,4),%eax - SET_VREG %eax, rINSTq - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_sub_int: /* 0x91 */ -/* File: x86_64/op_sub_int.S */ -/* File: x86_64/binop.S */ -/* - * Generic 32-bit binary operation. Provide an "instr" line that - * specifies an instruction that performs "result = eax op (rFP,%ecx,4)". - * This could be an x86 instruction or a function call. (If the result - * comes back in a register other than eax, you can override "result".) - * - * For: add-int, sub-int, and-int, or-int, - * xor-int, shl-int, shr-int, ushr-int - */ - /* binop vAA, vBB, vCC */ - movzbq 2(rPC), %rax # rax <- BB - movzbq 3(rPC), %rcx # rcx <- CC - GET_VREG %eax, %rax # eax <- vBB - subl (rFP,%rcx,4), %eax # ex: addl (rFP,%rcx,4),%eax - SET_VREG %eax, rINSTq - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_mul_int: /* 0x92 */ -/* File: x86_64/op_mul_int.S */ -/* File: x86_64/binop.S */ -/* - * Generic 32-bit binary operation. Provide an "instr" line that - * specifies an instruction that performs "result = eax op (rFP,%ecx,4)". - * This could be an x86 instruction or a function call. (If the result - * comes back in a register other than eax, you can override "result".) - * - * For: add-int, sub-int, and-int, or-int, - * xor-int, shl-int, shr-int, ushr-int - */ - /* binop vAA, vBB, vCC */ - movzbq 2(rPC), %rax # rax <- BB - movzbq 3(rPC), %rcx # rcx <- CC - GET_VREG %eax, %rax # eax <- vBB - imull (rFP,%rcx,4), %eax # ex: addl (rFP,%rcx,4),%eax - SET_VREG %eax, rINSTq - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_div_int: /* 0x93 */ -/* File: x86_64/op_div_int.S */ -/* File: x86_64/bindiv.S */ -/* - * 32-bit binary div/rem operation. Handles special case of op1=-1. - */ - /* div/rem vAA, vBB, vCC */ - movzbq 2(rPC), %rax # rax <- BB - movzbq 3(rPC), %rcx # rcx <- CC - .if 0 - GET_WIDE_VREG %rax, %rax # eax <- vBB - GET_WIDE_VREG %ecx, %rcx # ecx <- vCC - .else - GET_VREG %eax, %rax # eax <- vBB - GET_VREG %ecx, %rcx # ecx <- vCC - .endif - testl %ecx, %ecx - jz common_errDivideByZero - cmpl $-1, %ecx - je 2f - cdq # rdx:rax <- sign-extended of rax - idivl %ecx -1: - .if 0 - SET_WIDE_VREG %eax, rINSTq # eax <- vBB - .else - SET_VREG %eax, rINSTq # eax <- vBB - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 -2: - .if 0 - xorl %eax, %eax - .else - negl %eax - .endif - jmp 1b - - -/* ------------------------------ */ - .balign 128 -.L_op_rem_int: /* 0x94 */ -/* File: x86_64/op_rem_int.S */ -/* File: x86_64/bindiv.S */ -/* - * 32-bit binary div/rem operation. Handles special case of op1=-1. - */ - /* div/rem vAA, vBB, vCC */ - movzbq 2(rPC), %rax # rax <- BB - movzbq 3(rPC), %rcx # rcx <- CC - .if 0 - GET_WIDE_VREG %rax, %rax # eax <- vBB - GET_WIDE_VREG %ecx, %rcx # ecx <- vCC - .else - GET_VREG %eax, %rax # eax <- vBB - GET_VREG %ecx, %rcx # ecx <- vCC - .endif - testl %ecx, %ecx - jz common_errDivideByZero - cmpl $-1, %ecx - je 2f - cdq # rdx:rax <- sign-extended of rax - idivl %ecx -1: - .if 0 - SET_WIDE_VREG %edx, rINSTq # eax <- vBB - .else - SET_VREG %edx, rINSTq # eax <- vBB - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 -2: - .if 1 - xorl %edx, %edx - .else - negl %edx - .endif - jmp 1b - - -/* ------------------------------ */ - .balign 128 -.L_op_and_int: /* 0x95 */ -/* File: x86_64/op_and_int.S */ -/* File: x86_64/binop.S */ -/* - * Generic 32-bit binary operation. Provide an "instr" line that - * specifies an instruction that performs "result = eax op (rFP,%ecx,4)". - * This could be an x86 instruction or a function call. (If the result - * comes back in a register other than eax, you can override "result".) - * - * For: add-int, sub-int, and-int, or-int, - * xor-int, shl-int, shr-int, ushr-int - */ - /* binop vAA, vBB, vCC */ - movzbq 2(rPC), %rax # rax <- BB - movzbq 3(rPC), %rcx # rcx <- CC - GET_VREG %eax, %rax # eax <- vBB - andl (rFP,%rcx,4), %eax # ex: addl (rFP,%rcx,4),%eax - SET_VREG %eax, rINSTq - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_or_int: /* 0x96 */ -/* File: x86_64/op_or_int.S */ -/* File: x86_64/binop.S */ -/* - * Generic 32-bit binary operation. Provide an "instr" line that - * specifies an instruction that performs "result = eax op (rFP,%ecx,4)". - * This could be an x86 instruction or a function call. (If the result - * comes back in a register other than eax, you can override "result".) - * - * For: add-int, sub-int, and-int, or-int, - * xor-int, shl-int, shr-int, ushr-int - */ - /* binop vAA, vBB, vCC */ - movzbq 2(rPC), %rax # rax <- BB - movzbq 3(rPC), %rcx # rcx <- CC - GET_VREG %eax, %rax # eax <- vBB - orl (rFP,%rcx,4), %eax # ex: addl (rFP,%rcx,4),%eax - SET_VREG %eax, rINSTq - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_xor_int: /* 0x97 */ -/* File: x86_64/op_xor_int.S */ -/* File: x86_64/binop.S */ -/* - * Generic 32-bit binary operation. Provide an "instr" line that - * specifies an instruction that performs "result = eax op (rFP,%ecx,4)". - * This could be an x86 instruction or a function call. (If the result - * comes back in a register other than eax, you can override "result".) - * - * For: add-int, sub-int, and-int, or-int, - * xor-int, shl-int, shr-int, ushr-int - */ - /* binop vAA, vBB, vCC */ - movzbq 2(rPC), %rax # rax <- BB - movzbq 3(rPC), %rcx # rcx <- CC - GET_VREG %eax, %rax # eax <- vBB - xorl (rFP,%rcx,4), %eax # ex: addl (rFP,%rcx,4),%eax - SET_VREG %eax, rINSTq - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_shl_int: /* 0x98 */ -/* File: x86_64/op_shl_int.S */ -/* File: x86_64/binop1.S */ -/* - * Generic 32-bit binary operation in which both operands loaded to - * registers (op0 in eax, op1 in ecx). - */ - /* binop vAA, vBB, vCC */ - movzbq 2(rPC), %rax # eax <- BB - movzbq 3(rPC), %rcx # ecx <- CC - GET_VREG %ecx, %rcx # eax <- vCC - .if 0 - GET_WIDE_VREG %rax, %rax # rax <- vBB - sall %cl, %eax # ex: addl %ecx,%eax - SET_WIDE_VREG %rax, rINSTq - .else - GET_VREG %eax, %rax # eax <- vBB - sall %cl, %eax # ex: addl %ecx,%eax - SET_VREG %eax, rINSTq - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_shr_int: /* 0x99 */ -/* File: x86_64/op_shr_int.S */ -/* File: x86_64/binop1.S */ -/* - * Generic 32-bit binary operation in which both operands loaded to - * registers (op0 in eax, op1 in ecx). - */ - /* binop vAA, vBB, vCC */ - movzbq 2(rPC), %rax # eax <- BB - movzbq 3(rPC), %rcx # ecx <- CC - GET_VREG %ecx, %rcx # eax <- vCC - .if 0 - GET_WIDE_VREG %rax, %rax # rax <- vBB - sarl %cl, %eax # ex: addl %ecx,%eax - SET_WIDE_VREG %rax, rINSTq - .else - GET_VREG %eax, %rax # eax <- vBB - sarl %cl, %eax # ex: addl %ecx,%eax - SET_VREG %eax, rINSTq - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_ushr_int: /* 0x9a */ -/* File: x86_64/op_ushr_int.S */ -/* File: x86_64/binop1.S */ -/* - * Generic 32-bit binary operation in which both operands loaded to - * registers (op0 in eax, op1 in ecx). - */ - /* binop vAA, vBB, vCC */ - movzbq 2(rPC), %rax # eax <- BB - movzbq 3(rPC), %rcx # ecx <- CC - GET_VREG %ecx, %rcx # eax <- vCC - .if 0 - GET_WIDE_VREG %rax, %rax # rax <- vBB - shrl %cl, %eax # ex: addl %ecx,%eax - SET_WIDE_VREG %rax, rINSTq - .else - GET_VREG %eax, %rax # eax <- vBB - shrl %cl, %eax # ex: addl %ecx,%eax - SET_VREG %eax, rINSTq - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_add_long: /* 0x9b */ -/* File: x86_64/op_add_long.S */ -/* File: x86_64/binopWide.S */ -/* - * Generic 64-bit binary operation. - */ - /* binop vAA, vBB, vCC */ - movzbq 2(rPC), %rax # eax <- BB - movzbq 3(rPC), %rcx # ecx <- CC - GET_WIDE_VREG %rax, %rax # rax <- v[BB] - addq (rFP,%rcx,4), %rax # ex: addq (rFP,%rcx,4),%rax - SET_WIDE_VREG %rax, rINSTq # v[AA] <- rax - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_sub_long: /* 0x9c */ -/* File: x86_64/op_sub_long.S */ -/* File: x86_64/binopWide.S */ -/* - * Generic 64-bit binary operation. - */ - /* binop vAA, vBB, vCC */ - movzbq 2(rPC), %rax # eax <- BB - movzbq 3(rPC), %rcx # ecx <- CC - GET_WIDE_VREG %rax, %rax # rax <- v[BB] - subq (rFP,%rcx,4), %rax # ex: addq (rFP,%rcx,4),%rax - SET_WIDE_VREG %rax, rINSTq # v[AA] <- rax - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_mul_long: /* 0x9d */ -/* File: x86_64/op_mul_long.S */ -/* File: x86_64/binopWide.S */ -/* - * Generic 64-bit binary operation. - */ - /* binop vAA, vBB, vCC */ - movzbq 2(rPC), %rax # eax <- BB - movzbq 3(rPC), %rcx # ecx <- CC - GET_WIDE_VREG %rax, %rax # rax <- v[BB] - imulq (rFP,%rcx,4), %rax # ex: addq (rFP,%rcx,4),%rax - SET_WIDE_VREG %rax, rINSTq # v[AA] <- rax - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_div_long: /* 0x9e */ -/* File: x86_64/op_div_long.S */ -/* File: x86_64/bindiv.S */ -/* - * 32-bit binary div/rem operation. Handles special case of op1=-1. - */ - /* div/rem vAA, vBB, vCC */ - movzbq 2(rPC), %rax # rax <- BB - movzbq 3(rPC), %rcx # rcx <- CC - .if 1 - GET_WIDE_VREG %rax, %rax # eax <- vBB - GET_WIDE_VREG %rcx, %rcx # ecx <- vCC - .else - GET_VREG %eax, %rax # eax <- vBB - GET_VREG %rcx, %rcx # ecx <- vCC - .endif - testq %rcx, %rcx - jz common_errDivideByZero - cmpq $-1, %rcx - je 2f - cqo # rdx:rax <- sign-extended of rax - idivq %rcx -1: - .if 1 - SET_WIDE_VREG %rax, rINSTq # eax <- vBB - .else - SET_VREG %rax, rINSTq # eax <- vBB - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 -2: - .if 0 - xorq %rax, %rax - .else - negq %rax - .endif - jmp 1b - - -/* ------------------------------ */ - .balign 128 -.L_op_rem_long: /* 0x9f */ -/* File: x86_64/op_rem_long.S */ -/* File: x86_64/bindiv.S */ -/* - * 32-bit binary div/rem operation. Handles special case of op1=-1. - */ - /* div/rem vAA, vBB, vCC */ - movzbq 2(rPC), %rax # rax <- BB - movzbq 3(rPC), %rcx # rcx <- CC - .if 1 - GET_WIDE_VREG %rax, %rax # eax <- vBB - GET_WIDE_VREG %rcx, %rcx # ecx <- vCC - .else - GET_VREG %eax, %rax # eax <- vBB - GET_VREG %rcx, %rcx # ecx <- vCC - .endif - testq %rcx, %rcx - jz common_errDivideByZero - cmpq $-1, %rcx - je 2f - cqo # rdx:rax <- sign-extended of rax - idivq %rcx -1: - .if 1 - SET_WIDE_VREG %rdx, rINSTq # eax <- vBB - .else - SET_VREG %rdx, rINSTq # eax <- vBB - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 -2: - .if 1 - xorq %rdx, %rdx - .else - negq %rdx - .endif - jmp 1b - - -/* ------------------------------ */ - .balign 128 -.L_op_and_long: /* 0xa0 */ -/* File: x86_64/op_and_long.S */ -/* File: x86_64/binopWide.S */ -/* - * Generic 64-bit binary operation. - */ - /* binop vAA, vBB, vCC */ - movzbq 2(rPC), %rax # eax <- BB - movzbq 3(rPC), %rcx # ecx <- CC - GET_WIDE_VREG %rax, %rax # rax <- v[BB] - andq (rFP,%rcx,4), %rax # ex: addq (rFP,%rcx,4),%rax - SET_WIDE_VREG %rax, rINSTq # v[AA] <- rax - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_or_long: /* 0xa1 */ -/* File: x86_64/op_or_long.S */ -/* File: x86_64/binopWide.S */ -/* - * Generic 64-bit binary operation. - */ - /* binop vAA, vBB, vCC */ - movzbq 2(rPC), %rax # eax <- BB - movzbq 3(rPC), %rcx # ecx <- CC - GET_WIDE_VREG %rax, %rax # rax <- v[BB] - orq (rFP,%rcx,4), %rax # ex: addq (rFP,%rcx,4),%rax - SET_WIDE_VREG %rax, rINSTq # v[AA] <- rax - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_xor_long: /* 0xa2 */ -/* File: x86_64/op_xor_long.S */ -/* File: x86_64/binopWide.S */ -/* - * Generic 64-bit binary operation. - */ - /* binop vAA, vBB, vCC */ - movzbq 2(rPC), %rax # eax <- BB - movzbq 3(rPC), %rcx # ecx <- CC - GET_WIDE_VREG %rax, %rax # rax <- v[BB] - xorq (rFP,%rcx,4), %rax # ex: addq (rFP,%rcx,4),%rax - SET_WIDE_VREG %rax, rINSTq # v[AA] <- rax - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_shl_long: /* 0xa3 */ -/* File: x86_64/op_shl_long.S */ -/* File: x86_64/binop1.S */ -/* - * Generic 32-bit binary operation in which both operands loaded to - * registers (op0 in eax, op1 in ecx). - */ - /* binop vAA, vBB, vCC */ - movzbq 2(rPC), %rax # eax <- BB - movzbq 3(rPC), %rcx # ecx <- CC - GET_VREG %ecx, %rcx # eax <- vCC - .if 1 - GET_WIDE_VREG %rax, %rax # rax <- vBB - salq %cl, %rax # ex: addl %ecx,%eax - SET_WIDE_VREG %rax, rINSTq - .else - GET_VREG %eax, %rax # eax <- vBB - salq %cl, %rax # ex: addl %ecx,%eax - SET_VREG %eax, rINSTq - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_shr_long: /* 0xa4 */ -/* File: x86_64/op_shr_long.S */ -/* File: x86_64/binop1.S */ -/* - * Generic 32-bit binary operation in which both operands loaded to - * registers (op0 in eax, op1 in ecx). - */ - /* binop vAA, vBB, vCC */ - movzbq 2(rPC), %rax # eax <- BB - movzbq 3(rPC), %rcx # ecx <- CC - GET_VREG %ecx, %rcx # eax <- vCC - .if 1 - GET_WIDE_VREG %rax, %rax # rax <- vBB - sarq %cl, %rax # ex: addl %ecx,%eax - SET_WIDE_VREG %rax, rINSTq - .else - GET_VREG %eax, %rax # eax <- vBB - sarq %cl, %rax # ex: addl %ecx,%eax - SET_VREG %eax, rINSTq - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_ushr_long: /* 0xa5 */ -/* File: x86_64/op_ushr_long.S */ -/* File: x86_64/binop1.S */ -/* - * Generic 32-bit binary operation in which both operands loaded to - * registers (op0 in eax, op1 in ecx). - */ - /* binop vAA, vBB, vCC */ - movzbq 2(rPC), %rax # eax <- BB - movzbq 3(rPC), %rcx # ecx <- CC - GET_VREG %ecx, %rcx # eax <- vCC - .if 1 - GET_WIDE_VREG %rax, %rax # rax <- vBB - shrq %cl, %rax # ex: addl %ecx,%eax - SET_WIDE_VREG %rax, rINSTq - .else - GET_VREG %eax, %rax # eax <- vBB - shrq %cl, %rax # ex: addl %ecx,%eax - SET_VREG %eax, rINSTq - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_add_float: /* 0xa6 */ -/* File: x86_64/op_add_float.S */ -/* File: x86_64/sseBinop.S */ - movzbq 2(rPC), %rcx # ecx <- BB - movzbq 3(rPC), %rax # eax <- CC - movss VREG_ADDRESS(%rcx), %xmm0 # %xmm0 <- 1st src - addss VREG_ADDRESS(%rax), %xmm0 - movss %xmm0, VREG_ADDRESS(rINSTq) # vAA <- %xmm0 - pxor %xmm0, %xmm0 - movss %xmm0, VREG_REF_ADDRESS(rINSTq) # clear ref - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_sub_float: /* 0xa7 */ -/* File: x86_64/op_sub_float.S */ -/* File: x86_64/sseBinop.S */ - movzbq 2(rPC), %rcx # ecx <- BB - movzbq 3(rPC), %rax # eax <- CC - movss VREG_ADDRESS(%rcx), %xmm0 # %xmm0 <- 1st src - subss VREG_ADDRESS(%rax), %xmm0 - movss %xmm0, VREG_ADDRESS(rINSTq) # vAA <- %xmm0 - pxor %xmm0, %xmm0 - movss %xmm0, VREG_REF_ADDRESS(rINSTq) # clear ref - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_mul_float: /* 0xa8 */ -/* File: x86_64/op_mul_float.S */ -/* File: x86_64/sseBinop.S */ - movzbq 2(rPC), %rcx # ecx <- BB - movzbq 3(rPC), %rax # eax <- CC - movss VREG_ADDRESS(%rcx), %xmm0 # %xmm0 <- 1st src - mulss VREG_ADDRESS(%rax), %xmm0 - movss %xmm0, VREG_ADDRESS(rINSTq) # vAA <- %xmm0 - pxor %xmm0, %xmm0 - movss %xmm0, VREG_REF_ADDRESS(rINSTq) # clear ref - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_div_float: /* 0xa9 */ -/* File: x86_64/op_div_float.S */ -/* File: x86_64/sseBinop.S */ - movzbq 2(rPC), %rcx # ecx <- BB - movzbq 3(rPC), %rax # eax <- CC - movss VREG_ADDRESS(%rcx), %xmm0 # %xmm0 <- 1st src - divss VREG_ADDRESS(%rax), %xmm0 - movss %xmm0, VREG_ADDRESS(rINSTq) # vAA <- %xmm0 - pxor %xmm0, %xmm0 - movss %xmm0, VREG_REF_ADDRESS(rINSTq) # clear ref - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_rem_float: /* 0xaa */ -/* File: x86_64/op_rem_float.S */ - /* rem_float vAA, vBB, vCC */ - movzbq 3(rPC), %rcx # ecx <- BB - movzbq 2(rPC), %rax # eax <- CC - flds VREG_ADDRESS(%rcx) # vBB to fp stack - flds VREG_ADDRESS(%rax) # vCC to fp stack -1: - fprem - fstsw %ax - sahf - jp 1b - fstp %st(1) - fstps VREG_ADDRESS(rINSTq) # %st to vAA - CLEAR_REF rINSTq - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - -/* ------------------------------ */ - .balign 128 -.L_op_add_double: /* 0xab */ -/* File: x86_64/op_add_double.S */ -/* File: x86_64/sseBinop.S */ - movzbq 2(rPC), %rcx # ecx <- BB - movzbq 3(rPC), %rax # eax <- CC - movsd VREG_ADDRESS(%rcx), %xmm0 # %xmm0 <- 1st src - addsd VREG_ADDRESS(%rax), %xmm0 - movsd %xmm0, VREG_ADDRESS(rINSTq) # vAA <- %xmm0 - pxor %xmm0, %xmm0 - movsd %xmm0, VREG_REF_ADDRESS(rINSTq) # clear ref - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_sub_double: /* 0xac */ -/* File: x86_64/op_sub_double.S */ -/* File: x86_64/sseBinop.S */ - movzbq 2(rPC), %rcx # ecx <- BB - movzbq 3(rPC), %rax # eax <- CC - movsd VREG_ADDRESS(%rcx), %xmm0 # %xmm0 <- 1st src - subsd VREG_ADDRESS(%rax), %xmm0 - movsd %xmm0, VREG_ADDRESS(rINSTq) # vAA <- %xmm0 - pxor %xmm0, %xmm0 - movsd %xmm0, VREG_REF_ADDRESS(rINSTq) # clear ref - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_mul_double: /* 0xad */ -/* File: x86_64/op_mul_double.S */ -/* File: x86_64/sseBinop.S */ - movzbq 2(rPC), %rcx # ecx <- BB - movzbq 3(rPC), %rax # eax <- CC - movsd VREG_ADDRESS(%rcx), %xmm0 # %xmm0 <- 1st src - mulsd VREG_ADDRESS(%rax), %xmm0 - movsd %xmm0, VREG_ADDRESS(rINSTq) # vAA <- %xmm0 - pxor %xmm0, %xmm0 - movsd %xmm0, VREG_REF_ADDRESS(rINSTq) # clear ref - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_div_double: /* 0xae */ -/* File: x86_64/op_div_double.S */ -/* File: x86_64/sseBinop.S */ - movzbq 2(rPC), %rcx # ecx <- BB - movzbq 3(rPC), %rax # eax <- CC - movsd VREG_ADDRESS(%rcx), %xmm0 # %xmm0 <- 1st src - divsd VREG_ADDRESS(%rax), %xmm0 - movsd %xmm0, VREG_ADDRESS(rINSTq) # vAA <- %xmm0 - pxor %xmm0, %xmm0 - movsd %xmm0, VREG_REF_ADDRESS(rINSTq) # clear ref - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_rem_double: /* 0xaf */ -/* File: x86_64/op_rem_double.S */ - /* rem_double vAA, vBB, vCC */ - movzbq 3(rPC), %rcx # ecx <- BB - movzbq 2(rPC), %rax # eax <- CC - fldl VREG_ADDRESS(%rcx) # %st1 <- fp[vBB] - fldl VREG_ADDRESS(%rax) # %st0 <- fp[vCC] -1: - fprem - fstsw %ax - sahf - jp 1b - fstp %st(1) - fstpl VREG_ADDRESS(rINSTq) # fp[vAA] <- %st - CLEAR_WIDE_REF rINSTq - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - -/* ------------------------------ */ - .balign 128 -.L_op_add_int_2addr: /* 0xb0 */ -/* File: x86_64/op_add_int_2addr.S */ -/* File: x86_64/binop2addr.S */ -/* - * Generic 32-bit "/2addr" binary operation. Provide an "instr" line - * that specifies an instruction that performs "result = r0 op r1". - * This could be an instruction or a function call. - * - * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, - * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, - * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr, - * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr - */ - /* binop/2addr vA, vB */ - movl rINST, %ecx # rcx <- A+ - sarl $4, rINST # rINST <- B - andb $0xf, %cl # ecx <- A - GET_VREG %eax, rINSTq # eax <- vB - addl %eax, (rFP,%rcx,4) # for ex: addl %eax,(rFP,%ecx,4) - CLEAR_REF %rcx - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 - - -/* ------------------------------ */ - .balign 128 -.L_op_sub_int_2addr: /* 0xb1 */ -/* File: x86_64/op_sub_int_2addr.S */ -/* File: x86_64/binop2addr.S */ -/* - * Generic 32-bit "/2addr" binary operation. Provide an "instr" line - * that specifies an instruction that performs "result = r0 op r1". - * This could be an instruction or a function call. - * - * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, - * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, - * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr, - * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr - */ - /* binop/2addr vA, vB */ - movl rINST, %ecx # rcx <- A+ - sarl $4, rINST # rINST <- B - andb $0xf, %cl # ecx <- A - GET_VREG %eax, rINSTq # eax <- vB - subl %eax, (rFP,%rcx,4) # for ex: addl %eax,(rFP,%ecx,4) - CLEAR_REF %rcx - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 - - -/* ------------------------------ */ - .balign 128 -.L_op_mul_int_2addr: /* 0xb2 */ -/* File: x86_64/op_mul_int_2addr.S */ - /* mul vA, vB */ - movl rINST, %ecx # rcx <- A+ - sarl $4, rINST # rINST <- B - andb $0xf, %cl # ecx <- A - GET_VREG %eax, %rcx # eax <- vA - imull (rFP,rINSTq,4), %eax - SET_VREG %eax, %rcx - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 - -/* ------------------------------ */ - .balign 128 -.L_op_div_int_2addr: /* 0xb3 */ -/* File: x86_64/op_div_int_2addr.S */ -/* File: x86_64/bindiv2addr.S */ -/* - * 32-bit binary div/rem operation. Handles special case of op1=-1. - */ - /* div/rem/2addr vA, vB */ - movl rINST, %ecx # rcx <- BA - sarl $4, %ecx # rcx <- B - andb $0xf, rINSTbl # rINST <- A - .if 0 - GET_WIDE_VREG %rax, rINSTq # eax <- vA - GET_WIDE_VREG %ecx, %rcx # ecx <- vB - .else - GET_VREG %eax, rINSTq # eax <- vA - GET_VREG %ecx, %rcx # ecx <- vB - .endif - testl %ecx, %ecx - jz common_errDivideByZero - cmpl $-1, %ecx - je 2f - cdq # rdx:rax <- sign-extended of rax - idivl %ecx -1: - .if 0 - SET_WIDE_VREG %eax, rINSTq # vA <- result - .else - SET_VREG %eax, rINSTq # vA <- result - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 -2: - .if 0 - xorl %eax, %eax - .else - negl %eax - .endif - jmp 1b - - -/* ------------------------------ */ - .balign 128 -.L_op_rem_int_2addr: /* 0xb4 */ -/* File: x86_64/op_rem_int_2addr.S */ -/* File: x86_64/bindiv2addr.S */ -/* - * 32-bit binary div/rem operation. Handles special case of op1=-1. - */ - /* div/rem/2addr vA, vB */ - movl rINST, %ecx # rcx <- BA - sarl $4, %ecx # rcx <- B - andb $0xf, rINSTbl # rINST <- A - .if 0 - GET_WIDE_VREG %rax, rINSTq # eax <- vA - GET_WIDE_VREG %ecx, %rcx # ecx <- vB - .else - GET_VREG %eax, rINSTq # eax <- vA - GET_VREG %ecx, %rcx # ecx <- vB - .endif - testl %ecx, %ecx - jz common_errDivideByZero - cmpl $-1, %ecx - je 2f - cdq # rdx:rax <- sign-extended of rax - idivl %ecx -1: - .if 0 - SET_WIDE_VREG %edx, rINSTq # vA <- result - .else - SET_VREG %edx, rINSTq # vA <- result - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 -2: - .if 1 - xorl %edx, %edx - .else - negl %edx - .endif - jmp 1b - - -/* ------------------------------ */ - .balign 128 -.L_op_and_int_2addr: /* 0xb5 */ -/* File: x86_64/op_and_int_2addr.S */ -/* File: x86_64/binop2addr.S */ -/* - * Generic 32-bit "/2addr" binary operation. Provide an "instr" line - * that specifies an instruction that performs "result = r0 op r1". - * This could be an instruction or a function call. - * - * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, - * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, - * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr, - * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr - */ - /* binop/2addr vA, vB */ - movl rINST, %ecx # rcx <- A+ - sarl $4, rINST # rINST <- B - andb $0xf, %cl # ecx <- A - GET_VREG %eax, rINSTq # eax <- vB - andl %eax, (rFP,%rcx,4) # for ex: addl %eax,(rFP,%ecx,4) - CLEAR_REF %rcx - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 - - -/* ------------------------------ */ - .balign 128 -.L_op_or_int_2addr: /* 0xb6 */ -/* File: x86_64/op_or_int_2addr.S */ -/* File: x86_64/binop2addr.S */ -/* - * Generic 32-bit "/2addr" binary operation. Provide an "instr" line - * that specifies an instruction that performs "result = r0 op r1". - * This could be an instruction or a function call. - * - * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, - * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, - * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr, - * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr - */ - /* binop/2addr vA, vB */ - movl rINST, %ecx # rcx <- A+ - sarl $4, rINST # rINST <- B - andb $0xf, %cl # ecx <- A - GET_VREG %eax, rINSTq # eax <- vB - orl %eax, (rFP,%rcx,4) # for ex: addl %eax,(rFP,%ecx,4) - CLEAR_REF %rcx - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 - - -/* ------------------------------ */ - .balign 128 -.L_op_xor_int_2addr: /* 0xb7 */ -/* File: x86_64/op_xor_int_2addr.S */ -/* File: x86_64/binop2addr.S */ -/* - * Generic 32-bit "/2addr" binary operation. Provide an "instr" line - * that specifies an instruction that performs "result = r0 op r1". - * This could be an instruction or a function call. - * - * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, - * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, - * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr, - * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr - */ - /* binop/2addr vA, vB */ - movl rINST, %ecx # rcx <- A+ - sarl $4, rINST # rINST <- B - andb $0xf, %cl # ecx <- A - GET_VREG %eax, rINSTq # eax <- vB - xorl %eax, (rFP,%rcx,4) # for ex: addl %eax,(rFP,%ecx,4) - CLEAR_REF %rcx - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 - - -/* ------------------------------ */ - .balign 128 -.L_op_shl_int_2addr: /* 0xb8 */ -/* File: x86_64/op_shl_int_2addr.S */ -/* File: x86_64/shop2addr.S */ -/* - * Generic 32-bit "shift/2addr" operation. - */ - /* shift/2addr vA, vB */ - movl rINST, %ecx # ecx <- BA - sarl $4, %ecx # ecx <- B - GET_VREG %ecx, %rcx # ecx <- vBB - andb $0xf, rINSTbl # rINST <- A - .if 0 - GET_WIDE_VREG %rax, rINSTq # rax <- vAA - sall %cl, %eax # ex: sarl %cl, %eax - SET_WIDE_VREG %rax, rINSTq - .else - GET_VREG %eax, rINSTq # eax <- vAA - sall %cl, %eax # ex: sarl %cl, %eax - SET_VREG %eax, rINSTq - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 - - -/* ------------------------------ */ - .balign 128 -.L_op_shr_int_2addr: /* 0xb9 */ -/* File: x86_64/op_shr_int_2addr.S */ -/* File: x86_64/shop2addr.S */ -/* - * Generic 32-bit "shift/2addr" operation. - */ - /* shift/2addr vA, vB */ - movl rINST, %ecx # ecx <- BA - sarl $4, %ecx # ecx <- B - GET_VREG %ecx, %rcx # ecx <- vBB - andb $0xf, rINSTbl # rINST <- A - .if 0 - GET_WIDE_VREG %rax, rINSTq # rax <- vAA - sarl %cl, %eax # ex: sarl %cl, %eax - SET_WIDE_VREG %rax, rINSTq - .else - GET_VREG %eax, rINSTq # eax <- vAA - sarl %cl, %eax # ex: sarl %cl, %eax - SET_VREG %eax, rINSTq - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 - - -/* ------------------------------ */ - .balign 128 -.L_op_ushr_int_2addr: /* 0xba */ -/* File: x86_64/op_ushr_int_2addr.S */ -/* File: x86_64/shop2addr.S */ -/* - * Generic 32-bit "shift/2addr" operation. - */ - /* shift/2addr vA, vB */ - movl rINST, %ecx # ecx <- BA - sarl $4, %ecx # ecx <- B - GET_VREG %ecx, %rcx # ecx <- vBB - andb $0xf, rINSTbl # rINST <- A - .if 0 - GET_WIDE_VREG %rax, rINSTq # rax <- vAA - shrl %cl, %eax # ex: sarl %cl, %eax - SET_WIDE_VREG %rax, rINSTq - .else - GET_VREG %eax, rINSTq # eax <- vAA - shrl %cl, %eax # ex: sarl %cl, %eax - SET_VREG %eax, rINSTq - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 - - -/* ------------------------------ */ - .balign 128 -.L_op_add_long_2addr: /* 0xbb */ -/* File: x86_64/op_add_long_2addr.S */ -/* File: x86_64/binopWide2addr.S */ -/* - * Generic 64-bit binary operation. - */ - /* binop/2addr vA, vB */ - movl rINST, %ecx # rcx <- A+ - sarl $4, rINST # rINST <- B - andb $0xf, %cl # ecx <- A - GET_WIDE_VREG %rax, rINSTq # rax <- vB - addq %rax, (rFP,%rcx,4) # for ex: addq %rax,(rFP,%rcx,4) - CLEAR_WIDE_REF %rcx - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 - - -/* ------------------------------ */ - .balign 128 -.L_op_sub_long_2addr: /* 0xbc */ -/* File: x86_64/op_sub_long_2addr.S */ -/* File: x86_64/binopWide2addr.S */ -/* - * Generic 64-bit binary operation. - */ - /* binop/2addr vA, vB */ - movl rINST, %ecx # rcx <- A+ - sarl $4, rINST # rINST <- B - andb $0xf, %cl # ecx <- A - GET_WIDE_VREG %rax, rINSTq # rax <- vB - subq %rax, (rFP,%rcx,4) # for ex: addq %rax,(rFP,%rcx,4) - CLEAR_WIDE_REF %rcx - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 - - -/* ------------------------------ */ - .balign 128 -.L_op_mul_long_2addr: /* 0xbd */ -/* File: x86_64/op_mul_long_2addr.S */ - /* mul vA, vB */ - movl rINST, %ecx # rcx <- A+ - sarl $4, rINST # rINST <- B - andb $0xf, %cl # ecx <- A - GET_WIDE_VREG %rax, %rcx # rax <- vA - imulq (rFP,rINSTq,4), %rax - SET_WIDE_VREG %rax, %rcx - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 - -/* ------------------------------ */ - .balign 128 -.L_op_div_long_2addr: /* 0xbe */ -/* File: x86_64/op_div_long_2addr.S */ -/* File: x86_64/bindiv2addr.S */ -/* - * 32-bit binary div/rem operation. Handles special case of op1=-1. - */ - /* div/rem/2addr vA, vB */ - movl rINST, %ecx # rcx <- BA - sarl $4, %ecx # rcx <- B - andb $0xf, rINSTbl # rINST <- A - .if 1 - GET_WIDE_VREG %rax, rINSTq # eax <- vA - GET_WIDE_VREG %rcx, %rcx # ecx <- vB - .else - GET_VREG %eax, rINSTq # eax <- vA - GET_VREG %rcx, %rcx # ecx <- vB - .endif - testq %rcx, %rcx - jz common_errDivideByZero - cmpq $-1, %rcx - je 2f - cqo # rdx:rax <- sign-extended of rax - idivq %rcx -1: - .if 1 - SET_WIDE_VREG %rax, rINSTq # vA <- result - .else - SET_VREG %rax, rINSTq # vA <- result - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 -2: - .if 0 - xorq %rax, %rax - .else - negq %rax - .endif - jmp 1b - - -/* ------------------------------ */ - .balign 128 -.L_op_rem_long_2addr: /* 0xbf */ -/* File: x86_64/op_rem_long_2addr.S */ -/* File: x86_64/bindiv2addr.S */ -/* - * 32-bit binary div/rem operation. Handles special case of op1=-1. - */ - /* div/rem/2addr vA, vB */ - movl rINST, %ecx # rcx <- BA - sarl $4, %ecx # rcx <- B - andb $0xf, rINSTbl # rINST <- A - .if 1 - GET_WIDE_VREG %rax, rINSTq # eax <- vA - GET_WIDE_VREG %rcx, %rcx # ecx <- vB - .else - GET_VREG %eax, rINSTq # eax <- vA - GET_VREG %rcx, %rcx # ecx <- vB - .endif - testq %rcx, %rcx - jz common_errDivideByZero - cmpq $-1, %rcx - je 2f - cqo # rdx:rax <- sign-extended of rax - idivq %rcx -1: - .if 1 - SET_WIDE_VREG %rdx, rINSTq # vA <- result - .else - SET_VREG %rdx, rINSTq # vA <- result - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 -2: - .if 1 - xorq %rdx, %rdx - .else - negq %rdx - .endif - jmp 1b - - -/* ------------------------------ */ - .balign 128 -.L_op_and_long_2addr: /* 0xc0 */ -/* File: x86_64/op_and_long_2addr.S */ -/* File: x86_64/binopWide2addr.S */ -/* - * Generic 64-bit binary operation. - */ - /* binop/2addr vA, vB */ - movl rINST, %ecx # rcx <- A+ - sarl $4, rINST # rINST <- B - andb $0xf, %cl # ecx <- A - GET_WIDE_VREG %rax, rINSTq # rax <- vB - andq %rax, (rFP,%rcx,4) # for ex: addq %rax,(rFP,%rcx,4) - CLEAR_WIDE_REF %rcx - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 - - -/* ------------------------------ */ - .balign 128 -.L_op_or_long_2addr: /* 0xc1 */ -/* File: x86_64/op_or_long_2addr.S */ -/* File: x86_64/binopWide2addr.S */ -/* - * Generic 64-bit binary operation. - */ - /* binop/2addr vA, vB */ - movl rINST, %ecx # rcx <- A+ - sarl $4, rINST # rINST <- B - andb $0xf, %cl # ecx <- A - GET_WIDE_VREG %rax, rINSTq # rax <- vB - orq %rax, (rFP,%rcx,4) # for ex: addq %rax,(rFP,%rcx,4) - CLEAR_WIDE_REF %rcx - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 - - -/* ------------------------------ */ - .balign 128 -.L_op_xor_long_2addr: /* 0xc2 */ -/* File: x86_64/op_xor_long_2addr.S */ -/* File: x86_64/binopWide2addr.S */ -/* - * Generic 64-bit binary operation. - */ - /* binop/2addr vA, vB */ - movl rINST, %ecx # rcx <- A+ - sarl $4, rINST # rINST <- B - andb $0xf, %cl # ecx <- A - GET_WIDE_VREG %rax, rINSTq # rax <- vB - xorq %rax, (rFP,%rcx,4) # for ex: addq %rax,(rFP,%rcx,4) - CLEAR_WIDE_REF %rcx - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 - - -/* ------------------------------ */ - .balign 128 -.L_op_shl_long_2addr: /* 0xc3 */ -/* File: x86_64/op_shl_long_2addr.S */ -/* File: x86_64/shop2addr.S */ -/* - * Generic 32-bit "shift/2addr" operation. - */ - /* shift/2addr vA, vB */ - movl rINST, %ecx # ecx <- BA - sarl $4, %ecx # ecx <- B - GET_VREG %ecx, %rcx # ecx <- vBB - andb $0xf, rINSTbl # rINST <- A - .if 1 - GET_WIDE_VREG %rax, rINSTq # rax <- vAA - salq %cl, %rax # ex: sarl %cl, %eax - SET_WIDE_VREG %rax, rINSTq - .else - GET_VREG %eax, rINSTq # eax <- vAA - salq %cl, %rax # ex: sarl %cl, %eax - SET_VREG %eax, rINSTq - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 - - -/* ------------------------------ */ - .balign 128 -.L_op_shr_long_2addr: /* 0xc4 */ -/* File: x86_64/op_shr_long_2addr.S */ -/* File: x86_64/shop2addr.S */ -/* - * Generic 32-bit "shift/2addr" operation. - */ - /* shift/2addr vA, vB */ - movl rINST, %ecx # ecx <- BA - sarl $4, %ecx # ecx <- B - GET_VREG %ecx, %rcx # ecx <- vBB - andb $0xf, rINSTbl # rINST <- A - .if 1 - GET_WIDE_VREG %rax, rINSTq # rax <- vAA - sarq %cl, %rax # ex: sarl %cl, %eax - SET_WIDE_VREG %rax, rINSTq - .else - GET_VREG %eax, rINSTq # eax <- vAA - sarq %cl, %rax # ex: sarl %cl, %eax - SET_VREG %eax, rINSTq - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 - - -/* ------------------------------ */ - .balign 128 -.L_op_ushr_long_2addr: /* 0xc5 */ -/* File: x86_64/op_ushr_long_2addr.S */ -/* File: x86_64/shop2addr.S */ -/* - * Generic 32-bit "shift/2addr" operation. - */ - /* shift/2addr vA, vB */ - movl rINST, %ecx # ecx <- BA - sarl $4, %ecx # ecx <- B - GET_VREG %ecx, %rcx # ecx <- vBB - andb $0xf, rINSTbl # rINST <- A - .if 1 - GET_WIDE_VREG %rax, rINSTq # rax <- vAA - shrq %cl, %rax # ex: sarl %cl, %eax - SET_WIDE_VREG %rax, rINSTq - .else - GET_VREG %eax, rINSTq # eax <- vAA - shrq %cl, %rax # ex: sarl %cl, %eax - SET_VREG %eax, rINSTq - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 - - -/* ------------------------------ */ - .balign 128 -.L_op_add_float_2addr: /* 0xc6 */ -/* File: x86_64/op_add_float_2addr.S */ -/* File: x86_64/sseBinop2Addr.S */ - movl rINST, %ecx # ecx <- A+ - andl $0xf, %ecx # ecx <- A - movss VREG_ADDRESS(%rcx), %xmm0 # %xmm0 <- 1st src - sarl $4, rINST # rINST<- B - addss VREG_ADDRESS(rINSTq), %xmm0 - movss %xmm0, VREG_ADDRESS(%rcx) # vAA<- %xmm0 - pxor %xmm0, %xmm0 - movss %xmm0, VREG_REF_ADDRESS(rINSTq) # clear ref - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 - - -/* ------------------------------ */ - .balign 128 -.L_op_sub_float_2addr: /* 0xc7 */ -/* File: x86_64/op_sub_float_2addr.S */ -/* File: x86_64/sseBinop2Addr.S */ - movl rINST, %ecx # ecx <- A+ - andl $0xf, %ecx # ecx <- A - movss VREG_ADDRESS(%rcx), %xmm0 # %xmm0 <- 1st src - sarl $4, rINST # rINST<- B - subss VREG_ADDRESS(rINSTq), %xmm0 - movss %xmm0, VREG_ADDRESS(%rcx) # vAA<- %xmm0 - pxor %xmm0, %xmm0 - movss %xmm0, VREG_REF_ADDRESS(rINSTq) # clear ref - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 - - -/* ------------------------------ */ - .balign 128 -.L_op_mul_float_2addr: /* 0xc8 */ -/* File: x86_64/op_mul_float_2addr.S */ -/* File: x86_64/sseBinop2Addr.S */ - movl rINST, %ecx # ecx <- A+ - andl $0xf, %ecx # ecx <- A - movss VREG_ADDRESS(%rcx), %xmm0 # %xmm0 <- 1st src - sarl $4, rINST # rINST<- B - mulss VREG_ADDRESS(rINSTq), %xmm0 - movss %xmm0, VREG_ADDRESS(%rcx) # vAA<- %xmm0 - pxor %xmm0, %xmm0 - movss %xmm0, VREG_REF_ADDRESS(rINSTq) # clear ref - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 - - -/* ------------------------------ */ - .balign 128 -.L_op_div_float_2addr: /* 0xc9 */ -/* File: x86_64/op_div_float_2addr.S */ -/* File: x86_64/sseBinop2Addr.S */ - movl rINST, %ecx # ecx <- A+ - andl $0xf, %ecx # ecx <- A - movss VREG_ADDRESS(%rcx), %xmm0 # %xmm0 <- 1st src - sarl $4, rINST # rINST<- B - divss VREG_ADDRESS(rINSTq), %xmm0 - movss %xmm0, VREG_ADDRESS(%rcx) # vAA<- %xmm0 - pxor %xmm0, %xmm0 - movss %xmm0, VREG_REF_ADDRESS(rINSTq) # clear ref - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 - - -/* ------------------------------ */ - .balign 128 -.L_op_rem_float_2addr: /* 0xca */ -/* File: x86_64/op_rem_float_2addr.S */ - /* rem_float/2addr vA, vB */ - movzbq rINSTbl, %rcx # ecx <- A+ - sarl $4, rINST # rINST <- B - flds VREG_ADDRESS(rINSTq) # vB to fp stack - andb $0xf, %cl # ecx <- A - flds VREG_ADDRESS(%rcx) # vA to fp stack -1: - fprem - fstsw %ax - sahf - jp 1b - fstp %st(1) - fstps VREG_ADDRESS(%rcx) # %st to vA - CLEAR_REF %rcx - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 - -/* ------------------------------ */ - .balign 128 -.L_op_add_double_2addr: /* 0xcb */ -/* File: x86_64/op_add_double_2addr.S */ -/* File: x86_64/sseBinop2Addr.S */ - movl rINST, %ecx # ecx <- A+ - andl $0xf, %ecx # ecx <- A - movsd VREG_ADDRESS(%rcx), %xmm0 # %xmm0 <- 1st src - sarl $4, rINST # rINST<- B - addsd VREG_ADDRESS(rINSTq), %xmm0 - movsd %xmm0, VREG_ADDRESS(%rcx) # vAA<- %xmm0 - pxor %xmm0, %xmm0 - movsd %xmm0, VREG_REF_ADDRESS(rINSTq) # clear ref - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 - - -/* ------------------------------ */ - .balign 128 -.L_op_sub_double_2addr: /* 0xcc */ -/* File: x86_64/op_sub_double_2addr.S */ -/* File: x86_64/sseBinop2Addr.S */ - movl rINST, %ecx # ecx <- A+ - andl $0xf, %ecx # ecx <- A - movsd VREG_ADDRESS(%rcx), %xmm0 # %xmm0 <- 1st src - sarl $4, rINST # rINST<- B - subsd VREG_ADDRESS(rINSTq), %xmm0 - movsd %xmm0, VREG_ADDRESS(%rcx) # vAA<- %xmm0 - pxor %xmm0, %xmm0 - movsd %xmm0, VREG_REF_ADDRESS(rINSTq) # clear ref - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 - - -/* ------------------------------ */ - .balign 128 -.L_op_mul_double_2addr: /* 0xcd */ -/* File: x86_64/op_mul_double_2addr.S */ -/* File: x86_64/sseBinop2Addr.S */ - movl rINST, %ecx # ecx <- A+ - andl $0xf, %ecx # ecx <- A - movsd VREG_ADDRESS(%rcx), %xmm0 # %xmm0 <- 1st src - sarl $4, rINST # rINST<- B - mulsd VREG_ADDRESS(rINSTq), %xmm0 - movsd %xmm0, VREG_ADDRESS(%rcx) # vAA<- %xmm0 - pxor %xmm0, %xmm0 - movsd %xmm0, VREG_REF_ADDRESS(rINSTq) # clear ref - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 - - -/* ------------------------------ */ - .balign 128 -.L_op_div_double_2addr: /* 0xce */ -/* File: x86_64/op_div_double_2addr.S */ -/* File: x86_64/sseBinop2Addr.S */ - movl rINST, %ecx # ecx <- A+ - andl $0xf, %ecx # ecx <- A - movsd VREG_ADDRESS(%rcx), %xmm0 # %xmm0 <- 1st src - sarl $4, rINST # rINST<- B - divsd VREG_ADDRESS(rINSTq), %xmm0 - movsd %xmm0, VREG_ADDRESS(%rcx) # vAA<- %xmm0 - pxor %xmm0, %xmm0 - movsd %xmm0, VREG_REF_ADDRESS(rINSTq) # clear ref - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 - - -/* ------------------------------ */ - .balign 128 -.L_op_rem_double_2addr: /* 0xcf */ -/* File: x86_64/op_rem_double_2addr.S */ - /* rem_double/2addr vA, vB */ - movzbq rINSTbl, %rcx # ecx <- A+ - sarl $4, rINST # rINST <- B - fldl VREG_ADDRESS(rINSTq) # vB to fp stack - andb $0xf, %cl # ecx <- A - fldl VREG_ADDRESS(%rcx) # vA to fp stack -1: - fprem - fstsw %ax - sahf - jp 1b - fstp %st(1) - fstpl VREG_ADDRESS(%rcx) # %st to vA - CLEAR_WIDE_REF %rcx - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 - -/* ------------------------------ */ - .balign 128 -.L_op_add_int_lit16: /* 0xd0 */ -/* File: x86_64/op_add_int_lit16.S */ -/* File: x86_64/binopLit16.S */ -/* - * Generic 32-bit "lit16" binary operation. Provide an "instr" line - * that specifies an instruction that performs "result = eax op ecx". - * This could be an x86 instruction or a function call. (If the result - * comes back in a register other than eax, you can override "result".) - * - * For: add-int/lit16, rsub-int, - * and-int/lit16, or-int/lit16, xor-int/lit16 - */ - /* binop/lit16 vA, vB, #+CCCC */ - movl rINST, %eax # rax <- 000000BA - sarl $4, %eax # eax <- B - GET_VREG %eax, %rax # eax <- vB - andb $0xf, rINSTbl # rINST <- A - movswl 2(rPC), %ecx # ecx <- ssssCCCC - addl %ecx, %eax # for example: addl %ecx, %eax - SET_VREG %eax, rINSTq - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_rsub_int: /* 0xd1 */ -/* File: x86_64/op_rsub_int.S */ -/* this op is "rsub-int", but can be thought of as "rsub-int/lit16" */ -/* File: x86_64/binopLit16.S */ -/* - * Generic 32-bit "lit16" binary operation. Provide an "instr" line - * that specifies an instruction that performs "result = eax op ecx". - * This could be an x86 instruction or a function call. (If the result - * comes back in a register other than eax, you can override "result".) - * - * For: add-int/lit16, rsub-int, - * and-int/lit16, or-int/lit16, xor-int/lit16 - */ - /* binop/lit16 vA, vB, #+CCCC */ - movl rINST, %eax # rax <- 000000BA - sarl $4, %eax # eax <- B - GET_VREG %eax, %rax # eax <- vB - andb $0xf, rINSTbl # rINST <- A - movswl 2(rPC), %ecx # ecx <- ssssCCCC - subl %eax, %ecx # for example: addl %ecx, %eax - SET_VREG %ecx, rINSTq - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_mul_int_lit16: /* 0xd2 */ -/* File: x86_64/op_mul_int_lit16.S */ -/* File: x86_64/binopLit16.S */ -/* - * Generic 32-bit "lit16" binary operation. Provide an "instr" line - * that specifies an instruction that performs "result = eax op ecx". - * This could be an x86 instruction or a function call. (If the result - * comes back in a register other than eax, you can override "result".) - * - * For: add-int/lit16, rsub-int, - * and-int/lit16, or-int/lit16, xor-int/lit16 - */ - /* binop/lit16 vA, vB, #+CCCC */ - movl rINST, %eax # rax <- 000000BA - sarl $4, %eax # eax <- B - GET_VREG %eax, %rax # eax <- vB - andb $0xf, rINSTbl # rINST <- A - movswl 2(rPC), %ecx # ecx <- ssssCCCC - imull %ecx, %eax # for example: addl %ecx, %eax - SET_VREG %eax, rINSTq - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_div_int_lit16: /* 0xd3 */ -/* File: x86_64/op_div_int_lit16.S */ -/* File: x86_64/bindivLit16.S */ -/* - * 32-bit binary div/rem operation. Handles special case of op1=-1. - */ - /* div/rem/lit16 vA, vB, #+CCCC */ - /* Need A in rINST, ssssCCCC in ecx, vB in eax */ - movl rINST, %eax # rax <- 000000BA - sarl $4, %eax # eax <- B - GET_VREG %eax, %rax # eax <- vB - movswl 2(rPC), %ecx # ecx <- ssssCCCC - andb $0xf, rINSTbl # rINST <- A - testl %ecx, %ecx - jz common_errDivideByZero - cmpl $-1, %ecx - je 2f - cdq # rax <- sign-extended of eax - idivl %ecx -1: - SET_VREG %eax, rINSTq # vA <- result - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 -2: - .if 0 - xorl %eax, %eax - .else - negl %eax - .endif - jmp 1b - - -/* ------------------------------ */ - .balign 128 -.L_op_rem_int_lit16: /* 0xd4 */ -/* File: x86_64/op_rem_int_lit16.S */ -/* File: x86_64/bindivLit16.S */ -/* - * 32-bit binary div/rem operation. Handles special case of op1=-1. - */ - /* div/rem/lit16 vA, vB, #+CCCC */ - /* Need A in rINST, ssssCCCC in ecx, vB in eax */ - movl rINST, %eax # rax <- 000000BA - sarl $4, %eax # eax <- B - GET_VREG %eax, %rax # eax <- vB - movswl 2(rPC), %ecx # ecx <- ssssCCCC - andb $0xf, rINSTbl # rINST <- A - testl %ecx, %ecx - jz common_errDivideByZero - cmpl $-1, %ecx - je 2f - cdq # rax <- sign-extended of eax - idivl %ecx -1: - SET_VREG %edx, rINSTq # vA <- result - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 -2: - .if 1 - xorl %edx, %edx - .else - negl %edx - .endif - jmp 1b - - -/* ------------------------------ */ - .balign 128 -.L_op_and_int_lit16: /* 0xd5 */ -/* File: x86_64/op_and_int_lit16.S */ -/* File: x86_64/binopLit16.S */ -/* - * Generic 32-bit "lit16" binary operation. Provide an "instr" line - * that specifies an instruction that performs "result = eax op ecx". - * This could be an x86 instruction or a function call. (If the result - * comes back in a register other than eax, you can override "result".) - * - * For: add-int/lit16, rsub-int, - * and-int/lit16, or-int/lit16, xor-int/lit16 - */ - /* binop/lit16 vA, vB, #+CCCC */ - movl rINST, %eax # rax <- 000000BA - sarl $4, %eax # eax <- B - GET_VREG %eax, %rax # eax <- vB - andb $0xf, rINSTbl # rINST <- A - movswl 2(rPC), %ecx # ecx <- ssssCCCC - andl %ecx, %eax # for example: addl %ecx, %eax - SET_VREG %eax, rINSTq - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_or_int_lit16: /* 0xd6 */ -/* File: x86_64/op_or_int_lit16.S */ -/* File: x86_64/binopLit16.S */ -/* - * Generic 32-bit "lit16" binary operation. Provide an "instr" line - * that specifies an instruction that performs "result = eax op ecx". - * This could be an x86 instruction or a function call. (If the result - * comes back in a register other than eax, you can override "result".) - * - * For: add-int/lit16, rsub-int, - * and-int/lit16, or-int/lit16, xor-int/lit16 - */ - /* binop/lit16 vA, vB, #+CCCC */ - movl rINST, %eax # rax <- 000000BA - sarl $4, %eax # eax <- B - GET_VREG %eax, %rax # eax <- vB - andb $0xf, rINSTbl # rINST <- A - movswl 2(rPC), %ecx # ecx <- ssssCCCC - orl %ecx, %eax # for example: addl %ecx, %eax - SET_VREG %eax, rINSTq - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_xor_int_lit16: /* 0xd7 */ -/* File: x86_64/op_xor_int_lit16.S */ -/* File: x86_64/binopLit16.S */ -/* - * Generic 32-bit "lit16" binary operation. Provide an "instr" line - * that specifies an instruction that performs "result = eax op ecx". - * This could be an x86 instruction or a function call. (If the result - * comes back in a register other than eax, you can override "result".) - * - * For: add-int/lit16, rsub-int, - * and-int/lit16, or-int/lit16, xor-int/lit16 - */ - /* binop/lit16 vA, vB, #+CCCC */ - movl rINST, %eax # rax <- 000000BA - sarl $4, %eax # eax <- B - GET_VREG %eax, %rax # eax <- vB - andb $0xf, rINSTbl # rINST <- A - movswl 2(rPC), %ecx # ecx <- ssssCCCC - xorl %ecx, %eax # for example: addl %ecx, %eax - SET_VREG %eax, rINSTq - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_add_int_lit8: /* 0xd8 */ -/* File: x86_64/op_add_int_lit8.S */ -/* File: x86_64/binopLit8.S */ -/* - * Generic 32-bit "lit8" binary operation. Provide an "instr" line - * that specifies an instruction that performs "result = eax op ecx". - * This could be an x86 instruction or a function call. (If the result - * comes back in a register other than r0, you can override "result".) - * - * For: add-int/lit8, rsub-int/lit8 - * and-int/lit8, or-int/lit8, xor-int/lit8, - * shl-int/lit8, shr-int/lit8, ushr-int/lit8 - */ - /* binop/lit8 vAA, vBB, #+CC */ - movzbq 2(rPC), %rax # rax <- BB - movsbl 3(rPC), %ecx # rcx <- ssssssCC - GET_VREG %eax, %rax # eax <- rBB - addl %ecx, %eax # ex: addl %ecx,%eax - SET_VREG %eax, rINSTq - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_rsub_int_lit8: /* 0xd9 */ -/* File: x86_64/op_rsub_int_lit8.S */ -/* File: x86_64/binopLit8.S */ -/* - * Generic 32-bit "lit8" binary operation. Provide an "instr" line - * that specifies an instruction that performs "result = eax op ecx". - * This could be an x86 instruction or a function call. (If the result - * comes back in a register other than r0, you can override "result".) - * - * For: add-int/lit8, rsub-int/lit8 - * and-int/lit8, or-int/lit8, xor-int/lit8, - * shl-int/lit8, shr-int/lit8, ushr-int/lit8 - */ - /* binop/lit8 vAA, vBB, #+CC */ - movzbq 2(rPC), %rax # rax <- BB - movsbl 3(rPC), %ecx # rcx <- ssssssCC - GET_VREG %eax, %rax # eax <- rBB - subl %eax, %ecx # ex: addl %ecx,%eax - SET_VREG %ecx, rINSTq - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_mul_int_lit8: /* 0xda */ -/* File: x86_64/op_mul_int_lit8.S */ -/* File: x86_64/binopLit8.S */ -/* - * Generic 32-bit "lit8" binary operation. Provide an "instr" line - * that specifies an instruction that performs "result = eax op ecx". - * This could be an x86 instruction or a function call. (If the result - * comes back in a register other than r0, you can override "result".) - * - * For: add-int/lit8, rsub-int/lit8 - * and-int/lit8, or-int/lit8, xor-int/lit8, - * shl-int/lit8, shr-int/lit8, ushr-int/lit8 - */ - /* binop/lit8 vAA, vBB, #+CC */ - movzbq 2(rPC), %rax # rax <- BB - movsbl 3(rPC), %ecx # rcx <- ssssssCC - GET_VREG %eax, %rax # eax <- rBB - imull %ecx, %eax # ex: addl %ecx,%eax - SET_VREG %eax, rINSTq - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_div_int_lit8: /* 0xdb */ -/* File: x86_64/op_div_int_lit8.S */ -/* File: x86_64/bindivLit8.S */ -/* - * 32-bit div/rem "lit8" binary operation. Handles special case of - * op0=minint & op1=-1 - */ - /* div/rem/lit8 vAA, vBB, #+CC */ - movzbq 2(rPC), %rax # eax <- BB - movsbl 3(rPC), %ecx # ecx <- ssssssCC - GET_VREG %eax, %rax # eax <- rBB - testl %ecx, %ecx - je common_errDivideByZero - cmpl $-1, %ecx - je 2f - cdq # rax <- sign-extended of eax - idivl %ecx -1: - SET_VREG %eax, rINSTq # vA <- result - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 -2: - .if 0 - xorl %eax, %eax - .else - negl %eax - .endif - jmp 1b - - -/* ------------------------------ */ - .balign 128 -.L_op_rem_int_lit8: /* 0xdc */ -/* File: x86_64/op_rem_int_lit8.S */ -/* File: x86_64/bindivLit8.S */ -/* - * 32-bit div/rem "lit8" binary operation. Handles special case of - * op0=minint & op1=-1 - */ - /* div/rem/lit8 vAA, vBB, #+CC */ - movzbq 2(rPC), %rax # eax <- BB - movsbl 3(rPC), %ecx # ecx <- ssssssCC - GET_VREG %eax, %rax # eax <- rBB - testl %ecx, %ecx - je common_errDivideByZero - cmpl $-1, %ecx - je 2f - cdq # rax <- sign-extended of eax - idivl %ecx -1: - SET_VREG %edx, rINSTq # vA <- result - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 -2: - .if 1 - xorl %edx, %edx - .else - negl %edx - .endif - jmp 1b - - -/* ------------------------------ */ - .balign 128 -.L_op_and_int_lit8: /* 0xdd */ -/* File: x86_64/op_and_int_lit8.S */ -/* File: x86_64/binopLit8.S */ -/* - * Generic 32-bit "lit8" binary operation. Provide an "instr" line - * that specifies an instruction that performs "result = eax op ecx". - * This could be an x86 instruction or a function call. (If the result - * comes back in a register other than r0, you can override "result".) - * - * For: add-int/lit8, rsub-int/lit8 - * and-int/lit8, or-int/lit8, xor-int/lit8, - * shl-int/lit8, shr-int/lit8, ushr-int/lit8 - */ - /* binop/lit8 vAA, vBB, #+CC */ - movzbq 2(rPC), %rax # rax <- BB - movsbl 3(rPC), %ecx # rcx <- ssssssCC - GET_VREG %eax, %rax # eax <- rBB - andl %ecx, %eax # ex: addl %ecx,%eax - SET_VREG %eax, rINSTq - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_or_int_lit8: /* 0xde */ -/* File: x86_64/op_or_int_lit8.S */ -/* File: x86_64/binopLit8.S */ -/* - * Generic 32-bit "lit8" binary operation. Provide an "instr" line - * that specifies an instruction that performs "result = eax op ecx". - * This could be an x86 instruction or a function call. (If the result - * comes back in a register other than r0, you can override "result".) - * - * For: add-int/lit8, rsub-int/lit8 - * and-int/lit8, or-int/lit8, xor-int/lit8, - * shl-int/lit8, shr-int/lit8, ushr-int/lit8 - */ - /* binop/lit8 vAA, vBB, #+CC */ - movzbq 2(rPC), %rax # rax <- BB - movsbl 3(rPC), %ecx # rcx <- ssssssCC - GET_VREG %eax, %rax # eax <- rBB - orl %ecx, %eax # ex: addl %ecx,%eax - SET_VREG %eax, rINSTq - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_xor_int_lit8: /* 0xdf */ -/* File: x86_64/op_xor_int_lit8.S */ -/* File: x86_64/binopLit8.S */ -/* - * Generic 32-bit "lit8" binary operation. Provide an "instr" line - * that specifies an instruction that performs "result = eax op ecx". - * This could be an x86 instruction or a function call. (If the result - * comes back in a register other than r0, you can override "result".) - * - * For: add-int/lit8, rsub-int/lit8 - * and-int/lit8, or-int/lit8, xor-int/lit8, - * shl-int/lit8, shr-int/lit8, ushr-int/lit8 - */ - /* binop/lit8 vAA, vBB, #+CC */ - movzbq 2(rPC), %rax # rax <- BB - movsbl 3(rPC), %ecx # rcx <- ssssssCC - GET_VREG %eax, %rax # eax <- rBB - xorl %ecx, %eax # ex: addl %ecx,%eax - SET_VREG %eax, rINSTq - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_shl_int_lit8: /* 0xe0 */ -/* File: x86_64/op_shl_int_lit8.S */ -/* File: x86_64/binopLit8.S */ -/* - * Generic 32-bit "lit8" binary operation. Provide an "instr" line - * that specifies an instruction that performs "result = eax op ecx". - * This could be an x86 instruction or a function call. (If the result - * comes back in a register other than r0, you can override "result".) - * - * For: add-int/lit8, rsub-int/lit8 - * and-int/lit8, or-int/lit8, xor-int/lit8, - * shl-int/lit8, shr-int/lit8, ushr-int/lit8 - */ - /* binop/lit8 vAA, vBB, #+CC */ - movzbq 2(rPC), %rax # rax <- BB - movsbl 3(rPC), %ecx # rcx <- ssssssCC - GET_VREG %eax, %rax # eax <- rBB - sall %cl, %eax # ex: addl %ecx,%eax - SET_VREG %eax, rINSTq - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_shr_int_lit8: /* 0xe1 */ -/* File: x86_64/op_shr_int_lit8.S */ -/* File: x86_64/binopLit8.S */ -/* - * Generic 32-bit "lit8" binary operation. Provide an "instr" line - * that specifies an instruction that performs "result = eax op ecx". - * This could be an x86 instruction or a function call. (If the result - * comes back in a register other than r0, you can override "result".) - * - * For: add-int/lit8, rsub-int/lit8 - * and-int/lit8, or-int/lit8, xor-int/lit8, - * shl-int/lit8, shr-int/lit8, ushr-int/lit8 - */ - /* binop/lit8 vAA, vBB, #+CC */ - movzbq 2(rPC), %rax # rax <- BB - movsbl 3(rPC), %ecx # rcx <- ssssssCC - GET_VREG %eax, %rax # eax <- rBB - sarl %cl, %eax # ex: addl %ecx,%eax - SET_VREG %eax, rINSTq - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_ushr_int_lit8: /* 0xe2 */ -/* File: x86_64/op_ushr_int_lit8.S */ -/* File: x86_64/binopLit8.S */ -/* - * Generic 32-bit "lit8" binary operation. Provide an "instr" line - * that specifies an instruction that performs "result = eax op ecx". - * This could be an x86 instruction or a function call. (If the result - * comes back in a register other than r0, you can override "result".) - * - * For: add-int/lit8, rsub-int/lit8 - * and-int/lit8, or-int/lit8, xor-int/lit8, - * shl-int/lit8, shr-int/lit8, ushr-int/lit8 - */ - /* binop/lit8 vAA, vBB, #+CC */ - movzbq 2(rPC), %rax # rax <- BB - movsbl 3(rPC), %ecx # rcx <- ssssssCC - GET_VREG %eax, %rax # eax <- rBB - shrl %cl, %eax # ex: addl %ecx,%eax - SET_VREG %eax, rINSTq - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_iget_quick: /* 0xe3 */ -/* File: x86_64/op_iget_quick.S */ - /* For: iget-quick, iget-boolean-quick, iget-byte-quick, iget-char-quick, iget-short-quick, iget-wide-quick */ - /* op vA, vB, offset@CCCC */ - movl rINST, %ecx # rcx <- BA - sarl $4, %ecx # ecx <- B - GET_VREG %ecx, %rcx # vB (object we're operating on) - movzwq 2(rPC), %rax # eax <- field byte offset - testl %ecx, %ecx # is object null? - je common_errNullObject - andb $0xf,rINSTbl # rINST <- A - .if 0 - movq (%rcx,%rax,1), %rax - SET_WIDE_VREG %rax, rINSTq # fp[A] <- value - .else - movl (%rcx,%rax,1), %eax - SET_VREG %eax, rINSTq # fp[A] <- value - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - -/* ------------------------------ */ - .balign 128 -.L_op_iget_wide_quick: /* 0xe4 */ -/* File: x86_64/op_iget_wide_quick.S */ -/* File: x86_64/op_iget_quick.S */ - /* For: iget-quick, iget-boolean-quick, iget-byte-quick, iget-char-quick, iget-short-quick, iget-wide-quick */ - /* op vA, vB, offset@CCCC */ - movl rINST, %ecx # rcx <- BA - sarl $4, %ecx # ecx <- B - GET_VREG %ecx, %rcx # vB (object we're operating on) - movzwq 2(rPC), %rax # eax <- field byte offset - testl %ecx, %ecx # is object null? - je common_errNullObject - andb $0xf,rINSTbl # rINST <- A - .if 1 - movq (%rcx,%rax,1), %rax - SET_WIDE_VREG %rax, rINSTq # fp[A] <- value - .else - movswl (%rcx,%rax,1), %eax - SET_VREG %eax, rINSTq # fp[A] <- value - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_iget_object_quick: /* 0xe5 */ -/* File: x86_64/op_iget_object_quick.S */ - /* For: iget-object-quick */ - /* op vA, vB, offset@CCCC */ - .extern artIGetObjectFromMterp - movzbq rINSTbl, %rcx # rcx <- BA - sarl $4, %ecx # ecx <- B - GET_VREG OUT_32_ARG0, %rcx # vB (object we're operating on) - movzwl 2(rPC), OUT_32_ARG1 # eax <- field byte offset - EXPORT_PC - callq SYMBOL(artIGetObjectFromMterp) # (obj, offset) - cmpq $0, THREAD_EXCEPTION_OFFSET(rSELF) - jnz MterpException # bail out - andb $0xf, rINSTbl # rINST <- A - SET_VREG_OBJECT %eax, rINSTq # fp[A] <- value - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - -/* ------------------------------ */ - .balign 128 -.L_op_iput_quick: /* 0xe6 */ -/* File: x86_64/op_iput_quick.S */ - /* For: iput-quick, iput-object-quick */ - /* op vA, vB, offset@CCCC */ - movzbq rINSTbl, %rcx # rcx <- BA - sarl $4, %ecx # ecx <- B - GET_VREG %ecx, %rcx # vB (object we're operating on) - testl %ecx, %ecx # is object null? - je common_errNullObject - andb $0xf, rINSTbl # rINST <- A - GET_VREG rINST, rINSTq # rINST <- v[A] - movzwq 2(rPC), %rax # rax <- field byte offset - movl rINST, (%rcx,%rax,1) - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - -/* ------------------------------ */ - .balign 128 -.L_op_iput_wide_quick: /* 0xe7 */ -/* File: x86_64/op_iput_wide_quick.S */ - /* iput-wide-quick vA, vB, offset@CCCC */ - movzbq rINSTbl, %rcx # rcx<- BA - sarl $4, %ecx # ecx<- B - GET_VREG %ecx, %rcx # vB (object we're operating on) - testl %ecx, %ecx # is object null? - je common_errNullObject - movzwq 2(rPC), %rax # rax<- field byte offset - leaq (%rcx,%rax,1), %rcx # ecx<- Address of 64-bit target - andb $0xf, rINSTbl # rINST<- A - GET_WIDE_VREG %rax, rINSTq # rax<- fp[A]/fp[A+1] - movq %rax, (%rcx) # obj.field<- r0/r1 - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - -/* ------------------------------ */ - .balign 128 -.L_op_iput_object_quick: /* 0xe8 */ -/* File: x86_64/op_iput_object_quick.S */ - EXPORT_PC - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG0 - movq rPC, OUT_ARG1 - REFRESH_INST 232 - movl rINST, OUT_32_ARG2 - call SYMBOL(MterpIputObjectQuick) - testb %al, %al - jz MterpException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - -/* ------------------------------ */ - .balign 128 -.L_op_invoke_virtual_quick: /* 0xe9 */ -/* File: x86_64/op_invoke_virtual_quick.S */ -/* File: x86_64/invoke.S */ -/* - * Generic invoke handler wrapper. - */ - /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ - /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ - .extern MterpInvokeVirtualQuick - EXPORT_PC - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - movq rPC, OUT_ARG2 - REFRESH_INST 233 - movl rINST, OUT_32_ARG3 - call SYMBOL(MterpInvokeVirtualQuick) - testb %al, %al - jz MterpException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 - - -/* ------------------------------ */ - .balign 128 -.L_op_invoke_virtual_range_quick: /* 0xea */ -/* File: x86_64/op_invoke_virtual_range_quick.S */ -/* File: x86_64/invoke.S */ -/* - * Generic invoke handler wrapper. - */ - /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ - /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ - .extern MterpInvokeVirtualQuickRange - EXPORT_PC - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - movq rPC, OUT_ARG2 - REFRESH_INST 234 - movl rINST, OUT_32_ARG3 - call SYMBOL(MterpInvokeVirtualQuickRange) - testb %al, %al - jz MterpException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 - - -/* ------------------------------ */ - .balign 128 -.L_op_iput_boolean_quick: /* 0xeb */ -/* File: x86_64/op_iput_boolean_quick.S */ -/* File: x86_64/op_iput_quick.S */ - /* For: iput-quick, iput-object-quick */ - /* op vA, vB, offset@CCCC */ - movzbq rINSTbl, %rcx # rcx <- BA - sarl $4, %ecx # ecx <- B - GET_VREG %ecx, %rcx # vB (object we're operating on) - testl %ecx, %ecx # is object null? - je common_errNullObject - andb $0xf, rINSTbl # rINST <- A - GET_VREG rINST, rINSTq # rINST <- v[A] - movzwq 2(rPC), %rax # rax <- field byte offset - movb rINSTbl, (%rcx,%rax,1) - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_iput_byte_quick: /* 0xec */ -/* File: x86_64/op_iput_byte_quick.S */ -/* File: x86_64/op_iput_quick.S */ - /* For: iput-quick, iput-object-quick */ - /* op vA, vB, offset@CCCC */ - movzbq rINSTbl, %rcx # rcx <- BA - sarl $4, %ecx # ecx <- B - GET_VREG %ecx, %rcx # vB (object we're operating on) - testl %ecx, %ecx # is object null? - je common_errNullObject - andb $0xf, rINSTbl # rINST <- A - GET_VREG rINST, rINSTq # rINST <- v[A] - movzwq 2(rPC), %rax # rax <- field byte offset - movb rINSTbl, (%rcx,%rax,1) - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_iput_char_quick: /* 0xed */ -/* File: x86_64/op_iput_char_quick.S */ -/* File: x86_64/op_iput_quick.S */ - /* For: iput-quick, iput-object-quick */ - /* op vA, vB, offset@CCCC */ - movzbq rINSTbl, %rcx # rcx <- BA - sarl $4, %ecx # ecx <- B - GET_VREG %ecx, %rcx # vB (object we're operating on) - testl %ecx, %ecx # is object null? - je common_errNullObject - andb $0xf, rINSTbl # rINST <- A - GET_VREG rINST, rINSTq # rINST <- v[A] - movzwq 2(rPC), %rax # rax <- field byte offset - movw rINSTw, (%rcx,%rax,1) - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_iput_short_quick: /* 0xee */ -/* File: x86_64/op_iput_short_quick.S */ -/* File: x86_64/op_iput_quick.S */ - /* For: iput-quick, iput-object-quick */ - /* op vA, vB, offset@CCCC */ - movzbq rINSTbl, %rcx # rcx <- BA - sarl $4, %ecx # ecx <- B - GET_VREG %ecx, %rcx # vB (object we're operating on) - testl %ecx, %ecx # is object null? - je common_errNullObject - andb $0xf, rINSTbl # rINST <- A - GET_VREG rINST, rINSTq # rINST <- v[A] - movzwq 2(rPC), %rax # rax <- field byte offset - movw rINSTw, (%rcx,%rax,1) - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_iget_boolean_quick: /* 0xef */ -/* File: x86_64/op_iget_boolean_quick.S */ -/* File: x86_64/op_iget_quick.S */ - /* For: iget-quick, iget-boolean-quick, iget-byte-quick, iget-char-quick, iget-short-quick, iget-wide-quick */ - /* op vA, vB, offset@CCCC */ - movl rINST, %ecx # rcx <- BA - sarl $4, %ecx # ecx <- B - GET_VREG %ecx, %rcx # vB (object we're operating on) - movzwq 2(rPC), %rax # eax <- field byte offset - testl %ecx, %ecx # is object null? - je common_errNullObject - andb $0xf,rINSTbl # rINST <- A - .if 0 - movq (%rcx,%rax,1), %rax - SET_WIDE_VREG %rax, rINSTq # fp[A] <- value - .else - movsbl (%rcx,%rax,1), %eax - SET_VREG %eax, rINSTq # fp[A] <- value - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_iget_byte_quick: /* 0xf0 */ -/* File: x86_64/op_iget_byte_quick.S */ -/* File: x86_64/op_iget_quick.S */ - /* For: iget-quick, iget-boolean-quick, iget-byte-quick, iget-char-quick, iget-short-quick, iget-wide-quick */ - /* op vA, vB, offset@CCCC */ - movl rINST, %ecx # rcx <- BA - sarl $4, %ecx # ecx <- B - GET_VREG %ecx, %rcx # vB (object we're operating on) - movzwq 2(rPC), %rax # eax <- field byte offset - testl %ecx, %ecx # is object null? - je common_errNullObject - andb $0xf,rINSTbl # rINST <- A - .if 0 - movq (%rcx,%rax,1), %rax - SET_WIDE_VREG %rax, rINSTq # fp[A] <- value - .else - movsbl (%rcx,%rax,1), %eax - SET_VREG %eax, rINSTq # fp[A] <- value - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_iget_char_quick: /* 0xf1 */ -/* File: x86_64/op_iget_char_quick.S */ -/* File: x86_64/op_iget_quick.S */ - /* For: iget-quick, iget-boolean-quick, iget-byte-quick, iget-char-quick, iget-short-quick, iget-wide-quick */ - /* op vA, vB, offset@CCCC */ - movl rINST, %ecx # rcx <- BA - sarl $4, %ecx # ecx <- B - GET_VREG %ecx, %rcx # vB (object we're operating on) - movzwq 2(rPC), %rax # eax <- field byte offset - testl %ecx, %ecx # is object null? - je common_errNullObject - andb $0xf,rINSTbl # rINST <- A - .if 0 - movq (%rcx,%rax,1), %rax - SET_WIDE_VREG %rax, rINSTq # fp[A] <- value - .else - movzwl (%rcx,%rax,1), %eax - SET_VREG %eax, rINSTq # fp[A] <- value - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_iget_short_quick: /* 0xf2 */ -/* File: x86_64/op_iget_short_quick.S */ -/* File: x86_64/op_iget_quick.S */ - /* For: iget-quick, iget-boolean-quick, iget-byte-quick, iget-char-quick, iget-short-quick, iget-wide-quick */ - /* op vA, vB, offset@CCCC */ - movl rINST, %ecx # rcx <- BA - sarl $4, %ecx # ecx <- B - GET_VREG %ecx, %rcx # vB (object we're operating on) - movzwq 2(rPC), %rax # eax <- field byte offset - testl %ecx, %ecx # is object null? - je common_errNullObject - andb $0xf,rINSTbl # rINST <- A - .if 0 - movq (%rcx,%rax,1), %rax - SET_WIDE_VREG %rax, rINSTq # fp[A] <- value - .else - movswl (%rcx,%rax,1), %eax - SET_VREG %eax, rINSTq # fp[A] <- value - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 - - -/* ------------------------------ */ - .balign 128 -.L_op_invoke_lambda: /* 0xf3 */ -/* Transfer stub to alternate interpreter */ - jmp MterpFallback - - -/* ------------------------------ */ - .balign 128 -.L_op_unused_f4: /* 0xf4 */ -/* File: x86_64/op_unused_f4.S */ -/* File: x86_64/unused.S */ -/* - * Bail to reference interpreter to throw. - */ - jmp MterpFallback - - -/* ------------------------------ */ - .balign 128 -.L_op_capture_variable: /* 0xf5 */ -/* Transfer stub to alternate interpreter */ - jmp MterpFallback - - -/* ------------------------------ */ - .balign 128 -.L_op_create_lambda: /* 0xf6 */ -/* Transfer stub to alternate interpreter */ - jmp MterpFallback - - -/* ------------------------------ */ - .balign 128 -.L_op_liberate_variable: /* 0xf7 */ -/* Transfer stub to alternate interpreter */ - jmp MterpFallback - - -/* ------------------------------ */ - .balign 128 -.L_op_box_lambda: /* 0xf8 */ -/* Transfer stub to alternate interpreter */ - jmp MterpFallback - - -/* ------------------------------ */ - .balign 128 -.L_op_unbox_lambda: /* 0xf9 */ -/* Transfer stub to alternate interpreter */ - jmp MterpFallback - - -/* ------------------------------ */ - .balign 128 -.L_op_unused_fa: /* 0xfa */ -/* File: x86_64/op_unused_fa.S */ -/* File: x86_64/unused.S */ -/* - * Bail to reference interpreter to throw. - */ - jmp MterpFallback - - -/* ------------------------------ */ - .balign 128 -.L_op_unused_fb: /* 0xfb */ -/* File: x86_64/op_unused_fb.S */ -/* File: x86_64/unused.S */ -/* - * Bail to reference interpreter to throw. - */ - jmp MterpFallback - - -/* ------------------------------ */ - .balign 128 -.L_op_unused_fc: /* 0xfc */ -/* File: x86_64/op_unused_fc.S */ -/* File: x86_64/unused.S */ -/* - * Bail to reference interpreter to throw. - */ - jmp MterpFallback - - -/* ------------------------------ */ - .balign 128 -.L_op_unused_fd: /* 0xfd */ -/* File: x86_64/op_unused_fd.S */ -/* File: x86_64/unused.S */ -/* - * Bail to reference interpreter to throw. - */ - jmp MterpFallback - - -/* ------------------------------ */ - .balign 128 -.L_op_unused_fe: /* 0xfe */ -/* File: x86_64/op_unused_fe.S */ -/* File: x86_64/unused.S */ -/* - * Bail to reference interpreter to throw. - */ - jmp MterpFallback - - -/* ------------------------------ */ - .balign 128 -.L_op_unused_ff: /* 0xff */ -/* File: x86_64/op_unused_ff.S */ -/* File: x86_64/unused.S */ -/* - * Bail to reference interpreter to throw. - */ - jmp MterpFallback - - - .balign 128 - SIZE(SYMBOL(artMterpAsmInstructionStart),SYMBOL(artMterpAsmInstructionStart)) - .global SYMBOL(artMterpAsmInstructionEnd) -SYMBOL(artMterpAsmInstructionEnd): - -/* - * =========================================================================== - * Sister implementations - * =========================================================================== - */ - .global SYMBOL(artMterpAsmSisterStart) - FUNCTION_TYPE(SYMBOL(artMterpAsmSisterStart)) - .text - .balign 4 -SYMBOL(artMterpAsmSisterStart): - - SIZE(SYMBOL(artMterpAsmSisterStart),SYMBOL(artMterpAsmSisterStart)) - .global SYMBOL(artMterpAsmSisterEnd) -SYMBOL(artMterpAsmSisterEnd): - - - .global SYMBOL(artMterpAsmAltInstructionStart) - FUNCTION_TYPE(SYMBOL(artMterpAsmAltInstructionStart)) - .text - -SYMBOL(artMterpAsmAltInstructionStart) = .L_ALT_op_nop -/* ------------------------------ */ - .balign 128 -.L_ALT_op_nop: /* 0x00 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(0*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_move: /* 0x01 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(1*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_move_from16: /* 0x02 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(2*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_move_16: /* 0x03 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(3*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_move_wide: /* 0x04 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(4*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_move_wide_from16: /* 0x05 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(5*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_move_wide_16: /* 0x06 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(6*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_move_object: /* 0x07 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(7*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_move_object_from16: /* 0x08 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(8*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_move_object_16: /* 0x09 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(9*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_move_result: /* 0x0a */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(10*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_move_result_wide: /* 0x0b */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(11*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_move_result_object: /* 0x0c */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(12*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_move_exception: /* 0x0d */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(13*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_return_void: /* 0x0e */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(14*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_return: /* 0x0f */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(15*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_return_wide: /* 0x10 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(16*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_return_object: /* 0x11 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(17*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_const_4: /* 0x12 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(18*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_const_16: /* 0x13 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(19*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_const: /* 0x14 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(20*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_const_high16: /* 0x15 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(21*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_const_wide_16: /* 0x16 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(22*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_const_wide_32: /* 0x17 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(23*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_const_wide: /* 0x18 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(24*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_const_wide_high16: /* 0x19 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(25*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_const_string: /* 0x1a */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(26*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_const_string_jumbo: /* 0x1b */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(27*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_const_class: /* 0x1c */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(28*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_monitor_enter: /* 0x1d */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(29*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_monitor_exit: /* 0x1e */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(30*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_check_cast: /* 0x1f */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(31*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_instance_of: /* 0x20 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(32*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_array_length: /* 0x21 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(33*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_new_instance: /* 0x22 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(34*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_new_array: /* 0x23 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(35*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_filled_new_array: /* 0x24 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(36*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_filled_new_array_range: /* 0x25 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(37*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_fill_array_data: /* 0x26 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(38*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_throw: /* 0x27 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(39*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_goto: /* 0x28 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(40*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_goto_16: /* 0x29 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(41*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_goto_32: /* 0x2a */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(42*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_packed_switch: /* 0x2b */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(43*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_sparse_switch: /* 0x2c */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(44*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_cmpl_float: /* 0x2d */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(45*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_cmpg_float: /* 0x2e */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(46*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_cmpl_double: /* 0x2f */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(47*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_cmpg_double: /* 0x30 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(48*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_cmp_long: /* 0x31 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(49*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_if_eq: /* 0x32 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(50*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_if_ne: /* 0x33 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(51*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_if_lt: /* 0x34 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(52*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_if_ge: /* 0x35 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(53*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_if_gt: /* 0x36 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(54*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_if_le: /* 0x37 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(55*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_if_eqz: /* 0x38 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(56*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_if_nez: /* 0x39 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(57*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_if_ltz: /* 0x3a */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(58*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_if_gez: /* 0x3b */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(59*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_if_gtz: /* 0x3c */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(60*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_if_lez: /* 0x3d */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(61*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_unused_3e: /* 0x3e */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(62*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_unused_3f: /* 0x3f */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(63*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_unused_40: /* 0x40 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(64*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_unused_41: /* 0x41 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(65*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_unused_42: /* 0x42 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(66*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_unused_43: /* 0x43 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(67*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_aget: /* 0x44 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(68*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_aget_wide: /* 0x45 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(69*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_aget_object: /* 0x46 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(70*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_aget_boolean: /* 0x47 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(71*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_aget_byte: /* 0x48 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(72*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_aget_char: /* 0x49 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(73*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_aget_short: /* 0x4a */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(74*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_aput: /* 0x4b */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(75*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_aput_wide: /* 0x4c */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(76*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_aput_object: /* 0x4d */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(77*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_aput_boolean: /* 0x4e */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(78*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_aput_byte: /* 0x4f */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(79*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_aput_char: /* 0x50 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(80*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_aput_short: /* 0x51 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(81*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_iget: /* 0x52 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(82*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_iget_wide: /* 0x53 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(83*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_iget_object: /* 0x54 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(84*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_iget_boolean: /* 0x55 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(85*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_iget_byte: /* 0x56 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(86*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_iget_char: /* 0x57 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(87*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_iget_short: /* 0x58 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(88*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_iput: /* 0x59 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(89*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_iput_wide: /* 0x5a */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(90*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_iput_object: /* 0x5b */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(91*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_iput_boolean: /* 0x5c */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(92*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_iput_byte: /* 0x5d */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(93*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_iput_char: /* 0x5e */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(94*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_iput_short: /* 0x5f */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(95*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_sget: /* 0x60 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(96*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_sget_wide: /* 0x61 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(97*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_sget_object: /* 0x62 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(98*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_sget_boolean: /* 0x63 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(99*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_sget_byte: /* 0x64 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(100*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_sget_char: /* 0x65 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(101*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_sget_short: /* 0x66 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(102*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_sput: /* 0x67 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(103*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_sput_wide: /* 0x68 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(104*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_sput_object: /* 0x69 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(105*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_sput_boolean: /* 0x6a */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(106*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_sput_byte: /* 0x6b */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(107*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_sput_char: /* 0x6c */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(108*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_sput_short: /* 0x6d */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(109*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_invoke_virtual: /* 0x6e */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(110*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_invoke_super: /* 0x6f */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(111*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_invoke_direct: /* 0x70 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(112*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_invoke_static: /* 0x71 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(113*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_invoke_interface: /* 0x72 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(114*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_return_void_no_barrier: /* 0x73 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(115*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_invoke_virtual_range: /* 0x74 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(116*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_invoke_super_range: /* 0x75 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(117*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_invoke_direct_range: /* 0x76 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(118*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_invoke_static_range: /* 0x77 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(119*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_invoke_interface_range: /* 0x78 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(120*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_unused_79: /* 0x79 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(121*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_unused_7a: /* 0x7a */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(122*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_neg_int: /* 0x7b */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(123*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_not_int: /* 0x7c */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(124*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_neg_long: /* 0x7d */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(125*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_not_long: /* 0x7e */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(126*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_neg_float: /* 0x7f */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(127*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_neg_double: /* 0x80 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(128*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_int_to_long: /* 0x81 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(129*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_int_to_float: /* 0x82 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(130*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_int_to_double: /* 0x83 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(131*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_long_to_int: /* 0x84 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(132*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_long_to_float: /* 0x85 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(133*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_long_to_double: /* 0x86 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(134*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_float_to_int: /* 0x87 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(135*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_float_to_long: /* 0x88 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(136*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_float_to_double: /* 0x89 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(137*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_double_to_int: /* 0x8a */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(138*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_double_to_long: /* 0x8b */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(139*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_double_to_float: /* 0x8c */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(140*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_int_to_byte: /* 0x8d */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(141*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_int_to_char: /* 0x8e */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(142*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_int_to_short: /* 0x8f */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(143*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_add_int: /* 0x90 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(144*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_sub_int: /* 0x91 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(145*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_mul_int: /* 0x92 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(146*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_div_int: /* 0x93 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(147*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_rem_int: /* 0x94 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(148*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_and_int: /* 0x95 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(149*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_or_int: /* 0x96 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(150*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_xor_int: /* 0x97 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(151*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_shl_int: /* 0x98 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(152*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_shr_int: /* 0x99 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(153*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_ushr_int: /* 0x9a */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(154*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_add_long: /* 0x9b */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(155*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_sub_long: /* 0x9c */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(156*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_mul_long: /* 0x9d */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(157*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_div_long: /* 0x9e */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(158*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_rem_long: /* 0x9f */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(159*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_and_long: /* 0xa0 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(160*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_or_long: /* 0xa1 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(161*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_xor_long: /* 0xa2 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(162*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_shl_long: /* 0xa3 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(163*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_shr_long: /* 0xa4 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(164*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_ushr_long: /* 0xa5 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(165*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_add_float: /* 0xa6 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(166*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_sub_float: /* 0xa7 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(167*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_mul_float: /* 0xa8 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(168*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_div_float: /* 0xa9 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(169*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_rem_float: /* 0xaa */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(170*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_add_double: /* 0xab */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(171*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_sub_double: /* 0xac */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(172*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_mul_double: /* 0xad */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(173*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_div_double: /* 0xae */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(174*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_rem_double: /* 0xaf */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(175*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_add_int_2addr: /* 0xb0 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(176*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_sub_int_2addr: /* 0xb1 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(177*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_mul_int_2addr: /* 0xb2 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(178*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_div_int_2addr: /* 0xb3 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(179*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_rem_int_2addr: /* 0xb4 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(180*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_and_int_2addr: /* 0xb5 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(181*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_or_int_2addr: /* 0xb6 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(182*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_xor_int_2addr: /* 0xb7 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(183*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_shl_int_2addr: /* 0xb8 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(184*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_shr_int_2addr: /* 0xb9 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(185*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_ushr_int_2addr: /* 0xba */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(186*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_add_long_2addr: /* 0xbb */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(187*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_sub_long_2addr: /* 0xbc */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(188*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_mul_long_2addr: /* 0xbd */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(189*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_div_long_2addr: /* 0xbe */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(190*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_rem_long_2addr: /* 0xbf */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(191*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_and_long_2addr: /* 0xc0 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(192*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_or_long_2addr: /* 0xc1 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(193*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_xor_long_2addr: /* 0xc2 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(194*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_shl_long_2addr: /* 0xc3 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(195*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_shr_long_2addr: /* 0xc4 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(196*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_ushr_long_2addr: /* 0xc5 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(197*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_add_float_2addr: /* 0xc6 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(198*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_sub_float_2addr: /* 0xc7 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(199*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_mul_float_2addr: /* 0xc8 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(200*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_div_float_2addr: /* 0xc9 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(201*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_rem_float_2addr: /* 0xca */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(202*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_add_double_2addr: /* 0xcb */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(203*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_sub_double_2addr: /* 0xcc */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(204*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_mul_double_2addr: /* 0xcd */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(205*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_div_double_2addr: /* 0xce */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(206*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_rem_double_2addr: /* 0xcf */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(207*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_add_int_lit16: /* 0xd0 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(208*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_rsub_int: /* 0xd1 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(209*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_mul_int_lit16: /* 0xd2 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(210*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_div_int_lit16: /* 0xd3 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(211*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_rem_int_lit16: /* 0xd4 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(212*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_and_int_lit16: /* 0xd5 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(213*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_or_int_lit16: /* 0xd6 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(214*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_xor_int_lit16: /* 0xd7 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(215*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_add_int_lit8: /* 0xd8 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(216*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_rsub_int_lit8: /* 0xd9 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(217*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_mul_int_lit8: /* 0xda */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(218*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_div_int_lit8: /* 0xdb */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(219*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_rem_int_lit8: /* 0xdc */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(220*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_and_int_lit8: /* 0xdd */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(221*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_or_int_lit8: /* 0xde */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(222*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_xor_int_lit8: /* 0xdf */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(223*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_shl_int_lit8: /* 0xe0 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(224*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_shr_int_lit8: /* 0xe1 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(225*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_ushr_int_lit8: /* 0xe2 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(226*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_iget_quick: /* 0xe3 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(227*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_iget_wide_quick: /* 0xe4 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(228*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_iget_object_quick: /* 0xe5 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(229*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_iput_quick: /* 0xe6 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(230*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_iput_wide_quick: /* 0xe7 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(231*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_iput_object_quick: /* 0xe8 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(232*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_invoke_virtual_quick: /* 0xe9 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(233*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_invoke_virtual_range_quick: /* 0xea */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(234*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_iput_boolean_quick: /* 0xeb */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(235*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_iput_byte_quick: /* 0xec */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(236*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_iput_char_quick: /* 0xed */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(237*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_iput_short_quick: /* 0xee */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(238*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_iget_boolean_quick: /* 0xef */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(239*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_iget_byte_quick: /* 0xf0 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(240*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_iget_char_quick: /* 0xf1 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(241*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_iget_short_quick: /* 0xf2 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(242*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_invoke_lambda: /* 0xf3 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(243*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_unused_f4: /* 0xf4 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(244*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_capture_variable: /* 0xf5 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(245*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_create_lambda: /* 0xf6 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(246*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_liberate_variable: /* 0xf7 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(247*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_box_lambda: /* 0xf8 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(248*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_unbox_lambda: /* 0xf9 */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(249*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_unused_fa: /* 0xfa */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(250*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_unused_fb: /* 0xfb */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(251*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_unused_fc: /* 0xfc */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(252*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_unused_fd: /* 0xfd */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(253*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_unused_fe: /* 0xfe */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(254*128) - -/* ------------------------------ */ - .balign 128 -.L_ALT_op_unused_ff: /* 0xff */ -/* File: x86_64/alt_stub.S */ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(255*128) - - .balign 128 - SIZE(SYMBOL(artMterpAsmAltInstructionStart),SYMBOL(artMterpAsmAltInstructionStart)) - .global SYMBOL(artMterpAsmAltInstructionEnd) -SYMBOL(artMterpAsmAltInstructionEnd): -/* File: x86_64/footer.S */ -/* - * =========================================================================== - * Common subroutines and data - * =========================================================================== - */ - - .text - .align 2 - -/* - * We've detected a condition that will result in an exception, but the exception - * has not yet been thrown. Just bail out to the reference interpreter to deal with it. - * TUNING: for consistency, we may want to just go ahead and handle these here. - */ -#define MTERP_LOGGING 0 -common_errDivideByZero: - EXPORT_PC -#if MTERP_LOGGING - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpLogDivideByZeroException) -#endif - jmp MterpCommonFallback - -common_errArrayIndex: - EXPORT_PC -#if MTERP_LOGGING - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpLogArrayIndexException) -#endif - jmp MterpCommonFallback - -common_errNegativeArraySize: - EXPORT_PC -#if MTERP_LOGGING - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpLogNegativeArraySizeException) -#endif - jmp MterpCommonFallback - -common_errNoSuchMethod: - EXPORT_PC -#if MTERP_LOGGING - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpLogNoSuchMethodException) -#endif - jmp MterpCommonFallback - -common_errNullObject: - EXPORT_PC -#if MTERP_LOGGING - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpLogNullObjectException) -#endif - jmp MterpCommonFallback - -common_exceptionThrown: - EXPORT_PC -#if MTERP_LOGGING - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpLogExceptionThrownException) -#endif - jmp MterpCommonFallback - -MterpSuspendFallback: - EXPORT_PC -#if MTERP_LOGGING - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - movl THREAD_FLAGS_OFFSET(rSELF), OUT_32_ARG2 - call SYMBOL(MterpLogSuspendFallback) -#endif - jmp MterpCommonFallback - -/* - * If we're here, something is out of the ordinary. If there is a pending - * exception, handle it. Otherwise, roll back and retry with the reference - * interpreter. - */ -MterpPossibleException: - cmpq $0, THREAD_EXCEPTION_OFFSET(rSELF) - jz MterpFallback - /* intentional fallthrough - handle pending exception. */ - -/* - * On return from a runtime helper routine, we've found a pending exception. - * Can we handle it here - or need to bail out to caller? - * - */ -MterpException: - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpHandleException) - testb %al, %al - jz MterpExceptionReturn - REFRESH_IBASE - movq OFF_FP_CODE_ITEM(rFP), %rax - mov OFF_FP_DEX_PC(rFP), %ecx - leaq CODEITEM_INSNS_OFFSET(%rax), rPC - leaq (rPC, %rcx, 2), rPC - movq rPC, OFF_FP_DEX_PC_PTR(rFP) - /* resume execution at catch block */ - FETCH_INST - GOTO_NEXT - /* NOTE: no fallthrough */ - -/* - * Check for suspend check request. Assumes rINST already loaded, rPC advanced and - * still needs to get the opcode and branch to it, and flags are in lr. - */ -MterpCheckSuspendAndContinue: - REFRESH_IBASE - testl $(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(rSELF) - jz 1f - EXPORT_PC - movq rSELF, OUT_ARG0 - call SYMBOL(MterpSuspendCheck) -1: - GOTO_NEXT - -/* - * Bail out to reference interpreter. - */ -MterpFallback: - EXPORT_PC -#if MTERP_LOGGING - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpLogFallback) -#endif -MterpCommonFallback: - xorl %eax, %eax - jmp MterpDone - -/* - * On entry: - * uint32_t* rFP (should still be live, pointer to base of vregs) - */ -MterpExceptionReturn: - movl $1, %eax - jmp MterpDone -MterpReturn: - movq OFF_FP_RESULT_REGISTER(rFP), %rdx - movq %rax, (%rdx) - movl $1, %eax -MterpDone: - /* Restore callee save register */ - movq RBX_SPILL(%rsp), %rbx - movq RBP_SPILL(%rsp), %rbp - movq R12_SPILL(%rsp), %r12 - movq R13_SPILL(%rsp), %r13 - movq R14_SPILL(%rsp), %r14 - movq R15_SPILL(%rsp), %r15 - - /* pop up frame */ - addq $FRAME_SIZE, %rsp - .cfi_adjust_cfa_offset -FRAME_SIZE - ret - - .cfi_endproc - SIZE(ExecuteMterpImpl,ExecuteMterpImpl) - diff --git a/runtime/interpreter/mterp/rebuild.sh b/runtime/interpreter/mterp/rebuild.sh index 0c2d5479a0..ac8794581c 100755 --- a/runtime/interpreter/mterp/rebuild.sh +++ b/runtime/interpreter/mterp/rebuild.sh @@ -21,4 +21,4 @@ set -e # for arch in arm x86 mips arm64 x86_64 mips64; do TARGET_ARCH_EXT=$arch make -f Makefile_mterp; done -for arch in arm x86 arm64 x86_64 ; do TARGET_ARCH_EXT=$arch make -f Makefile_mterp; done +for arch in arm x86 arm64 ; do TARGET_ARCH_EXT=$arch make -f Makefile_mterp; done diff --git a/runtime/interpreter/mterp/x86_64/alt_stub.S b/runtime/interpreter/mterp/x86_64/alt_stub.S deleted file mode 100644 index 6fcebbba3c..0000000000 --- a/runtime/interpreter/mterp/x86_64/alt_stub.S +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle - * any interesting requests and then jump to the real instruction - * handler. Unlike the Arm handler, we can't do this as a tail call - * because rIBASE is caller save and we need to reload it. - * - * Note that unlike in the Arm implementation, we should never arrive - * here with a zero breakFlag because we always refresh rIBASE on - * return. - */ - .extern MterpCheckBefore - EXPORT_PC - REFRESH_IBASE - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpCheckBefore) # (self, shadow_frame) - jmp .L_op_nop+(${opnum}*${handler_size_bytes}) diff --git a/runtime/interpreter/mterp/x86_64/bincmp.S b/runtime/interpreter/mterp/x86_64/bincmp.S deleted file mode 100644 index 5e4225fd86..0000000000 --- a/runtime/interpreter/mterp/x86_64/bincmp.S +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Generic two-operand compare-and-branch operation. Provide a "revcmp" - * fragment that specifies the *reverse* comparison to perform, e.g. - * for "if-le" you would use "gt". - * - * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le - */ - /* if-cmp vA, vB, +CCCC */ - movl rINST, %ecx # rcx <- A+ - sarl $$4, rINST # rINST <- B - andb $$0xf, %cl # rcx <- A - GET_VREG %eax, %rcx # eax <- vA - cmpl VREG_ADDRESS(rINSTq), %eax # compare (vA, vB) - movl $$2, %eax # assume not taken - j${revcmp} 1f - movswq 2(rPC),%rax # Get signed branch offset -1: - addq %rax, %rax # rax <- AA * 2 - leaq (rPC, %rax), rPC - FETCH_INST - jg 2f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -2: - GOTO_NEXT diff --git a/runtime/interpreter/mterp/x86_64/bindiv.S b/runtime/interpreter/mterp/x86_64/bindiv.S deleted file mode 100644 index e10d1dc4b1..0000000000 --- a/runtime/interpreter/mterp/x86_64/bindiv.S +++ /dev/null @@ -1,34 +0,0 @@ -%default {"result":"","second":"","wide":"","suffix":"","rem":"0","ext":"cdq"} -/* - * 32-bit binary div/rem operation. Handles special case of op1=-1. - */ - /* div/rem vAA, vBB, vCC */ - movzbq 2(rPC), %rax # rax <- BB - movzbq 3(rPC), %rcx # rcx <- CC - .if $wide - GET_WIDE_VREG %rax, %rax # eax <- vBB - GET_WIDE_VREG $second, %rcx # ecx <- vCC - .else - GET_VREG %eax, %rax # eax <- vBB - GET_VREG $second, %rcx # ecx <- vCC - .endif - test${suffix} $second, $second - jz common_errDivideByZero - cmp${suffix} $$-1, $second - je 2f - $ext # rdx:rax <- sign-extended of rax - idiv${suffix} $second -1: - .if $wide - SET_WIDE_VREG $result, rINSTq # eax <- vBB - .else - SET_VREG $result, rINSTq # eax <- vBB - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 -2: - .if $rem - xor${suffix} $result, $result - .else - neg${suffix} $result - .endif - jmp 1b diff --git a/runtime/interpreter/mterp/x86_64/bindiv2addr.S b/runtime/interpreter/mterp/x86_64/bindiv2addr.S deleted file mode 100644 index 8b9bc953d2..0000000000 --- a/runtime/interpreter/mterp/x86_64/bindiv2addr.S +++ /dev/null @@ -1,35 +0,0 @@ -%default {"result":"","second":"","wide":"","suffix":"","rem":"0","ext":"cdq"} -/* - * 32-bit binary div/rem operation. Handles special case of op1=-1. - */ - /* div/rem/2addr vA, vB */ - movl rINST, %ecx # rcx <- BA - sarl $$4, %ecx # rcx <- B - andb $$0xf, rINSTbl # rINST <- A - .if $wide - GET_WIDE_VREG %rax, rINSTq # eax <- vA - GET_WIDE_VREG $second, %rcx # ecx <- vB - .else - GET_VREG %eax, rINSTq # eax <- vA - GET_VREG $second, %rcx # ecx <- vB - .endif - test${suffix} $second, $second - jz common_errDivideByZero - cmp${suffix} $$-1, $second - je 2f - $ext # rdx:rax <- sign-extended of rax - idiv${suffix} $second -1: - .if $wide - SET_WIDE_VREG $result, rINSTq # vA <- result - .else - SET_VREG $result, rINSTq # vA <- result - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 -2: - .if $rem - xor${suffix} $result, $result - .else - neg${suffix} $result - .endif - jmp 1b diff --git a/runtime/interpreter/mterp/x86_64/bindivLit16.S b/runtime/interpreter/mterp/x86_64/bindivLit16.S deleted file mode 100644 index 80dbce2975..0000000000 --- a/runtime/interpreter/mterp/x86_64/bindivLit16.S +++ /dev/null @@ -1,27 +0,0 @@ -%default {"result":"","rem":"0"} -/* - * 32-bit binary div/rem operation. Handles special case of op1=-1. - */ - /* div/rem/lit16 vA, vB, #+CCCC */ - /* Need A in rINST, ssssCCCC in ecx, vB in eax */ - movl rINST, %eax # rax <- 000000BA - sarl $$4, %eax # eax <- B - GET_VREG %eax, %rax # eax <- vB - movswl 2(rPC), %ecx # ecx <- ssssCCCC - andb $$0xf, rINSTbl # rINST <- A - testl %ecx, %ecx - jz common_errDivideByZero - cmpl $$-1, %ecx - je 2f - cdq # rax <- sign-extended of eax - idivl %ecx -1: - SET_VREG $result, rINSTq # vA <- result - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 -2: - .if $rem - xorl $result, $result - .else - negl $result - .endif - jmp 1b diff --git a/runtime/interpreter/mterp/x86_64/bindivLit8.S b/runtime/interpreter/mterp/x86_64/bindivLit8.S deleted file mode 100644 index ab535f3fb0..0000000000 --- a/runtime/interpreter/mterp/x86_64/bindivLit8.S +++ /dev/null @@ -1,25 +0,0 @@ -%default {"result":"","rem":"0"} -/* - * 32-bit div/rem "lit8" binary operation. Handles special case of - * op0=minint & op1=-1 - */ - /* div/rem/lit8 vAA, vBB, #+CC */ - movzbq 2(rPC), %rax # eax <- BB - movsbl 3(rPC), %ecx # ecx <- ssssssCC - GET_VREG %eax, %rax # eax <- rBB - testl %ecx, %ecx - je common_errDivideByZero - cmpl $$-1, %ecx - je 2f - cdq # rax <- sign-extended of eax - idivl %ecx -1: - SET_VREG $result, rINSTq # vA <- result - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 -2: - .if $rem - xorl $result, $result - .else - negl $result - .endif - jmp 1b diff --git a/runtime/interpreter/mterp/x86_64/binop.S b/runtime/interpreter/mterp/x86_64/binop.S deleted file mode 100644 index 962dd61eea..0000000000 --- a/runtime/interpreter/mterp/x86_64/binop.S +++ /dev/null @@ -1,17 +0,0 @@ -%default {"result":"%eax"} -/* - * Generic 32-bit binary operation. Provide an "instr" line that - * specifies an instruction that performs "result = eax op (rFP,%ecx,4)". - * This could be an x86 instruction or a function call. (If the result - * comes back in a register other than eax, you can override "result".) - * - * For: add-int, sub-int, and-int, or-int, - * xor-int, shl-int, shr-int, ushr-int - */ - /* binop vAA, vBB, vCC */ - movzbq 2(rPC), %rax # rax <- BB - movzbq 3(rPC), %rcx # rcx <- CC - GET_VREG %eax, %rax # eax <- vBB - $instr # ex: addl (rFP,%rcx,4),%eax - SET_VREG $result, rINSTq - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/binop1.S b/runtime/interpreter/mterp/x86_64/binop1.S deleted file mode 100644 index bdd57325aa..0000000000 --- a/runtime/interpreter/mterp/x86_64/binop1.S +++ /dev/null @@ -1,19 +0,0 @@ -%default {"wide":"0"} -/* - * Generic 32-bit binary operation in which both operands loaded to - * registers (op0 in eax, op1 in ecx). - */ - /* binop vAA, vBB, vCC */ - movzbq 2(rPC), %rax # eax <- BB - movzbq 3(rPC), %rcx # ecx <- CC - GET_VREG %ecx, %rcx # eax <- vCC - .if $wide - GET_WIDE_VREG %rax, %rax # rax <- vBB - $instr # ex: addl %ecx,%eax - SET_WIDE_VREG %rax, rINSTq - .else - GET_VREG %eax, %rax # eax <- vBB - $instr # ex: addl %ecx,%eax - SET_VREG %eax, rINSTq - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/binop2addr.S b/runtime/interpreter/mterp/x86_64/binop2addr.S deleted file mode 100644 index 4448a815e9..0000000000 --- a/runtime/interpreter/mterp/x86_64/binop2addr.S +++ /dev/null @@ -1,19 +0,0 @@ -%default {"result":"%eax"} -/* - * Generic 32-bit "/2addr" binary operation. Provide an "instr" line - * that specifies an instruction that performs "result = r0 op r1". - * This could be an instruction or a function call. - * - * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, - * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, - * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr, - * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr - */ - /* binop/2addr vA, vB */ - movl rINST, %ecx # rcx <- A+ - sarl $$4, rINST # rINST <- B - andb $$0xf, %cl # ecx <- A - GET_VREG %eax, rINSTq # eax <- vB - $instr # for ex: addl %eax,(rFP,%ecx,4) - CLEAR_REF %rcx - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/binopLit16.S b/runtime/interpreter/mterp/x86_64/binopLit16.S deleted file mode 100644 index de43b53d5c..0000000000 --- a/runtime/interpreter/mterp/x86_64/binopLit16.S +++ /dev/null @@ -1,19 +0,0 @@ -%default {"result":"%eax"} -/* - * Generic 32-bit "lit16" binary operation. Provide an "instr" line - * that specifies an instruction that performs "result = eax op ecx". - * This could be an x86 instruction or a function call. (If the result - * comes back in a register other than eax, you can override "result".) - * - * For: add-int/lit16, rsub-int, - * and-int/lit16, or-int/lit16, xor-int/lit16 - */ - /* binop/lit16 vA, vB, #+CCCC */ - movl rINST, %eax # rax <- 000000BA - sarl $$4, %eax # eax <- B - GET_VREG %eax, %rax # eax <- vB - andb $$0xf, rINSTbl # rINST <- A - movswl 2(rPC), %ecx # ecx <- ssssCCCC - $instr # for example: addl %ecx, %eax - SET_VREG $result, rINSTq - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/binopLit8.S b/runtime/interpreter/mterp/x86_64/binopLit8.S deleted file mode 100644 index 995002b7e4..0000000000 --- a/runtime/interpreter/mterp/x86_64/binopLit8.S +++ /dev/null @@ -1,18 +0,0 @@ -%default {"result":"%eax"} -/* - * Generic 32-bit "lit8" binary operation. Provide an "instr" line - * that specifies an instruction that performs "result = eax op ecx". - * This could be an x86 instruction or a function call. (If the result - * comes back in a register other than r0, you can override "result".) - * - * For: add-int/lit8, rsub-int/lit8 - * and-int/lit8, or-int/lit8, xor-int/lit8, - * shl-int/lit8, shr-int/lit8, ushr-int/lit8 - */ - /* binop/lit8 vAA, vBB, #+CC */ - movzbq 2(rPC), %rax # rax <- BB - movsbl 3(rPC), %ecx # rcx <- ssssssCC - GET_VREG %eax, %rax # eax <- rBB - $instr # ex: addl %ecx,%eax - SET_VREG $result, rINSTq - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/binopWide.S b/runtime/interpreter/mterp/x86_64/binopWide.S deleted file mode 100644 index f92f18e013..0000000000 --- a/runtime/interpreter/mterp/x86_64/binopWide.S +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Generic 64-bit binary operation. - */ - /* binop vAA, vBB, vCC */ - movzbq 2(rPC), %rax # eax <- BB - movzbq 3(rPC), %rcx # ecx <- CC - GET_WIDE_VREG %rax, %rax # rax <- v[BB] - $instr # ex: addq (rFP,%rcx,4),%rax - SET_WIDE_VREG %rax, rINSTq # v[AA] <- rax - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/binopWide2addr.S b/runtime/interpreter/mterp/x86_64/binopWide2addr.S deleted file mode 100644 index d9e6cfbc9d..0000000000 --- a/runtime/interpreter/mterp/x86_64/binopWide2addr.S +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Generic 64-bit binary operation. - */ - /* binop/2addr vA, vB */ - movl rINST, %ecx # rcx <- A+ - sarl $$4, rINST # rINST <- B - andb $$0xf, %cl # ecx <- A - GET_WIDE_VREG %rax, rINSTq # rax <- vB - $instr # for ex: addq %rax,(rFP,%rcx,4) - CLEAR_WIDE_REF %rcx - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/cvtfp_int.S b/runtime/interpreter/mterp/x86_64/cvtfp_int.S deleted file mode 100644 index 1472bd26bd..0000000000 --- a/runtime/interpreter/mterp/x86_64/cvtfp_int.S +++ /dev/null @@ -1,27 +0,0 @@ -%default {"fp_suffix":"","i_suffix":"","max_const":"","result_reg":"","wide":""} -/* On fp to int conversions, Java requires that - * if the result > maxint, it should be clamped to maxint. If it is less - * than minint, it should be clamped to minint. If it is a nan, the result - * should be zero. Further, the rounding mode is to truncate. - */ - /* float/double to int/long vA, vB */ - movl rINST, %ecx # rcx <- A+ - sarl $$4, rINST # rINST <- B - andb $$0xf, %cl # ecx <- A - movs${fp_suffix} VREG_ADDRESS(rINSTq), %xmm0 - mov${i_suffix} ${max_const}, ${result_reg} - cvtsi2s${fp_suffix}${i_suffix} ${result_reg}, %xmm1 - comis${fp_suffix} %xmm1, %xmm0 - jae 1f - jp 2f - cvtts${fp_suffix}2si${i_suffix} %xmm0, ${result_reg} - jmp 1f -2: - xor${i_suffix} ${result_reg}, ${result_reg} -1: - .if $wide - SET_WIDE_VREG ${result_reg}, %rcx - .else - SET_VREG ${result_reg}, %rcx - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/entry.S b/runtime/interpreter/mterp/x86_64/entry.S deleted file mode 100644 index 4b868f2ae1..0000000000 --- a/runtime/interpreter/mterp/x86_64/entry.S +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* - * Interpreter entry point. - */ - - .text - .global SYMBOL(ExecuteMterpImpl) - FUNCTION_TYPE(ExecuteMterpImpl) - -/* - * On entry: - * 0 Thread* self - * 1 code_item - * 2 ShadowFrame - * 3 JValue* result_register - * - */ - -SYMBOL(ExecuteMterpImpl): - .cfi_startproc - - /* Allocate frame */ - subq $$FRAME_SIZE, %rsp - .cfi_adjust_cfa_offset FRAME_SIZE - - /* Spill callee save regs */ - movq %rbx, RBX_SPILL(%rsp) - movq %rbp, RBP_SPILL(%rsp) - movq %r12, R12_SPILL(%rsp) - movq %r13, R13_SPILL(%rsp) - movq %r14, R14_SPILL(%rsp) - movq %r15, R15_SPILL(%rsp) - - /* Remember the return register */ - movq IN_ARG3, SHADOWFRAME_RESULT_REGISTER_OFFSET(IN_ARG2) - - /* Remember the code_item */ - movq IN_ARG1, SHADOWFRAME_CODE_ITEM_OFFSET(IN_ARG2) - - /* set up "named" registers */ - movl SHADOWFRAME_NUMBER_OF_VREGS_OFFSET(IN_ARG2), %eax - leaq SHADOWFRAME_VREGS_OFFSET(IN_ARG2), rFP - leaq (rFP, %rax, 4), rREFS - movl SHADOWFRAME_DEX_PC_OFFSET(IN_ARG2), %eax - leaq CODEITEM_INSNS_OFFSET(IN_ARG1), rPC - leaq (rPC, %rax, 2), rPC - EXPORT_PC - - /* Starting ibase */ - movq IN_ARG0, rSELF - REFRESH_IBASE - - /* start executing the instruction at rPC */ - FETCH_INST - GOTO_NEXT - /* NOTE: no fallthrough */ diff --git a/runtime/interpreter/mterp/x86_64/fallback.S b/runtime/interpreter/mterp/x86_64/fallback.S deleted file mode 100644 index 8d61166f63..0000000000 --- a/runtime/interpreter/mterp/x86_64/fallback.S +++ /dev/null @@ -1,3 +0,0 @@ -/* Transfer stub to alternate interpreter */ - jmp MterpFallback - diff --git a/runtime/interpreter/mterp/x86_64/footer.S b/runtime/interpreter/mterp/x86_64/footer.S deleted file mode 100644 index 165f380ba4..0000000000 --- a/runtime/interpreter/mterp/x86_64/footer.S +++ /dev/null @@ -1,166 +0,0 @@ -/* - * =========================================================================== - * Common subroutines and data - * =========================================================================== - */ - - .text - .align 2 - -/* - * We've detected a condition that will result in an exception, but the exception - * has not yet been thrown. Just bail out to the reference interpreter to deal with it. - * TUNING: for consistency, we may want to just go ahead and handle these here. - */ -#define MTERP_LOGGING 0 -common_errDivideByZero: - EXPORT_PC -#if MTERP_LOGGING - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpLogDivideByZeroException) -#endif - jmp MterpCommonFallback - -common_errArrayIndex: - EXPORT_PC -#if MTERP_LOGGING - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpLogArrayIndexException) -#endif - jmp MterpCommonFallback - -common_errNegativeArraySize: - EXPORT_PC -#if MTERP_LOGGING - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpLogNegativeArraySizeException) -#endif - jmp MterpCommonFallback - -common_errNoSuchMethod: - EXPORT_PC -#if MTERP_LOGGING - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpLogNoSuchMethodException) -#endif - jmp MterpCommonFallback - -common_errNullObject: - EXPORT_PC -#if MTERP_LOGGING - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpLogNullObjectException) -#endif - jmp MterpCommonFallback - -common_exceptionThrown: - EXPORT_PC -#if MTERP_LOGGING - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpLogExceptionThrownException) -#endif - jmp MterpCommonFallback - -MterpSuspendFallback: - EXPORT_PC -#if MTERP_LOGGING - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - movl THREAD_FLAGS_OFFSET(rSELF), OUT_32_ARG2 - call SYMBOL(MterpLogSuspendFallback) -#endif - jmp MterpCommonFallback - -/* - * If we're here, something is out of the ordinary. If there is a pending - * exception, handle it. Otherwise, roll back and retry with the reference - * interpreter. - */ -MterpPossibleException: - cmpq $$0, THREAD_EXCEPTION_OFFSET(rSELF) - jz MterpFallback - /* intentional fallthrough - handle pending exception. */ - -/* - * On return from a runtime helper routine, we've found a pending exception. - * Can we handle it here - or need to bail out to caller? - * - */ -MterpException: - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpHandleException) - testb %al, %al - jz MterpExceptionReturn - REFRESH_IBASE - movq OFF_FP_CODE_ITEM(rFP), %rax - mov OFF_FP_DEX_PC(rFP), %ecx - leaq CODEITEM_INSNS_OFFSET(%rax), rPC - leaq (rPC, %rcx, 2), rPC - movq rPC, OFF_FP_DEX_PC_PTR(rFP) - /* resume execution at catch block */ - FETCH_INST - GOTO_NEXT - /* NOTE: no fallthrough */ - -/* - * Check for suspend check request. Assumes rINST already loaded, rPC advanced and - * still needs to get the opcode and branch to it, and flags are in lr. - */ -MterpCheckSuspendAndContinue: - REFRESH_IBASE - testl $$(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(rSELF) - jz 1f - EXPORT_PC - movq rSELF, OUT_ARG0 - call SYMBOL(MterpSuspendCheck) -1: - GOTO_NEXT - -/* - * Bail out to reference interpreter. - */ -MterpFallback: - EXPORT_PC -#if MTERP_LOGGING - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - call SYMBOL(MterpLogFallback) -#endif -MterpCommonFallback: - xorl %eax, %eax - jmp MterpDone - -/* - * On entry: - * uint32_t* rFP (should still be live, pointer to base of vregs) - */ -MterpExceptionReturn: - movl $$1, %eax - jmp MterpDone -MterpReturn: - movq OFF_FP_RESULT_REGISTER(rFP), %rdx - movq %rax, (%rdx) - movl $$1, %eax -MterpDone: - /* Restore callee save register */ - movq RBX_SPILL(%rsp), %rbx - movq RBP_SPILL(%rsp), %rbp - movq R12_SPILL(%rsp), %r12 - movq R13_SPILL(%rsp), %r13 - movq R14_SPILL(%rsp), %r14 - movq R15_SPILL(%rsp), %r15 - - /* pop up frame */ - addq $$FRAME_SIZE, %rsp - .cfi_adjust_cfa_offset -FRAME_SIZE - ret - - .cfi_endproc - SIZE(ExecuteMterpImpl,ExecuteMterpImpl) diff --git a/runtime/interpreter/mterp/x86_64/fpcmp.S b/runtime/interpreter/mterp/x86_64/fpcmp.S deleted file mode 100644 index 806bc2b1ef..0000000000 --- a/runtime/interpreter/mterp/x86_64/fpcmp.S +++ /dev/null @@ -1,35 +0,0 @@ -%default {"suff":"d","nanval":"pos"} -/* - * Compare two floating-point values. Puts 0, 1, or -1 into the - * destination register based on the results of the comparison. - * - * int compare(x, y) { - * if (x == y) { - * return 0; - * } else if (x < y) { - * return -1; - * } else if (x > y) { - * return 1; - * } else { - * return nanval ? 1 : -1; - * } - * } - */ - /* op vAA, vBB, vCC */ - movzbq 3(rPC), %rcx # ecx<- CC - movzbq 2(rPC), %rax # eax<- BB - movs${suff} VREG_ADDRESS(%rax), %xmm0 - xor %eax, %eax - ucomis${suff} VREG_ADDRESS(%rcx), %xmm0 - jp .L${opcode}_nan_is_${nanval} - je .L${opcode}_finish - jb .L${opcode}_less -.L${opcode}_nan_is_pos: - addb $$1, %al - jmp .L${opcode}_finish -.L${opcode}_nan_is_neg: -.L${opcode}_less: - movl $$-1, %eax -.L${opcode}_finish: - SET_VREG %eax, rINSTq - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/fpcvt.S b/runtime/interpreter/mterp/x86_64/fpcvt.S deleted file mode 100644 index 657869e0bd..0000000000 --- a/runtime/interpreter/mterp/x86_64/fpcvt.S +++ /dev/null @@ -1,17 +0,0 @@ -%default {"source_suffix":"","dest_suffix":"","wide":""} -/* - * Generic 32-bit FP conversion operation. - */ - /* unop vA, vB */ - movl rINST, %ecx # rcx <- A+ - sarl $$4, rINST # rINST <- B - andb $$0xf, %cl # ecx <- A - cvts${source_suffix}2s${dest_suffix} VREG_ADDRESS(rINSTq), %xmm0 - .if $wide - movsd %xmm0, VREG_ADDRESS(%rcx) - CLEAR_WIDE_REF %rcx - .else - movss %xmm0, VREG_ADDRESS(%rcx) - CLEAR_REF %rcx - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/header.S b/runtime/interpreter/mterp/x86_64/header.S deleted file mode 100644 index fadfc19690..0000000000 --- a/runtime/interpreter/mterp/x86_64/header.S +++ /dev/null @@ -1,289 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - Art assembly interpreter notes: - - First validate assembly code by implementing ExecuteXXXImpl() style body (doesn't - handle invoke, allows higher-level code to create frame & shadow frame. - - Once that's working, support direct entry code & eliminate shadow frame (and - excess locals allocation. - - Some (hopefully) temporary ugliness. We'll treat rFP as pointing to the - base of the vreg array within the shadow frame. Access the other fields, - dex_pc_, method_ and number_of_vregs_ via negative offsets. For now, we'll continue - the shadow frame mechanism of double-storing object references - via rFP & - number_of_vregs_. - - */ - -/* -x86_64 ABI general notes: - -Caller save set: - rax, rdx, rcx, rsi, rdi, r8-r11, st(0)-st(7) -Callee save set: - rbx, rbp, r12-r15 -Return regs: - 32-bit in eax - 64-bit in rax - fp on xmm0 - -First 8 fp parameters came in xmm0-xmm7. -First 6 non-fp parameters came in rdi, rsi, rdx, rcx, r8, r9. -Other parameters passed on stack, pushed right-to-left. On entry to target, first -param is at 8(%esp). Traditional entry code is: - -Stack must be 16-byte aligned to support SSE in native code. - -If we're not doing variable stack allocation (alloca), the frame pointer can be -eliminated and all arg references adjusted to be esp relative. -*/ - -/* -Mterp and x86_64 notes: - -Some key interpreter variables will be assigned to registers. - - nick reg purpose - rSELF rbp pointer to ThreadSelf. - rPC r12 interpreted program counter, used for fetching instructions - rFP r13 interpreted frame pointer, used for accessing locals and args - rINSTw bx first 16-bit code of current instruction - rINSTbl bl opcode portion of instruction word - rINSTbh bh high byte of inst word, usually contains src/tgt reg names - rIBASE r14 base of instruction handler table - rREFS r15 base of object references in shadow frame. - -Notes: - o High order 16 bits of ebx must be zero on entry to handler - o rPC, rFP, rINSTw/rINSTbl valid on handler entry and exit - o eax and ecx are scratch, rINSTw/ebx sometimes scratch - -Macros are provided for common operations. Each macro MUST emit only -one instruction to make instruction-counting easier. They MUST NOT alter -unspecified registers or condition codes. -*/ - -/* - * This is a #include, not a %include, because we want the C pre-processor - * to expand the macros into assembler assignment statements. - */ -#include "asm_support.h" - -/* - * Handle mac compiler specific - */ -#if defined(__APPLE__) - #define MACRO_LITERAL(value) $$(value) - #define FUNCTION_TYPE(name) - #define SIZE(start,end) - // Mac OS' symbols have an _ prefix. - #define SYMBOL(name) _ ## name -#else - #define MACRO_LITERAL(value) $$value - #define FUNCTION_TYPE(name) .type name, @function - #define SIZE(start,end) .size start, .-end - #define SYMBOL(name) name -#endif - -/* Frame size must be 16-byte aligned. - * Remember about 8 bytes for return address - */ -#define FRAME_SIZE 56 - -/* Frame diagram while executing ExecuteMterpImpl, high to low addresses */ -#define IN_ARG3 %rcx -#define IN_ARG2 %rdx -#define IN_ARG1 %rsi -#define IN_ARG0 %rdi -#define CALLER_RP (FRAME_SIZE + 0) -/* Spill offsets relative to %esp */ -#define RBX_SPILL (FRAME_SIZE - 8) -#define RBP_SPILL (FRAME_SIZE - 16) -#define R12_SPILL (FRAME_SIZE - 24) -#define R13_SPILL (FRAME_SIZE - 32) -#define R14_SPILL (FRAME_SIZE - 40) -#define R15_SPILL (FRAME_SIZE - 48) -/* Out Arg offsets, relative to %esp */ -#define OUT_ARG3 %rcx -#define OUT_ARG2 %rdx -#define OUT_ARG1 %rsi -#define OUT_ARG0 %rdi -#define OUT_32_ARG3 %ecx -#define OUT_32_ARG2 %edx -#define OUT_32_ARG1 %esi -#define OUT_32_ARG0 %edi -#define OUT_FP_ARG1 %xmm1 -#define OUT_FP_ARG0 %xmm0 - -/* During bringup, we'll use the shadow frame model instead of rFP */ -/* single-purpose registers, given names for clarity */ -#define rSELF %rbp -#define rPC %r12 -#define rFP %r13 -#define rINST %ebx -#define rINSTq %rbx -#define rINSTw %bx -#define rINSTbh %bh -#define rINSTbl %bl -#define rIBASE %r14 -#define rREFS %r15 - -/* - * Instead of holding a pointer to the shadow frame, we keep rFP at the base of the vregs. So, - * to access other shadow frame fields, we need to use a backwards offset. Define those here. - */ -#define OFF_FP(a) (a - SHADOWFRAME_VREGS_OFFSET) -#define OFF_FP_NUMBER_OF_VREGS OFF_FP(SHADOWFRAME_NUMBER_OF_VREGS_OFFSET) -#define OFF_FP_DEX_PC OFF_FP(SHADOWFRAME_DEX_PC_OFFSET) -#define OFF_FP_LINK OFF_FP(SHADOWFRAME_LINK_OFFSET) -#define OFF_FP_METHOD OFF_FP(SHADOWFRAME_METHOD_OFFSET) -#define OFF_FP_RESULT_REGISTER OFF_FP(SHADOWFRAME_RESULT_REGISTER_OFFSET) -#define OFF_FP_DEX_PC_PTR OFF_FP(SHADOWFRAME_DEX_PC_PTR_OFFSET) -#define OFF_FP_CODE_ITEM OFF_FP(SHADOWFRAME_CODE_ITEM_OFFSET) -#define OFF_FP_SHADOWFRAME (-SHADOWFRAME_VREGS_OFFSET) - -/* - * - * The reference interpreter performs explicit suspect checks, which is somewhat wasteful. - * Dalvik's interpreter folded suspend checks into the jump table mechanism, and eventually - * mterp should do so as well. - */ -#define MTERP_SUSPEND 0 - -/* - * "export" the PC to dex_pc field in the shadow frame, f/b/o future exception objects. Must - * be done *before* something throws. - * - * It's okay to do this more than once. - * - * NOTE: the fast interpreter keeps track of dex pc as a direct pointer to the mapped - * dex byte codes. However, the rest of the runtime expects dex pc to be an instruction - * offset into the code_items_[] array. For effiency, we will "export" the - * current dex pc as a direct pointer using the EXPORT_PC macro, and rely on GetDexPC - * to convert to a dex pc when needed. - */ -.macro EXPORT_PC - movq rPC, OFF_FP_DEX_PC_PTR(rFP) -.endm - -/* - * Refresh handler table. - * IBase handles uses the caller save register so we must restore it after each call. - * Also it is used as a result of some 64-bit operations (like imul) and we should - * restore it in such cases also. - * - */ -.macro REFRESH_IBASE - movq THREAD_CURRENT_IBASE_OFFSET(rSELF), rIBASE -.endm - -/* - * Refresh rINST. - * At enter to handler rINST does not contain the opcode number. - * However some utilities require the full value, so this macro - * restores the opcode number. - */ -.macro REFRESH_INST _opnum - movb rINSTbl, rINSTbh - movb $$\_opnum, rINSTbl -.endm - -/* - * Fetch the next instruction from rPC into rINSTw. Does not advance rPC. - */ -.macro FETCH_INST - movzwq (rPC), rINSTq -.endm - -/* - * Remove opcode from rINST, compute the address of handler and jump to it. - */ -.macro GOTO_NEXT - movzx rINSTbl,%eax - movzbl rINSTbh,rINST - shll MACRO_LITERAL(${handler_size_bits}), %eax - addq rIBASE, %rax - jmp *%rax -.endm - -/* - * Advance rPC by instruction count. - */ -.macro ADVANCE_PC _count - leaq 2*\_count(rPC), rPC -.endm - -/* - * Advance rPC by instruction count, fetch instruction and jump to handler. - */ -.macro ADVANCE_PC_FETCH_AND_GOTO_NEXT _count - ADVANCE_PC \_count - FETCH_INST - GOTO_NEXT -.endm - -/* - * Get/set the 32-bit value from a Dalvik register. - */ -#define VREG_ADDRESS(_vreg) (rFP,_vreg,4) -#define VREG_REF_ADDRESS(_vreg) (rREFS,_vreg,4) - -.macro GET_VREG _reg _vreg - movl (rFP,\_vreg,4), \_reg -.endm - -/* Read wide value. */ -.macro GET_WIDE_VREG _reg _vreg - movq (rFP,\_vreg,4), \_reg -.endm - -.macro SET_VREG _reg _vreg - movl \_reg, (rFP,\_vreg,4) - movl MACRO_LITERAL(0), (rREFS,\_vreg,4) -.endm - -/* Write wide value. reg is clobbered. */ -.macro SET_WIDE_VREG _reg _vreg - movq \_reg, (rFP,\_vreg,4) - xorq \_reg, \_reg - movq \_reg, (rREFS,\_vreg,4) -.endm - -.macro SET_VREG_OBJECT _reg _vreg - movl \_reg, (rFP,\_vreg,4) - movl \_reg, (rREFS,\_vreg,4) -.endm - -.macro GET_VREG_HIGH _reg _vreg - movl 4(rFP,\_vreg,4), \_reg -.endm - -.macro SET_VREG_HIGH _reg _vreg - movl \_reg, 4(rFP,\_vreg,4) - movl MACRO_LITERAL(0), 4(rREFS,\_vreg,4) -.endm - -.macro CLEAR_REF _vreg - movl MACRO_LITERAL(0), (rREFS,\_vreg,4) -.endm - -.macro CLEAR_WIDE_REF _vreg - movl MACRO_LITERAL(0), (rREFS,\_vreg,4) - movl MACRO_LITERAL(0), 4(rREFS,\_vreg,4) -.endm diff --git a/runtime/interpreter/mterp/x86_64/invoke.S b/runtime/interpreter/mterp/x86_64/invoke.S deleted file mode 100644 index 86eccdbf91..0000000000 --- a/runtime/interpreter/mterp/x86_64/invoke.S +++ /dev/null @@ -1,17 +0,0 @@ -%default { "helper":"UndefinedInvokeHandler" } -/* - * Generic invoke handler wrapper. - */ - /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ - /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ - .extern $helper - EXPORT_PC - movq rSELF, OUT_ARG0 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 - movq rPC, OUT_ARG2 - REFRESH_INST ${opnum} - movl rINST, OUT_32_ARG3 - call SYMBOL($helper) - testb %al, %al - jz MterpException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 diff --git a/runtime/interpreter/mterp/x86_64/op_add_double.S b/runtime/interpreter/mterp/x86_64/op_add_double.S deleted file mode 100644 index cb462cb816..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_add_double.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/sseBinop.S" {"instr":"adds","suff":"d"} diff --git a/runtime/interpreter/mterp/x86_64/op_add_double_2addr.S b/runtime/interpreter/mterp/x86_64/op_add_double_2addr.S deleted file mode 100644 index 063bde3fb3..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_add_double_2addr.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/sseBinop2Addr.S" {"instr":"adds","suff":"d"} diff --git a/runtime/interpreter/mterp/x86_64/op_add_float.S b/runtime/interpreter/mterp/x86_64/op_add_float.S deleted file mode 100644 index 7753bf8870..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_add_float.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/sseBinop.S" {"instr":"adds","suff":"s"} diff --git a/runtime/interpreter/mterp/x86_64/op_add_float_2addr.S b/runtime/interpreter/mterp/x86_64/op_add_float_2addr.S deleted file mode 100644 index 6c8005b182..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_add_float_2addr.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/sseBinop2Addr.S" {"instr":"adds","suff":"s"} diff --git a/runtime/interpreter/mterp/x86_64/op_add_int.S b/runtime/interpreter/mterp/x86_64/op_add_int.S deleted file mode 100644 index e316be7b9d..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_add_int.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/binop.S" {"instr":"addl (rFP,%rcx,4), %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_add_int_2addr.S b/runtime/interpreter/mterp/x86_64/op_add_int_2addr.S deleted file mode 100644 index 2ff82935ae..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_add_int_2addr.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/binop2addr.S" {"instr":"addl %eax, (rFP,%rcx,4)"} diff --git a/runtime/interpreter/mterp/x86_64/op_add_int_lit16.S b/runtime/interpreter/mterp/x86_64/op_add_int_lit16.S deleted file mode 100644 index bfeb7caabc..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_add_int_lit16.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/binopLit16.S" {"instr":"addl %ecx, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_add_int_lit8.S b/runtime/interpreter/mterp/x86_64/op_add_int_lit8.S deleted file mode 100644 index 8954844eae..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_add_int_lit8.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/binopLit8.S" {"instr":"addl %ecx, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_add_long.S b/runtime/interpreter/mterp/x86_64/op_add_long.S deleted file mode 100644 index 89131ffe0e..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_add_long.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/binopWide.S" {"instr":"addq (rFP,%rcx,4), %rax"} diff --git a/runtime/interpreter/mterp/x86_64/op_add_long_2addr.S b/runtime/interpreter/mterp/x86_64/op_add_long_2addr.S deleted file mode 100644 index fed98bc3e6..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_add_long_2addr.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/binopWide2addr.S" {"instr":"addq %rax, (rFP,%rcx,4)"} diff --git a/runtime/interpreter/mterp/x86_64/op_aget.S b/runtime/interpreter/mterp/x86_64/op_aget.S deleted file mode 100644 index 58d49481cf..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_aget.S +++ /dev/null @@ -1,24 +0,0 @@ -%default { "load":"movl", "shift":"4", "data_offset":"MIRROR_INT_ARRAY_DATA_OFFSET", "wide":"0" } -/* - * Array get, 32 bits or less. vAA <- vBB[vCC]. - * - * for: aget, aget-boolean, aget-byte, aget-char, aget-short, aget-wide - * - */ - /* op vAA, vBB, vCC */ - movzbq 2(rPC), %rax # eax <- BB - movzbq 3(rPC), %rcx # ecx <- CC - GET_VREG %eax, %rax # eax <- vBB (array object) - GET_VREG %ecx, %rcx # ecx <- vCC (requested index) - testl %eax, %eax # null array object? - je common_errNullObject # bail if so - cmpl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ecx - jae common_errArrayIndex # index >= length, bail. - .if $wide - movq $data_offset(%rax,%rcx,8), %rax - SET_WIDE_VREG %rax, rINSTq - .else - $load $data_offset(%rax,%rcx,$shift), %eax - SET_VREG %eax, rINSTq - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_aget_boolean.S b/runtime/interpreter/mterp/x86_64/op_aget_boolean.S deleted file mode 100644 index cf7bdb582b..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_aget_boolean.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/op_aget.S" { "load":"movzbl", "shift":"1", "data_offset":"MIRROR_BOOLEAN_ARRAY_DATA_OFFSET" } diff --git a/runtime/interpreter/mterp/x86_64/op_aget_byte.S b/runtime/interpreter/mterp/x86_64/op_aget_byte.S deleted file mode 100644 index 1cbb569024..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_aget_byte.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/op_aget.S" { "load":"movsbl", "shift":"1", "data_offset":"MIRROR_BYTE_ARRAY_DATA_OFFSET" } diff --git a/runtime/interpreter/mterp/x86_64/op_aget_char.S b/runtime/interpreter/mterp/x86_64/op_aget_char.S deleted file mode 100644 index 45c90851fd..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_aget_char.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/op_aget.S" { "load":"movzwl", "shift":"2", "data_offset":"MIRROR_CHAR_ARRAY_DATA_OFFSET" } diff --git a/runtime/interpreter/mterp/x86_64/op_aget_object.S b/runtime/interpreter/mterp/x86_64/op_aget_object.S deleted file mode 100644 index 8baedeab5e..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_aget_object.S +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Array object get. vAA <- vBB[vCC]. - * - * for: aget-object - */ - /* op vAA, vBB, vCC */ - movzbq 2(rPC), %rax # rax <- BB - movzbq 3(rPC), %rcx # rcx <- CC - GET_VREG OUT_32_ARG0, %rax # eax <- vBB (array object) - GET_VREG OUT_32_ARG1, %rcx # ecx <- vCC (requested index) - EXPORT_PC - call SYMBOL(artAGetObjectFromMterp) # (array, index) - cmpq $$0, THREAD_EXCEPTION_OFFSET(rSELF) - jnz MterpException - SET_VREG_OBJECT %eax, rINSTq - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_aget_short.S b/runtime/interpreter/mterp/x86_64/op_aget_short.S deleted file mode 100644 index 82c4a1ddf3..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_aget_short.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/op_aget.S" { "load":"movswl", "shift":"2", "data_offset":"MIRROR_SHORT_ARRAY_DATA_OFFSET" } diff --git a/runtime/interpreter/mterp/x86_64/op_aget_wide.S b/runtime/interpreter/mterp/x86_64/op_aget_wide.S deleted file mode 100644 index 4f2771b9c4..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_aget_wide.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/op_aget.S" { "load":"movq", "shift":"8", "data_offset":"MIRROR_WIDE_ARRAY_DATA_OFFSET", "wide":"1" } diff --git a/runtime/interpreter/mterp/x86_64/op_and_int.S b/runtime/interpreter/mterp/x86_64/op_and_int.S deleted file mode 100644 index 446988993d..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_and_int.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/binop.S" {"instr":"andl (rFP,%rcx,4), %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_and_int_2addr.S b/runtime/interpreter/mterp/x86_64/op_and_int_2addr.S deleted file mode 100644 index 16315bba03..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_and_int_2addr.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/binop2addr.S" {"instr":"andl %eax, (rFP,%rcx,4)"} diff --git a/runtime/interpreter/mterp/x86_64/op_and_int_lit16.S b/runtime/interpreter/mterp/x86_64/op_and_int_lit16.S deleted file mode 100644 index 63e851b449..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_and_int_lit16.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/binopLit16.S" {"instr":"andl %ecx, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_and_int_lit8.S b/runtime/interpreter/mterp/x86_64/op_and_int_lit8.S deleted file mode 100644 index da7a20fdff..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_and_int_lit8.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/binopLit8.S" {"instr":"andl %ecx, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_and_long.S b/runtime/interpreter/mterp/x86_64/op_and_long.S deleted file mode 100644 index ce1dd264dd..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_and_long.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/binopWide.S" {"instr":"andq (rFP,%rcx,4), %rax"} diff --git a/runtime/interpreter/mterp/x86_64/op_and_long_2addr.S b/runtime/interpreter/mterp/x86_64/op_and_long_2addr.S deleted file mode 100644 index d17ab8d58b..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_and_long_2addr.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/binopWide2addr.S" {"instr":"andq %rax, (rFP,%rcx,4)"} diff --git a/runtime/interpreter/mterp/x86_64/op_aput.S b/runtime/interpreter/mterp/x86_64/op_aput.S deleted file mode 100644 index 11500ad201..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_aput.S +++ /dev/null @@ -1,23 +0,0 @@ -%default { "reg":"rINST", "store":"movl", "shift":"4", "data_offset":"MIRROR_INT_ARRAY_DATA_OFFSET", "wide":"0" } -/* - * Array put, 32 bits or less. vBB[vCC] <- vAA. - * - * for: aput, aput-boolean, aput-byte, aput-char, aput-short, aput-wide - * - */ - /* op vAA, vBB, vCC */ - movzbq 2(rPC), %rax # rax <- BB - movzbq 3(rPC), %rcx # rcx <- CC - GET_VREG %eax, %rax # eax <- vBB (array object) - GET_VREG %ecx, %rcx # ecx <- vCC (requested index) - testl %eax, %eax # null array object? - je common_errNullObject # bail if so - cmpl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ecx - jae common_errArrayIndex # index >= length, bail. - .if $wide - GET_WIDE_VREG rINSTq, rINSTq - .else - GET_VREG rINST, rINSTq - .endif - $store $reg, $data_offset(%rax,%rcx,$shift) - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_aput_boolean.S b/runtime/interpreter/mterp/x86_64/op_aput_boolean.S deleted file mode 100644 index 7d77a86528..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_aput_boolean.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/op_aput.S" { "reg":"rINSTbl", "store":"movb", "shift":"1", "data_offset":"MIRROR_BOOLEAN_ARRAY_DATA_OFFSET" } diff --git a/runtime/interpreter/mterp/x86_64/op_aput_byte.S b/runtime/interpreter/mterp/x86_64/op_aput_byte.S deleted file mode 100644 index 7a1723e0fe..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_aput_byte.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/op_aput.S" { "reg":"rINSTbl", "store":"movb", "shift":"1", "data_offset":"MIRROR_BYTE_ARRAY_DATA_OFFSET" } diff --git a/runtime/interpreter/mterp/x86_64/op_aput_char.S b/runtime/interpreter/mterp/x86_64/op_aput_char.S deleted file mode 100644 index f8f50a3b2e..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_aput_char.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/op_aput.S" { "reg":"rINSTw", "store":"movw", "shift":"2", "data_offset":"MIRROR_CHAR_ARRAY_DATA_OFFSET" } diff --git a/runtime/interpreter/mterp/x86_64/op_aput_object.S b/runtime/interpreter/mterp/x86_64/op_aput_object.S deleted file mode 100644 index b1bae0f457..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_aput_object.S +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Store an object into an array. vBB[vCC] <- vAA. - */ - /* op vAA, vBB, vCC */ - EXPORT_PC - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG0 - movq rPC, OUT_ARG1 - REFRESH_INST ${opnum} - movq rINSTq, OUT_ARG2 - call SYMBOL(MterpAputObject) # (array, index) - testb %al, %al - jz MterpPossibleException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_aput_short.S b/runtime/interpreter/mterp/x86_64/op_aput_short.S deleted file mode 100644 index 481fd6847b..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_aput_short.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/op_aput.S" { "reg":"rINSTw", "store":"movw", "shift":"2", "data_offset":"MIRROR_SHORT_ARRAY_DATA_OFFSET" } diff --git a/runtime/interpreter/mterp/x86_64/op_aput_wide.S b/runtime/interpreter/mterp/x86_64/op_aput_wide.S deleted file mode 100644 index 5bbd39b0b6..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_aput_wide.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/op_aput.S" { "reg":"rINSTq", "store":"movq", "shift":"8", "data_offset":"MIRROR_WIDE_ARRAY_DATA_OFFSET", "wide":"1" } diff --git a/runtime/interpreter/mterp/x86_64/op_array_length.S b/runtime/interpreter/mterp/x86_64/op_array_length.S deleted file mode 100644 index e80d665160..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_array_length.S +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Return the length of an array. - */ - movl rINST, %eax # eax <- BA - sarl $$4, rINST # rINST <- B - GET_VREG %ecx, rINSTq # ecx <- vB (object ref) - testl %ecx, %ecx # is null? - je common_errNullObject - andb $$0xf, %al # eax <- A - movl MIRROR_ARRAY_LENGTH_OFFSET(%rcx), rINST - SET_VREG rINST, %rax - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/op_check_cast.S b/runtime/interpreter/mterp/x86_64/op_check_cast.S deleted file mode 100644 index f8fa7b2036..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_check_cast.S +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Check to see if a cast from one class to another is allowed. - */ - /* check-cast vAA, class@BBBB */ - EXPORT_PC - movzwq 2(rPC), OUT_ARG0 # OUT_ARG0 <- BBBB - leaq VREG_ADDRESS(rINSTq), OUT_ARG1 - movq OFF_FP_METHOD(rFP), OUT_ARG2 - movq rSELF, OUT_ARG3 - call SYMBOL(MterpCheckCast) # (index, &obj, method, self) - testb %al, %al - jnz MterpPossibleException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_cmp_long.S b/runtime/interpreter/mterp/x86_64/op_cmp_long.S deleted file mode 100644 index 23ca3e5e6d..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_cmp_long.S +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Compare two 64-bit values. Puts 0, 1, or -1 into the destination - * register based on the results of the comparison. - */ - /* cmp-long vAA, vBB, vCC */ - movzbq 2(rPC), %rdx # edx <- BB - movzbq 3(rPC), %rcx # ecx <- CC - GET_WIDE_VREG %rdx, %rdx # rdx <- v[BB] - xorl %eax, %eax - xorl %edi, %edi - addb $$1, %al - movl $$-1, %esi - cmpq VREG_ADDRESS(%rcx), %rdx - cmovl %esi, %edi - cmovg %eax, %edi - SET_VREG %edi, rINSTq - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_cmpg_double.S b/runtime/interpreter/mterp/x86_64/op_cmpg_double.S deleted file mode 100644 index 7c0aa1bdba..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_cmpg_double.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/fpcmp.S" {"suff":"d","nanval":"pos"} diff --git a/runtime/interpreter/mterp/x86_64/op_cmpg_float.S b/runtime/interpreter/mterp/x86_64/op_cmpg_float.S deleted file mode 100644 index 14e8472672..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_cmpg_float.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/fpcmp.S" {"suff":"s","nanval":"pos"} diff --git a/runtime/interpreter/mterp/x86_64/op_cmpl_double.S b/runtime/interpreter/mterp/x86_64/op_cmpl_double.S deleted file mode 100644 index 1d4c4243ae..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_cmpl_double.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/fpcmp.S" {"suff":"d","nanval":"neg"} diff --git a/runtime/interpreter/mterp/x86_64/op_cmpl_float.S b/runtime/interpreter/mterp/x86_64/op_cmpl_float.S deleted file mode 100644 index 97a12a6a7d..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_cmpl_float.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/fpcmp.S" {"suff":"s","nanval":"neg"} diff --git a/runtime/interpreter/mterp/x86_64/op_const.S b/runtime/interpreter/mterp/x86_64/op_const.S deleted file mode 100644 index 3cfafdb13b..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_const.S +++ /dev/null @@ -1,4 +0,0 @@ - /* const vAA, #+BBBBbbbb */ - movl 2(rPC), %eax # grab all 32 bits at once - SET_VREG %eax, rINSTq # vAA<- eax - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 diff --git a/runtime/interpreter/mterp/x86_64/op_const_16.S b/runtime/interpreter/mterp/x86_64/op_const_16.S deleted file mode 100644 index 1a139c683e..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_const_16.S +++ /dev/null @@ -1,4 +0,0 @@ - /* const/16 vAA, #+BBBB */ - movswl 2(rPC), %ecx # ecx <- ssssBBBB - SET_VREG %ecx, rINSTq # vAA <- ssssBBBB - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_const_4.S b/runtime/interpreter/mterp/x86_64/op_const_4.S deleted file mode 100644 index 23c4816f82..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_const_4.S +++ /dev/null @@ -1,7 +0,0 @@ - /* const/4 vA, #+B */ - movsbl rINSTbl, %eax # eax <-ssssssBx - movl $$0xf, rINST - andl %eax, rINST # rINST <- A - sarl $$4, %eax - SET_VREG %eax, rINSTq - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/op_const_class.S b/runtime/interpreter/mterp/x86_64/op_const_class.S deleted file mode 100644 index 494920a4a8..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_const_class.S +++ /dev/null @@ -1,10 +0,0 @@ - /* const/class vAA, Class@BBBB */ - EXPORT_PC - movzwq 2(rPC), OUT_ARG0 # eax <- OUT_ARG0 - movq rINSTq, OUT_ARG1 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG2 - movq rSELF, OUT_ARG3 - call SYMBOL(MterpConstClass) # (index, tgt_reg, shadow_frame, self) - testb %al, %al - jnz MterpPossibleException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_const_high16.S b/runtime/interpreter/mterp/x86_64/op_const_high16.S deleted file mode 100644 index 64e633c7a0..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_const_high16.S +++ /dev/null @@ -1,5 +0,0 @@ - /* const/high16 vAA, #+BBBB0000 */ - movzwl 2(rPC), %eax # eax <- 0000BBBB - sall $$16, %eax # eax <- BBBB0000 - SET_VREG %eax, rINSTq # vAA <- eax - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_const_string.S b/runtime/interpreter/mterp/x86_64/op_const_string.S deleted file mode 100644 index 7c199ecad9..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_const_string.S +++ /dev/null @@ -1,10 +0,0 @@ - /* const/string vAA, String@BBBB */ - EXPORT_PC - movzwq 2(rPC), OUT_ARG0 # OUT_ARG0 <- BBBB - movq rINSTq, OUT_ARG1 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG2 - movq rSELF, OUT_ARG3 - call SYMBOL(MterpConstString) # (index, tgt_reg, shadow_frame, self) - testb %al, %al - jnz MterpPossibleException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_const_string_jumbo.S b/runtime/interpreter/mterp/x86_64/op_const_string_jumbo.S deleted file mode 100644 index ae03d20f4f..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_const_string_jumbo.S +++ /dev/null @@ -1,10 +0,0 @@ - /* const/string vAA, String@BBBBBBBB */ - EXPORT_PC - movl 2(rPC), OUT_32_ARG0 # OUT_32_ARG0 <- BBBB - movq rINSTq, OUT_ARG1 - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG2 - movq rSELF, OUT_ARG3 - call SYMBOL(MterpConstString) # (index, tgt_reg, shadow_frame, self) - testb %al, %al - jnz MterpPossibleException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 diff --git a/runtime/interpreter/mterp/x86_64/op_const_wide.S b/runtime/interpreter/mterp/x86_64/op_const_wide.S deleted file mode 100644 index 5615177175..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_const_wide.S +++ /dev/null @@ -1,4 +0,0 @@ - /* const-wide vAA, #+HHHHhhhhBBBBbbbb */ - movq 2(rPC), %rax # rax <- HHHHhhhhBBBBbbbb - SET_WIDE_VREG %rax, rINSTq - ADVANCE_PC_FETCH_AND_GOTO_NEXT 5 diff --git a/runtime/interpreter/mterp/x86_64/op_const_wide_16.S b/runtime/interpreter/mterp/x86_64/op_const_wide_16.S deleted file mode 100644 index 593b62466f..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_const_wide_16.S +++ /dev/null @@ -1,4 +0,0 @@ - /* const-wide/16 vAA, #+BBBB */ - movswq 2(rPC), %rax # rax <- ssssBBBB - SET_WIDE_VREG %rax, rINSTq # store - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_const_wide_32.S b/runtime/interpreter/mterp/x86_64/op_const_wide_32.S deleted file mode 100644 index 5ef3636129..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_const_wide_32.S +++ /dev/null @@ -1,4 +0,0 @@ - /* const-wide/32 vAA, #+BBBBbbbb */ - movslq 2(rPC), %rax # eax <- ssssssssBBBBbbbb - SET_WIDE_VREG %rax, rINSTq # store - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 diff --git a/runtime/interpreter/mterp/x86_64/op_const_wide_high16.S b/runtime/interpreter/mterp/x86_64/op_const_wide_high16.S deleted file mode 100644 index b86b4e582b..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_const_wide_high16.S +++ /dev/null @@ -1,5 +0,0 @@ - /* const-wide/high16 vAA, #+BBBB000000000000 */ - movzwq 2(rPC), %rax # eax <- 0000BBBB - salq $$48, %rax # eax <- BBBB0000 - SET_WIDE_VREG %rax, rINSTq # v[AA+0] <- eax - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_div_double.S b/runtime/interpreter/mterp/x86_64/op_div_double.S deleted file mode 100644 index 45c700c066..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_div_double.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/sseBinop.S" {"instr":"divs","suff":"d"} diff --git a/runtime/interpreter/mterp/x86_64/op_div_double_2addr.S b/runtime/interpreter/mterp/x86_64/op_div_double_2addr.S deleted file mode 100644 index 83f270e245..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_div_double_2addr.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/sseBinop2Addr.S" {"instr":"divs","suff":"d"} diff --git a/runtime/interpreter/mterp/x86_64/op_div_float.S b/runtime/interpreter/mterp/x86_64/op_div_float.S deleted file mode 100644 index aa90b24698..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_div_float.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/sseBinop.S" {"instr":"divs","suff":"s"} diff --git a/runtime/interpreter/mterp/x86_64/op_div_float_2addr.S b/runtime/interpreter/mterp/x86_64/op_div_float_2addr.S deleted file mode 100644 index f0f8f1a6c8..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_div_float_2addr.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/sseBinop2Addr.S" {"instr":"divs","suff":"s"} diff --git a/runtime/interpreter/mterp/x86_64/op_div_int.S b/runtime/interpreter/mterp/x86_64/op_div_int.S deleted file mode 100644 index bba5a176a0..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_div_int.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/bindiv.S" {"result":"%eax","second":"%ecx","wide":"0","suffix":"l"} diff --git a/runtime/interpreter/mterp/x86_64/op_div_int_2addr.S b/runtime/interpreter/mterp/x86_64/op_div_int_2addr.S deleted file mode 100644 index fa4255ddfa..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_div_int_2addr.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/bindiv2addr.S" {"result":"%eax","second":"%ecx","wide":"0","suffix":"l"} diff --git a/runtime/interpreter/mterp/x86_64/op_div_int_lit16.S b/runtime/interpreter/mterp/x86_64/op_div_int_lit16.S deleted file mode 100644 index 3fa1e09fd6..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_div_int_lit16.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/bindivLit16.S" {"result":"%eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_div_int_lit8.S b/runtime/interpreter/mterp/x86_64/op_div_int_lit8.S deleted file mode 100644 index 859883e5c7..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_div_int_lit8.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/bindivLit8.S" {"result":"%eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_div_long.S b/runtime/interpreter/mterp/x86_64/op_div_long.S deleted file mode 100644 index a061a88b13..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_div_long.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/bindiv.S" {"result":"%rax","second":"%rcx","wide":"1","suffix":"q","ext":"cqo"} diff --git a/runtime/interpreter/mterp/x86_64/op_div_long_2addr.S b/runtime/interpreter/mterp/x86_64/op_div_long_2addr.S deleted file mode 100644 index 8886e68248..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_div_long_2addr.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/bindiv2addr.S" {"result":"%rax","second":"%rcx","wide":"1","suffix":"q","ext":"cqo"} diff --git a/runtime/interpreter/mterp/x86_64/op_double_to_float.S b/runtime/interpreter/mterp/x86_64/op_double_to_float.S deleted file mode 100644 index cea1482038..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_double_to_float.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/fpcvt.S" {"source_suffix":"d","dest_suffix":"s","wide":"0"} diff --git a/runtime/interpreter/mterp/x86_64/op_double_to_int.S b/runtime/interpreter/mterp/x86_64/op_double_to_int.S deleted file mode 100644 index a9965edcc3..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_double_to_int.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/cvtfp_int.S" {"fp_suffix":"d","i_suffix":"l","max_const":"$0x7fffffff","result_reg":"%eax","wide":"0"} diff --git a/runtime/interpreter/mterp/x86_64/op_double_to_long.S b/runtime/interpreter/mterp/x86_64/op_double_to_long.S deleted file mode 100644 index 179e6a1605..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_double_to_long.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/cvtfp_int.S" {"fp_suffix":"d","i_suffix":"q","max_const":"$0x7fffffffffffffff","result_reg":"%rax","wide":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_fill_array_data.S b/runtime/interpreter/mterp/x86_64/op_fill_array_data.S deleted file mode 100644 index 626bad47c9..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_fill_array_data.S +++ /dev/null @@ -1,9 +0,0 @@ - /* fill-array-data vAA, +BBBBBBBB */ - EXPORT_PC - movl 2(rPC), %ecx # ecx <- BBBBbbbb - leaq (rPC,%rcx,2), OUT_ARG1 # OUT_ARG1 <- PC + BBBBbbbb*2 - GET_VREG OUT_32_ARG0, rINSTq # OUT_ARG0 <- vAA (array object) - call SYMBOL(MterpFillArrayData) # (obj, payload) - testb %al, %al # 0 means an exception is thrown - jz MterpPossibleException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 diff --git a/runtime/interpreter/mterp/x86_64/op_filled_new_array.S b/runtime/interpreter/mterp/x86_64/op_filled_new_array.S deleted file mode 100644 index a7f7ddc2a0..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_filled_new_array.S +++ /dev/null @@ -1,17 +0,0 @@ -%default { "helper":"MterpFilledNewArray" } -/* - * Create a new array with elements filled from registers. - * - * for: filled-new-array, filled-new-array/range - */ - /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ - /* op {vCCCC..v(CCCC+AA-1)}, type@BBBB */ - .extern $helper - EXPORT_PC - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG0 - movq rPC, OUT_ARG1 - movq rSELF, OUT_ARG2 - call SYMBOL($helper) - testb %al, %al # 0 means an exception is thrown - jz MterpPossibleException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 diff --git a/runtime/interpreter/mterp/x86_64/op_filled_new_array_range.S b/runtime/interpreter/mterp/x86_64/op_filled_new_array_range.S deleted file mode 100644 index 4ca79a3fe1..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_filled_new_array_range.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/op_filled_new_array.S" { "helper":"MterpFilledNewArrayRange" } diff --git a/runtime/interpreter/mterp/x86_64/op_float_to_double.S b/runtime/interpreter/mterp/x86_64/op_float_to_double.S deleted file mode 100644 index 7855205575..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_float_to_double.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/fpcvt.S" {"source_suffix":"s","dest_suffix":"d","wide":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_float_to_int.S b/runtime/interpreter/mterp/x86_64/op_float_to_int.S deleted file mode 100644 index cb90555405..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_float_to_int.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/cvtfp_int.S" {"fp_suffix":"s","i_suffix":"l","max_const":"$0x7fffffff","result_reg":"%eax","wide":"0"} diff --git a/runtime/interpreter/mterp/x86_64/op_float_to_long.S b/runtime/interpreter/mterp/x86_64/op_float_to_long.S deleted file mode 100644 index 96bb4eee6f..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_float_to_long.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/cvtfp_int.S" {"fp_suffix":"s","i_suffix":"q","max_const":"$0x7fffffffffffffff","result_reg":"%rax","wide":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_goto.S b/runtime/interpreter/mterp/x86_64/op_goto.S deleted file mode 100644 index 05a2dda1c0..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_goto.S +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Unconditional branch, 8-bit offset. - * - * The branch distance is a signed code-unit offset, which we need to - * double to get a byte offset. - */ - /* goto +AA */ - movsbq rINSTbl, %rax # rax <- ssssssAA - addq %rax, %rax # rax <- AA * 2 - leaq (rPC, %rax), rPC - FETCH_INST - jg 1f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -1: - GOTO_NEXT diff --git a/runtime/interpreter/mterp/x86_64/op_goto_16.S b/runtime/interpreter/mterp/x86_64/op_goto_16.S deleted file mode 100644 index 029749c50a..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_goto_16.S +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Unconditional branch, 16-bit offset. - * - * The branch distance is a signed code-unit offset, which we need to - * double to get a byte offset. - */ - /* goto/16 +AAAA */ - movswq 2(rPC), %rax # rax <- ssssAAAA - addq %rax, %rax # rax <- AA * 2 - leaq (rPC, %rax), rPC - FETCH_INST - jg 1f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -1: - GOTO_NEXT diff --git a/runtime/interpreter/mterp/x86_64/op_goto_32.S b/runtime/interpreter/mterp/x86_64/op_goto_32.S deleted file mode 100644 index 28233108e5..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_goto_32.S +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Unconditional branch, 32-bit offset. - * - * The branch distance is a signed code-unit offset, which we need to - * double to get a byte offset. - * - * Because we need the SF bit set, we'll use an adds - * to convert from Dalvik offset to byte offset. - */ - /* goto/32 +AAAAAAAA */ - movslq 2(rPC), %rax # rax <- AAAAAAAA - addq %rax, %rax # rax <- AA * 2 - leaq (rPC, %rax), rPC - FETCH_INST - jg 1f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -1: - GOTO_NEXT diff --git a/runtime/interpreter/mterp/x86_64/op_if_eq.S b/runtime/interpreter/mterp/x86_64/op_if_eq.S deleted file mode 100644 index d56ce72461..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_if_eq.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/bincmp.S" { "revcmp":"ne" } diff --git a/runtime/interpreter/mterp/x86_64/op_if_eqz.S b/runtime/interpreter/mterp/x86_64/op_if_eqz.S deleted file mode 100644 index a0fc4448a3..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_if_eqz.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/zcmp.S" { "revcmp":"ne" } diff --git a/runtime/interpreter/mterp/x86_64/op_if_ge.S b/runtime/interpreter/mterp/x86_64/op_if_ge.S deleted file mode 100644 index a7832efb68..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_if_ge.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/bincmp.S" { "revcmp":"l" } diff --git a/runtime/interpreter/mterp/x86_64/op_if_gez.S b/runtime/interpreter/mterp/x86_64/op_if_gez.S deleted file mode 100644 index f9af5db933..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_if_gez.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/zcmp.S" { "revcmp":"l" } diff --git a/runtime/interpreter/mterp/x86_64/op_if_gt.S b/runtime/interpreter/mterp/x86_64/op_if_gt.S deleted file mode 100644 index 70f2b9e12f..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_if_gt.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/bincmp.S" { "revcmp":"le" } diff --git a/runtime/interpreter/mterp/x86_64/op_if_gtz.S b/runtime/interpreter/mterp/x86_64/op_if_gtz.S deleted file mode 100644 index 2fb0d50937..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_if_gtz.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/zcmp.S" { "revcmp":"le" } diff --git a/runtime/interpreter/mterp/x86_64/op_if_le.S b/runtime/interpreter/mterp/x86_64/op_if_le.S deleted file mode 100644 index 321962a040..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_if_le.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/bincmp.S" { "revcmp":"g" } diff --git a/runtime/interpreter/mterp/x86_64/op_if_lez.S b/runtime/interpreter/mterp/x86_64/op_if_lez.S deleted file mode 100644 index d3dc334f7b..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_if_lez.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/zcmp.S" { "revcmp":"g" } diff --git a/runtime/interpreter/mterp/x86_64/op_if_lt.S b/runtime/interpreter/mterp/x86_64/op_if_lt.S deleted file mode 100644 index f028005844..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_if_lt.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/bincmp.S" { "revcmp":"ge" } diff --git a/runtime/interpreter/mterp/x86_64/op_if_ltz.S b/runtime/interpreter/mterp/x86_64/op_if_ltz.S deleted file mode 100644 index 383d73aa7a..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_if_ltz.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/zcmp.S" { "revcmp":"ge" } diff --git a/runtime/interpreter/mterp/x86_64/op_if_ne.S b/runtime/interpreter/mterp/x86_64/op_if_ne.S deleted file mode 100644 index ac6e063cd1..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_if_ne.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/bincmp.S" { "revcmp":"e" } diff --git a/runtime/interpreter/mterp/x86_64/op_if_nez.S b/runtime/interpreter/mterp/x86_64/op_if_nez.S deleted file mode 100644 index c96e4f3d16..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_if_nez.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/zcmp.S" { "revcmp":"e" } diff --git a/runtime/interpreter/mterp/x86_64/op_iget.S b/runtime/interpreter/mterp/x86_64/op_iget.S deleted file mode 100644 index a0d0fafba1..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_iget.S +++ /dev/null @@ -1,27 +0,0 @@ -%default { "is_object":"0", "helper":"artGet32InstanceFromCode", "wide":"0"} -/* - * General instance field get. - * - * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short, iget-wide - */ - EXPORT_PC - movzbq rINSTbl, %rcx # rcx <- BA - movzwl 2(rPC), OUT_32_ARG0 # eax <- field ref CCCC - sarl $$4, %ecx # ecx <- B - GET_VREG OUT_32_ARG1, %rcx # the object pointer - movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer - movq rSELF, OUT_ARG3 - call SYMBOL($helper) - cmpq $$0, THREAD_EXCEPTION_OFFSET(rSELF) - jnz MterpException # bail out - andb $$0xf, rINSTbl # rINST <- A - .if $is_object - SET_VREG_OBJECT %eax, rINSTq # fp[A] <-value - .else - .if $wide - SET_WIDE_VREG %rax, rINSTq # fp[A] <-value - .else - SET_VREG %eax, rINSTq # fp[A] <-value - .endif - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_iget_boolean.S b/runtime/interpreter/mterp/x86_64/op_iget_boolean.S deleted file mode 100644 index 6ac55231b8..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_iget_boolean.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/op_iget.S" { "helper":"artGetBooleanInstanceFromCode" } diff --git a/runtime/interpreter/mterp/x86_64/op_iget_boolean_quick.S b/runtime/interpreter/mterp/x86_64/op_iget_boolean_quick.S deleted file mode 100644 index 07139c75ee..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_iget_boolean_quick.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/op_iget_quick.S" { "load":"movsbl" } diff --git a/runtime/interpreter/mterp/x86_64/op_iget_byte.S b/runtime/interpreter/mterp/x86_64/op_iget_byte.S deleted file mode 100644 index 6a861b1b7a..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_iget_byte.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/op_iget.S" { "helper":"artGetByteInstanceFromCode" } diff --git a/runtime/interpreter/mterp/x86_64/op_iget_byte_quick.S b/runtime/interpreter/mterp/x86_64/op_iget_byte_quick.S deleted file mode 100644 index 07139c75ee..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_iget_byte_quick.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/op_iget_quick.S" { "load":"movsbl" } diff --git a/runtime/interpreter/mterp/x86_64/op_iget_char.S b/runtime/interpreter/mterp/x86_64/op_iget_char.S deleted file mode 100644 index 021a0f1b24..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_iget_char.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/op_iget.S" { "helper":"artGetCharInstanceFromCode" } diff --git a/runtime/interpreter/mterp/x86_64/op_iget_char_quick.S b/runtime/interpreter/mterp/x86_64/op_iget_char_quick.S deleted file mode 100644 index 8cb3be3b65..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_iget_char_quick.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/op_iget_quick.S" { "load":"movzwl" } diff --git a/runtime/interpreter/mterp/x86_64/op_iget_object.S b/runtime/interpreter/mterp/x86_64/op_iget_object.S deleted file mode 100644 index d92bc9c345..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_iget_object.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/op_iget.S" { "is_object":"1", "helper":"artGetObjInstanceFromCode" } diff --git a/runtime/interpreter/mterp/x86_64/op_iget_object_quick.S b/runtime/interpreter/mterp/x86_64/op_iget_object_quick.S deleted file mode 100644 index 964d20ad74..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_iget_object_quick.S +++ /dev/null @@ -1,14 +0,0 @@ - /* For: iget-object-quick */ - /* op vA, vB, offset@CCCC */ - .extern artIGetObjectFromMterp - movzbq rINSTbl, %rcx # rcx <- BA - sarl $$4, %ecx # ecx <- B - GET_VREG OUT_32_ARG0, %rcx # vB (object we're operating on) - movzwl 2(rPC), OUT_32_ARG1 # eax <- field byte offset - EXPORT_PC - callq SYMBOL(artIGetObjectFromMterp) # (obj, offset) - cmpq $$0, THREAD_EXCEPTION_OFFSET(rSELF) - jnz MterpException # bail out - andb $$0xf, rINSTbl # rINST <- A - SET_VREG_OBJECT %eax, rINSTq # fp[A] <- value - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_iget_quick.S b/runtime/interpreter/mterp/x86_64/op_iget_quick.S deleted file mode 100644 index bfb7530167..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_iget_quick.S +++ /dev/null @@ -1,18 +0,0 @@ -%default { "load":"movl", "wide":"0"} - /* For: iget-quick, iget-boolean-quick, iget-byte-quick, iget-char-quick, iget-short-quick, iget-wide-quick */ - /* op vA, vB, offset@CCCC */ - movl rINST, %ecx # rcx <- BA - sarl $$4, %ecx # ecx <- B - GET_VREG %ecx, %rcx # vB (object we're operating on) - movzwq 2(rPC), %rax # eax <- field byte offset - testl %ecx, %ecx # is object null? - je common_errNullObject - andb $$0xf,rINSTbl # rINST <- A - .if $wide - movq (%rcx,%rax,1), %rax - SET_WIDE_VREG %rax, rINSTq # fp[A] <- value - .else - ${load} (%rcx,%rax,1), %eax - SET_VREG %eax, rINSTq # fp[A] <- value - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_iget_short.S b/runtime/interpreter/mterp/x86_64/op_iget_short.S deleted file mode 100644 index f158bea573..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_iget_short.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/op_iget.S" { "helper":"artGetShortInstanceFromCode" } diff --git a/runtime/interpreter/mterp/x86_64/op_iget_short_quick.S b/runtime/interpreter/mterp/x86_64/op_iget_short_quick.S deleted file mode 100644 index 56ca858e74..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_iget_short_quick.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/op_iget_quick.S" { "load":"movswl" } diff --git a/runtime/interpreter/mterp/x86_64/op_iget_wide.S b/runtime/interpreter/mterp/x86_64/op_iget_wide.S deleted file mode 100644 index 74bb9ffe1c..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_iget_wide.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/op_iget.S" { "helper":"artGet64InstanceFromCode", "wide":"1" } diff --git a/runtime/interpreter/mterp/x86_64/op_iget_wide_quick.S b/runtime/interpreter/mterp/x86_64/op_iget_wide_quick.S deleted file mode 100644 index 169d625529..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_iget_wide_quick.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/op_iget_quick.S" { "load":"movswl", "wide":"1" } diff --git a/runtime/interpreter/mterp/x86_64/op_instance_of.S b/runtime/interpreter/mterp/x86_64/op_instance_of.S deleted file mode 100644 index 6be37f9166..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_instance_of.S +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Check to see if an object reference is an instance of a class. - * - * Most common situation is a non-null object, being compared against - * an already-resolved class. - */ - /* instance-of vA, vB, class@CCCC */ - EXPORT_PC - movzwl 2(rPC), OUT_32_ARG0 # OUT_32_ARG0 <- CCCC - movl rINST, %eax # eax <- BA - sarl $$4, %eax # eax <- B - leaq VREG_ADDRESS(%rax), OUT_ARG1 # Get object address - movq OFF_FP_METHOD(rFP), OUT_ARG2 - movq rSELF, OUT_ARG3 - call SYMBOL(MterpInstanceOf) # (index, &obj, method, self) - movsbl %al, %eax - cmpq $$0, THREAD_EXCEPTION_OFFSET(rSELF) - jnz MterpException - andb $$0xf, rINSTbl # rINSTbl <- A - SET_VREG %eax, rINSTq - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_int_to_byte.S b/runtime/interpreter/mterp/x86_64/op_int_to_byte.S deleted file mode 100644 index f4e578f868..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_int_to_byte.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/unop.S" {"instr":"movsbl %al, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_int_to_char.S b/runtime/interpreter/mterp/x86_64/op_int_to_char.S deleted file mode 100644 index c1bf17f271..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_int_to_char.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/unop.S" {"instr":"movzwl %ax,%eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_int_to_double.S b/runtime/interpreter/mterp/x86_64/op_int_to_double.S deleted file mode 100644 index 27ebf42dbb..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_int_to_double.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/fpcvt.S" {"source_suffix":"i","dest_suffix":"dl","wide":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_int_to_float.S b/runtime/interpreter/mterp/x86_64/op_int_to_float.S deleted file mode 100644 index 5a98d44337..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_int_to_float.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/fpcvt.S" {"source_suffix":"i","dest_suffix":"sl","wide":"0"} diff --git a/runtime/interpreter/mterp/x86_64/op_int_to_long.S b/runtime/interpreter/mterp/x86_64/op_int_to_long.S deleted file mode 100644 index 9281137a54..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_int_to_long.S +++ /dev/null @@ -1,8 +0,0 @@ - /* int to long vA, vB */ - movzbq rINSTbl, %rax # rax <- +A - sarl $$4, %eax # eax <- B - andb $$0xf, rINSTbl # rINST <- A - movslq VREG_ADDRESS(%rax), %rax - SET_WIDE_VREG %rax, rINSTq # v[A] <- %rax - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 - diff --git a/runtime/interpreter/mterp/x86_64/op_int_to_short.S b/runtime/interpreter/mterp/x86_64/op_int_to_short.S deleted file mode 100644 index 6ae6b50f34..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_int_to_short.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/unop.S" {"instr":"movswl %ax, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_invoke_direct.S b/runtime/interpreter/mterp/x86_64/op_invoke_direct.S deleted file mode 100644 index 9628589b03..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_invoke_direct.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/invoke.S" { "helper":"MterpInvokeDirect" } diff --git a/runtime/interpreter/mterp/x86_64/op_invoke_direct_range.S b/runtime/interpreter/mterp/x86_64/op_invoke_direct_range.S deleted file mode 100644 index 09ac8812fc..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_invoke_direct_range.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/invoke.S" { "helper":"MterpInvokeDirectRange" } diff --git a/runtime/interpreter/mterp/x86_64/op_invoke_interface.S b/runtime/interpreter/mterp/x86_64/op_invoke_interface.S deleted file mode 100644 index 76d9cd426f..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_invoke_interface.S +++ /dev/null @@ -1,8 +0,0 @@ -%include "x86_64/invoke.S" { "helper":"MterpInvokeInterface" } -/* - * Handle an interface method call. - * - * for: invoke-interface, invoke-interface/range - */ - /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ - /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ diff --git a/runtime/interpreter/mterp/x86_64/op_invoke_interface_range.S b/runtime/interpreter/mterp/x86_64/op_invoke_interface_range.S deleted file mode 100644 index 785b43c1a8..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_invoke_interface_range.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/invoke.S" { "helper":"MterpInvokeInterfaceRange" } diff --git a/runtime/interpreter/mterp/x86_64/op_invoke_static.S b/runtime/interpreter/mterp/x86_64/op_invoke_static.S deleted file mode 100644 index dd8027d58c..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_invoke_static.S +++ /dev/null @@ -1,2 +0,0 @@ -%include "x86_64/invoke.S" { "helper":"MterpInvokeStatic" } - diff --git a/runtime/interpreter/mterp/x86_64/op_invoke_static_range.S b/runtime/interpreter/mterp/x86_64/op_invoke_static_range.S deleted file mode 100644 index ee26074f92..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_invoke_static_range.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/invoke.S" { "helper":"MterpInvokeStaticRange" } diff --git a/runtime/interpreter/mterp/x86_64/op_invoke_super.S b/runtime/interpreter/mterp/x86_64/op_invoke_super.S deleted file mode 100644 index d07f8d555b..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_invoke_super.S +++ /dev/null @@ -1,8 +0,0 @@ -%include "x86_64/invoke.S" { "helper":"MterpInvokeSuper" } -/* - * Handle a "super" method call. - * - * for: invoke-super, invoke-super/range - */ - /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ - /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ diff --git a/runtime/interpreter/mterp/x86_64/op_invoke_super_range.S b/runtime/interpreter/mterp/x86_64/op_invoke_super_range.S deleted file mode 100644 index 7245cfd405..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_invoke_super_range.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/invoke.S" { "helper":"MterpInvokeSuperRange" } diff --git a/runtime/interpreter/mterp/x86_64/op_invoke_virtual.S b/runtime/interpreter/mterp/x86_64/op_invoke_virtual.S deleted file mode 100644 index 19c708bd2a..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_invoke_virtual.S +++ /dev/null @@ -1,8 +0,0 @@ -%include "x86_64/invoke.S" { "helper":"MterpInvokeVirtual" } -/* - * Handle a virtual method call. - * - * for: invoke-virtual, invoke-virtual/range - */ - /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ - /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ diff --git a/runtime/interpreter/mterp/x86_64/op_invoke_virtual_quick.S b/runtime/interpreter/mterp/x86_64/op_invoke_virtual_quick.S deleted file mode 100644 index 313bd058b1..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_invoke_virtual_quick.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/invoke.S" { "helper":"MterpInvokeVirtualQuick" } diff --git a/runtime/interpreter/mterp/x86_64/op_invoke_virtual_range.S b/runtime/interpreter/mterp/x86_64/op_invoke_virtual_range.S deleted file mode 100644 index 424ad321a3..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_invoke_virtual_range.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/invoke.S" { "helper":"MterpInvokeVirtualRange" } diff --git a/runtime/interpreter/mterp/x86_64/op_invoke_virtual_range_quick.S b/runtime/interpreter/mterp/x86_64/op_invoke_virtual_range_quick.S deleted file mode 100644 index 556f718ffb..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_invoke_virtual_range_quick.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/invoke.S" { "helper":"MterpInvokeVirtualQuickRange" } diff --git a/runtime/interpreter/mterp/x86_64/op_iput.S b/runtime/interpreter/mterp/x86_64/op_iput.S deleted file mode 100644 index 6b7cb1cc84..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_iput.S +++ /dev/null @@ -1,20 +0,0 @@ -%default { "handler":"artSet32InstanceFromMterp"} -/* - * General 32-bit instance field put. - * - * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short - */ - /* op vA, vB, field@CCCC */ - .extern $handler - EXPORT_PC - movzwl 2(rPC), OUT_32_ARG0 # field ref <- 0000CCCC - movzbq rINSTbl, %rcx # rcx<- BA - sarl $$4, %ecx # ecx<- B - GET_VREG OUT_32_ARG1, %rcx # the object pointer - andb $$0xf, rINSTbl # rINST<- A - GET_VREG OUT_32_ARG2, rINSTq # fp[A] - movq OFF_FP_METHOD(rFP), OUT_ARG3 # referrer - call SYMBOL($handler) - testb %al, %al - jnz MterpPossibleException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_iput_boolean.S b/runtime/interpreter/mterp/x86_64/op_iput_boolean.S deleted file mode 100644 index cb4b1cde45..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_iput_boolean.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/op_iput.S" { "handler":"artSet8InstanceFromMterp" } diff --git a/runtime/interpreter/mterp/x86_64/op_iput_boolean_quick.S b/runtime/interpreter/mterp/x86_64/op_iput_boolean_quick.S deleted file mode 100644 index 6bd060e4f3..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_iput_boolean_quick.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/op_iput_quick.S" { "reg":"rINSTbl", "store":"movb" } diff --git a/runtime/interpreter/mterp/x86_64/op_iput_byte.S b/runtime/interpreter/mterp/x86_64/op_iput_byte.S deleted file mode 100644 index cb4b1cde45..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_iput_byte.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/op_iput.S" { "handler":"artSet8InstanceFromMterp" } diff --git a/runtime/interpreter/mterp/x86_64/op_iput_byte_quick.S b/runtime/interpreter/mterp/x86_64/op_iput_byte_quick.S deleted file mode 100644 index 6bd060e4f3..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_iput_byte_quick.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/op_iput_quick.S" { "reg":"rINSTbl", "store":"movb" } diff --git a/runtime/interpreter/mterp/x86_64/op_iput_char.S b/runtime/interpreter/mterp/x86_64/op_iput_char.S deleted file mode 100644 index b4e147cf5e..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_iput_char.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/op_iput.S" { "handler":"artSet16InstanceFromMterp" } diff --git a/runtime/interpreter/mterp/x86_64/op_iput_char_quick.S b/runtime/interpreter/mterp/x86_64/op_iput_char_quick.S deleted file mode 100644 index 3da96d53af..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_iput_char_quick.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/op_iput_quick.S" { "reg":"rINSTw", "store":"movw" } diff --git a/runtime/interpreter/mterp/x86_64/op_iput_object.S b/runtime/interpreter/mterp/x86_64/op_iput_object.S deleted file mode 100644 index 828712d8ba..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_iput_object.S +++ /dev/null @@ -1,10 +0,0 @@ - EXPORT_PC - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG0 - movq rPC, OUT_ARG1 - REFRESH_INST ${opnum} - movl rINST, OUT_32_ARG2 - movq rSELF, OUT_ARG3 - call SYMBOL(MterpIputObject) - testb %al, %al - jz MterpException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_iput_object_quick.S b/runtime/interpreter/mterp/x86_64/op_iput_object_quick.S deleted file mode 100644 index b5b128ab7f..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_iput_object_quick.S +++ /dev/null @@ -1,9 +0,0 @@ - EXPORT_PC - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG0 - movq rPC, OUT_ARG1 - REFRESH_INST ${opnum} - movl rINST, OUT_32_ARG2 - call SYMBOL(MterpIputObjectQuick) - testb %al, %al - jz MterpException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_iput_quick.S b/runtime/interpreter/mterp/x86_64/op_iput_quick.S deleted file mode 100644 index ecaf98e415..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_iput_quick.S +++ /dev/null @@ -1,13 +0,0 @@ -%default { "reg":"rINST", "store":"movl" } - /* For: iput-quick, iput-object-quick */ - /* op vA, vB, offset@CCCC */ - movzbq rINSTbl, %rcx # rcx <- BA - sarl $$4, %ecx # ecx <- B - GET_VREG %ecx, %rcx # vB (object we're operating on) - testl %ecx, %ecx # is object null? - je common_errNullObject - andb $$0xf, rINSTbl # rINST <- A - GET_VREG rINST, rINSTq # rINST <- v[A] - movzwq 2(rPC), %rax # rax <- field byte offset - ${store} ${reg}, (%rcx,%rax,1) - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_iput_short.S b/runtime/interpreter/mterp/x86_64/op_iput_short.S deleted file mode 100644 index b4e147cf5e..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_iput_short.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/op_iput.S" { "handler":"artSet16InstanceFromMterp" } diff --git a/runtime/interpreter/mterp/x86_64/op_iput_short_quick.S b/runtime/interpreter/mterp/x86_64/op_iput_short_quick.S deleted file mode 100644 index 3da96d53af..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_iput_short_quick.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/op_iput_quick.S" { "reg":"rINSTw", "store":"movw" } diff --git a/runtime/interpreter/mterp/x86_64/op_iput_wide.S b/runtime/interpreter/mterp/x86_64/op_iput_wide.S deleted file mode 100644 index e59717b846..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_iput_wide.S +++ /dev/null @@ -1,14 +0,0 @@ - /* iput-wide vA, vB, field@CCCC */ - .extern artSet64InstanceFromMterp - EXPORT_PC - movzwq 2(rPC), OUT_ARG0 # field ref CCCC - movzbq rINSTbl, %rcx # rcx <- BA - sarl $$4, %ecx # ecx <- B - GET_VREG OUT_32_ARG1, %rcx # the object pointer - andb $$0xf, rINSTbl # rINST <- A - leaq VREG_ADDRESS(rINSTq), OUT_ARG2 # &fp[A] - movq OFF_FP_METHOD(rFP), OUT_ARG3 # referrer - call SYMBOL(artSet64InstanceFromMterp) - testb %al, %al - jnz MterpPossibleException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_iput_wide_quick.S b/runtime/interpreter/mterp/x86_64/op_iput_wide_quick.S deleted file mode 100644 index 473189d007..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_iput_wide_quick.S +++ /dev/null @@ -1,12 +0,0 @@ - /* iput-wide-quick vA, vB, offset@CCCC */ - movzbq rINSTbl, %rcx # rcx<- BA - sarl $$4, %ecx # ecx<- B - GET_VREG %ecx, %rcx # vB (object we're operating on) - testl %ecx, %ecx # is object null? - je common_errNullObject - movzwq 2(rPC), %rax # rax<- field byte offset - leaq (%rcx,%rax,1), %rcx # ecx<- Address of 64-bit target - andb $$0xf, rINSTbl # rINST<- A - GET_WIDE_VREG %rax, rINSTq # rax<- fp[A]/fp[A+1] - movq %rax, (%rcx) # obj.field<- r0/r1 - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_long_to_double.S b/runtime/interpreter/mterp/x86_64/op_long_to_double.S deleted file mode 100644 index 7cdae32373..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_long_to_double.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/fpcvt.S" {"source_suffix":"i","dest_suffix":"dq","wide":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_long_to_float.S b/runtime/interpreter/mterp/x86_64/op_long_to_float.S deleted file mode 100644 index 7553348633..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_long_to_float.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/fpcvt.S" {"source_suffix":"i","dest_suffix":"sq","wide":"0"} diff --git a/runtime/interpreter/mterp/x86_64/op_long_to_int.S b/runtime/interpreter/mterp/x86_64/op_long_to_int.S deleted file mode 100644 index 7b50c8e0b3..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_long_to_int.S +++ /dev/null @@ -1,2 +0,0 @@ -/* we ignore the high word, making this equivalent to a 32-bit reg move */ -%include "x86_64/op_move.S" diff --git a/runtime/interpreter/mterp/x86_64/op_monitor_enter.S b/runtime/interpreter/mterp/x86_64/op_monitor_enter.S deleted file mode 100644 index 411091f23e..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_monitor_enter.S +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Synchronize on an object. - */ - /* monitor-enter vAA */ - EXPORT_PC - GET_VREG OUT_32_ARG0, rINSTq - movq rSELF, OUT_ARG1 - call SYMBOL(artLockObjectFromCode) # (object, self) - testq %rax, %rax - jnz MterpException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/op_monitor_exit.S b/runtime/interpreter/mterp/x86_64/op_monitor_exit.S deleted file mode 100644 index 72d9a23a87..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_monitor_exit.S +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Unlock an object. - * - * Exceptions that occur when unlocking a monitor need to appear as - * if they happened at the following instruction. See the Dalvik - * instruction spec. - */ - /* monitor-exit vAA */ - EXPORT_PC - GET_VREG OUT_32_ARG0, rINSTq - movq rSELF, OUT_ARG1 - call SYMBOL(artUnlockObjectFromCode) # (object, self) - testq %rax, %rax - jnz MterpException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/op_move.S b/runtime/interpreter/mterp/x86_64/op_move.S deleted file mode 100644 index ccaac2caa8..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_move.S +++ /dev/null @@ -1,13 +0,0 @@ -%default { "is_object":"0" } - /* for move, move-object, long-to-int */ - /* op vA, vB */ - movl rINST, %eax # eax <- BA - andb $$0xf, %al # eax <- A - shrl $$4, rINST # rINST <- B - GET_VREG %edx, rINSTq - .if $is_object - SET_VREG_OBJECT %edx, %rax # fp[A] <- fp[B] - .else - SET_VREG %edx, %rax # fp[A] <- fp[B] - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/op_move_16.S b/runtime/interpreter/mterp/x86_64/op_move_16.S deleted file mode 100644 index 6a813eb5ce..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_move_16.S +++ /dev/null @@ -1,12 +0,0 @@ -%default { "is_object":"0" } - /* for: move/16, move-object/16 */ - /* op vAAAA, vBBBB */ - movzwq 4(rPC), %rcx # ecx <- BBBB - movzwq 2(rPC), %rax # eax <- AAAA - GET_VREG %edx, %rcx - .if $is_object - SET_VREG_OBJECT %edx, %rax # fp[A] <- fp[B] - .else - SET_VREG %edx, %rax # fp[A] <- fp[B] - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 diff --git a/runtime/interpreter/mterp/x86_64/op_move_exception.S b/runtime/interpreter/mterp/x86_64/op_move_exception.S deleted file mode 100644 index d0a14fdc8d..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_move_exception.S +++ /dev/null @@ -1,5 +0,0 @@ - /* move-exception vAA */ - movl THREAD_EXCEPTION_OFFSET(rSELF), %eax - SET_VREG_OBJECT %eax, rINSTq # fp[AA] <- exception object - movl $$0, THREAD_EXCEPTION_OFFSET(rSELF) - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/op_move_from16.S b/runtime/interpreter/mterp/x86_64/op_move_from16.S deleted file mode 100644 index 150e9c2f2c..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_move_from16.S +++ /dev/null @@ -1,11 +0,0 @@ -%default { "is_object":"0" } - /* for: move/from16, move-object/from16 */ - /* op vAA, vBBBB */ - movzwq 2(rPC), %rax # eax <- BBBB - GET_VREG %edx, %rax # edx <- fp[BBBB] - .if $is_object - SET_VREG_OBJECT %edx, rINSTq # fp[A] <- fp[B] - .else - SET_VREG %edx, rINSTq # fp[A] <- fp[B] - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_move_object.S b/runtime/interpreter/mterp/x86_64/op_move_object.S deleted file mode 100644 index 0d866496e8..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_move_object.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/op_move.S" {"is_object":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_move_object_16.S b/runtime/interpreter/mterp/x86_64/op_move_object_16.S deleted file mode 100644 index 32541ff2bf..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_move_object_16.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/op_move_16.S" {"is_object":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_move_object_from16.S b/runtime/interpreter/mterp/x86_64/op_move_object_from16.S deleted file mode 100644 index 983e4abae5..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_move_object_from16.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/op_move_from16.S" {"is_object":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_move_result.S b/runtime/interpreter/mterp/x86_64/op_move_result.S deleted file mode 100644 index 8268344bce..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_move_result.S +++ /dev/null @@ -1,11 +0,0 @@ -%default { "is_object":"0" } - /* for: move-result, move-result-object */ - /* op vAA */ - movq OFF_FP_RESULT_REGISTER(rFP), %rax # get pointer to result JType. - movl (%rax), %eax # r0 <- result.i. - .if $is_object - SET_VREG_OBJECT %eax, rINSTq # fp[A] <- fp[B] - .else - SET_VREG %eax, rINSTq # fp[A] <- fp[B] - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/op_move_result_object.S b/runtime/interpreter/mterp/x86_64/op_move_result_object.S deleted file mode 100644 index c5aac17f41..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_move_result_object.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/op_move_result.S" {"is_object":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_move_result_wide.S b/runtime/interpreter/mterp/x86_64/op_move_result_wide.S deleted file mode 100644 index 03de783627..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_move_result_wide.S +++ /dev/null @@ -1,5 +0,0 @@ - /* move-result-wide vAA */ - movq OFF_FP_RESULT_REGISTER(rFP), %rax # get pointer to result JType. - movq (%rax), %rdx # Get wide - SET_WIDE_VREG %rdx, rINSTq # v[AA] <- rdx - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/op_move_wide.S b/runtime/interpreter/mterp/x86_64/op_move_wide.S deleted file mode 100644 index 508f8cc152..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_move_wide.S +++ /dev/null @@ -1,8 +0,0 @@ - /* move-wide vA, vB */ - /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */ - movl rINST, %ecx # ecx <- BA - sarl $$4, rINST # rINST <- B - andb $$0xf, %cl # ecx <- A - GET_WIDE_VREG %rdx, rINSTq # rdx <- v[B] - SET_WIDE_VREG %rdx, %rcx # v[A] <- rdx - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/op_move_wide_16.S b/runtime/interpreter/mterp/x86_64/op_move_wide_16.S deleted file mode 100644 index ce371a920e..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_move_wide_16.S +++ /dev/null @@ -1,7 +0,0 @@ - /* move-wide/16 vAAAA, vBBBB */ - /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */ - movzwq 4(rPC), %rcx # ecx<- BBBB - movzwq 2(rPC), %rax # eax<- AAAA - GET_WIDE_VREG %rdx, %rcx # rdx <- v[B] - SET_WIDE_VREG %rdx, %rax # v[A] <- rdx - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 diff --git a/runtime/interpreter/mterp/x86_64/op_move_wide_from16.S b/runtime/interpreter/mterp/x86_64/op_move_wide_from16.S deleted file mode 100644 index 0d6971a674..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_move_wide_from16.S +++ /dev/null @@ -1,6 +0,0 @@ - /* move-wide/from16 vAA, vBBBB */ - /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */ - movzwl 2(rPC), %ecx # ecx <- BBBB - GET_WIDE_VREG %rdx, %rcx # rdx <- v[B] - SET_WIDE_VREG %rdx, rINSTq # v[A] <- rdx - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_mul_double.S b/runtime/interpreter/mterp/x86_64/op_mul_double.S deleted file mode 100644 index 1f4bcb3d00..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_mul_double.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/sseBinop.S" {"instr":"muls","suff":"d"} diff --git a/runtime/interpreter/mterp/x86_64/op_mul_double_2addr.S b/runtime/interpreter/mterp/x86_64/op_mul_double_2addr.S deleted file mode 100644 index 9850a28995..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_mul_double_2addr.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/sseBinop2Addr.S" {"instr":"muls","suff":"d"} diff --git a/runtime/interpreter/mterp/x86_64/op_mul_float.S b/runtime/interpreter/mterp/x86_64/op_mul_float.S deleted file mode 100644 index 85960e9dec..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_mul_float.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/sseBinop.S" {"instr":"muls","suff":"s"} diff --git a/runtime/interpreter/mterp/x86_64/op_mul_float_2addr.S b/runtime/interpreter/mterp/x86_64/op_mul_float_2addr.S deleted file mode 100644 index 6d36b6a178..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_mul_float_2addr.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/sseBinop2Addr.S" {"instr":"muls","suff":"s"} diff --git a/runtime/interpreter/mterp/x86_64/op_mul_int.S b/runtime/interpreter/mterp/x86_64/op_mul_int.S deleted file mode 100644 index 5f3923a20e..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_mul_int.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/binop.S" {"instr":"imull (rFP,%rcx,4), %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_mul_int_2addr.S b/runtime/interpreter/mterp/x86_64/op_mul_int_2addr.S deleted file mode 100644 index 0b5af8a927..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_mul_int_2addr.S +++ /dev/null @@ -1,8 +0,0 @@ - /* mul vA, vB */ - movl rINST, %ecx # rcx <- A+ - sarl $$4, rINST # rINST <- B - andb $$0xf, %cl # ecx <- A - GET_VREG %eax, %rcx # eax <- vA - imull (rFP,rINSTq,4), %eax - SET_VREG %eax, %rcx - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/op_mul_int_lit16.S b/runtime/interpreter/mterp/x86_64/op_mul_int_lit16.S deleted file mode 100644 index a4cfdbce3e..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_mul_int_lit16.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/binopLit16.S" {"instr":"imull %ecx, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_mul_int_lit8.S b/runtime/interpreter/mterp/x86_64/op_mul_int_lit8.S deleted file mode 100644 index 89e9acb77d..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_mul_int_lit8.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/binopLit8.S" {"instr":"imull %ecx, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_mul_long.S b/runtime/interpreter/mterp/x86_64/op_mul_long.S deleted file mode 100644 index 2b853705cf..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_mul_long.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/binopWide.S" {"instr":"imulq (rFP,%rcx,4), %rax"} diff --git a/runtime/interpreter/mterp/x86_64/op_mul_long_2addr.S b/runtime/interpreter/mterp/x86_64/op_mul_long_2addr.S deleted file mode 100644 index 167128b4d1..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_mul_long_2addr.S +++ /dev/null @@ -1,8 +0,0 @@ - /* mul vA, vB */ - movl rINST, %ecx # rcx <- A+ - sarl $$4, rINST # rINST <- B - andb $$0xf, %cl # ecx <- A - GET_WIDE_VREG %rax, %rcx # rax <- vA - imulq (rFP,rINSTq,4), %rax - SET_WIDE_VREG %rax, %rcx - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/op_neg_double.S b/runtime/interpreter/mterp/x86_64/op_neg_double.S deleted file mode 100644 index 2c14b091cb..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_neg_double.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/unop.S" {"preinstr":" movq $0x8000000000000000, %rsi", "instr":" xorq %rsi, %rax", "wide":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_neg_float.S b/runtime/interpreter/mterp/x86_64/op_neg_float.S deleted file mode 100644 index 148b21ec9a..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_neg_float.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/unop.S" {"instr":" xorl $0x80000000, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_neg_int.S b/runtime/interpreter/mterp/x86_64/op_neg_int.S deleted file mode 100644 index f90a937a8b..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_neg_int.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/unop.S" {"instr":" negl %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_neg_long.S b/runtime/interpreter/mterp/x86_64/op_neg_long.S deleted file mode 100644 index 18fc3ccece..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_neg_long.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/unop.S" {"instr":" negq %rax", "wide":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_new_array.S b/runtime/interpreter/mterp/x86_64/op_new_array.S deleted file mode 100644 index 9831a0b888..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_new_array.S +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Allocate an array of objects, specified with the array class - * and a count. - * - * The verifier guarantees that this is an array class, so we don't - * check for it here. - */ - /* new-array vA, vB, class@CCCC */ - EXPORT_PC - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG0 - movq rPC, OUT_ARG1 - REFRESH_INST ${opnum} - movq rINSTq, OUT_ARG2 - movq rSELF, OUT_ARG3 - call SYMBOL(MterpNewArray) - testb %al, %al # 0 means an exception is thrown - jz MterpPossibleException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_new_instance.S b/runtime/interpreter/mterp/x86_64/op_new_instance.S deleted file mode 100644 index fc8c8cd98c..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_new_instance.S +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Create a new instance of a class. - */ - /* new-instance vAA, class@BBBB */ - EXPORT_PC - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG0 - movq rSELF, OUT_ARG1 - REFRESH_INST ${opnum} - movq rINSTq, OUT_ARG2 - call SYMBOL(MterpNewInstance) - testb %al, %al # 0 means an exception is thrown - jz MterpPossibleException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_nop.S b/runtime/interpreter/mterp/x86_64/op_nop.S deleted file mode 100644 index 4cb68e392e..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_nop.S +++ /dev/null @@ -1 +0,0 @@ - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/op_not_int.S b/runtime/interpreter/mterp/x86_64/op_not_int.S deleted file mode 100644 index 463d080de9..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_not_int.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/unop.S" {"instr":" notl %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_not_long.S b/runtime/interpreter/mterp/x86_64/op_not_long.S deleted file mode 100644 index c97bb9ea1a..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_not_long.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/unop.S" {"instr":" notq %rax", "wide":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_or_int.S b/runtime/interpreter/mterp/x86_64/op_or_int.S deleted file mode 100644 index 730310f6af..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_or_int.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/binop.S" {"instr":"orl (rFP,%rcx,4), %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_or_int_2addr.S b/runtime/interpreter/mterp/x86_64/op_or_int_2addr.S deleted file mode 100644 index f722e4dd9c..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_or_int_2addr.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/binop2addr.S" {"instr":"orl %eax, (rFP,%rcx,4)"} diff --git a/runtime/interpreter/mterp/x86_64/op_or_int_lit16.S b/runtime/interpreter/mterp/x86_64/op_or_int_lit16.S deleted file mode 100644 index fee86c7c62..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_or_int_lit16.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/binopLit16.S" {"instr":"orl %ecx, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_or_int_lit8.S b/runtime/interpreter/mterp/x86_64/op_or_int_lit8.S deleted file mode 100644 index 81104c7e56..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_or_int_lit8.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/binopLit8.S" {"instr":"orl %ecx, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_or_long.S b/runtime/interpreter/mterp/x86_64/op_or_long.S deleted file mode 100644 index 6c70a2001e..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_or_long.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/binopWide.S" {"instr":"orq (rFP,%rcx,4), %rax"} diff --git a/runtime/interpreter/mterp/x86_64/op_or_long_2addr.S b/runtime/interpreter/mterp/x86_64/op_or_long_2addr.S deleted file mode 100644 index 546da1de2d..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_or_long_2addr.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/binopWide2addr.S" {"instr":"orq %rax, (rFP,%rcx,4)"} diff --git a/runtime/interpreter/mterp/x86_64/op_packed_switch.S b/runtime/interpreter/mterp/x86_64/op_packed_switch.S deleted file mode 100644 index 0400ca45cf..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_packed_switch.S +++ /dev/null @@ -1,27 +0,0 @@ -%default { "func":"MterpDoPackedSwitch" } -/* - * Handle a packed-switch or sparse-switch instruction. In both cases - * we decode it and hand it off to a helper function. - * - * We don't really expect backward branches in a switch statement, but - * they're perfectly legal, so we check for them here. - * - * for: packed-switch, sparse-switch - */ - /* op vAA, +BBBB */ - movslq 2(rPC), OUT_ARG0 # rcx <- BBBBbbbb - leaq (rPC,OUT_ARG0,2), OUT_ARG0 # rcx <- PC + BBBBbbbb*2 - GET_VREG OUT_32_ARG1, rINSTq # eax <- vAA - call SYMBOL($func) - addl %eax, %eax - movslq %eax, %rax - leaq (rPC, %rax), rPC - FETCH_INST - jg 1f -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -1: - GOTO_NEXT diff --git a/runtime/interpreter/mterp/x86_64/op_rem_double.S b/runtime/interpreter/mterp/x86_64/op_rem_double.S deleted file mode 100644 index 00aed787cb..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_rem_double.S +++ /dev/null @@ -1,14 +0,0 @@ - /* rem_double vAA, vBB, vCC */ - movzbq 3(rPC), %rcx # ecx <- BB - movzbq 2(rPC), %rax # eax <- CC - fldl VREG_ADDRESS(%rcx) # %st1 <- fp[vBB] - fldl VREG_ADDRESS(%rax) # %st0 <- fp[vCC] -1: - fprem - fstsw %ax - sahf - jp 1b - fstp %st(1) - fstpl VREG_ADDRESS(rINSTq) # fp[vAA] <- %st - CLEAR_WIDE_REF rINSTq - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_rem_double_2addr.S b/runtime/interpreter/mterp/x86_64/op_rem_double_2addr.S deleted file mode 100644 index 9768266e21..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_rem_double_2addr.S +++ /dev/null @@ -1,15 +0,0 @@ - /* rem_double/2addr vA, vB */ - movzbq rINSTbl, %rcx # ecx <- A+ - sarl $$4, rINST # rINST <- B - fldl VREG_ADDRESS(rINSTq) # vB to fp stack - andb $$0xf, %cl # ecx <- A - fldl VREG_ADDRESS(%rcx) # vA to fp stack -1: - fprem - fstsw %ax - sahf - jp 1b - fstp %st(1) - fstpl VREG_ADDRESS(%rcx) # %st to vA - CLEAR_WIDE_REF %rcx - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/op_rem_float.S b/runtime/interpreter/mterp/x86_64/op_rem_float.S deleted file mode 100644 index 5af28accec..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_rem_float.S +++ /dev/null @@ -1,14 +0,0 @@ - /* rem_float vAA, vBB, vCC */ - movzbq 3(rPC), %rcx # ecx <- BB - movzbq 2(rPC), %rax # eax <- CC - flds VREG_ADDRESS(%rcx) # vBB to fp stack - flds VREG_ADDRESS(%rax) # vCC to fp stack -1: - fprem - fstsw %ax - sahf - jp 1b - fstp %st(1) - fstps VREG_ADDRESS(rINSTq) # %st to vAA - CLEAR_REF rINSTq - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_rem_float_2addr.S b/runtime/interpreter/mterp/x86_64/op_rem_float_2addr.S deleted file mode 100644 index e9282a8de9..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_rem_float_2addr.S +++ /dev/null @@ -1,15 +0,0 @@ - /* rem_float/2addr vA, vB */ - movzbq rINSTbl, %rcx # ecx <- A+ - sarl $$4, rINST # rINST <- B - flds VREG_ADDRESS(rINSTq) # vB to fp stack - andb $$0xf, %cl # ecx <- A - flds VREG_ADDRESS(%rcx) # vA to fp stack -1: - fprem - fstsw %ax - sahf - jp 1b - fstp %st(1) - fstps VREG_ADDRESS(%rcx) # %st to vA - CLEAR_REF %rcx - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/op_rem_int.S b/runtime/interpreter/mterp/x86_64/op_rem_int.S deleted file mode 100644 index fd77d7cdfe..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_rem_int.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/bindiv.S" {"result":"%edx","second":"%ecx","wide":"0","suffix":"l","rem":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_rem_int_2addr.S b/runtime/interpreter/mterp/x86_64/op_rem_int_2addr.S deleted file mode 100644 index 25ffbf713b..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_rem_int_2addr.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/bindiv2addr.S" {"result":"%edx","second":"%ecx","wide":"0","suffix":"l","rem":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_rem_int_lit16.S b/runtime/interpreter/mterp/x86_64/op_rem_int_lit16.S deleted file mode 100644 index 21cc37087d..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_rem_int_lit16.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/bindivLit16.S" {"result":"%edx","rem":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_rem_int_lit8.S b/runtime/interpreter/mterp/x86_64/op_rem_int_lit8.S deleted file mode 100644 index 2eb0150f63..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_rem_int_lit8.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/bindivLit8.S" {"result":"%edx","rem":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_rem_long.S b/runtime/interpreter/mterp/x86_64/op_rem_long.S deleted file mode 100644 index efa721520d..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_rem_long.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/bindiv.S" {"result":"%rdx","second":"%rcx","wide":"1","suffix":"q","ext":"cqo","rem":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_rem_long_2addr.S b/runtime/interpreter/mterp/x86_64/op_rem_long_2addr.S deleted file mode 100644 index ce0dd86539..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_rem_long_2addr.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/bindiv2addr.S" {"result":"%rdx","second":"%rcx","wide":"1","suffix":"q","rem":"1","ext":"cqo"} diff --git a/runtime/interpreter/mterp/x86_64/op_return.S b/runtime/interpreter/mterp/x86_64/op_return.S deleted file mode 100644 index 14f4f8a446..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_return.S +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Return a 32-bit value. - * - * for: return, return-object - */ - /* op vAA */ - .extern MterpThreadFenceForConstructor - call SYMBOL(MterpThreadFenceForConstructor) - testl $$(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(rSELF) - jz 1f - movq rSELF, OUT_ARG0 - call SYMBOL(MterpSuspendCheck) -1: - GET_VREG %eax, rINSTq # eax <- vAA - jmp MterpReturn diff --git a/runtime/interpreter/mterp/x86_64/op_return_object.S b/runtime/interpreter/mterp/x86_64/op_return_object.S deleted file mode 100644 index 1ae69a501c..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_return_object.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/op_return.S" diff --git a/runtime/interpreter/mterp/x86_64/op_return_void.S b/runtime/interpreter/mterp/x86_64/op_return_void.S deleted file mode 100644 index 46a5753c87..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_return_void.S +++ /dev/null @@ -1,9 +0,0 @@ - .extern MterpThreadFenceForConstructor - call SYMBOL(MterpThreadFenceForConstructor) - testl $$(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(rSELF) - jz 1f - movq rSELF, OUT_ARG0 - call SYMBOL(MterpSuspendCheck) -1: - xorq %rax, %rax - jmp MterpReturn diff --git a/runtime/interpreter/mterp/x86_64/op_return_void_no_barrier.S b/runtime/interpreter/mterp/x86_64/op_return_void_no_barrier.S deleted file mode 100644 index 92e3506d1d..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_return_void_no_barrier.S +++ /dev/null @@ -1,7 +0,0 @@ - testl $$(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(rSELF) - jz 1f - movq rSELF, OUT_ARG0 - call SYMBOL(MterpSuspendCheck) -1: - xorq %rax, %rax - jmp MterpReturn diff --git a/runtime/interpreter/mterp/x86_64/op_return_wide.S b/runtime/interpreter/mterp/x86_64/op_return_wide.S deleted file mode 100644 index f2d6e04cab..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_return_wide.S +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Return a 64-bit value. - */ - /* return-wide vAA */ - .extern MterpThreadFenceForConstructor - call SYMBOL(MterpThreadFenceForConstructor) - testl $$(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(rSELF) - jz 1f - movq rSELF, OUT_ARG0 - call SYMBOL(MterpSuspendCheck) -1: - GET_WIDE_VREG %rax, rINSTq # eax <- v[AA] - jmp MterpReturn diff --git a/runtime/interpreter/mterp/x86_64/op_rsub_int.S b/runtime/interpreter/mterp/x86_64/op_rsub_int.S deleted file mode 100644 index 2dd20026df..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_rsub_int.S +++ /dev/null @@ -1,2 +0,0 @@ -/* this op is "rsub-int", but can be thought of as "rsub-int/lit16" */ -%include "x86_64/binopLit16.S" {"instr":"subl %eax, %ecx","result":"%ecx"} diff --git a/runtime/interpreter/mterp/x86_64/op_rsub_int_lit8.S b/runtime/interpreter/mterp/x86_64/op_rsub_int_lit8.S deleted file mode 100644 index 64d0d8a704..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_rsub_int_lit8.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/binopLit8.S" {"instr":"subl %eax, %ecx" , "result":"%ecx"} diff --git a/runtime/interpreter/mterp/x86_64/op_sget.S b/runtime/interpreter/mterp/x86_64/op_sget.S deleted file mode 100644 index 38d9a5e6c8..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_sget.S +++ /dev/null @@ -1,25 +0,0 @@ -%default { "is_object":"0", "helper":"artGet32StaticFromCode", "wide":"0" } -/* - * General SGET handler wrapper. - * - * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short, sget-wide - */ - /* op vAA, field@BBBB */ - .extern $helper - EXPORT_PC - movzwq 2(rPC), OUT_ARG0 # field ref CCCC - movq OFF_FP_METHOD(rFP), OUT_ARG1 # referrer - movq rSELF, OUT_ARG2 # self - call SYMBOL($helper) - cmpl $$0, THREAD_EXCEPTION_OFFSET(rSELF) - jnz MterpException - .if $is_object - SET_VREG_OBJECT %eax, rINSTq # fp[A] <- value - .else - .if $wide - SET_WIDE_VREG %rax, rINSTq # fp[A] <- value - .else - SET_VREG %eax, rINSTq # fp[A] <- value - .endif - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_sget_boolean.S b/runtime/interpreter/mterp/x86_64/op_sget_boolean.S deleted file mode 100644 index 7d358daec2..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_sget_boolean.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/op_sget.S" {"helper":"artGetBooleanStaticFromCode"} diff --git a/runtime/interpreter/mterp/x86_64/op_sget_byte.S b/runtime/interpreter/mterp/x86_64/op_sget_byte.S deleted file mode 100644 index 79d9ff448b..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_sget_byte.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/op_sget.S" {"helper":"artGetByteStaticFromCode"} diff --git a/runtime/interpreter/mterp/x86_64/op_sget_char.S b/runtime/interpreter/mterp/x86_64/op_sget_char.S deleted file mode 100644 index 448861052f..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_sget_char.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/op_sget.S" {"helper":"artGetCharStaticFromCode"} diff --git a/runtime/interpreter/mterp/x86_64/op_sget_object.S b/runtime/interpreter/mterp/x86_64/op_sget_object.S deleted file mode 100644 index 09b627e124..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_sget_object.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/op_sget.S" {"is_object":"1", "helper":"artGetObjStaticFromCode"} diff --git a/runtime/interpreter/mterp/x86_64/op_sget_short.S b/runtime/interpreter/mterp/x86_64/op_sget_short.S deleted file mode 100644 index 47ac23803c..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_sget_short.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/op_sget.S" {"helper":"artGetShortStaticFromCode"} diff --git a/runtime/interpreter/mterp/x86_64/op_sget_wide.S b/runtime/interpreter/mterp/x86_64/op_sget_wide.S deleted file mode 100644 index aa223434cf..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_sget_wide.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/op_sget.S" {"helper":"artGet64StaticFromCode", "wide":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_shl_int.S b/runtime/interpreter/mterp/x86_64/op_shl_int.S deleted file mode 100644 index fa1edb7555..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_shl_int.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/binop1.S" {"instr":"sall %cl, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_shl_int_2addr.S b/runtime/interpreter/mterp/x86_64/op_shl_int_2addr.S deleted file mode 100644 index dd962792c9..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_shl_int_2addr.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/shop2addr.S" {"instr":"sall %cl, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_shl_int_lit8.S b/runtime/interpreter/mterp/x86_64/op_shl_int_lit8.S deleted file mode 100644 index 39b23ae1fb..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_shl_int_lit8.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/binopLit8.S" {"instr":"sall %cl, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_shl_long.S b/runtime/interpreter/mterp/x86_64/op_shl_long.S deleted file mode 100644 index fdc7cb64de..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_shl_long.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/binop1.S" {"instr":"salq %cl, %rax","wide":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_shl_long_2addr.S b/runtime/interpreter/mterp/x86_64/op_shl_long_2addr.S deleted file mode 100644 index 546633f7d5..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_shl_long_2addr.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/shop2addr.S" {"instr":"salq %cl, %rax","wide":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_shr_int.S b/runtime/interpreter/mterp/x86_64/op_shr_int.S deleted file mode 100644 index fc289f4638..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_shr_int.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/binop1.S" {"instr":"sarl %cl, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_shr_int_2addr.S b/runtime/interpreter/mterp/x86_64/op_shr_int_2addr.S deleted file mode 100644 index 0e5bca7057..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_shr_int_2addr.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/shop2addr.S" {"instr":"sarl %cl, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_shr_int_lit8.S b/runtime/interpreter/mterp/x86_64/op_shr_int_lit8.S deleted file mode 100644 index 3cc9307569..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_shr_int_lit8.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/binopLit8.S" {"instr":"sarl %cl, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_shr_long.S b/runtime/interpreter/mterp/x86_64/op_shr_long.S deleted file mode 100644 index 25028d3560..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_shr_long.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/binop1.S" {"instr":"sarq %cl, %rax","wide":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_shr_long_2addr.S b/runtime/interpreter/mterp/x86_64/op_shr_long_2addr.S deleted file mode 100644 index 373841322d..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_shr_long_2addr.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/shop2addr.S" {"instr":"sarq %cl, %rax","wide":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_sparse_switch.S b/runtime/interpreter/mterp/x86_64/op_sparse_switch.S deleted file mode 100644 index 0eaa514813..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_sparse_switch.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/op_packed_switch.S" { "func":"MterpDoSparseSwitch" } diff --git a/runtime/interpreter/mterp/x86_64/op_sput.S b/runtime/interpreter/mterp/x86_64/op_sput.S deleted file mode 100644 index e92b03273b..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_sput.S +++ /dev/null @@ -1,17 +0,0 @@ -%default { "helper":"artSet32StaticFromCode"} -/* - * General SPUT handler wrapper. - * - * for: sput, sput-boolean, sput-byte, sput-char, sput-short - */ - /* op vAA, field@BBBB */ - .extern $helper - EXPORT_PC - movzwq 2(rPC), OUT_ARG0 # field ref BBBB - GET_VREG OUT_32_ARG1, rINSTq # fp[AA] - movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer - movq rSELF, OUT_ARG3 # self - call SYMBOL($helper) - testb %al, %al - jnz MterpException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_sput_boolean.S b/runtime/interpreter/mterp/x86_64/op_sput_boolean.S deleted file mode 100644 index 8718915cb2..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_sput_boolean.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/op_sput.S" {"helper":"artSet8StaticFromCode"} diff --git a/runtime/interpreter/mterp/x86_64/op_sput_byte.S b/runtime/interpreter/mterp/x86_64/op_sput_byte.S deleted file mode 100644 index 8718915cb2..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_sput_byte.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/op_sput.S" {"helper":"artSet8StaticFromCode"} diff --git a/runtime/interpreter/mterp/x86_64/op_sput_char.S b/runtime/interpreter/mterp/x86_64/op_sput_char.S deleted file mode 100644 index 2fe9d14816..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_sput_char.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/op_sput.S" {"helper":"artSet16StaticFromCode"} diff --git a/runtime/interpreter/mterp/x86_64/op_sput_object.S b/runtime/interpreter/mterp/x86_64/op_sput_object.S deleted file mode 100644 index eb5a37673e..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_sput_object.S +++ /dev/null @@ -1,10 +0,0 @@ - EXPORT_PC - leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG0 - movq rPC, OUT_ARG1 - REFRESH_INST ${opnum} - movq rINSTq, OUT_ARG2 - movq rSELF, OUT_ARG3 - call SYMBOL(MterpSputObject) - testb %al, %al - jz MterpException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_sput_short.S b/runtime/interpreter/mterp/x86_64/op_sput_short.S deleted file mode 100644 index 2fe9d14816..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_sput_short.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/op_sput.S" {"helper":"artSet16StaticFromCode"} diff --git a/runtime/interpreter/mterp/x86_64/op_sput_wide.S b/runtime/interpreter/mterp/x86_64/op_sput_wide.S deleted file mode 100644 index c4bc269eb6..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_sput_wide.S +++ /dev/null @@ -1,15 +0,0 @@ -/* - * SPUT_WIDE handler wrapper. - * - */ - /* sput-wide vAA, field@BBBB */ - .extern artSet64IndirectStaticFromMterp - EXPORT_PC - movzwq 2(rPC), OUT_ARG0 # field ref BBBB - movq OFF_FP_METHOD(rFP), OUT_ARG1 # referrer - leaq VREG_ADDRESS(rINSTq), OUT_ARG2 # &fp[AA] - movq rSELF, OUT_ARG3 # self - call SYMBOL(artSet64IndirectStaticFromMterp) - testb %al, %al - jnz MterpException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_sub_double.S b/runtime/interpreter/mterp/x86_64/op_sub_double.S deleted file mode 100644 index 952667e831..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_sub_double.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/sseBinop.S" {"instr":"subs","suff":"d"} diff --git a/runtime/interpreter/mterp/x86_64/op_sub_double_2addr.S b/runtime/interpreter/mterp/x86_64/op_sub_double_2addr.S deleted file mode 100644 index 0bd5dbb8ff..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_sub_double_2addr.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/sseBinop2Addr.S" {"instr":"subs","suff":"d"} diff --git a/runtime/interpreter/mterp/x86_64/op_sub_float.S b/runtime/interpreter/mterp/x86_64/op_sub_float.S deleted file mode 100644 index ea0ae14f5b..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_sub_float.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/sseBinop.S" {"instr":"subs","suff":"s"} diff --git a/runtime/interpreter/mterp/x86_64/op_sub_float_2addr.S b/runtime/interpreter/mterp/x86_64/op_sub_float_2addr.S deleted file mode 100644 index 9dd17805c8..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_sub_float_2addr.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/sseBinop2Addr.S" {"instr":"subs","suff":"s"} diff --git a/runtime/interpreter/mterp/x86_64/op_sub_int.S b/runtime/interpreter/mterp/x86_64/op_sub_int.S deleted file mode 100644 index 560394f43f..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_sub_int.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/binop.S" {"instr":"subl (rFP,%rcx,4), %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_sub_int_2addr.S b/runtime/interpreter/mterp/x86_64/op_sub_int_2addr.S deleted file mode 100644 index 6f50f78f41..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_sub_int_2addr.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/binop2addr.S" {"instr":"subl %eax, (rFP,%rcx,4)"} diff --git a/runtime/interpreter/mterp/x86_64/op_sub_long.S b/runtime/interpreter/mterp/x86_64/op_sub_long.S deleted file mode 100644 index 7fa54e7a11..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_sub_long.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/binopWide.S" {"instr":"subq (rFP,%rcx,4), %rax"} diff --git a/runtime/interpreter/mterp/x86_64/op_sub_long_2addr.S b/runtime/interpreter/mterp/x86_64/op_sub_long_2addr.S deleted file mode 100644 index c18be10919..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_sub_long_2addr.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/binopWide2addr.S" {"instr":"subq %rax, (rFP,%rcx,4)"} diff --git a/runtime/interpreter/mterp/x86_64/op_throw.S b/runtime/interpreter/mterp/x86_64/op_throw.S deleted file mode 100644 index 22ed990645..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_throw.S +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Throw an exception object in the current thread. - */ - /* throw vAA */ - EXPORT_PC - GET_VREG %eax, rINSTq # eax<- vAA (exception object) - testb %al, %al - jz common_errNullObject - movq %rax, THREAD_EXCEPTION_OFFSET(rSELF) - jmp MterpException diff --git a/runtime/interpreter/mterp/x86_64/op_unused_3e.S b/runtime/interpreter/mterp/x86_64/op_unused_3e.S deleted file mode 100644 index 280615f08b..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_unused_3e.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/unused.S" diff --git a/runtime/interpreter/mterp/x86_64/op_unused_3f.S b/runtime/interpreter/mterp/x86_64/op_unused_3f.S deleted file mode 100644 index 280615f08b..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_unused_3f.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/unused.S" diff --git a/runtime/interpreter/mterp/x86_64/op_unused_40.S b/runtime/interpreter/mterp/x86_64/op_unused_40.S deleted file mode 100644 index 280615f08b..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_unused_40.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/unused.S" diff --git a/runtime/interpreter/mterp/x86_64/op_unused_41.S b/runtime/interpreter/mterp/x86_64/op_unused_41.S deleted file mode 100644 index 280615f08b..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_unused_41.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/unused.S" diff --git a/runtime/interpreter/mterp/x86_64/op_unused_42.S b/runtime/interpreter/mterp/x86_64/op_unused_42.S deleted file mode 100644 index 280615f08b..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_unused_42.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/unused.S" diff --git a/runtime/interpreter/mterp/x86_64/op_unused_43.S b/runtime/interpreter/mterp/x86_64/op_unused_43.S deleted file mode 100644 index 280615f08b..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_unused_43.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/unused.S" diff --git a/runtime/interpreter/mterp/x86_64/op_unused_79.S b/runtime/interpreter/mterp/x86_64/op_unused_79.S deleted file mode 100644 index 280615f08b..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_unused_79.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/unused.S" diff --git a/runtime/interpreter/mterp/x86_64/op_unused_7a.S b/runtime/interpreter/mterp/x86_64/op_unused_7a.S deleted file mode 100644 index 280615f08b..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_unused_7a.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/unused.S" diff --git a/runtime/interpreter/mterp/x86_64/op_unused_f4.S b/runtime/interpreter/mterp/x86_64/op_unused_f4.S deleted file mode 100644 index 280615f08b..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_unused_f4.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/unused.S" diff --git a/runtime/interpreter/mterp/x86_64/op_unused_fa.S b/runtime/interpreter/mterp/x86_64/op_unused_fa.S deleted file mode 100644 index 280615f08b..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_unused_fa.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/unused.S" diff --git a/runtime/interpreter/mterp/x86_64/op_unused_fb.S b/runtime/interpreter/mterp/x86_64/op_unused_fb.S deleted file mode 100644 index 280615f08b..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_unused_fb.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/unused.S" diff --git a/runtime/interpreter/mterp/x86_64/op_unused_fc.S b/runtime/interpreter/mterp/x86_64/op_unused_fc.S deleted file mode 100644 index 280615f08b..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_unused_fc.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/unused.S" diff --git a/runtime/interpreter/mterp/x86_64/op_unused_fd.S b/runtime/interpreter/mterp/x86_64/op_unused_fd.S deleted file mode 100644 index 280615f08b..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_unused_fd.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/unused.S" diff --git a/runtime/interpreter/mterp/x86_64/op_unused_fe.S b/runtime/interpreter/mterp/x86_64/op_unused_fe.S deleted file mode 100644 index 280615f08b..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_unused_fe.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/unused.S" diff --git a/runtime/interpreter/mterp/x86_64/op_unused_ff.S b/runtime/interpreter/mterp/x86_64/op_unused_ff.S deleted file mode 100644 index 280615f08b..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_unused_ff.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/unused.S" diff --git a/runtime/interpreter/mterp/x86_64/op_ushr_int.S b/runtime/interpreter/mterp/x86_64/op_ushr_int.S deleted file mode 100644 index dd91086371..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_ushr_int.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/binop1.S" {"instr":"shrl %cl, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_ushr_int_2addr.S b/runtime/interpreter/mterp/x86_64/op_ushr_int_2addr.S deleted file mode 100644 index d38aedd234..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_ushr_int_2addr.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/shop2addr.S" {"instr":"shrl %cl, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_ushr_int_lit8.S b/runtime/interpreter/mterp/x86_64/op_ushr_int_lit8.S deleted file mode 100644 index f7ff8abc86..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_ushr_int_lit8.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/binopLit8.S" {"instr":"shrl %cl, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_ushr_long.S b/runtime/interpreter/mterp/x86_64/op_ushr_long.S deleted file mode 100644 index 7c6daca05d..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_ushr_long.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/binop1.S" {"instr":"shrq %cl, %rax","wide":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_ushr_long_2addr.S b/runtime/interpreter/mterp/x86_64/op_ushr_long_2addr.S deleted file mode 100644 index cd6a22c6fa..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_ushr_long_2addr.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/shop2addr.S" {"instr":"shrq %cl, %rax","wide":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_xor_int.S b/runtime/interpreter/mterp/x86_64/op_xor_int.S deleted file mode 100644 index b295d74de0..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_xor_int.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/binop.S" {"instr":"xorl (rFP,%rcx,4), %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_xor_int_2addr.S b/runtime/interpreter/mterp/x86_64/op_xor_int_2addr.S deleted file mode 100644 index 879bfc05dc..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_xor_int_2addr.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/binop2addr.S" {"instr":"xorl %eax, (rFP,%rcx,4)"} diff --git a/runtime/interpreter/mterp/x86_64/op_xor_int_lit16.S b/runtime/interpreter/mterp/x86_64/op_xor_int_lit16.S deleted file mode 100644 index 5d375a1cf6..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_xor_int_lit16.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/binopLit16.S" {"instr":"xorl %ecx, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_xor_int_lit8.S b/runtime/interpreter/mterp/x86_64/op_xor_int_lit8.S deleted file mode 100644 index 54cce9c18e..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_xor_int_lit8.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/binopLit8.S" {"instr":"xorl %ecx, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_xor_long.S b/runtime/interpreter/mterp/x86_64/op_xor_long.S deleted file mode 100644 index 52b44e29c1..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_xor_long.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/binopWide.S" {"instr":"xorq (rFP,%rcx,4), %rax"} diff --git a/runtime/interpreter/mterp/x86_64/op_xor_long_2addr.S b/runtime/interpreter/mterp/x86_64/op_xor_long_2addr.S deleted file mode 100644 index d75c4ba6ce..0000000000 --- a/runtime/interpreter/mterp/x86_64/op_xor_long_2addr.S +++ /dev/null @@ -1 +0,0 @@ -%include "x86_64/binopWide2addr.S" {"instr":"xorq %rax, (rFP,%rcx,4)"} diff --git a/runtime/interpreter/mterp/x86_64/shop2addr.S b/runtime/interpreter/mterp/x86_64/shop2addr.S deleted file mode 100644 index 6b06d002fc..0000000000 --- a/runtime/interpreter/mterp/x86_64/shop2addr.S +++ /dev/null @@ -1,19 +0,0 @@ -%default {"wide":"0"} -/* - * Generic 32-bit "shift/2addr" operation. - */ - /* shift/2addr vA, vB */ - movl rINST, %ecx # ecx <- BA - sarl $$4, %ecx # ecx <- B - GET_VREG %ecx, %rcx # ecx <- vBB - andb $$0xf, rINSTbl # rINST <- A - .if $wide - GET_WIDE_VREG %rax, rINSTq # rax <- vAA - $instr # ex: sarl %cl, %eax - SET_WIDE_VREG %rax, rINSTq - .else - GET_VREG %eax, rINSTq # eax <- vAA - $instr # ex: sarl %cl, %eax - SET_VREG %eax, rINSTq - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/sseBinop.S b/runtime/interpreter/mterp/x86_64/sseBinop.S deleted file mode 100644 index 09d3364de7..0000000000 --- a/runtime/interpreter/mterp/x86_64/sseBinop.S +++ /dev/null @@ -1,9 +0,0 @@ -%default {"instr":"","suff":""} - movzbq 2(rPC), %rcx # ecx <- BB - movzbq 3(rPC), %rax # eax <- CC - movs${suff} VREG_ADDRESS(%rcx), %xmm0 # %xmm0 <- 1st src - ${instr}${suff} VREG_ADDRESS(%rax), %xmm0 - movs${suff} %xmm0, VREG_ADDRESS(rINSTq) # vAA <- %xmm0 - pxor %xmm0, %xmm0 - movs${suff} %xmm0, VREG_REF_ADDRESS(rINSTq) # clear ref - ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/sseBinop2Addr.S b/runtime/interpreter/mterp/x86_64/sseBinop2Addr.S deleted file mode 100644 index 084166b95d..0000000000 --- a/runtime/interpreter/mterp/x86_64/sseBinop2Addr.S +++ /dev/null @@ -1,10 +0,0 @@ -%default {"instr":"","suff":""} - movl rINST, %ecx # ecx <- A+ - andl $$0xf, %ecx # ecx <- A - movs${suff} VREG_ADDRESS(%rcx), %xmm0 # %xmm0 <- 1st src - sarl $$4, rINST # rINST<- B - ${instr}${suff} VREG_ADDRESS(rINSTq), %xmm0 - movs${suff} %xmm0, VREG_ADDRESS(%rcx) # vAA<- %xmm0 - pxor %xmm0, %xmm0 - movs${suff} %xmm0, VREG_REF_ADDRESS(rINSTq) # clear ref - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/unop.S b/runtime/interpreter/mterp/x86_64/unop.S deleted file mode 100644 index 1777123f36..0000000000 --- a/runtime/interpreter/mterp/x86_64/unop.S +++ /dev/null @@ -1,22 +0,0 @@ -%default {"preinstr":"", "instr":"", "wide":"0"} -/* - * Generic 32/64-bit unary operation. Provide an "instr" line that - * specifies an instruction that performs "result = op eax". - */ - /* unop vA, vB */ - movl rINST, %ecx # rcx <- A+ - sarl $$4,rINST # rINST <- B - .if ${wide} - GET_WIDE_VREG %rax, rINSTq # rax <- vB - .else - GET_VREG %eax, rINSTq # eax <- vB - .endif - andb $$0xf,%cl # ecx <- A -$preinstr -$instr - .if ${wide} - SET_WIDE_VREG %rax, %rcx - .else - SET_VREG %eax, %rcx - .endif - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/unused.S b/runtime/interpreter/mterp/x86_64/unused.S deleted file mode 100644 index c95ef947d3..0000000000 --- a/runtime/interpreter/mterp/x86_64/unused.S +++ /dev/null @@ -1,4 +0,0 @@ -/* - * Bail to reference interpreter to throw. - */ - jmp MterpFallback diff --git a/runtime/interpreter/mterp/x86_64/zcmp.S b/runtime/interpreter/mterp/x86_64/zcmp.S deleted file mode 100644 index e503ec1f38..0000000000 --- a/runtime/interpreter/mterp/x86_64/zcmp.S +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Generic one-operand compare-and-branch operation. Provide a "revcmp" - * fragment that specifies the *reverse* comparison to perform, e.g. - * for "if-le" you would use "gt". - * - * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez - */ - /* if-cmp vAA, +BBBB */ - cmpl $$0, VREG_ADDRESS(rINSTq) # compare (vA, 0) - movl $$2, %eax # assume branch not taken - j${revcmp} 1f - movswq 2(rPC),%rax # fetch signed displacement -1: - addq %rax, %rax # eax <- AA * 2 - leaq (rPC, %rax), rPC - FETCH_INST - jg 2f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -2: - GOTO_NEXT -- GitLab From a1de9188a05afdecca8cd04ecc4fefbac8b9880f Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Thu, 25 Feb 2016 11:37:38 +0000 Subject: [PATCH 056/204] Optimizing: Reduce memory usage of HInstructions. Pack narrow fields and flags into a single 32-bit field. Change-Id: Ib2f7abf987caee0339018d21f0d498f8db63542d --- compiler/dex/compiler_enums.h | 1 + compiler/optimizing/builder.cc | 8 +- compiler/optimizing/code_generator_arm.cc | 2 +- compiler/optimizing/code_generator_arm64.cc | 2 +- compiler/optimizing/code_generator_mips.cc | 2 +- compiler/optimizing/code_generator_x86.cc | 2 +- compiler/optimizing/code_generator_x86_64.cc | 2 +- compiler/optimizing/inliner.cc | 3 +- compiler/optimizing/nodes.cc | 11 +- compiler/optimizing/nodes.h | 624 ++++++++++++------- runtime/base/bit_field.h | 11 +- runtime/primitive.h | 1 + 12 files changed, 438 insertions(+), 231 deletions(-) diff --git a/compiler/dex/compiler_enums.h b/compiler/dex/compiler_enums.h index b78b3d7d75..8800e4b08f 100644 --- a/compiler/dex/compiler_enums.h +++ b/compiler/dex/compiler_enums.h @@ -569,6 +569,7 @@ enum MemBarrierKind { kStoreStore, kAnyAny, kNTStoreStore, + kLastBarrierKind = kNTStoreStore }; std::ostream& operator<<(std::ostream& os, const MemBarrierKind& kind); diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc index 35ec7d41ff..57660c2623 100644 --- a/compiler/optimizing/builder.cc +++ b/compiler/optimizing/builder.cc @@ -282,7 +282,7 @@ void HGraphBuilder::InsertTryBoundaryBlocks(const DexFile::CodeItem& code_item) // Found a predecessor not covered by the same TryItem. Insert entering // boundary block. HTryBoundary* try_entry = - new (arena_) HTryBoundary(HTryBoundary::kEntry, try_block->GetDexPc()); + new (arena_) HTryBoundary(HTryBoundary::BoundaryKind::kEntry, try_block->GetDexPc()); try_block->CreateImmediateDominator()->AddInstruction(try_entry); LinkToCatchBlocks(try_entry, code_item, entry.second); break; @@ -316,7 +316,7 @@ void HGraphBuilder::InsertTryBoundaryBlocks(const DexFile::CodeItem& code_item) // Insert TryBoundary and link to catch blocks. HTryBoundary* try_exit = - new (arena_) HTryBoundary(HTryBoundary::kExit, successor->GetDexPc()); + new (arena_) HTryBoundary(HTryBoundary::BoundaryKind::kExit, successor->GetDexPc()); graph_->SplitEdge(try_block, successor)->AddInstruction(try_exit); LinkToCatchBlocks(try_exit, code_item, entry.second); } @@ -2843,7 +2843,7 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32 case Instruction::MONITOR_ENTER: { current_block_->AddInstruction(new (arena_) HMonitorOperation( LoadLocal(instruction.VRegA_11x(), Primitive::kPrimNot, dex_pc), - HMonitorOperation::kEnter, + HMonitorOperation::OperationKind::kEnter, dex_pc)); break; } @@ -2851,7 +2851,7 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32 case Instruction::MONITOR_EXIT: { current_block_->AddInstruction(new (arena_) HMonitorOperation( LoadLocal(instruction.VRegA_11x(), Primitive::kPrimNot, dex_pc), - HMonitorOperation::kExit, + HMonitorOperation::OperationKind::kExit, dex_pc)); break; } diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index 10d3426a58..aa9b01f30b 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -6594,7 +6594,7 @@ void LocationsBuilderARM::VisitClassTableGet(HClassTableGet* instruction) { void InstructionCodeGeneratorARM::VisitClassTableGet(HClassTableGet* instruction) { LocationSummary* locations = instruction->GetLocations(); uint32_t method_offset = 0; - if (instruction->GetTableKind() == HClassTableGet::kVTable) { + if (instruction->GetTableKind() == HClassTableGet::TableKind::kVTable) { method_offset = mirror::Class::EmbeddedVTableEntryOffset( instruction->GetIndex(), kArmPointerSize).SizeValue(); } else { diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index beb75f0afc..c27209f0b1 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -5030,7 +5030,7 @@ void LocationsBuilderARM64::VisitClassTableGet(HClassTableGet* instruction) { void InstructionCodeGeneratorARM64::VisitClassTableGet(HClassTableGet* instruction) { LocationSummary* locations = instruction->GetLocations(); uint32_t method_offset = 0; - if (instruction->GetTableKind() == HClassTableGet::kVTable) { + if (instruction->GetTableKind() == HClassTableGet::TableKind::kVTable) { method_offset = mirror::Class::EmbeddedVTableEntryOffset( instruction->GetIndex(), kArm64PointerSize).SizeValue(); } else { diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc index 8d3d94b79d..f3c12efd8d 100644 --- a/compiler/optimizing/code_generator_mips.cc +++ b/compiler/optimizing/code_generator_mips.cc @@ -5245,7 +5245,7 @@ void LocationsBuilderMIPS::VisitClassTableGet(HClassTableGet* instruction) { void InstructionCodeGeneratorMIPS::VisitClassTableGet(HClassTableGet* instruction) { LocationSummary* locations = instruction->GetLocations(); uint32_t method_offset = 0; - if (instruction->GetTableKind() == HClassTableGet::kVTable) { + if (instruction->GetTableKind() == HClassTableGet::TableKind::kVTable) { method_offset = mirror::Class::EmbeddedVTableEntryOffset( instruction->GetIndex(), kMipsPointerSize).SizeValue(); } else { diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 88e42f3faf..6b4a18c688 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -4127,7 +4127,7 @@ void LocationsBuilderX86::VisitClassTableGet(HClassTableGet* instruction) { void InstructionCodeGeneratorX86::VisitClassTableGet(HClassTableGet* instruction) { LocationSummary* locations = instruction->GetLocations(); uint32_t method_offset = 0; - if (instruction->GetTableKind() == HClassTableGet::kVTable) { + if (instruction->GetTableKind() == HClassTableGet::TableKind::kVTable) { method_offset = mirror::Class::EmbeddedVTableEntryOffset( instruction->GetIndex(), kX86PointerSize).SizeValue(); } else { diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index bb24c6f59c..c132663016 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -3998,7 +3998,7 @@ void LocationsBuilderX86_64::VisitClassTableGet(HClassTableGet* instruction) { void InstructionCodeGeneratorX86_64::VisitClassTableGet(HClassTableGet* instruction) { LocationSummary* locations = instruction->GetLocations(); uint32_t method_offset = 0; - if (instruction->GetTableKind() == HClassTableGet::kVTable) { + if (instruction->GetTableKind() == HClassTableGet::TableKind::kVTable) { method_offset = mirror::Class::EmbeddedVTableEntryOffset( instruction->GetIndex(), kX86_64PointerSize).SizeValue(); } else { diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index 02a1acc240..d55009554f 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -651,7 +651,8 @@ bool HInliner::TryInlinePolymorphicCallToSameTarget(HInvoke* invoke_instruction, HClassTableGet* class_table_get = new (graph_->GetArena()) HClassTableGet( receiver_class, type, - invoke_instruction->IsInvokeVirtual() ? HClassTableGet::kVTable : HClassTableGet::kIMTable, + invoke_instruction->IsInvokeVirtual() ? HClassTableGet::TableKind::kVTable + : HClassTableGet::TableKind::kIMTable, method_offset, invoke_instruction->GetDexPc()); diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index f9acb089ee..27a5b97f5f 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -2206,7 +2206,8 @@ void HInstruction::SetReferenceTypeInfo(ReferenceTypeInfo rti) { CheckAgainstUpperBound(rti, AsBoundType()->GetUpperBound()); } } - reference_type_info_ = rti; + reference_type_handle_ = rti.GetTypeHandle(); + SetPackedFlag(rti.IsExact()); } void HBoundType::SetUpperBound(const ReferenceTypeInfo& upper_bound, bool can_be_null) { @@ -2217,17 +2218,15 @@ void HBoundType::SetUpperBound(const ReferenceTypeInfo& upper_bound, bool can_be CheckAgainstUpperBound(GetReferenceTypeInfo(), upper_bound); } upper_bound_ = upper_bound; - upper_can_be_null_ = can_be_null; + SetPackedFlag(can_be_null); } -ReferenceTypeInfo::ReferenceTypeInfo() : type_handle_(TypeHandle()), is_exact_(false) {} - -ReferenceTypeInfo::ReferenceTypeInfo(TypeHandle type_handle, bool is_exact) - : type_handle_(type_handle), is_exact_(is_exact) { +ReferenceTypeInfo ReferenceTypeInfo::Create(TypeHandle type_handle, bool is_exact) { if (kIsDebugBuild) { ScopedObjectAccess soa(Thread::Current()); DCHECK(IsValidHandle(type_handle)); } + return ReferenceTypeInfo(type_handle, is_exact); } std::ostream& operator<<(std::ostream& os, const ReferenceTypeInfo& rhs) { diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 4185b2f2c5..174b29a806 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -154,8 +154,9 @@ class ReferenceTypeInfo : ValueObject { public: typedef Handle TypeHandle; - static ReferenceTypeInfo Create(TypeHandle type_handle, bool is_exact) { - // The constructor will check that the type_handle is valid. + static ReferenceTypeInfo Create(TypeHandle type_handle, bool is_exact); + + static ReferenceTypeInfo CreateUnchecked(TypeHandle type_handle, bool is_exact) { return ReferenceTypeInfo(type_handle, is_exact); } @@ -254,8 +255,9 @@ class ReferenceTypeInfo : ValueObject { } private: - ReferenceTypeInfo(); - ReferenceTypeInfo(TypeHandle type_handle, bool is_exact); + ReferenceTypeInfo() : type_handle_(TypeHandle()), is_exact_(false) {} + ReferenceTypeInfo(TypeHandle type_handle, bool is_exact) + : type_handle_(type_handle), is_exact_(is_exact) { } // The class of the object. TypeHandle type_handle_; @@ -1847,13 +1849,15 @@ class HInstruction : public ArenaObject { dex_pc_(dex_pc), id_(-1), ssa_index_(-1), - emitted_at_use_site_(false), + packed_fields_(0u), environment_(nullptr), locations_(nullptr), live_interval_(nullptr), lifetime_position_(kNoLifetime), side_effects_(side_effects), - reference_type_info_(ReferenceTypeInfo::CreateInvalid()) {} + reference_type_handle_(ReferenceTypeInfo::CreateInvalid().GetTypeHandle()) { + SetPackedFlag(ReferenceTypeInfo::CreateInvalid().IsExact()); + } virtual ~HInstruction() {} @@ -1922,7 +1926,8 @@ class HInstruction : public ArenaObject { ReferenceTypeInfo GetReferenceTypeInfo() const { DCHECK_EQ(GetType(), Primitive::kPrimNot); - return reference_type_info_; + return ReferenceTypeInfo::CreateUnchecked(reference_type_handle_, + GetPackedFlag());; } void AddUseAt(HInstruction* user, size_t index) { @@ -2111,13 +2116,45 @@ class HInstruction : public ArenaObject { // The caller must ensure that this is safe to do. void RemoveEnvironmentUsers(); - bool IsEmittedAtUseSite() const { return emitted_at_use_site_; } - void MarkEmittedAtUseSite() { emitted_at_use_site_ = true; } + bool IsEmittedAtUseSite() const { return GetPackedFlag(); } + void MarkEmittedAtUseSite() { SetPackedFlag(true); } protected: + // If set, the machine code for this instruction is assumed to be generated by + // its users. Used by liveness analysis to compute use positions accordingly. + static constexpr size_t kFlagEmittedAtUseSite = 0u; + static constexpr size_t kFlagReferenceTypeIsExact = kFlagEmittedAtUseSite + 1; + static constexpr size_t kNumberOfGenericPackedBits = kFlagReferenceTypeIsExact + 1; + static constexpr size_t kMaxNumberOfPackedBits = sizeof(uint32_t) * kBitsPerByte; + virtual const HUserRecord InputRecordAt(size_t i) const = 0; virtual void SetRawInputRecordAt(size_t index, const HUserRecord& input) = 0; + uint32_t GetPackedFields() const { + return packed_fields_; + } + + template + bool GetPackedFlag() const { + return (packed_fields_ & (1u << flag)) != 0u; + } + + template + void SetPackedFlag(bool value = true) { + packed_fields_ = (packed_fields_ & ~(1u << flag)) | ((value ? 1u : 0u) << flag); + } + + template + typename BitFieldType::value_type GetPackedField() const { + return BitFieldType::Decode(packed_fields_); + } + + template + void SetPackedField(typename BitFieldType::value_type value) { + DCHECK(IsUint(static_cast(value))); + packed_fields_ = BitFieldType::Update(value, packed_fields_); + } + private: void RemoveEnvironmentUser(HUseListNode* use_node) { env_uses_.Remove(use_node); } @@ -2134,9 +2171,8 @@ class HInstruction : public ArenaObject { // When doing liveness analysis, instructions that have uses get an SSA index. int ssa_index_; - // If set, the machine code for this instruction is assumed to be generated by - // its users. Used by liveness analysis to compute use positions accordingly. - bool emitted_at_use_site_; + // Packed fields. + uint32_t packed_fields_; // List of instructions that have this instruction as input. HUseList uses_; @@ -2160,8 +2196,10 @@ class HInstruction : public ArenaObject { SideEffects side_effects_; + // The reference handle part of the reference type info. + // The IsExact() flag is stored in packed fields. // TODO: for primitive types this should be marked as invalid. - ReferenceTypeInfo reference_type_info_; + ReferenceTypeInfo::TypeHandle reference_type_handle_; friend class GraphChecker; friend class HBasicBlock; @@ -2287,13 +2325,23 @@ template class HExpression : public HTemplateInstruction { public: HExpression(Primitive::Type type, SideEffects side_effects, uint32_t dex_pc) - : HTemplateInstruction(side_effects, dex_pc), type_(type) {} + : HTemplateInstruction(side_effects, dex_pc) { + this->template SetPackedField(type); + } virtual ~HExpression() {} - Primitive::Type GetType() const OVERRIDE { return type_; } + Primitive::Type GetType() const OVERRIDE { + return TypeField::Decode(this->GetPackedFields()); + } protected: - Primitive::Type type_; + static constexpr size_t kFieldType = HInstruction::kNumberOfGenericPackedBits; + static constexpr size_t kFieldTypeSize = + MinimumBitsToStore(static_cast(Primitive::kPrimLast)); + static constexpr size_t kNumberOfExpressionPackedBits = kFieldType + kFieldTypeSize; + static_assert(kNumberOfExpressionPackedBits <= HInstruction::kMaxNumberOfPackedBits, + "Too many packed fields."); + using TypeField = BitField; }; // Represents dex's RETURN_VOID opcode. A HReturnVoid is a control flow @@ -2583,13 +2631,16 @@ class HIf : public HTemplateInstruction<1> { // higher indices in no particular order. class HTryBoundary : public HTemplateInstruction<0> { public: - enum BoundaryKind { + enum class BoundaryKind { kEntry, kExit, + kLast = kExit }; explicit HTryBoundary(BoundaryKind kind, uint32_t dex_pc = kNoDexPc) - : HTemplateInstruction(SideEffects::None(), dex_pc), kind_(kind) {} + : HTemplateInstruction(SideEffects::None(), dex_pc) { + SetPackedField(kind); + } bool IsControlFlow() const OVERRIDE { return true; } @@ -2615,14 +2666,22 @@ class HTryBoundary : public HTemplateInstruction<0> { } } - bool IsEntry() const { return kind_ == BoundaryKind::kEntry; } + BoundaryKind GetBoundaryKind() const { return GetPackedField(); } + bool IsEntry() const { return GetBoundaryKind() == BoundaryKind::kEntry; } bool HasSameExceptionHandlersAs(const HTryBoundary& other) const; DECLARE_INSTRUCTION(TryBoundary); private: - const BoundaryKind kind_; + static constexpr size_t kFieldBoundaryKind = kNumberOfGenericPackedBits; + static constexpr size_t kFieldBoundaryKindSize = + MinimumBitsToStore(static_cast(BoundaryKind::kLast)); + static constexpr size_t kNumberOfTryBoundaryPackedBits = + kFieldBoundaryKind + kFieldBoundaryKindSize; + static_assert(kNumberOfTryBoundaryPackedBits <= kMaxNumberOfPackedBits, + "Too many packed fields."); + using BoundaryKindField = BitField; DISALLOW_COPY_AND_ASSIGN(HTryBoundary); }; @@ -2668,9 +2727,10 @@ class HCurrentMethod : public HExpression<0> { // of a class. class HClassTableGet : public HExpression<1> { public: - enum TableKind { + enum class TableKind { kVTable, kIMTable, + kLast = kIMTable }; HClassTableGet(HInstruction* cls, Primitive::Type type, @@ -2678,26 +2738,33 @@ class HClassTableGet : public HExpression<1> { size_t index, uint32_t dex_pc) : HExpression(type, SideEffects::None(), dex_pc), - index_(index), - table_kind_(kind) { + index_(index) { + SetPackedField(kind); SetRawInputAt(0, cls); } bool CanBeMoved() const OVERRIDE { return true; } bool InstructionDataEquals(HInstruction* other) const OVERRIDE { return other->AsClassTableGet()->GetIndex() == index_ && - other->AsClassTableGet()->GetTableKind() == table_kind_; + other->AsClassTableGet()->GetPackedFields() == GetPackedFields(); } - TableKind GetTableKind() const { return table_kind_; } + TableKind GetTableKind() const { return GetPackedField(); } size_t GetIndex() const { return index_; } DECLARE_INSTRUCTION(ClassTableGet); private: + static constexpr size_t kFieldTableKind = kNumberOfExpressionPackedBits; + static constexpr size_t kFieldTableKindSize = + MinimumBitsToStore(static_cast(TableKind::kLast)); + static constexpr size_t kNumberOfClassTableGetPackedBits = kFieldTableKind + kFieldTableKindSize; + static_assert(kNumberOfClassTableGetPackedBits <= kMaxNumberOfPackedBits, + "Too many packed fields."); + using TableKindField = BitField; + // The index of the ArtMethod in the table. const size_t index_; - const TableKind table_kind_; DISALLOW_COPY_AND_ASSIGN(HClassTableGet); }; @@ -2864,6 +2931,7 @@ enum class ComparisonBias { kNoBias, // bias is not applicable (i.e. for long operation) kGtBias, // return 1 for NaN comparisons kLtBias, // return -1 for NaN comparisons + kLast = kLtBias }; std::ostream& operator<<(std::ostream& os, const ComparisonBias& rhs); @@ -2871,8 +2939,9 @@ std::ostream& operator<<(std::ostream& os, const ComparisonBias& rhs); class HCondition : public HBinaryOperation { public: HCondition(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc) - : HBinaryOperation(Primitive::kPrimBoolean, first, second, SideEffects::None(), dex_pc), - bias_(ComparisonBias::kNoBias) {} + : HBinaryOperation(Primitive::kPrimBoolean, first, second, SideEffects::None(), dex_pc) { + SetPackedField(ComparisonBias::kNoBias); + } // For code generation purposes, returns whether this instruction is just before // `instruction`, and disregard moves in between. @@ -2884,12 +2953,12 @@ class HCondition : public HBinaryOperation { virtual IfCondition GetOppositeCondition() const = 0; - bool IsGtBias() const { return bias_ == ComparisonBias::kGtBias; } - ComparisonBias GetBias() const { return bias_; } - void SetBias(ComparisonBias bias) { bias_ = bias; } + bool IsGtBias() const { return GetBias() == ComparisonBias::kGtBias; } + ComparisonBias GetBias() const { return GetPackedField(); } + void SetBias(ComparisonBias bias) { SetPackedField(bias); } bool InstructionDataEquals(HInstruction* other) const OVERRIDE { - return bias_ == other->AsCondition()->bias_; + return GetPackedFields() == other->AsCondition()->GetPackedFields(); } bool IsFPConditionTrueIfNaN() const { @@ -2905,6 +2974,16 @@ class HCondition : public HBinaryOperation { } protected: + // Needed if we merge a HCompare into a HCondition. + static constexpr size_t kFieldComparisonBias = kNumberOfExpressionPackedBits; + static constexpr size_t kFieldComparisonBiasSize = + MinimumBitsToStore(static_cast(ComparisonBias::kLast)); + static constexpr size_t kNumberOfConditionPackedBits = + kFieldComparisonBias + kFieldComparisonBiasSize; + static_assert(kNumberOfConditionPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); + using ComparisonBiasField = + BitField; + template int32_t Compare(T x, T y) const { return x > y ? 1 : (x < y ? -1 : 0); } @@ -2922,9 +3001,6 @@ class HCondition : public HBinaryOperation { } private: - // Needed if we merge a HCompare into a HCondition. - ComparisonBias bias_; - DISALLOW_COPY_AND_ASSIGN(HCondition); }; @@ -3337,8 +3413,8 @@ class HCompare : public HBinaryOperation { first, second, SideEffectsForArchRuntimeCalls(type), - dex_pc), - bias_(bias) { + dex_pc) { + SetPackedField(bias); DCHECK_EQ(type, first->GetType()); DCHECK_EQ(type, second->GetType()); } @@ -3373,16 +3449,16 @@ class HCompare : public HBinaryOperation { } bool InstructionDataEquals(HInstruction* other) const OVERRIDE { - return bias_ == other->AsCompare()->bias_; + return GetPackedFields() == other->AsCompare()->GetPackedFields(); } - ComparisonBias GetBias() const { return bias_; } + ComparisonBias GetBias() const { return GetPackedField(); } // Does this compare instruction have a "gt bias" (vs an "lt bias")? - // Only meaninfgul for floating-point comparisons. + // Only meaningful for floating-point comparisons. bool IsGtBias() const { DCHECK(Primitive::IsFloatingPointType(InputAt(0)->GetType())) << InputAt(0)->GetType(); - return bias_ == ComparisonBias::kGtBias; + return GetBias() == ComparisonBias::kGtBias; } static SideEffects SideEffectsForArchRuntimeCalls(Primitive::Type type) { @@ -3393,6 +3469,15 @@ class HCompare : public HBinaryOperation { DECLARE_INSTRUCTION(Compare); protected: + static constexpr size_t kFieldComparisonBias = kNumberOfExpressionPackedBits; + static constexpr size_t kFieldComparisonBiasSize = + MinimumBitsToStore(static_cast(ComparisonBias::kLast)); + static constexpr size_t kNumberOfComparePackedBits = + kFieldComparisonBias + kFieldComparisonBiasSize; + static_assert(kNumberOfComparePackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); + using ComparisonBiasField = + BitField; + // Return an integer constant containing the result of a comparison evaluated at compile time. HIntConstant* MakeConstantComparison(int32_t value, uint32_t dex_pc) const { DCHECK(value == -1 || value == 0 || value == 1) << value; @@ -3400,8 +3485,6 @@ class HCompare : public HBinaryOperation { } private: - const ComparisonBias bias_; - DISALLOW_COPY_AND_ASSIGN(HCompare); }; @@ -3469,9 +3552,9 @@ class HNewInstance : public HExpression<2> { : HExpression(Primitive::kPrimNot, SideEffects::CanTriggerGC(), dex_pc), type_index_(type_index), dex_file_(dex_file), - can_throw_(can_throw), - finalizable_(finalizable), entrypoint_(entrypoint) { + SetPackedFlag(can_throw); + SetPackedFlag(finalizable); SetRawInputAt(0, cls); SetRawInputAt(1, current_method); } @@ -3485,9 +3568,9 @@ class HNewInstance : public HExpression<2> { // It may throw when called on type that's not instantiable/accessible. // It can throw OOME. // TODO: distinguish between the two cases so we can for example allow allocation elimination. - bool CanThrow() const OVERRIDE { return can_throw_ || true; } + bool CanThrow() const OVERRIDE { return GetPackedFlag() || true; } - bool IsFinalizable() const { return finalizable_; } + bool IsFinalizable() const { return GetPackedFlag(); } bool CanBeNull() const OVERRIDE { return false; } @@ -3502,10 +3585,14 @@ class HNewInstance : public HExpression<2> { DECLARE_INSTRUCTION(NewInstance); private: + static constexpr size_t kFlagCanThrow = kNumberOfExpressionPackedBits; + static constexpr size_t kFlagFinalizable = kFlagCanThrow + 1; + static constexpr size_t kNumberOfNewInstancePackedBits = kFlagFinalizable + 1; + static_assert(kNumberOfNewInstancePackedBits <= kMaxNumberOfPackedBits, + "Too many packed fields."); + const uint16_t type_index_; const DexFile& dex_file_; - const bool can_throw_; - const bool finalizable_; QuickEntrypointEnum entrypoint_; DISALLOW_COPY_AND_ASSIGN(HNewInstance); @@ -3555,12 +3642,14 @@ class HInvoke : public HInstruction { // inputs at the end of their list of inputs. uint32_t GetNumberOfArguments() const { return number_of_arguments_; } - Primitive::Type GetType() const OVERRIDE { return return_type_; } + Primitive::Type GetType() const OVERRIDE { return GetPackedField(); } uint32_t GetDexMethodIndex() const { return dex_method_index_; } const DexFile& GetDexFile() const { return GetEnvironment()->GetDexFile(); } - InvokeType GetOriginalInvokeType() const { return original_invoke_type_; } + InvokeType GetOriginalInvokeType() const { + return GetPackedField(); + } Intrinsics GetIntrinsic() const { return intrinsic_; @@ -3575,7 +3664,7 @@ class HInvoke : public HInstruction { return GetEnvironment()->IsFromInlinedInvoke(); } - bool CanThrow() const OVERRIDE { return can_throw_; } + bool CanThrow() const OVERRIDE { return GetPackedFlag(); } bool CanBeMoved() const OVERRIDE { return IsIntrinsic(); } @@ -3596,6 +3685,20 @@ class HInvoke : public HInstruction { DECLARE_ABSTRACT_INSTRUCTION(Invoke); protected: + static constexpr size_t kFieldOriginalInvokeType = kNumberOfGenericPackedBits; + static constexpr size_t kFieldOriginalInvokeTypeSize = + MinimumBitsToStore(static_cast(kMaxInvokeType)); + static constexpr size_t kFieldReturnType = + kFieldOriginalInvokeType + kFieldOriginalInvokeTypeSize; + static constexpr size_t kFieldReturnTypeSize = + MinimumBitsToStore(static_cast(Primitive::kPrimLast)); + static constexpr size_t kFlagCanThrow = kFieldReturnType + kFieldReturnTypeSize; + static constexpr size_t kNumberOfInvokePackedBits = kFlagCanThrow + 1; + static_assert(kNumberOfInvokePackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); + using OriginalInvokeTypeField = + BitField; + using ReturnTypeField = BitField; + HInvoke(ArenaAllocator* arena, uint32_t number_of_arguments, uint32_t number_of_other_inputs, @@ -3608,12 +3711,12 @@ class HInvoke : public HInstruction { number_of_arguments_(number_of_arguments), inputs_(number_of_arguments + number_of_other_inputs, arena->Adapter(kArenaAllocInvokeInputs)), - return_type_(return_type), dex_method_index_(dex_method_index), - original_invoke_type_(original_invoke_type), - can_throw_(true), intrinsic_(Intrinsics::kNone), intrinsic_optimizations_(0) { + SetPackedField(return_type); + SetPackedField(original_invoke_type); + SetPackedFlag(true); } const HUserRecord InputRecordAt(size_t index) const OVERRIDE { @@ -3624,14 +3727,11 @@ class HInvoke : public HInstruction { inputs_[index] = input; } - void SetCanThrow(bool can_throw) { can_throw_ = can_throw; } + void SetCanThrow(bool can_throw) { SetPackedFlag(can_throw); } uint32_t number_of_arguments_; ArenaVector> inputs_; - const Primitive::Type return_type_; const uint32_t dex_method_index_; - const InvokeType original_invoke_type_; - bool can_throw_; Intrinsics intrinsic_; // A magic word holding optimizations for intrinsics. See intrinsics.h. @@ -3672,6 +3772,7 @@ class HInvokeStaticOrDirect : public HInvoke { kNone, // Class already initialized. kExplicit, // Static call having explicit clinit check as last input. kImplicit, // Static call implicitly requiring a clinit check. + kLast = kImplicit }; // Determines how to load the target ArtMethod*. @@ -3692,7 +3793,7 @@ class HInvokeStaticOrDirect : public HInvoke { // the image relocatable or not. kDirectAddressWithFixup, - // Load from resoved methods array in the dex cache using a PC-relative load. + // Load from resolved methods array in the dex cache using a PC-relative load. // Used when we need to use the dex cache, for example for invoke-static that // may cause class initialization (the entry may point to a resolution method), // and we know that we can access the dex cache arrays using a PC-relative load. @@ -3764,10 +3865,11 @@ class HInvokeStaticOrDirect : public HInvoke { dex_pc, method_index, original_invoke_type), - optimized_invoke_type_(optimized_invoke_type), - clinit_check_requirement_(clinit_check_requirement), target_method_(target_method), - dispatch_info_(dispatch_info) { } + dispatch_info_(dispatch_info) { + SetPackedField(optimized_invoke_type); + SetPackedField(clinit_check_requirement); + } void SetDispatchInfo(const DispatchInfo& dispatch_info) { bool had_current_method_input = HasCurrentMethodInput(); @@ -3799,7 +3901,7 @@ class HInvokeStaticOrDirect : public HInvoke { } bool CanBeNull() const OVERRIDE { - return return_type_ == Primitive::kPrimNot && !IsStringInit(); + return GetPackedField() == Primitive::kPrimNot && !IsStringInit(); } // Get the index of the special input, if any. @@ -3810,9 +3912,12 @@ class HInvokeStaticOrDirect : public HInvoke { uint32_t GetSpecialInputIndex() const { return GetNumberOfArguments(); } bool HasSpecialInput() const { return GetNumberOfArguments() != InputCount(); } - InvokeType GetOptimizedInvokeType() const { return optimized_invoke_type_; } + InvokeType GetOptimizedInvokeType() const { + return GetPackedField(); + } + void SetOptimizedInvokeType(InvokeType invoke_type) { - optimized_invoke_type_ = invoke_type; + SetPackedField(invoke_type); } MethodLoadKind GetMethodLoadKind() const { return dispatch_info_.method_load_kind; } @@ -3859,7 +3964,9 @@ class HInvokeStaticOrDirect : public HInvoke { return dispatch_info_.direct_code_ptr; } - ClinitCheckRequirement GetClinitCheckRequirement() const { return clinit_check_requirement_; } + ClinitCheckRequirement GetClinitCheckRequirement() const { + return GetPackedField(); + } // Is this instruction a call to a static method? bool IsStatic() const { @@ -3877,7 +3984,7 @@ class HInvokeStaticOrDirect : public HInvoke { DCHECK(last_input->IsLoadClass() || last_input->IsClinitCheck()) << last_input->DebugName(); RemoveAsUserOfInput(last_input_index); inputs_.pop_back(); - clinit_check_requirement_ = new_requirement; + SetPackedField(new_requirement); DCHECK(!IsStaticWithExplicitClinitCheck()); } @@ -3893,13 +4000,13 @@ class HInvokeStaticOrDirect : public HInvoke { // Is this a call to a static method whose declaring class has an // explicit initialization check in the graph? bool IsStaticWithExplicitClinitCheck() const { - return IsStatic() && (clinit_check_requirement_ == ClinitCheckRequirement::kExplicit); + return IsStatic() && (GetClinitCheckRequirement() == ClinitCheckRequirement::kExplicit); } // Is this a call to a static method whose declaring class has an // implicit intialization check requirement? bool IsStaticWithImplicitClinitCheck() const { - return IsStatic() && (clinit_check_requirement_ == ClinitCheckRequirement::kImplicit); + return IsStatic() && (GetClinitCheckRequirement() == ClinitCheckRequirement::kImplicit); } // Does this method load kind need the current method as an input? @@ -3928,8 +4035,23 @@ class HInvokeStaticOrDirect : public HInvoke { void RemoveInputAt(size_t index); private: - InvokeType optimized_invoke_type_; - ClinitCheckRequirement clinit_check_requirement_; + static constexpr size_t kFieldOptimizedInvokeType = kNumberOfInvokePackedBits; + static constexpr size_t kFieldOptimizedInvokeTypeSize = + MinimumBitsToStore(static_cast(kMaxInvokeType)); + static constexpr size_t kFieldClinitCheckRequirement = + kFieldOptimizedInvokeType + kFieldOptimizedInvokeTypeSize; + static constexpr size_t kFieldClinitCheckRequirementSize = + MinimumBitsToStore(static_cast(ClinitCheckRequirement::kLast)); + static constexpr size_t kNumberOfInvokeStaticOrDirectPackedBits = + kFieldClinitCheckRequirement + kFieldClinitCheckRequirementSize; + static_assert(kNumberOfInvokeStaticOrDirectPackedBits <= kMaxNumberOfPackedBits, + "Too many packed fields."); + using OptimizedInvokeTypeField = + BitField; + using ClinitCheckRequirementField = BitField; + // The target method may refer to different dex file or method index than the original // invoke. This happens for sharpened calls and for calls where a method was redeclared // in derived class to increase visibility. @@ -4595,32 +4717,35 @@ class HParameterValue : public HExpression<0> { : HExpression(parameter_type, SideEffects::None(), kNoDexPc), dex_file_(dex_file), type_index_(type_index), - index_(index), - is_this_(is_this), - can_be_null_(!is_this) {} + index_(index) { + SetPackedFlag(is_this); + SetPackedFlag(!is_this); + } const DexFile& GetDexFile() const { return dex_file_; } uint16_t GetTypeIndex() const { return type_index_; } uint8_t GetIndex() const { return index_; } - bool IsThis() const { return is_this_; } + bool IsThis() const { return GetPackedFlag(); } - bool CanBeNull() const OVERRIDE { return can_be_null_; } - void SetCanBeNull(bool can_be_null) { can_be_null_ = can_be_null; } + bool CanBeNull() const OVERRIDE { return GetPackedFlag(); } + void SetCanBeNull(bool can_be_null) { SetPackedFlag(can_be_null); } DECLARE_INSTRUCTION(ParameterValue); private: + // Whether or not the parameter value corresponds to 'this' argument. + static constexpr size_t kFlagIsThis = kNumberOfExpressionPackedBits; + static constexpr size_t kFlagCanBeNull = kFlagIsThis + 1; + static constexpr size_t kNumberOfParameterValuePackedBits = kFlagCanBeNull + 1; + static_assert(kNumberOfParameterValuePackedBits <= kMaxNumberOfPackedBits, + "Too many packed fields."); + const DexFile& dex_file_; const uint16_t type_index_; // The index of this parameter in the parameters list. Must be less // than HGraph::number_of_in_vregs_. const uint8_t index_; - // Whether or not the parameter value corresponds to 'this' argument. - const bool is_this_; - - bool can_be_null_; - DISALLOW_COPY_AND_ASSIGN(HParameterValue); }; @@ -4745,14 +4870,14 @@ class HPhi : public HInstruction { uint32_t dex_pc = kNoDexPc) : HInstruction(SideEffects::None(), dex_pc), inputs_(number_of_inputs, arena->Adapter(kArenaAllocPhiInputs)), - reg_number_(reg_number), - type_(ToPhiType(type)), - // Phis are constructed live and marked dead if conflicting or unused. - // Individual steps of SsaBuilder should assume that if a phi has been - // marked dead, it can be ignored and will be removed by SsaPhiElimination. - is_live_(true), - can_be_null_(true) { - DCHECK_NE(type_, Primitive::kPrimVoid); + reg_number_(reg_number) { + SetPackedField(ToPhiType(type)); + DCHECK_NE(GetType(), Primitive::kPrimVoid); + // Phis are constructed live and marked dead if conflicting or unused. + // Individual steps of SsaBuilder should assume that if a phi has been + // marked dead, it can be ignored and will be removed by SsaPhiElimination. + SetPackedFlag(true); + SetPackedFlag(true); } // Returns a type equivalent to the given `type`, but that a `HPhi` can hold. @@ -4775,27 +4900,27 @@ class HPhi : public HInstruction { void AddInput(HInstruction* input); void RemoveInputAt(size_t index); - Primitive::Type GetType() const OVERRIDE { return type_; } + Primitive::Type GetType() const OVERRIDE { return GetPackedField(); } void SetType(Primitive::Type new_type) { // Make sure that only valid type changes occur. The following are allowed: // (1) int -> float/ref (primitive type propagation), // (2) long -> double (primitive type propagation). - DCHECK(type_ == new_type || - (type_ == Primitive::kPrimInt && new_type == Primitive::kPrimFloat) || - (type_ == Primitive::kPrimInt && new_type == Primitive::kPrimNot) || - (type_ == Primitive::kPrimLong && new_type == Primitive::kPrimDouble)); - type_ = new_type; + DCHECK(GetType() == new_type || + (GetType() == Primitive::kPrimInt && new_type == Primitive::kPrimFloat) || + (GetType() == Primitive::kPrimInt && new_type == Primitive::kPrimNot) || + (GetType() == Primitive::kPrimLong && new_type == Primitive::kPrimDouble)); + SetPackedField(new_type); } - bool CanBeNull() const OVERRIDE { return can_be_null_; } - void SetCanBeNull(bool can_be_null) { can_be_null_ = can_be_null; } + bool CanBeNull() const OVERRIDE { return GetPackedFlag(); } + void SetCanBeNull(bool can_be_null) { SetPackedFlag(can_be_null); } uint32_t GetRegNumber() const { return reg_number_; } - void SetDead() { is_live_ = false; } - void SetLive() { is_live_ = true; } - bool IsDead() const { return !is_live_; } - bool IsLive() const { return is_live_; } + void SetDead() { SetPackedFlag(false); } + void SetLive() { SetPackedFlag(true); } + bool IsDead() const { return !IsLive(); } + bool IsLive() const { return GetPackedFlag(); } bool IsVRegEquivalentOf(HInstruction* other) const { return other != nullptr @@ -4830,11 +4955,17 @@ class HPhi : public HInstruction { } private: + static constexpr size_t kFieldType = HInstruction::kNumberOfGenericPackedBits; + static constexpr size_t kFieldTypeSize = + MinimumBitsToStore(static_cast(Primitive::kPrimLast)); + static constexpr size_t kFlagIsLive = kFieldType + kFieldTypeSize; + static constexpr size_t kFlagCanBeNull = kFlagIsLive + 1; + static constexpr size_t kNumberOfPhiPackedBits = kFlagCanBeNull + 1; + static_assert(kNumberOfPhiPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); + using TypeField = BitField; + ArenaVector > inputs_; const uint32_t reg_number_; - Primitive::Type type_; - bool is_live_; - bool can_be_null_; DISALLOW_COPY_AND_ASSIGN(HPhi); }; @@ -4973,8 +5104,8 @@ class HInstanceFieldSet : public HTemplateInstruction<2> { field_idx, declaring_class_def_index, dex_file, - dex_cache), - value_can_be_null_(true) { + dex_cache) { + SetPackedFlag(true); SetRawInputAt(0, object); SetRawInputAt(1, value); } @@ -4988,14 +5119,18 @@ class HInstanceFieldSet : public HTemplateInstruction<2> { Primitive::Type GetFieldType() const { return field_info_.GetFieldType(); } bool IsVolatile() const { return field_info_.IsVolatile(); } HInstruction* GetValue() const { return InputAt(1); } - bool GetValueCanBeNull() const { return value_can_be_null_; } - void ClearValueCanBeNull() { value_can_be_null_ = false; } + bool GetValueCanBeNull() const { return GetPackedFlag(); } + void ClearValueCanBeNull() { SetPackedFlag(false); } DECLARE_INSTRUCTION(InstanceFieldSet); private: + static constexpr size_t kFlagValueCanBeNull = kNumberOfGenericPackedBits; + static constexpr size_t kNumberOfInstanceFieldSetPackedBits = kFlagValueCanBeNull + 1; + static_assert(kNumberOfInstanceFieldSetPackedBits <= kMaxNumberOfPackedBits, + "Too many packed fields."); + const FieldInfo field_info_; - bool value_can_be_null_; DISALLOW_COPY_AND_ASSIGN(HInstanceFieldSet); }; @@ -5064,11 +5199,11 @@ class HArraySet : public HTemplateInstruction<3> { SideEffects::ArrayWriteOfType(expected_component_type).Union( SideEffectsForArchRuntimeCalls(value->GetType())).Union( additional_side_effects), - dex_pc), - expected_component_type_(expected_component_type), - needs_type_check_(value->GetType() == Primitive::kPrimNot), - value_can_be_null_(true), - static_type_of_array_is_object_array_(false) { + dex_pc) { + SetPackedField(expected_component_type); + SetPackedFlag(value->GetType() == Primitive::kPrimNot); + SetPackedFlag(true); + SetPackedFlag(false); SetRawInputAt(0, array); SetRawInputAt(1, index); SetRawInputAt(2, value); @@ -5076,11 +5211,11 @@ class HArraySet : public HTemplateInstruction<3> { bool NeedsEnvironment() const OVERRIDE { // We call a runtime method to throw ArrayStoreException. - return needs_type_check_; + return NeedsTypeCheck(); } // Can throw ArrayStoreException. - bool CanThrow() const OVERRIDE { return needs_type_check_; } + bool CanThrow() const OVERRIDE { return NeedsTypeCheck(); } bool CanDoImplicitNullCheckOn(HInstruction* obj ATTRIBUTE_UNUSED) const OVERRIDE { // TODO: Same as for ArrayGet. @@ -5088,20 +5223,22 @@ class HArraySet : public HTemplateInstruction<3> { } void ClearNeedsTypeCheck() { - needs_type_check_ = false; + SetPackedFlag(false); } void ClearValueCanBeNull() { - value_can_be_null_ = false; + SetPackedFlag(false); } void SetStaticTypeOfArrayIsObjectArray() { - static_type_of_array_is_object_array_ = true; + SetPackedFlag(true); } - bool GetValueCanBeNull() const { return value_can_be_null_; } - bool NeedsTypeCheck() const { return needs_type_check_; } - bool StaticTypeOfArrayIsObjectArray() const { return static_type_of_array_is_object_array_; } + bool GetValueCanBeNull() const { return GetPackedFlag(); } + bool NeedsTypeCheck() const { return GetPackedFlag(); } + bool StaticTypeOfArrayIsObjectArray() const { + return GetPackedFlag(); + } HInstruction* GetArray() const { return InputAt(0); } HInstruction* GetIndex() const { return InputAt(1); } @@ -5115,11 +5252,11 @@ class HArraySet : public HTemplateInstruction<3> { Primitive::Type value_type = GetValue()->GetType(); return ((value_type == Primitive::kPrimFloat) || (value_type == Primitive::kPrimDouble)) ? value_type - : expected_component_type_; + : GetRawExpectedComponentType(); } Primitive::Type GetRawExpectedComponentType() const { - return expected_component_type_; + return GetPackedField(); } static SideEffects SideEffectsForArchRuntimeCalls(Primitive::Type value_type) { @@ -5129,12 +5266,20 @@ class HArraySet : public HTemplateInstruction<3> { DECLARE_INSTRUCTION(ArraySet); private: - const Primitive::Type expected_component_type_; - bool needs_type_check_; - bool value_can_be_null_; + static constexpr size_t kFieldExpectedComponentType = kNumberOfGenericPackedBits; + static constexpr size_t kFieldExpectedComponentTypeSize = + MinimumBitsToStore(static_cast(Primitive::kPrimLast)); + static constexpr size_t kFlagNeedsTypeCheck = + kFieldExpectedComponentType + kFieldExpectedComponentTypeSize; + static constexpr size_t kFlagValueCanBeNull = kFlagNeedsTypeCheck + 1; // Cached information for the reference_type_info_ so that codegen // does not need to inspect the static type. - bool static_type_of_array_is_object_array_; + static constexpr size_t kFlagStaticTypeOfArrayIsObjectArray = kFlagValueCanBeNull + 1; + static constexpr size_t kNumberOfArraySetPackedBits = + kFlagStaticTypeOfArrayIsObjectArray + 1; + static_assert(kNumberOfArraySetPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); + using ExpectedComponentTypeField = + BitField; DISALLOW_COPY_AND_ASSIGN(HArraySet); }; @@ -5244,14 +5389,15 @@ class HLoadClass : public HExpression<1> { : HExpression(Primitive::kPrimNot, SideEffectsForArchRuntimeCalls(), dex_pc), type_index_(type_index), dex_file_(dex_file), - is_referrers_class_(is_referrers_class), - generate_clinit_check_(false), - needs_access_check_(needs_access_check), - is_in_dex_cache_(is_in_dex_cache), loaded_class_rti_(ReferenceTypeInfo::CreateInvalid()) { // Referrers class should not need access check. We never inline unverified // methods so we can't possibly end up in this situation. - DCHECK(!is_referrers_class_ || !needs_access_check_); + DCHECK(!is_referrers_class || !needs_access_check); + + SetPackedFlag(is_referrers_class); + SetPackedFlag(needs_access_check); + SetPackedFlag(is_in_dex_cache); + SetPackedFlag(false); SetRawInputAt(0, current_method); } @@ -5262,39 +5408,31 @@ class HLoadClass : public HExpression<1> { // Whether or not we need to generate the clinit check is processed in // prepare_for_register_allocator based on existing HInvokes and HClinitChecks. return other->AsLoadClass()->type_index_ == type_index_ && - other->AsLoadClass()->needs_access_check_ == needs_access_check_; + other->AsLoadClass()->GetPackedFields() == GetPackedFields(); } size_t ComputeHashCode() const OVERRIDE { return type_index_; } uint16_t GetTypeIndex() const { return type_index_; } - bool IsReferrersClass() const { return is_referrers_class_; } bool CanBeNull() const OVERRIDE { return false; } bool NeedsEnvironment() const OVERRIDE { return CanCallRuntime(); } - bool MustGenerateClinitCheck() const { - return generate_clinit_check_; - } - void SetMustGenerateClinitCheck(bool generate_clinit_check) { // The entrypoint the code generator is going to call does not do // clinit of the class. DCHECK(!NeedsAccessCheck()); - generate_clinit_check_ = generate_clinit_check; + SetPackedFlag(generate_clinit_check); } bool CanCallRuntime() const { return MustGenerateClinitCheck() || - (!is_referrers_class_ && !is_in_dex_cache_) || - needs_access_check_; + (!IsReferrersClass() && !IsInDexCache()) || + NeedsAccessCheck(); } - bool NeedsAccessCheck() const { - return needs_access_check_; - } bool CanThrow() const OVERRIDE { return CanCallRuntime(); @@ -5312,25 +5450,31 @@ class HLoadClass : public HExpression<1> { const DexFile& GetDexFile() { return dex_file_; } - bool NeedsDexCacheOfDeclaringClass() const OVERRIDE { return !is_referrers_class_; } + bool NeedsDexCacheOfDeclaringClass() const OVERRIDE { return !IsReferrersClass(); } static SideEffects SideEffectsForArchRuntimeCalls() { return SideEffects::CanTriggerGC(); } - bool IsInDexCache() const { return is_in_dex_cache_; } + bool IsReferrersClass() const { return GetPackedFlag(); } + bool NeedsAccessCheck() const { return GetPackedFlag(); } + bool IsInDexCache() const { return GetPackedFlag(); } + bool MustGenerateClinitCheck() const { return GetPackedFlag(); } DECLARE_INSTRUCTION(LoadClass); private: - const uint16_t type_index_; - const DexFile& dex_file_; - const bool is_referrers_class_; + static constexpr size_t kFlagIsReferrersClass = kNumberOfExpressionPackedBits; + static constexpr size_t kFlagNeedsAccessCheck = kFlagIsReferrersClass + 1; + static constexpr size_t kFlagIsInDexCache = kFlagNeedsAccessCheck + 1; // Whether this instruction must generate the initialization check. // Used for code generation. - bool generate_clinit_check_; - const bool needs_access_check_; - const bool is_in_dex_cache_; + static constexpr size_t kFlagGenerateClInitCheck = kFlagIsInDexCache + 1; + static constexpr size_t kNumberOfLoadClassPackedBits = kFlagGenerateClInitCheck + 1; + static_assert(kNumberOfLoadClassPackedBits < kMaxNumberOfPackedBits, "Too many packed fields."); + + const uint16_t type_index_; + const DexFile& dex_file_; ReferenceTypeInfo loaded_class_rti_; @@ -5344,8 +5488,8 @@ class HLoadString : public HExpression<1> { uint32_t dex_pc, bool is_in_dex_cache) : HExpression(Primitive::kPrimNot, SideEffectsForArchRuntimeCalls(), dex_pc), - string_index_(string_index), - is_in_dex_cache_(is_in_dex_cache) { + string_index_(string_index) { + SetPackedFlag(is_in_dex_cache); SetRawInputAt(0, current_method); } @@ -5364,18 +5508,22 @@ class HLoadString : public HExpression<1> { bool NeedsDexCacheOfDeclaringClass() const OVERRIDE { return true; } bool CanBeNull() const OVERRIDE { return false; } - bool IsInDexCache() const { return is_in_dex_cache_; } bool CanThrow() const OVERRIDE { return !IsInDexCache(); } static SideEffects SideEffectsForArchRuntimeCalls() { return SideEffects::CanTriggerGC(); } + bool IsInDexCache() const { return GetPackedFlag(); } + DECLARE_INSTRUCTION(LoadString); private: + static constexpr size_t kFlagIsInDexCache = kNumberOfExpressionPackedBits; + static constexpr size_t kNumberOfLoadStringPackedBits = kFlagIsInDexCache + 1; + static_assert(kNumberOfLoadStringPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); + const uint32_t string_index_; - const bool is_in_dex_cache_; DISALLOW_COPY_AND_ASSIGN(HLoadString); }; @@ -5482,8 +5630,8 @@ class HStaticFieldSet : public HTemplateInstruction<2> { field_idx, declaring_class_def_index, dex_file, - dex_cache), - value_can_be_null_(true) { + dex_cache) { + SetPackedFlag(true); SetRawInputAt(0, cls); SetRawInputAt(1, value); } @@ -5494,14 +5642,18 @@ class HStaticFieldSet : public HTemplateInstruction<2> { bool IsVolatile() const { return field_info_.IsVolatile(); } HInstruction* GetValue() const { return InputAt(1); } - bool GetValueCanBeNull() const { return value_can_be_null_; } - void ClearValueCanBeNull() { value_can_be_null_ = false; } + bool GetValueCanBeNull() const { return GetPackedFlag(); } + void ClearValueCanBeNull() { SetPackedFlag(false); } DECLARE_INSTRUCTION(StaticFieldSet); private: + static constexpr size_t kFlagValueCanBeNull = kNumberOfGenericPackedBits; + static constexpr size_t kNumberOfStaticFieldSetPackedBits = kFlagValueCanBeNull + 1; + static_assert(kNumberOfStaticFieldSetPackedBits <= kMaxNumberOfPackedBits, + "Too many packed fields."); + const FieldInfo field_info_; - bool value_can_be_null_; DISALLOW_COPY_AND_ASSIGN(HStaticFieldSet); }; @@ -5539,8 +5691,8 @@ class HUnresolvedInstanceFieldSet : public HTemplateInstruction<2> { uint32_t field_index, uint32_t dex_pc) : HTemplateInstruction(SideEffects::AllExceptGCDependency(), dex_pc), - field_type_(field_type), field_index_(field_index) { + SetPackedField(field_type); DCHECK_EQ(field_type, value->GetType()); SetRawInputAt(0, obj); SetRawInputAt(1, value); @@ -5549,13 +5701,21 @@ class HUnresolvedInstanceFieldSet : public HTemplateInstruction<2> { bool NeedsEnvironment() const OVERRIDE { return true; } bool CanThrow() const OVERRIDE { return true; } - Primitive::Type GetFieldType() const { return field_type_; } + Primitive::Type GetFieldType() const { return GetPackedField(); } uint32_t GetFieldIndex() const { return field_index_; } DECLARE_INSTRUCTION(UnresolvedInstanceFieldSet); private: - const Primitive::Type field_type_; + static constexpr size_t kFieldFieldType = HInstruction::kNumberOfGenericPackedBits; + static constexpr size_t kFieldFieldTypeSize = + MinimumBitsToStore(static_cast(Primitive::kPrimLast)); + static constexpr size_t kNumberOfUnresolvedStaticFieldSetPackedBits = + kFieldFieldType + kFieldFieldTypeSize; + static_assert(kNumberOfUnresolvedStaticFieldSetPackedBits <= HInstruction::kMaxNumberOfPackedBits, + "Too many packed fields."); + using FieldTypeField = BitField; + const uint32_t field_index_; DISALLOW_COPY_AND_ASSIGN(HUnresolvedInstanceFieldSet); @@ -5591,8 +5751,8 @@ class HUnresolvedStaticFieldSet : public HTemplateInstruction<1> { uint32_t field_index, uint32_t dex_pc) : HTemplateInstruction(SideEffects::AllExceptGCDependency(), dex_pc), - field_type_(field_type), field_index_(field_index) { + SetPackedField(field_type); DCHECK_EQ(field_type, value->GetType()); SetRawInputAt(0, value); } @@ -5600,13 +5760,21 @@ class HUnresolvedStaticFieldSet : public HTemplateInstruction<1> { bool NeedsEnvironment() const OVERRIDE { return true; } bool CanThrow() const OVERRIDE { return true; } - Primitive::Type GetFieldType() const { return field_type_; } + Primitive::Type GetFieldType() const { return GetPackedField(); } uint32_t GetFieldIndex() const { return field_index_; } DECLARE_INSTRUCTION(UnresolvedStaticFieldSet); private: - const Primitive::Type field_type_; + static constexpr size_t kFieldFieldType = HInstruction::kNumberOfGenericPackedBits; + static constexpr size_t kFieldFieldTypeSize = + MinimumBitsToStore(static_cast(Primitive::kPrimLast)); + static constexpr size_t kNumberOfUnresolvedStaticFieldSetPackedBits = + kFieldFieldType + kFieldFieldTypeSize; + static_assert(kNumberOfUnresolvedStaticFieldSetPackedBits <= HInstruction::kMaxNumberOfPackedBits, + "Too many packed fields."); + using FieldTypeField = BitField; + const uint32_t field_index_; DISALLOW_COPY_AND_ASSIGN(HUnresolvedStaticFieldSet); @@ -5670,7 +5838,8 @@ enum class TypeCheckKind { kAbstractClassCheck, // Can just walk the super class chain, starting one up. kInterfaceCheck, // No optimization yet when checking against an interface. kArrayObjectCheck, // Can just check if the array is not primitive. - kArrayCheck // No optimization yet when checking against a generic array. + kArrayCheck, // No optimization yet when checking against a generic array. + kLast = kArrayCheck }; std::ostream& operator<<(std::ostream& os, TypeCheckKind rhs); @@ -5683,9 +5852,9 @@ class HInstanceOf : public HExpression<2> { uint32_t dex_pc) : HExpression(Primitive::kPrimBoolean, SideEffectsForArchRuntimeCalls(check_kind), - dex_pc), - check_kind_(check_kind), - must_do_null_check_(true) { + dex_pc) { + SetPackedField(check_kind); + SetPackedFlag(true); SetRawInputAt(0, object); SetRawInputAt(1, constant); } @@ -5697,16 +5866,14 @@ class HInstanceOf : public HExpression<2> { } bool NeedsEnvironment() const OVERRIDE { - return CanCallRuntime(check_kind_); + return CanCallRuntime(GetTypeCheckKind()); } - bool IsExactCheck() const { return check_kind_ == TypeCheckKind::kExactCheck; } - - TypeCheckKind GetTypeCheckKind() const { return check_kind_; } - // Used only in code generation. - bool MustDoNullCheck() const { return must_do_null_check_; } - void ClearMustDoNullCheck() { must_do_null_check_ = false; } + bool MustDoNullCheck() const { return GetPackedFlag(); } + void ClearMustDoNullCheck() { SetPackedFlag(false); } + TypeCheckKind GetTypeCheckKind() const { return GetPackedField(); } + bool IsExactCheck() const { return GetTypeCheckKind() == TypeCheckKind::kExactCheck; } static bool CanCallRuntime(TypeCheckKind check_kind) { // Mips currently does runtime calls for any other checks. @@ -5720,8 +5887,13 @@ class HInstanceOf : public HExpression<2> { DECLARE_INSTRUCTION(InstanceOf); private: - const TypeCheckKind check_kind_; - bool must_do_null_check_; + static constexpr size_t kFieldTypeCheckKind = kNumberOfExpressionPackedBits; + static constexpr size_t kFieldTypeCheckKindSize = + MinimumBitsToStore(static_cast(TypeCheckKind::kLast)); + static constexpr size_t kFlagMustDoNullCheck = kFieldTypeCheckKind + kFieldTypeCheckKindSize; + static constexpr size_t kNumberOfInstanceOfPackedBits = kFlagMustDoNullCheck + 1; + static_assert(kNumberOfInstanceOfPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); + using TypeCheckKindField = BitField; DISALLOW_COPY_AND_ASSIGN(HInstanceOf); }; @@ -5730,28 +5902,35 @@ class HBoundType : public HExpression<1> { public: HBoundType(HInstruction* input, uint32_t dex_pc = kNoDexPc) : HExpression(Primitive::kPrimNot, SideEffects::None(), dex_pc), - upper_bound_(ReferenceTypeInfo::CreateInvalid()), - upper_can_be_null_(true), - can_be_null_(true) { + upper_bound_(ReferenceTypeInfo::CreateInvalid()) { + SetPackedFlag(true); + SetPackedFlag(true); DCHECK_EQ(input->GetType(), Primitive::kPrimNot); SetRawInputAt(0, input); } // {Get,Set}Upper* should only be used in reference type propagation. const ReferenceTypeInfo& GetUpperBound() const { return upper_bound_; } - bool GetUpperCanBeNull() const { return upper_can_be_null_; } + bool GetUpperCanBeNull() const { return GetPackedFlag(); } void SetUpperBound(const ReferenceTypeInfo& upper_bound, bool can_be_null); void SetCanBeNull(bool can_be_null) { - DCHECK(upper_can_be_null_ || !can_be_null); - can_be_null_ = can_be_null; + DCHECK(GetUpperCanBeNull() || !can_be_null); + SetPackedFlag(can_be_null); } - bool CanBeNull() const OVERRIDE { return can_be_null_; } + bool CanBeNull() const OVERRIDE { return GetPackedFlag(); } DECLARE_INSTRUCTION(BoundType); private: + // Represents the top constraint that can_be_null_ cannot exceed (i.e. if this + // is false then CanBeNull() cannot be true). + static constexpr size_t kFlagUpperCanBeNull = kNumberOfExpressionPackedBits; + static constexpr size_t kFlagCanBeNull = kFlagUpperCanBeNull + 1; + static constexpr size_t kNumberOfBoundTypePackedBits = kFlagCanBeNull + 1; + static_assert(kNumberOfBoundTypePackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); + // Encodes the most upper class that this instruction can have. In other words // it is always the case that GetUpperBound().IsSupertypeOf(GetReferenceType()). // It is used to bound the type in cases like: @@ -5759,10 +5938,6 @@ class HBoundType : public HExpression<1> { // // uper_bound_ will be ClassX // } ReferenceTypeInfo upper_bound_; - // Represents the top constraint that can_be_null_ cannot exceed (i.e. if this - // is false then can_be_null_ cannot be true). - bool upper_can_be_null_; - bool can_be_null_; DISALLOW_COPY_AND_ASSIGN(HBoundType); }; @@ -5773,9 +5948,9 @@ class HCheckCast : public HTemplateInstruction<2> { HLoadClass* constant, TypeCheckKind check_kind, uint32_t dex_pc) - : HTemplateInstruction(SideEffects::CanTriggerGC(), dex_pc), - check_kind_(check_kind), - must_do_null_check_(true) { + : HTemplateInstruction(SideEffects::CanTriggerGC(), dex_pc) { + SetPackedField(check_kind); + SetPackedFlag(true); SetRawInputAt(0, object); SetRawInputAt(1, constant); } @@ -5793,17 +5968,21 @@ class HCheckCast : public HTemplateInstruction<2> { bool CanThrow() const OVERRIDE { return true; } - bool MustDoNullCheck() const { return must_do_null_check_; } - void ClearMustDoNullCheck() { must_do_null_check_ = false; } - TypeCheckKind GetTypeCheckKind() const { return check_kind_; } - - bool IsExactCheck() const { return check_kind_ == TypeCheckKind::kExactCheck; } + bool MustDoNullCheck() const { return GetPackedFlag(); } + void ClearMustDoNullCheck() { SetPackedFlag(false); } + TypeCheckKind GetTypeCheckKind() const { return GetPackedField(); } + bool IsExactCheck() const { return GetTypeCheckKind() == TypeCheckKind::kExactCheck; } DECLARE_INSTRUCTION(CheckCast); private: - const TypeCheckKind check_kind_; - bool must_do_null_check_; + static constexpr size_t kFieldTypeCheckKind = kNumberOfGenericPackedBits; + static constexpr size_t kFieldTypeCheckKindSize = + MinimumBitsToStore(static_cast(TypeCheckKind::kLast)); + static constexpr size_t kFlagMustDoNullCheck = kFieldTypeCheckKind + kFieldTypeCheckKindSize; + static constexpr size_t kNumberOfCheckCastPackedBits = kFlagMustDoNullCheck + 1; + static_assert(kNumberOfCheckCastPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); + using TypeCheckKindField = BitField; DISALLOW_COPY_AND_ASSIGN(HCheckCast); }; @@ -5812,30 +5991,40 @@ class HMemoryBarrier : public HTemplateInstruction<0> { public: explicit HMemoryBarrier(MemBarrierKind barrier_kind, uint32_t dex_pc = kNoDexPc) : HTemplateInstruction( - SideEffects::AllWritesAndReads(), dex_pc), // Assume write/read on all fields/arrays. - barrier_kind_(barrier_kind) {} + SideEffects::AllWritesAndReads(), dex_pc) { // Assume write/read on all fields/arrays. + SetPackedField(barrier_kind); + } - MemBarrierKind GetBarrierKind() { return barrier_kind_; } + MemBarrierKind GetBarrierKind() { return GetPackedField(); } DECLARE_INSTRUCTION(MemoryBarrier); private: - const MemBarrierKind barrier_kind_; + static constexpr size_t kFieldBarrierKind = HInstruction::kNumberOfGenericPackedBits; + static constexpr size_t kFieldBarrierKindSize = + MinimumBitsToStore(static_cast(kLastBarrierKind)); + static constexpr size_t kNumberOfMemoryBarrierPackedBits = + kFieldBarrierKind + kFieldBarrierKindSize; + static_assert(kNumberOfMemoryBarrierPackedBits <= kMaxNumberOfPackedBits, + "Too many packed fields."); + using BarrierKindField = BitField; DISALLOW_COPY_AND_ASSIGN(HMemoryBarrier); }; class HMonitorOperation : public HTemplateInstruction<1> { public: - enum OperationKind { + enum class OperationKind { kEnter, kExit, + kLast = kExit }; HMonitorOperation(HInstruction* object, OperationKind kind, uint32_t dex_pc) : HTemplateInstruction( - SideEffects::AllExceptGCDependency(), dex_pc), // Assume write/read on all fields/arrays. - kind_(kind) { + SideEffects::AllExceptGCDependency(), // Assume write/read on all fields/arrays. + dex_pc) { + SetPackedField(kind); SetRawInputAt(0, object); } @@ -5849,13 +6038,20 @@ class HMonitorOperation : public HTemplateInstruction<1> { return IsEnter(); } - - bool IsEnter() const { return kind_ == kEnter; } + OperationKind GetOperationKind() const { return GetPackedField(); } + bool IsEnter() const { return GetOperationKind() == OperationKind::kEnter; } DECLARE_INSTRUCTION(MonitorOperation); private: - const OperationKind kind_; + static constexpr size_t kFieldOperationKind = HInstruction::kNumberOfGenericPackedBits; + static constexpr size_t kFieldOperationKindSize = + MinimumBitsToStore(static_cast(OperationKind::kLast)); + static constexpr size_t kNumberOfMonitorOperationPackedBits = + kFieldOperationKind + kFieldOperationKindSize; + static_assert(kNumberOfMonitorOperationPackedBits <= HInstruction::kMaxNumberOfPackedBits, + "Too many packed fields."); + using OperationKindField = BitField; private: DISALLOW_COPY_AND_ASSIGN(HMonitorOperation); diff --git a/runtime/base/bit_field.h b/runtime/base/bit_field.h index fd65d500aa..a80ca28d2e 100644 --- a/runtime/base/bit_field.h +++ b/runtime/base/bit_field.h @@ -26,9 +26,18 @@ static constexpr uintptr_t kUintPtrTOne = 1U; // BitField is a template for encoding and decoding a bit field inside // an unsigned machine word. -template +template class BitField { public: + typedef T value_type; + static constexpr size_t position = kPosition; + static constexpr size_t size = kSize; + + static_assert(position < sizeof(uintptr_t) * kBitsPerByte, "Invalid position."); + static_assert(size != 0u, "Invalid size."); + static_assert(size <= sizeof(uintptr_t) * kBitsPerByte, "Invalid size."); + static_assert(size + position <= sizeof(uintptr_t) * kBitsPerByte, "Invalid position + size."); + // Tells whether the provided value fits into the bit field. static bool IsValid(T value) { return (static_cast(value) & ~((kUintPtrTOne << size) - 1)) == 0; diff --git a/runtime/primitive.h b/runtime/primitive.h index ca42c4790c..2454a2117b 100644 --- a/runtime/primitive.h +++ b/runtime/primitive.h @@ -46,6 +46,7 @@ class Primitive { kPrimFloat, kPrimDouble, kPrimVoid, + kPrimLast = kPrimVoid }; static Type GetType(char type) { -- GitLab From 2a6aad9d388bd29bff04aeec3eb9429d436d1873 Mon Sep 17 00:00:00 2001 From: Aart Bik Date: Thu, 25 Feb 2016 11:32:32 -0800 Subject: [PATCH 057/204] Implement fp to bits methods as intrinsics. Rationale: Better optimization, better performance. Results on libcore benchmark: Most gain is from moving the invariant call out of the loop after we detect everything is a side-effect free intrinsic. But generated code in general case is much cleaner too. Before: timeFloatToIntBits() in 181 ms. timeFloatToRawIntBits() in 35 ms. timeDoubleToLongBits() in 208 ms. timeDoubleToRawLongBits() in 35 ms. After: timeFloatToIntBits() in 36 ms. timeFloatToRawIntBits() in 35 ms. timeDoubleToLongBits() in 35 ms. timeDoubleToRawLongBits() in 34 ms. bug=11548336 Change-Id: I6e001bd3708e800bd75a82b8950fb3a0fc01766e --- compiler/dex/quick/dex_file_method_inliner.cc | 11 ++ compiler/dex/quick/dex_file_method_inliner.h | 2 + compiler/optimizing/instruction_simplifier.cc | 84 +++++++++--- compiler/optimizing/intrinsics.cc | 4 + compiler/optimizing/intrinsics_arm.cc | 2 + compiler/optimizing/intrinsics_arm64.cc | 2 + compiler/optimizing/intrinsics_list.h | 2 + compiler/optimizing/intrinsics_mips.cc | 2 + compiler/optimizing/intrinsics_mips64.cc | 2 + compiler/optimizing/intrinsics_x86.cc | 2 + compiler/optimizing/intrinsics_x86_64.cc | 2 + runtime/quick/inline_method_analyser.h | 2 + test/577-checker-fp2int/expected.txt | 1 + test/577-checker-fp2int/info.txt | 1 + test/577-checker-fp2int/src/Main.java | 122 ++++++++++++++++++ 15 files changed, 222 insertions(+), 19 deletions(-) create mode 100644 test/577-checker-fp2int/expected.txt create mode 100644 test/577-checker-fp2int/info.txt create mode 100644 test/577-checker-fp2int/src/Main.java diff --git a/compiler/dex/quick/dex_file_method_inliner.cc b/compiler/dex/quick/dex_file_method_inliner.cc index 209f101199..ad4ddadd2f 100644 --- a/compiler/dex/quick/dex_file_method_inliner.cc +++ b/compiler/dex/quick/dex_file_method_inliner.cc @@ -37,6 +37,8 @@ namespace { // anonymous namespace static constexpr bool kIntrinsicIsStatic[] = { true, // kIntrinsicDoubleCvt true, // kIntrinsicFloatCvt + true, // kIntrinsicFloat2Int + true, // kIntrinsicDouble2Long true, // kIntrinsicFloatIsInfinite true, // kIntrinsicDoubleIsInfinite true, // kIntrinsicFloatIsNaN @@ -106,6 +108,8 @@ static_assert(arraysize(kIntrinsicIsStatic) == kInlineOpNop, "arraysize of kIntrinsicIsStatic unexpected"); static_assert(kIntrinsicIsStatic[kIntrinsicDoubleCvt], "DoubleCvt must be static"); static_assert(kIntrinsicIsStatic[kIntrinsicFloatCvt], "FloatCvt must be static"); +static_assert(kIntrinsicIsStatic[kIntrinsicFloat2Int], "Float2Int must be static"); +static_assert(kIntrinsicIsStatic[kIntrinsicDouble2Long], "Double2Long must be static"); static_assert(kIntrinsicIsStatic[kIntrinsicFloatIsInfinite], "FloatIsInfinite must be static"); static_assert(kIntrinsicIsStatic[kIntrinsicDoubleIsInfinite], "DoubleIsInfinite must be static"); static_assert(kIntrinsicIsStatic[kIntrinsicFloatIsNaN], "FloatIsNaN must be static"); @@ -277,6 +281,8 @@ const char* const DexFileMethodInliner::kNameCacheNames[] = { "equals", // kNameCacheEquals "getCharsNoCheck", // kNameCacheGetCharsNoCheck "isEmpty", // kNameCacheIsEmpty + "floatToIntBits", // kNameCacheFloatToIntBits + "doubleToLongBits", // kNameCacheDoubleToLongBits "isInfinite", // kNameCacheIsInfinite "isNaN", // kNameCacheIsNaN "indexOf", // kNameCacheIndexOf @@ -472,6 +478,9 @@ const DexFileMethodInliner::IntrinsicDef DexFileMethodInliner::kIntrinsicMethods INTRINSIC(JavaLangFloat, FloatToRawIntBits, F_I, kIntrinsicFloatCvt, 0), INTRINSIC(JavaLangFloat, IntBitsToFloat, I_F, kIntrinsicFloatCvt, kIntrinsicFlagToFloatingPoint), + INTRINSIC(JavaLangFloat, FloatToIntBits, F_I, kIntrinsicFloat2Int, 0), + INTRINSIC(JavaLangDouble, DoubleToLongBits, D_J, kIntrinsicDouble2Long, 0), + INTRINSIC(JavaLangFloat, IsInfinite, F_Z, kIntrinsicFloatIsInfinite, 0), INTRINSIC(JavaLangDouble, IsInfinite, D_Z, kIntrinsicDoubleIsInfinite, 0), INTRINSIC(JavaLangFloat, IsNaN, F_Z, kIntrinsicFloatIsNaN, 0), @@ -791,6 +800,8 @@ bool DexFileMethodInliner::GenIntrinsic(Mir2Lir* backend, CallInfo* info) { intrinsic.d.data & kIntrinsicFlagIsOrdered); case kIntrinsicSystemArrayCopyCharArray: return backend->GenInlinedArrayCopyCharArray(info); + case kIntrinsicFloat2Int: + case kIntrinsicDouble2Long: case kIntrinsicFloatIsInfinite: case kIntrinsicDoubleIsInfinite: case kIntrinsicFloatIsNaN: diff --git a/compiler/dex/quick/dex_file_method_inliner.h b/compiler/dex/quick/dex_file_method_inliner.h index 59b8a533ae..b465db2c54 100644 --- a/compiler/dex/quick/dex_file_method_inliner.h +++ b/compiler/dex/quick/dex_file_method_inliner.h @@ -190,6 +190,8 @@ class DexFileMethodInliner { kNameCacheEquals, kNameCacheGetCharsNoCheck, kNameCacheIsEmpty, + kNameCacheFloatToIntBits, + kNameCacheDoubleToLongBits, kNameCacheIsInfinite, kNameCacheIsNaN, kNameCacheIndexOf, diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index 13d3f752c3..f8a9a94e62 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -93,6 +93,7 @@ class InstructionSimplifierVisitor : public HGraphDelegateVisitor { void SimplifyStringEquals(HInvoke* invoke); void SimplifyCompare(HInvoke* invoke, bool has_zero_op); void SimplifyIsNaN(HInvoke* invoke); + void SimplifyFP2Int(HInvoke* invoke); OptimizingCompilerStats* stats_; bool simplification_occurred_ = false; @@ -1562,26 +1563,71 @@ void InstructionSimplifierVisitor::SimplifyIsNaN(HInvoke* invoke) { invoke->GetBlock()->ReplaceAndRemoveInstructionWith(invoke, condition); } +void InstructionSimplifierVisitor::SimplifyFP2Int(HInvoke* invoke) { + DCHECK(invoke->IsInvokeStaticOrDirect()); + uint32_t dex_pc = invoke->GetDexPc(); + HInstruction* x = invoke->InputAt(0); + Primitive::Type type = x->GetType(); + // Set proper bit pattern for NaN and replace intrinsic with raw version. + HInstruction* nan; + if (type == Primitive::kPrimDouble) { + nan = GetGraph()->GetLongConstant(0x7ff8000000000000L); + invoke->SetIntrinsic(Intrinsics::kDoubleDoubleToRawLongBits, + kNeedsEnvironmentOrCache, + kNoSideEffects, + kNoThrow); + } else { + DCHECK_EQ(type, Primitive::kPrimFloat); + nan = GetGraph()->GetIntConstant(0x7fc00000); + invoke->SetIntrinsic(Intrinsics::kFloatFloatToRawIntBits, + kNeedsEnvironmentOrCache, + kNoSideEffects, + kNoThrow); + } + // Test IsNaN(x), which is the same as x != x. + HCondition* condition = new (GetGraph()->GetArena()) HNotEqual(x, x, dex_pc); + condition->SetBias(ComparisonBias::kLtBias); + invoke->GetBlock()->InsertInstructionBefore(condition, invoke->GetNext()); + // Select between the two. + HInstruction* select = new (GetGraph()->GetArena()) HSelect(condition, nan, invoke, dex_pc); + invoke->GetBlock()->InsertInstructionBefore(select, condition->GetNext()); + invoke->ReplaceWithExceptInReplacementAtIndex(select, 0); // false at index 0 +} + void InstructionSimplifierVisitor::VisitInvoke(HInvoke* instruction) { - if (instruction->GetIntrinsic() == Intrinsics::kStringEquals) { - SimplifyStringEquals(instruction); - } else if (instruction->GetIntrinsic() == Intrinsics::kSystemArrayCopy) { - SimplifySystemArrayCopy(instruction); - } else if (instruction->GetIntrinsic() == Intrinsics::kIntegerRotateRight || - instruction->GetIntrinsic() == Intrinsics::kLongRotateRight) { - SimplifyRotate(instruction, false); - } else if (instruction->GetIntrinsic() == Intrinsics::kIntegerRotateLeft || - instruction->GetIntrinsic() == Intrinsics::kLongRotateLeft) { - SimplifyRotate(instruction, true); - } else if (instruction->GetIntrinsic() == Intrinsics::kIntegerCompare || - instruction->GetIntrinsic() == Intrinsics::kLongCompare) { - SimplifyCompare(instruction, /* is_signum */ false); - } else if (instruction->GetIntrinsic() == Intrinsics::kIntegerSignum || - instruction->GetIntrinsic() == Intrinsics::kLongSignum) { - SimplifyCompare(instruction, /* is_signum */ true); - } else if (instruction->GetIntrinsic() == Intrinsics::kFloatIsNaN || - instruction->GetIntrinsic() == Intrinsics::kDoubleIsNaN) { - SimplifyIsNaN(instruction); + switch (instruction->GetIntrinsic()) { + case Intrinsics::kStringEquals: + SimplifyStringEquals(instruction); + break; + case Intrinsics::kSystemArrayCopy: + SimplifySystemArrayCopy(instruction); + break; + case Intrinsics::kIntegerRotateRight: + case Intrinsics::kLongRotateRight: + SimplifyRotate(instruction, false); + break; + case Intrinsics::kIntegerRotateLeft: + case Intrinsics::kLongRotateLeft: + SimplifyRotate(instruction, true); + break; + case Intrinsics::kIntegerCompare: + case Intrinsics::kLongCompare: + SimplifyCompare(instruction, /* is_signum */ false); + break; + case Intrinsics::kIntegerSignum: + case Intrinsics::kLongSignum: + SimplifyCompare(instruction, /* is_signum */ true); + break; + case Intrinsics::kFloatIsNaN: + case Intrinsics::kDoubleIsNaN: + SimplifyIsNaN(instruction); + break; + case Intrinsics::kFloatFloatToIntBits: + case Intrinsics::kDoubleDoubleToLongBits: + SimplifyFP2Int(instruction); + break; + default: + break; } } diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc index 316e86b4c9..3ed0278871 100644 --- a/compiler/optimizing/intrinsics.cc +++ b/compiler/optimizing/intrinsics.cc @@ -130,6 +130,10 @@ static Intrinsics GetIntrinsic(InlineMethod method) { case kIntrinsicFloatCvt: return ((method.d.data & kIntrinsicFlagToFloatingPoint) == 0) ? Intrinsics::kFloatFloatToRawIntBits : Intrinsics::kFloatIntBitsToFloat; + case kIntrinsicFloat2Int: + return Intrinsics::kFloatFloatToIntBits; + case kIntrinsicDouble2Long: + return Intrinsics::kDoubleDoubleToLongBits; // Floating-point tests. case kIntrinsicFloatIsInfinite: diff --git a/compiler/optimizing/intrinsics_arm.cc b/compiler/optimizing/intrinsics_arm.cc index 8cbdcbbcaf..555bd7fa6b 100644 --- a/compiler/optimizing/intrinsics_arm.cc +++ b/compiler/optimizing/intrinsics_arm.cc @@ -1944,6 +1944,8 @@ UNIMPLEMENTED_INTRINSIC(IntegerLowestOneBit) UNIMPLEMENTED_INTRINSIC(LongLowestOneBit) // Handled as HIR instructions. +UNIMPLEMENTED_INTRINSIC(FloatFloatToIntBits) +UNIMPLEMENTED_INTRINSIC(DoubleDoubleToLongBits) UNIMPLEMENTED_INTRINSIC(FloatIsNaN) UNIMPLEMENTED_INTRINSIC(DoubleIsNaN) UNIMPLEMENTED_INTRINSIC(IntegerRotateLeft) diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc index b5f15fe22d..f6b4779575 100644 --- a/compiler/optimizing/intrinsics_arm64.cc +++ b/compiler/optimizing/intrinsics_arm64.cc @@ -1626,6 +1626,8 @@ UNIMPLEMENTED_INTRINSIC(IntegerLowestOneBit) UNIMPLEMENTED_INTRINSIC(LongLowestOneBit) // Handled as HIR instructions. +UNIMPLEMENTED_INTRINSIC(FloatFloatToIntBits) +UNIMPLEMENTED_INTRINSIC(DoubleDoubleToLongBits) UNIMPLEMENTED_INTRINSIC(FloatIsNaN) UNIMPLEMENTED_INTRINSIC(DoubleIsNaN) UNIMPLEMENTED_INTRINSIC(IntegerRotateLeft) diff --git a/compiler/optimizing/intrinsics_list.h b/compiler/optimizing/intrinsics_list.h index 88217b308e..e1aea924cf 100644 --- a/compiler/optimizing/intrinsics_list.h +++ b/compiler/optimizing/intrinsics_list.h @@ -23,10 +23,12 @@ #define INTRINSICS_LIST(V) \ V(DoubleDoubleToRawLongBits, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ + V(DoubleDoubleToLongBits, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ V(DoubleIsInfinite, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ V(DoubleIsNaN, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ V(DoubleLongBitsToDouble, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ V(FloatFloatToRawIntBits, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ + V(FloatFloatToIntBits, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ V(FloatIsInfinite, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ V(FloatIsNaN, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ V(FloatIntBitsToFloat, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc index 2f183c3a62..a737d8100a 100644 --- a/compiler/optimizing/intrinsics_mips.cc +++ b/compiler/optimizing/intrinsics_mips.cc @@ -1772,6 +1772,8 @@ UNIMPLEMENTED_INTRINSIC(IntegerLowestOneBit) UNIMPLEMENTED_INTRINSIC(LongLowestOneBit) // Handled as HIR instructions. +UNIMPLEMENTED_INTRINSIC(FloatFloatToIntBits) +UNIMPLEMENTED_INTRINSIC(DoubleDoubleToLongBits) UNIMPLEMENTED_INTRINSIC(FloatIsNaN) UNIMPLEMENTED_INTRINSIC(DoubleIsNaN) UNIMPLEMENTED_INTRINSIC(IntegerCompare) diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc index bd4f5329da..ca2652b74a 100644 --- a/compiler/optimizing/intrinsics_mips64.cc +++ b/compiler/optimizing/intrinsics_mips64.cc @@ -1824,6 +1824,8 @@ UNIMPLEMENTED_INTRINSIC(IntegerLowestOneBit) UNIMPLEMENTED_INTRINSIC(LongLowestOneBit) // Handled as HIR instructions. +UNIMPLEMENTED_INTRINSIC(FloatFloatToIntBits) +UNIMPLEMENTED_INTRINSIC(DoubleDoubleToLongBits) UNIMPLEMENTED_INTRINSIC(FloatIsNaN) UNIMPLEMENTED_INTRINSIC(DoubleIsNaN) UNIMPLEMENTED_INTRINSIC(IntegerCompare) diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc index 260a8773fb..0df4553f56 100644 --- a/compiler/optimizing/intrinsics_x86.cc +++ b/compiler/optimizing/intrinsics_x86.cc @@ -2642,6 +2642,8 @@ UNIMPLEMENTED_INTRINSIC(IntegerLowestOneBit) UNIMPLEMENTED_INTRINSIC(LongLowestOneBit) // Handled as HIR instructions. +UNIMPLEMENTED_INTRINSIC(FloatFloatToIntBits) +UNIMPLEMENTED_INTRINSIC(DoubleDoubleToLongBits) UNIMPLEMENTED_INTRINSIC(FloatIsNaN) UNIMPLEMENTED_INTRINSIC(DoubleIsNaN) UNIMPLEMENTED_INTRINSIC(IntegerRotateLeft) diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc index 93e8c00e5a..2a9e684d11 100644 --- a/compiler/optimizing/intrinsics_x86_64.cc +++ b/compiler/optimizing/intrinsics_x86_64.cc @@ -2719,6 +2719,8 @@ UNIMPLEMENTED_INTRINSIC(FloatIsInfinite) UNIMPLEMENTED_INTRINSIC(DoubleIsInfinite) // Handled as HIR instructions. +UNIMPLEMENTED_INTRINSIC(FloatFloatToIntBits) +UNIMPLEMENTED_INTRINSIC(DoubleDoubleToLongBits) UNIMPLEMENTED_INTRINSIC(FloatIsNaN) UNIMPLEMENTED_INTRINSIC(DoubleIsNaN) UNIMPLEMENTED_INTRINSIC(IntegerRotateLeft) diff --git a/runtime/quick/inline_method_analyser.h b/runtime/quick/inline_method_analyser.h index 0b09a70be4..7e84b405e7 100644 --- a/runtime/quick/inline_method_analyser.h +++ b/runtime/quick/inline_method_analyser.h @@ -37,6 +37,8 @@ class MethodVerifier; enum InlineMethodOpcode : uint16_t { kIntrinsicDoubleCvt, kIntrinsicFloatCvt, + kIntrinsicFloat2Int, + kIntrinsicDouble2Long, kIntrinsicFloatIsInfinite, kIntrinsicDoubleIsInfinite, kIntrinsicFloatIsNaN, diff --git a/test/577-checker-fp2int/expected.txt b/test/577-checker-fp2int/expected.txt new file mode 100644 index 0000000000..b0aad4deb5 --- /dev/null +++ b/test/577-checker-fp2int/expected.txt @@ -0,0 +1 @@ +passed diff --git a/test/577-checker-fp2int/info.txt b/test/577-checker-fp2int/info.txt new file mode 100644 index 0000000000..d22a0eab9d --- /dev/null +++ b/test/577-checker-fp2int/info.txt @@ -0,0 +1 @@ +Unit test for float/double to raw bits conversions. diff --git a/test/577-checker-fp2int/src/Main.java b/test/577-checker-fp2int/src/Main.java new file mode 100644 index 0000000000..e3f1230beb --- /dev/null +++ b/test/577-checker-fp2int/src/Main.java @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Main { + + /// CHECK-START: int Main.f2int(float) instruction_simplifier (before) + /// CHECK-DAG: <> InvokeStaticOrDirect intrinsic:FloatFloatToIntBits + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: int Main.f2int(float) instruction_simplifier (after) + /// CHECK-DAG: <> InvokeStaticOrDirect [<>] intrinsic:FloatFloatToRawIntBits + /// CHECK-DAG: <> NotEqual [<>,<>] + /// CHECK-DAG: <> Select [<>,{{i\d+}},<>] + /// CHECK-DAG: Return [<>] + private static int f2int(float f) { + return Float.floatToIntBits(f); + } + + /// CHECK-START: long Main.d2long(double) instruction_simplifier (before) + /// CHECK-DAG: <> InvokeStaticOrDirect intrinsic:DoubleDoubleToLongBits + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: long Main.d2long(double) instruction_simplifier (after) + /// CHECK-DAG: <> InvokeStaticOrDirect [<>] intrinsic:DoubleDoubleToRawLongBits + /// CHECK-DAG: <> NotEqual [<>,<>] + /// CHECK-DAG: <> Select [<>,{{j\d+}},<>] + /// CHECK-DAG: Return [<>] + private static long d2long(double d) { + return Double.doubleToLongBits(d); + } + + public static void main(String args[]) { + // A few distinct numbers. + expectEquals32(0xff800000, f2int(Float.NEGATIVE_INFINITY)); + expectEquals32(0xbf800000, f2int(-1.0f)); + expectEquals32(0x80000000, f2int(-0.0f)); + expectEquals32(0x00000000, f2int(+0.0f)); + expectEquals32(0x3f800000, f2int(+1.0f)); + expectEquals32(0x7f800000, f2int(Float.POSITIVE_INFINITY)); + + // A few others. + for (int i = 0; i <= 100; i++) { + expectEquals32(i, f2int(Float.intBitsToFloat(i))); + } + + // A few NaN numbers. + float[] fvals = { + Float.intBitsToFloat(0x7f800001), + Float.intBitsToFloat(0x7fa00000), + Float.intBitsToFloat(0x7fc00000), + Float.intBitsToFloat(0x7fffffff), + Float.intBitsToFloat(0xff800001), + Float.intBitsToFloat(0xffa00000), + Float.intBitsToFloat(0xffc00000), + Float.intBitsToFloat(0xffffffff) + }; + for (int i = 0; i < fvals.length; i++) { + expectEquals32(0x7fc00000, f2int(fvals[i])); + } + + // A few distinct numbers. + expectEquals64(0xfff0000000000000L, d2long(Double.NEGATIVE_INFINITY)); + expectEquals64(0xbff0000000000000L, d2long(-1.0d)); + expectEquals64(0x8000000000000000L, d2long(-0.0d)); + expectEquals64(0x0000000000000000L, d2long(+0.0d)); + expectEquals64(0x3ff0000000000000L, d2long(+1.0d)); + expectEquals64(0x7ff0000000000000L, d2long(Double.POSITIVE_INFINITY)); + + // A few others. + for (long l = 0; l <= 100; l++) { + expectEquals64(l, d2long(Double.longBitsToDouble(l))); + } + + // A few NaN numbers. + double[] dvals = { + Double.longBitsToDouble(0x7ff0000000000001L), + Double.longBitsToDouble(0x7ff4000000000000L), + Double.longBitsToDouble(0x7ff8000000000000L), + Double.longBitsToDouble(0x7fffffffffffffffL), + Double.longBitsToDouble(0xfff0000000000001L), + Double.longBitsToDouble(0xfff4000000000000L), + Double.longBitsToDouble(0xfff8000000000000L), + Double.longBitsToDouble(0xffffffffffffffffL) + }; + for (int i = 0; i < dvals.length; i++) { + expectEquals64(0x7ff8000000000000L, d2long(dvals[i])); + } + + System.out.println("passed"); + } + + private static void expectEquals32(int expected, int result) { + if (expected != result) { + throw new Error("Expected: " + + Integer.toHexString(expected) + + ", found: " + + Integer.toHexString(result)); + } + } + + private static void expectEquals64(long expected, long result) { + if (expected != result) { + throw new Error("Expected: " + + Long.toHexString(expected) + + ", found: " + + Long.toHexString(result)); + } + } +} -- GitLab From e9d2ca237620b0af28eed9fef52b09bddebdbc6f Mon Sep 17 00:00:00 2001 From: Alex Light Date: Thu, 25 Feb 2016 16:13:54 -0800 Subject: [PATCH 058/204] Add JNI tests for lambdas Bug: 27259142 Change-Id: I2a1b31db85dc487c1e6a62609d4497c3a4d4d3a6 --- test/004-JniTest/build | 28 ++++++++++++++++++++++++++++ test/004-JniTest/expected.txt | 3 +++ test/004-JniTest/jni_test.cc | 19 +++++++++++++++++++ test/004-JniTest/src/Main.java | 17 +++++++++++++++++ 4 files changed, 67 insertions(+) create mode 100755 test/004-JniTest/build diff --git a/test/004-JniTest/build b/test/004-JniTest/build new file mode 100755 index 0000000000..e8e9f31ef4 --- /dev/null +++ b/test/004-JniTest/build @@ -0,0 +1,28 @@ +#!/bin/bash +# +# Copyright 2016 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# Make us exit on a failure. +# +set -e + +# Hard-wired use of experimental jack. +# TODO: fix this temporary work-around for lambdas, see b/19467889 +export USE_JACK=true +# export JACK_SERVER=false +# export JACK_REPOSITORY="${ANDROID_BUILD_TOP}/prebuilts/sdk/tools/jacks" + +# e.g. /foo/bar/jack-3.10.ALPHA.jar -> 3.10.ALPHA +# export JACK_VERSION="$(find "$JACK_REPOSITORY" -name '*ALPHA*' | sed 's/.*jack-//g' | sed 's/[.]jar//g')" +./default-build "$@" --experimental lambdas diff --git a/test/004-JniTest/expected.txt b/test/004-JniTest/expected.txt index 155c6ae5f3..f7e404d30b 100644 --- a/test/004-JniTest/expected.txt +++ b/test/004-JniTest/expected.txt @@ -55,3 +55,6 @@ Calling method AbstractInterface->JniCallSoftConflictMethod on object of type Co DefaultInterface.JniCallSoftConflictMethod Calling method ConflictInterface->JniCallConflictDefaultMethod on object of type ConcreteClass EXCEPTION OCCURED: java.lang.IncompatibleClassChangeError: Conflicting default method implementations void ConflictInterface.JniCallConflictDefaultMethod() +hi-lambda: λ +hi-default δλ +hi-default δλ diff --git a/test/004-JniTest/jni_test.cc b/test/004-JniTest/jni_test.cc index f632331fe3..2bdf8d1e71 100644 --- a/test/004-JniTest/jni_test.cc +++ b/test/004-JniTest/jni_test.cc @@ -721,3 +721,22 @@ class JniCallDefaultMethodsTest { extern "C" JNIEXPORT void JNICALL Java_Main_testCallDefaultMethods(JNIEnv* env) { JniCallDefaultMethodsTest(env).Test(); } + +static void InvokeSpecificMethod(JNIEnv* env, jobject obj, const char* method) { + jclass lambda_class = env->FindClass("LambdaInterface"); + assert(!env->ExceptionCheck()); + assert(lambda_class != nullptr); + jmethodID method_id = env->GetMethodID(lambda_class, method, "()V"); + assert(!env->ExceptionCheck()); + env->CallVoidMethod(obj, method_id); + assert(!env->ExceptionCheck()); +} + +extern "C" JNIEXPORT void JNICALL Java_Main_testInvokeLambdaDefaultMethod( + JNIEnv* e, jclass, jobject l) { + InvokeSpecificMethod(e, l, "sayHiTwice"); +} + +extern "C" JNIEXPORT void JNICALL Java_Main_testInvokeLambdaMethod(JNIEnv* e, jclass, jobject l) { + InvokeSpecificMethod(e, l, "sayHi"); +} diff --git a/test/004-JniTest/src/Main.java b/test/004-JniTest/src/Main.java index 9f4a8522e7..e0530d8d62 100644 --- a/test/004-JniTest/src/Main.java +++ b/test/004-JniTest/src/Main.java @@ -40,6 +40,10 @@ public class Main { testProxyGetMethodID(); testJniCriticalSectionAndGc(); testCallDefaultMethods(); + String lambda = "λ"; + testInvokeLambdaMethod(() -> { System.out.println("hi-lambda: " + lambda); }); + String def = "δ"; + testInvokeLambdaDefaultMethod(() -> { System.out.println("hi-default " + def + lambda); }); } private static native void testCallDefaultMethods(); @@ -255,6 +259,19 @@ public class Main { } private static native void enterJniCriticalSection(int arraySize, byte[] array0, byte[] array); + + private static native void testInvokeLambdaMethod(LambdaInterface iface); + + private static native void testInvokeLambdaDefaultMethod(LambdaInterface iface); +} + +@FunctionalInterface +interface LambdaInterface { + public void sayHi(); + public default void sayHiTwice() { + sayHi(); + sayHi(); + } } class JniCallNonvirtualTest { -- GitLab From 942dc298d7af3e60fe40c69b4b416ef144e5c723 Mon Sep 17 00:00:00 2001 From: Dimitry Ivanov Date: Wed, 24 Feb 2016 13:33:33 -0800 Subject: [PATCH 059/204] Simplify LoadNativeLibrary() With ApplicationLoaders.getClassLoader() ensuring linker-namespace initialization there is no longer need for LoadNativeLibrary() and callers to pass along namespace-specific information to art. This change removes unnecessary parameters of such calls. Bug: http://b/27189432 Bug: http://b/22548808 Change-Id: I341d35a2d5195e634678b352f4361f8712984b69 (cherry picked from commit c286a7fcd8a446c086127bf03fd07f904e017336) --- compiler/jni/jni_compiler_test.cc | 6 ++---- runtime/java_vm_ext.cc | 17 +++++++++++------ runtime/java_vm_ext.h | 6 ++++-- runtime/native/java_lang_Runtime.cc | 16 +++++++++------- runtime/openjdkjvm/OpenjdkJvm.cc | 9 ++++----- runtime/runtime.cc | 8 ++------ runtime/well_known_classes.cc | 2 +- 7 files changed, 33 insertions(+), 31 deletions(-) diff --git a/compiler/jni/jni_compiler_test.cc b/compiler/jni/jni_compiler_test.cc index 8d60be20ee..cf836a9c9f 100644 --- a/compiler/jni/jni_compiler_test.cc +++ b/compiler/jni/jni_compiler_test.cc @@ -220,8 +220,7 @@ void JniCompilerTest::CompileAndRunIntMethodThroughStubImpl() { std::string reason; ASSERT_TRUE(Runtime::Current()->GetJavaVM()-> - LoadNativeLibrary(env_, "", class_loader_, /* is_shared_namespace */ false, - nullptr, nullptr, &reason)) + LoadNativeLibrary(env_, "", class_loader_, nullptr, &reason)) << reason; jint result = env_->CallNonvirtualIntMethod(jobj_, jklass_, jmethod_, 24); @@ -236,8 +235,7 @@ void JniCompilerTest::CompileAndRunStaticIntMethodThroughStubImpl() { std::string reason; ASSERT_TRUE(Runtime::Current()->GetJavaVM()-> - LoadNativeLibrary(env_, "", class_loader_, /* is_shared_namespace */ false, - nullptr, nullptr, &reason)) + LoadNativeLibrary(env_, "", class_loader_, nullptr, &reason)) << reason; jint result = env_->CallStaticIntMethod(jklass_, jmethod_, 42); diff --git a/runtime/java_vm_ext.cc b/runtime/java_vm_ext.cc index 5c4419333b..191c0c7cf2 100644 --- a/runtime/java_vm_ext.cc +++ b/runtime/java_vm_ext.cc @@ -716,9 +716,11 @@ void JavaVMExt::UnloadNativeLibraries() { libraries_.get()->UnloadNativeLibraries(); } -bool JavaVMExt::LoadNativeLibrary(JNIEnv* env, const std::string& path, jobject class_loader, - bool is_shared_namespace, jstring library_path, - jstring permitted_path, std::string* error_msg) { +bool JavaVMExt::LoadNativeLibrary(JNIEnv* env, + const std::string& path, + jobject class_loader, + jstring library_path, + std::string* error_msg) { error_msg->clear(); // See if we've already loaded this library. If we have, and the class loader @@ -777,9 +779,12 @@ bool JavaVMExt::LoadNativeLibrary(JNIEnv* env, const std::string& path, jobject Locks::mutator_lock_->AssertNotHeld(self); const char* path_str = path.empty() ? nullptr : path.c_str(); - void* handle = android::OpenNativeLibrary(env, runtime_->GetTargetSdkVersion(), path_str, - class_loader, is_shared_namespace, library_path, - permitted_path); + void* handle = android::OpenNativeLibrary(env, + runtime_->GetTargetSdkVersion(), + path_str, + class_loader, + library_path); + bool needs_native_bridge = false; if (handle == nullptr) { if (android::NativeBridgeIsSupported(path_str)) { diff --git a/runtime/java_vm_ext.h b/runtime/java_vm_ext.h index 8cae1e52d2..3d055cd7ce 100644 --- a/runtime/java_vm_ext.h +++ b/runtime/java_vm_ext.h @@ -85,8 +85,10 @@ class JavaVMExt : public JavaVM { * Returns 'true' on success. On failure, sets 'error_msg' to a * human-readable description of the error. */ - bool LoadNativeLibrary(JNIEnv* env, const std::string& path, jobject class_loader, - bool is_shared_namespace, jstring library_path, jstring permitted_path, + bool LoadNativeLibrary(JNIEnv* env, + const std::string& path, + jobject class_loader, + jstring library_path, std::string* error_msg); // Unload native libraries with cleared class loaders. diff --git a/runtime/native/java_lang_Runtime.cc b/runtime/native/java_lang_Runtime.cc index c177f19b56..df794e1249 100644 --- a/runtime/native/java_lang_Runtime.cc +++ b/runtime/native/java_lang_Runtime.cc @@ -67,9 +67,11 @@ static void SetLdLibraryPath(JNIEnv* env, jstring javaLdLibraryPath) { #endif } -static jstring Runtime_nativeLoad(JNIEnv* env, jclass, jstring javaFilename, jobject javaLoader, - jboolean isSharedNamespace, jstring javaLibrarySearchPath, - jstring javaLibraryPermittedPath) { +static jstring Runtime_nativeLoad(JNIEnv* env, + jclass, + jstring javaFilename, + jobject javaLoader, + jstring javaLibrarySearchPath) { ScopedUtfChars filename(env, javaFilename); if (filename.c_str() == nullptr) { return nullptr; @@ -79,7 +81,9 @@ static jstring Runtime_nativeLoad(JNIEnv* env, jclass, jstring javaFilename, job // Starting with N nativeLoad uses classloader local // linker namespace instead of global LD_LIBRARY_PATH - // (23 is Marshmallow) + // (23 is Marshmallow). This call is here to preserve + // backwards compatibility for the apps targeting sdk + // version <= 23 if (target_sdk_version == 0) { SetLdLibraryPath(env, javaLibrarySearchPath); } @@ -90,9 +94,7 @@ static jstring Runtime_nativeLoad(JNIEnv* env, jclass, jstring javaFilename, job bool success = vm->LoadNativeLibrary(env, filename.c_str(), javaLoader, - isSharedNamespace == JNI_TRUE, javaLibrarySearchPath, - javaLibraryPermittedPath, &error_msg); if (success) { return nullptr; @@ -121,7 +123,7 @@ static JNINativeMethod gMethods[] = { NATIVE_METHOD(Runtime, gc, "()V"), NATIVE_METHOD(Runtime, maxMemory, "!()J"), NATIVE_METHOD(Runtime, nativeExit, "(I)V"), - NATIVE_METHOD(Runtime, nativeLoad, "(Ljava/lang/String;Ljava/lang/ClassLoader;ZLjava/lang/String;Ljava/lang/String;)Ljava/lang/String;"), + NATIVE_METHOD(Runtime, nativeLoad, "(Ljava/lang/String;Ljava/lang/ClassLoader;Ljava/lang/String;)Ljava/lang/String;"), NATIVE_METHOD(Runtime, totalMemory, "!()J"), }; diff --git a/runtime/openjdkjvm/OpenjdkJvm.cc b/runtime/openjdkjvm/OpenjdkJvm.cc index 725067a351..d377457eb2 100644 --- a/runtime/openjdkjvm/OpenjdkJvm.cc +++ b/runtime/openjdkjvm/OpenjdkJvm.cc @@ -329,9 +329,10 @@ static void SetLdLibraryPath(JNIEnv* env, jstring javaLdLibraryPath) { } -JNIEXPORT jstring JVM_NativeLoad(JNIEnv* env, jstring javaFilename, jobject javaLoader, - jboolean isSharedNamespace, jstring javaLibrarySearchPath, - jstring javaLibraryPermittedPath) { +JNIEXPORT jstring JVM_NativeLoad(JNIEnv* env, + jstring javaFilename, + jobject javaLoader, + jstring javaLibrarySearchPath) { ScopedUtfChars filename(env, javaFilename); if (filename.c_str() == NULL) { return NULL; @@ -354,9 +355,7 @@ JNIEXPORT jstring JVM_NativeLoad(JNIEnv* env, jstring javaFilename, jobject java bool success = vm->LoadNativeLibrary(env, filename.c_str(), javaLoader, - isSharedNamespace == JNI_TRUE, javaLibrarySearchPath, - javaLibraryPermittedPath, &error_msg); if (success) { return nullptr; diff --git a/runtime/runtime.cc b/runtime/runtime.cc index eb5455a4cd..a82974cfc9 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -1276,9 +1276,7 @@ void Runtime::InitNativeMethods() { // libcore can't because it's the library that implements System.loadLibrary! { std::string error_msg; - if (!java_vm_->LoadNativeLibrary(env, "libjavacore.so", nullptr, - /* is_shared_namespace */ false, - nullptr, nullptr, &error_msg)) { + if (!java_vm_->LoadNativeLibrary(env, "libjavacore.so", nullptr, nullptr, &error_msg)) { LOG(FATAL) << "LoadNativeLibrary failed for \"libjavacore.so\": " << error_msg; } } @@ -1287,9 +1285,7 @@ void Runtime::InitNativeMethods() { ? "libopenjdkd.so" : "libopenjdk.so"; std::string error_msg; - if (!java_vm_->LoadNativeLibrary(env, kOpenJdkLibrary, nullptr, - /* is_shared_namespace */ false, - nullptr, nullptr, &error_msg)) { + if (!java_vm_->LoadNativeLibrary(env, kOpenJdkLibrary, nullptr, nullptr, &error_msg)) { LOG(FATAL) << "LoadNativeLibrary failed for \"" << kOpenJdkLibrary << "\": " << error_msg; } } diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc index cfa8329a36..d288943528 100644 --- a/runtime/well_known_classes.cc +++ b/runtime/well_known_classes.cc @@ -385,7 +385,7 @@ void WellKnownClasses::LateInit(JNIEnv* env) { ScopedLocalRef java_lang_Runtime(env, env->FindClass("java/lang/Runtime")); java_lang_Runtime_nativeLoad = CacheMethod(env, java_lang_Runtime.get(), true, "nativeLoad", - "(Ljava/lang/String;Ljava/lang/ClassLoader;ZLjava/lang/String;Ljava/lang/String;)" + "(Ljava/lang/String;Ljava/lang/ClassLoader;Ljava/lang/String;)" "Ljava/lang/String;"); } -- GitLab From c5dd319c574f67d11a71f1b60ac6c34bfe93b750 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Wed, 9 Dec 2015 16:38:30 -0800 Subject: [PATCH 060/204] Add and use loaded class profiling Class profiling is a way to keep track of which classes are resolved. From here the compiler can use this information to generate a smaller app image. TODO: Add tests for profile stuff. Bug: 22858531 (cherry picked from commit 8913fc1a27df8cf3b37fd99e94d87f290591328e) Change-Id: Ifcd09230cbdc266305bc1247e0d31e7920eb353e --- compiler/driver/compiler_driver.cc | 13 ++- compiler/driver/compiler_driver_test.cc | 4 +- dex2oat/dex2oat.cc | 24 +++- profman/profile_assistant_test.cc | 4 +- runtime/class_linker.cc | 114 ++++++++++++++++++- runtime/class_linker.h | 10 ++ runtime/dex_cache_resolved_classes.h | 71 ++++++++++++ runtime/gc/space/image_space.cc | 2 +- runtime/jit/offline_profiling_info.cc | 111 +++++++++++++++--- runtime/jit/offline_profiling_info.h | 21 +++- runtime/jit/profile_compilation_info_test.cc | 11 +- runtime/jit/profile_saver.cc | 36 ++++-- runtime/jit/profile_saver.h | 1 + 13 files changed, 377 insertions(+), 45 deletions(-) create mode 100644 runtime/dex_cache_resolved_classes.h diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index a9fec30bfe..3100b6da0f 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -384,7 +384,9 @@ CompilerDriver::CompilerDriver( compiler_->Init(); - CHECK_EQ(boot_image_, image_classes_.get() != nullptr); + if (boot_image_) { + CHECK(image_classes_.get() != nullptr) << "Expected image classes for boot image"; + } } CompilerDriver::~CompilerDriver() { @@ -868,12 +870,13 @@ void CompilerDriver::PreCompile(jobject class_loader, } bool CompilerDriver::IsImageClass(const char* descriptor) const { - if (!IsBootImage()) { - // NOTE: Currently only reachable from InitImageMethodVisitor for the app image case. - return true; - } else { + if (image_classes_ != nullptr) { + // If we have a set of image classes, use those. return image_classes_->find(descriptor) != image_classes_->end(); } + // No set of image classes, assume we include all the classes. + // NOTE: Currently only reachable from InitImageMethodVisitor for the app image case. + return !IsBootImage(); } bool CompilerDriver::IsClassToCompile(const char* descriptor) const { diff --git a/compiler/driver/compiler_driver_test.cc b/compiler/driver/compiler_driver_test.cc index 478588561f..00375641f3 100644 --- a/compiler/driver/compiler_driver_test.cc +++ b/compiler/driver/compiler_driver_test.cc @@ -250,8 +250,8 @@ class CompilerDriverProfileTest : public CompilerDriverTest { ProfileCompilationInfo info; for (const std::unique_ptr& dex_file : dex_files) { std::string key = ProfileCompilationInfo::GetProfileDexFileKey(dex_file->GetLocation()); - profile_info_.AddData(key, dex_file->GetLocationChecksum(), 1); - profile_info_.AddData(key, dex_file->GetLocationChecksum(), 2); + profile_info_.AddMethodIndex(key, dex_file->GetLocationChecksum(), 1); + profile_info_.AddMethodIndex(key, dex_file->GetLocationChecksum(), 2); } return &profile_info_; } diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index dfcb4bcaec..d9a2f300d9 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -1263,6 +1263,24 @@ class Dex2Oat FINAL { dex_caches_.clear(); } + void LoadClassProfileDescriptors() { + if (profile_compilation_info_ != nullptr && app_image_) { + Runtime* runtime = Runtime::Current(); + CHECK(runtime != nullptr); + std::set resolved_classes( + profile_compilation_info_->GetResolvedClasses()); + image_classes_.reset(new std::unordered_set( + runtime->GetClassLinker()->GetClassDescriptorsForProfileKeys(resolved_classes))); + VLOG(compiler) << "Loaded " << image_classes_->size() + << " image class descriptors from profile"; + if (VLOG_IS_ON(compiler)) { + for (const std::string& s : *image_classes_) { + LOG(INFO) << "Image class " << s; + } + } + } + } + // Set up the environment for compilation. Includes starting the runtime and loading/opening the // boot class path. bool Setup() { @@ -1610,7 +1628,10 @@ class Dex2Oat FINAL { // The non moving space is right after the oat file. Put the preferred app image location // right after the non moving space so that we ideally get a continuous immune region for // the GC. - const size_t non_moving_space_capacity = heap->GetNonMovingSpace()->Capacity(); + // Use the default non moving space capacity since dex2oat does not have a separate non- + // moving space. This means the runtime's non moving space space size will be as large + // as the growth limit for dex2oat, but smaller in the zygote. + const size_t non_moving_space_capacity = gc::Heap::kDefaultNonMovingSpaceCapacity; image_base_ += non_moving_space_capacity; VLOG(compiler) << "App image base=" << reinterpret_cast(image_base_); } @@ -2475,6 +2496,7 @@ static void b13564922() { } static int CompileImage(Dex2Oat& dex2oat) { + dex2oat.LoadClassProfileDescriptors(); dex2oat.Compile(); if (!dex2oat.WriteOatFiles()) { diff --git a/profman/profile_assistant_test.cc b/profman/profile_assistant_test.cc index 3faa8eb53f..b0d5df2b3b 100644 --- a/profman/profile_assistant_test.cc +++ b/profman/profile_assistant_test.cc @@ -37,8 +37,8 @@ class ProfileAssistantTest : public CommonRuntimeTest { std::string dex_location2 = "location2" + id; uint32_t dex_location_checksum2 = 10 * checksum; for (uint16_t i = start_method_index; i < start_method_index + number_of_methods; i++) { - ASSERT_TRUE(info->AddData(dex_location1, dex_location_checksum1, i)); - ASSERT_TRUE(info->AddData(dex_location2, dex_location_checksum2, i)); + ASSERT_TRUE(info->AddMethodIndex(dex_location1, dex_location_checksum1, i)); + ASSERT_TRUE(info->AddMethodIndex(dex_location2, dex_location_checksum2, i)); } ASSERT_TRUE(info->Save(GetFd(profile))); ASSERT_EQ(0, profile.GetFile()->Flush()); diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index cd4daeb7bd..b5e6532b6e 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -58,6 +58,7 @@ #include "interpreter/interpreter.h" #include "jit/jit.h" #include "jit/jit_code_cache.h" +#include "jit/offline_profiling_info.h" #include "leb128.h" #include "linear_alloc.h" #include "mirror/class.h" @@ -1615,7 +1616,8 @@ bool ClassLinker::AddImageSpace( VLOG(image) << name->ToModifiedUtf8(); } *error_msg = "Rejecting application image due to class loader mismatch"; - return false; + // Ignore class loader mismatch for now since these would just use possibly incorrect + // oat code anyways. The structural class check should be done in the parent. } } } @@ -7628,6 +7630,116 @@ void ClassLinker::CleanupClassLoaders() { } } +std::set ClassLinker::GetResolvedClasses(bool ignore_boot_classes) { + ScopedObjectAccess soa(Thread::Current()); + ScopedAssertNoThreadSuspension ants(soa.Self(), __FUNCTION__); + std::set ret; + VLOG(class_linker) << "Collecting resolved classes"; + const uint64_t start_time = NanoTime(); + ReaderMutexLock mu(soa.Self(), *DexLock()); + // Loop through all the dex caches and inspect resolved classes. + for (const ClassLinker::DexCacheData& data : GetDexCachesData()) { + if (soa.Self()->IsJWeakCleared(data.weak_root)) { + continue; + } + mirror::DexCache* dex_cache = + down_cast(soa.Self()->DecodeJObject(data.weak_root)); + if (dex_cache == nullptr) { + continue; + } + const DexFile* dex_file = dex_cache->GetDexFile(); + const std::string& location = dex_file->GetLocation(); + const size_t num_class_defs = dex_file->NumClassDefs(); + // Use the resolved types, this will miss array classes. + const size_t num_types = dex_file->NumTypeIds(); + VLOG(class_linker) << "Collecting class profile for dex file " << location + << " types=" << num_types << " class_defs=" << num_class_defs; + DexCacheResolvedClasses resolved_classes(dex_file->GetLocation(), + dex_file->GetLocationChecksum()); + size_t num_resolved = 0; + std::unordered_set class_set; + CHECK_EQ(num_types, dex_cache->NumResolvedTypes()); + for (size_t i = 0; i < num_types; ++i) { + mirror::Class* klass = dex_cache->GetResolvedType(i); + // Filter out null class loader since that is the boot class loader. + if (klass == nullptr || (ignore_boot_classes && klass->GetClassLoader() == nullptr)) { + continue; + } + ++num_resolved; + DCHECK(!klass->IsProxyClass()); + DCHECK(klass->IsResolved()); + mirror::DexCache* klass_dex_cache = klass->GetDexCache(); + if (klass_dex_cache == dex_cache) { + const size_t class_def_idx = klass->GetDexClassDefIndex(); + DCHECK(klass->IsResolved()); + CHECK_LT(class_def_idx, num_class_defs); + class_set.insert(class_def_idx); + } + } + + if (!class_set.empty()) { + auto it = ret.find(resolved_classes); + if (it != ret.end()) { + // Already have the key, union the class def idxs. + it->AddClasses(class_set.begin(), class_set.end()); + } else { + resolved_classes.AddClasses(class_set.begin(), class_set.end()); + ret.insert(resolved_classes); + } + } + + VLOG(class_linker) << "Dex location " << location << " has " << num_resolved << " / " + << num_class_defs << " resolved classes"; + } + VLOG(class_linker) << "Collecting class profile took " << PrettyDuration(NanoTime() - start_time); + return ret; +} + +std::unordered_set ClassLinker::GetClassDescriptorsForProfileKeys( + const std::set& classes) { + std::unordered_set ret; + Thread* const self = Thread::Current(); + std::unordered_map location_to_dex_file; + ScopedObjectAccess soa(self); + ScopedAssertNoThreadSuspension ants(soa.Self(), __FUNCTION__); + ReaderMutexLock mu(self, *DexLock()); + for (const ClassLinker::DexCacheData& data : GetDexCachesData()) { + if (!self->IsJWeakCleared(data.weak_root)) { + mirror::DexCache* dex_cache = + down_cast(soa.Self()->DecodeJObject(data.weak_root)); + if (dex_cache != nullptr) { + const DexFile* dex_file = dex_cache->GetDexFile(); + // There could be duplicates if two dex files with the same location are mapped. + location_to_dex_file.emplace( + ProfileCompilationInfo::GetProfileDexFileKey(dex_file->GetLocation()), dex_file); + } + } + } + for (const DexCacheResolvedClasses& info : classes) { + const std::string& profile_key = info.GetDexLocation(); + auto found = location_to_dex_file.find(profile_key); + if (found != location_to_dex_file.end()) { + const DexFile* dex_file = found->second; + VLOG(profiler) << "Found opened dex file for " << dex_file->GetLocation() << " with " + << info.GetClasses().size() << " classes"; + DCHECK_EQ(dex_file->GetLocationChecksum(), info.GetLocationChecksum()); + for (uint16_t class_def_idx : info.GetClasses()) { + if (class_def_idx >= dex_file->NumClassDefs()) { + LOG(WARNING) << "Class def index " << class_def_idx << " >= " << dex_file->NumClassDefs(); + continue; + } + const DexFile::TypeId& type_id = dex_file->GetTypeId( + dex_file->GetClassDef(class_def_idx).class_idx_); + const char* descriptor = dex_file->GetTypeDescriptor(type_id); + ret.insert(descriptor); + } + } else { + VLOG(class_linker) << "Failed to find opened dex file for profile key " << profile_key; + } + } + return ret; +} + // Instantiate ResolveMethod. template ArtMethod* ClassLinker::ResolveMethod( const DexFile& dex_file, diff --git a/runtime/class_linker.h b/runtime/class_linker.h index aa55dac7be..729617ddab 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -17,8 +17,10 @@ #ifndef ART_RUNTIME_CLASS_LINKER_H_ #define ART_RUNTIME_CLASS_LINKER_H_ +#include #include #include +#include #include #include @@ -27,6 +29,7 @@ #include "base/macros.h" #include "base/mutex.h" #include "class_table.h" +#include "dex_cache_resolved_classes.h" #include "dex_file.h" #include "gc_root.h" #include "jni.h" @@ -589,6 +592,13 @@ class ClassLinker { static bool ShouldUseInterpreterEntrypoint(ArtMethod* method, const void* quick_code) SHARED_REQUIRES(Locks::mutator_lock_); + std::set GetResolvedClasses(bool ignore_boot_classes) + REQUIRES(!dex_lock_); + + std::unordered_set GetClassDescriptorsForProfileKeys( + const std::set& classes) + REQUIRES(!dex_lock_); + struct DexCacheData { // Weak root to the DexCache. Note: Do not decode this unnecessarily or else class unloading may // not work properly. diff --git a/runtime/dex_cache_resolved_classes.h b/runtime/dex_cache_resolved_classes.h new file mode 100644 index 0000000000..80c12cb642 --- /dev/null +++ b/runtime/dex_cache_resolved_classes.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_DEX_CACHE_RESOLVED_CLASSES_H_ +#define ART_RUNTIME_DEX_CACHE_RESOLVED_CLASSES_H_ + +#include +#include +#include + +namespace art { + +// Data structure for passing around which classes belonging to a dex cache / dex file are resolved. +class DexCacheResolvedClasses { + public: + DexCacheResolvedClasses(const std::string& dex_location, uint32_t location_checksum) + : dex_location_(dex_location), + location_checksum_(location_checksum) {} + + // Only compare the key elements, ignore the resolved classes. + int Compare(const DexCacheResolvedClasses& other) const { + if (location_checksum_ != other.location_checksum_) { + return static_cast(location_checksum_ - other.location_checksum_); + } + return dex_location_.compare(other.dex_location_); + } + + template + void AddClasses(InputIt begin, InputIt end) const { + classes_.insert(begin, end); + } + + const std::string& GetDexLocation() const { + return dex_location_; + } + + uint32_t GetLocationChecksum() const { + return location_checksum_; + } + + const std::unordered_set& GetClasses() const { + return classes_; + } + + private: + const std::string dex_location_; + const uint32_t location_checksum_; + // Array of resolved class def indexes. + mutable std::unordered_set classes_; +}; + +inline bool operator<(const DexCacheResolvedClasses& a, const DexCacheResolvedClasses& b) { + return a.Compare(b) < 0; +} + +} // namespace art + +#endif // ART_RUNTIME_DEX_CACHE_RESOLVED_CLASSES_H_ diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc index 4ef36a449d..5aaf1045f9 100644 --- a/runtime/gc/space/image_space.cc +++ b/runtime/gc/space/image_space.cc @@ -1278,7 +1278,7 @@ ImageSpace* ImageSpace::Init(const char* image_filename, PROT_READ | PROT_WRITE, /*low_4gb*/true, /*reuse*/false, - out_error_msg)); + /*out*/out_error_msg)); if (map != nullptr) { const size_t stored_size = image_header->GetDataSize(); const size_t write_offset = sizeof(ImageHeader); // Skip the header. diff --git a/runtime/jit/offline_profiling_info.cc b/runtime/jit/offline_profiling_info.cc index 747b112f57..67c9b5f679 100644 --- a/runtime/jit/offline_profiling_info.cc +++ b/runtime/jit/offline_profiling_info.cc @@ -48,9 +48,11 @@ std::string ProfileCompilationInfo::GetProfileDexFileKey(const std::string& dex_ } } -bool ProfileCompilationInfo::SaveProfilingInfo(const std::string& filename, - const std::vector& methods) { - if (methods.empty()) { +bool ProfileCompilationInfo::SaveProfilingInfo( + const std::string& filename, + const std::vector& methods, + const std::set& resolved_classes) { + if (methods.empty() && resolved_classes.empty()) { VLOG(profiler) << "No info to save to " << filename; return true; } @@ -71,14 +73,17 @@ bool ProfileCompilationInfo::SaveProfilingInfo(const std::string& filename, } { ScopedObjectAccess soa(Thread::Current()); - for (auto it = methods.begin(); it != methods.end(); it++) { - const DexFile* dex_file = (*it)->GetDexFile(); - if (!info.AddData(GetProfileDexFileKey(dex_file->GetLocation()), - dex_file->GetLocationChecksum(), - (*it)->GetDexMethodIndex())) { + for (ArtMethod* method : methods) { + const DexFile* dex_file = method->GetDexFile(); + if (!info.AddMethodIndex(GetProfileDexFileKey(dex_file->GetLocation()), + dex_file->GetLocationChecksum(), + method->GetDexMethodIndex())) { return false; } } + for (const DexCacheResolvedClasses& dex_cache : resolved_classes) { + info.AddResolvedClasses(dex_cache); + } } if (!flock.GetFile()->ClearContent()) { @@ -116,13 +121,14 @@ static bool WriteToFile(int fd, const std::ostringstream& os) { static constexpr const char kFieldSeparator = ','; static constexpr const char kLineSeparator = '\n'; +static constexpr const char* kClassesMarker = "classes"; /** * Serialization format: - * dex_location1,dex_location_checksum1,method_id11,method_id12... - * dex_location2,dex_location_checksum2,method_id21,method_id22... + * dex_location1,dex_location_checksum1,method_id11,method_id12...,classes,class_id1,class_id2... + * dex_location2,dex_location_checksum2,method_id21,method_id22...,classes,class_id1,class_id2... * e.g. - * app.apk,131232145,11,23,454,54 + * app.apk,131232145,11,23,454,54,classes,1,2,4,1234 * app.apk:classes5.dex,218490184,39,13,49,1 **/ bool ProfileCompilationInfo::Save(int fd) { @@ -133,11 +139,20 @@ bool ProfileCompilationInfo::Save(int fd) { for (const auto& it : info_) { const std::string& dex_location = it.first; const DexFileData& dex_data = it.second; + if (dex_data.method_set.empty() && dex_data.class_set.empty()) { + continue; + } os << dex_location << kFieldSeparator << dex_data.checksum; for (auto method_it : dex_data.method_set) { os << kFieldSeparator << method_it; } + if (!dex_data.class_set.empty()) { + os << kFieldSeparator << kClassesMarker; + for (auto class_id : dex_data.class_set) { + os << kFieldSeparator << class_id; + } + } os << kLineSeparator; } @@ -168,18 +183,50 @@ static void SplitString(const std::string& s, char separator, std::vectorsecond.checksum != checksum) { LOG(WARNING) << "Checksum mismatch for dex " << dex_location; + return nullptr; + } + return &info_it->second; +} + +bool ProfileCompilationInfo::AddResolvedClasses(const DexCacheResolvedClasses& classes) { + const std::string dex_location = GetProfileDexFileKey(classes.GetDexLocation()); + const uint32_t checksum = classes.GetLocationChecksum(); + DexFileData* const data = GetOrAddDexFileData(dex_location, checksum); + if (data == nullptr) { return false; } - info_it->second.method_set.insert(method_idx); + data->class_set.insert(classes.GetClasses().begin(), classes.GetClasses().end()); + return true; +} + +bool ProfileCompilationInfo::AddMethodIndex(const std::string& dex_location, + uint32_t checksum, + uint16_t method_idx) { + DexFileData* const data = GetOrAddDexFileData(dex_location, checksum); + if (data == nullptr) { + return false; + } + data->method_set.insert(method_idx); + return true; +} + +bool ProfileCompilationInfo::AddClassIndex(const std::string& dex_location, + uint32_t checksum, + uint16_t class_idx) { + DexFileData* const data = GetOrAddDexFileData(dex_location, checksum); + if (data == nullptr) { + return false; + } + data->class_set.insert(class_idx); return true; } @@ -198,12 +245,30 @@ bool ProfileCompilationInfo::ProcessLine(const std::string& line) { } for (size_t i = 2; i < parts.size(); i++) { + if (parts[i] == kClassesMarker) { + ++i; + // All of the remaining idx are class def indexes. + for (++i; i < parts.size(); ++i) { + uint32_t class_def_idx; + if (!ParseInt(parts[i].c_str(), &class_def_idx)) { + LOG(WARNING) << "Cannot parse class_def_idx " << parts[i]; + return false; + } else if (class_def_idx >= std::numeric_limits::max()) { + LOG(WARNING) << "Class def idx " << class_def_idx << " is larger than uint16_t max"; + return false; + } + if (!AddClassIndex(dex_location, checksum, class_def_idx)) { + return false; + } + } + break; + } uint32_t method_idx; if (!ParseInt(parts[i].c_str(), &method_idx)) { LOG(WARNING) << "Cannot parse method_idx " << parts[i]; return false; } - if (!AddData(dex_location, checksum, method_idx)) { + if (!AddMethodIndex(dex_location, checksum, method_idx)) { return false; } } @@ -280,6 +345,8 @@ bool ProfileCompilationInfo::Load(const ProfileCompilationInfo& other) { } info_it->second.method_set.insert(other_dex_data.method_set.begin(), other_dex_data.method_set.end()); + info_it->second.class_set.insert(other_dex_data.class_set.begin(), + other_dex_data.class_set.end()); } return true; } @@ -347,4 +414,16 @@ bool ProfileCompilationInfo::Equals(const ProfileCompilationInfo& other) { return info_.Equals(other.info_); } +std::set ProfileCompilationInfo::GetResolvedClasses() const { + std::set ret; + for (auto&& pair : info_) { + const std::string& profile_key = pair.first; + const DexFileData& data = pair.second; + DexCacheResolvedClasses classes(profile_key, data.checksum); + classes.AddClasses(data.class_set.begin(), data.class_set.end()); + ret.insert(classes); + } + return ret; +} + } // namespace art diff --git a/runtime/jit/offline_profiling_info.h b/runtime/jit/offline_profiling_info.h index edc591c2eb..ee7ce27e4c 100644 --- a/runtime/jit/offline_profiling_info.h +++ b/runtime/jit/offline_profiling_info.h @@ -21,6 +21,7 @@ #include #include "atomic.h" +#include "dex_cache_resolved_classes.h" #include "dex_file.h" #include "method_reference.h" #include "safe_map.h" @@ -28,6 +29,7 @@ namespace art { class ArtMethod; +class DexCacheProfileData; // TODO: rename file. /** @@ -43,7 +45,8 @@ class ProfileCompilationInfo { // Note that the saving proceeds only if the file can be locked for exclusive access. // If not (the locking is not blocking), the function does not save and returns false. static bool SaveProfilingInfo(const std::string& filename, - const std::vector& methods); + const std::vector& methods, + const std::set& resolved_classes); // Loads profile information from the given file descriptor. bool Load(int fd); @@ -68,14 +71,17 @@ class ProfileCompilationInfo { bool Equals(const ProfileCompilationInfo& other); static std::string GetProfileDexFileKey(const std::string& dex_location); - private: - bool AddData(const std::string& dex_location, uint32_t checksum, uint16_t method_idx); - bool ProcessLine(const std::string& line); + // Returns the class descriptors for all of the classes in the profiles' class sets. + // Note the dex location is actually the profile key, the caller needs to call back in to the + // profile info stuff to generate a map back to the dex location. + std::set GetResolvedClasses() const; + private: struct DexFileData { explicit DexFileData(uint32_t location_checksum) : checksum(location_checksum) {} uint32_t checksum; std::set method_set; + std::set class_set; bool operator==(const DexFileData& other) const { return checksum == other.checksum && method_set == other.method_set; @@ -84,6 +90,13 @@ class ProfileCompilationInfo { using DexFileToProfileInfoMap = SafeMap; + DexFileData* GetOrAddDexFileData(const std::string& dex_location, uint32_t checksum); + bool AddMethodIndex(const std::string& dex_location, uint32_t checksum, uint16_t method_idx); + bool AddClassIndex(const std::string& dex_location, uint32_t checksum, uint16_t class_idx); + bool AddResolvedClasses(const DexCacheResolvedClasses& classes) + SHARED_REQUIRES(Locks::mutator_lock_); + bool ProcessLine(const std::string& line); + friend class ProfileCompilationInfoTest; friend class CompilerDriverProfileTest; friend class ProfileAssistantTest; diff --git a/runtime/jit/profile_compilation_info_test.cc b/runtime/jit/profile_compilation_info_test.cc index 482ea06395..fdd8c6e600 100644 --- a/runtime/jit/profile_compilation_info_test.cc +++ b/runtime/jit/profile_compilation_info_test.cc @@ -53,7 +53,7 @@ class ProfileCompilationInfoTest : public CommonRuntimeTest { uint32_t checksum, uint16_t method_index, ProfileCompilationInfo* info) { - return info->AddData(dex_location, checksum, method_index); + return info->AddMethodIndex(dex_location, checksum, method_index); } uint32_t GetFd(const ScratchFile& file) { @@ -73,8 +73,11 @@ TEST_F(ProfileCompilationInfoTest, SaveArtMethods) { ASSERT_NE(class_loader, nullptr); // Save virtual methods from Main. + std::set resolved_classes; std::vector main_methods = GetVirtualMethods(class_loader, "LMain;"); - ASSERT_TRUE(ProfileCompilationInfo::SaveProfilingInfo(profile.GetFilename(), main_methods)); + ASSERT_TRUE(ProfileCompilationInfo::SaveProfilingInfo(profile.GetFilename(), + main_methods, + resolved_classes)); // Check that what we saved is in the profile. ProfileCompilationInfo info1; @@ -89,7 +92,9 @@ TEST_F(ProfileCompilationInfoTest, SaveArtMethods) { // Save virtual methods from Second. std::vector second_methods = GetVirtualMethods(class_loader, "LSecond;"); - ASSERT_TRUE(ProfileCompilationInfo::SaveProfilingInfo(profile.GetFilename(), second_methods)); + ASSERT_TRUE(ProfileCompilationInfo::SaveProfilingInfo(profile.GetFilename(), + second_methods, + resolved_classes)); // Check that what we saved is in the profile (methods form Main and Second). ProfileCompilationInfo info2; diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc index b1a5a4b24c..ab26f6ffa9 100644 --- a/runtime/jit/profile_saver.cc +++ b/runtime/jit/profile_saver.cc @@ -32,6 +32,7 @@ static constexpr const uint64_t kMinimumTimeBetweenCodeCacheUpdatesNs = 2000 * k static constexpr const uint64_t kRandomDelayMaxMs = 20 * 1000; // 20 seconds static constexpr const uint64_t kMaxBackoffMs = 5 * 60 * 1000; // 5 minutes static constexpr const uint64_t kSavePeriodMs = 10 * 1000; // 10 seconds +static constexpr const uint64_t kInitialDelayMs = 2 * 1000; // 2 seconds static constexpr const double kBackoffCoef = 1.5; static constexpr const uint32_t kMinimumNrOrMethodsToSave = 10; @@ -45,6 +46,7 @@ ProfileSaver::ProfileSaver(const std::string& output_filename, : jit_code_cache_(jit_code_cache), code_cache_last_update_time_ns_(0), shutting_down_(false), + first_profile_(true), wait_lock_("ProfileSaver wait lock"), period_condition_("ProfileSaver period condition", wait_lock_) { AddTrackedLocations(output_filename, code_paths); @@ -56,13 +58,18 @@ void ProfileSaver::Run() { uint64_t save_period_ms = kSavePeriodMs; VLOG(profiler) << "Save profiling information every " << save_period_ms << " ms"; - while (true) { - if (ShuttingDown(self)) { - break; - } - uint64_t random_sleep_delay_ms = rand() % kRandomDelayMaxMs; - uint64_t sleep_time_ms = save_period_ms + random_sleep_delay_ms; + bool first_iteration = true; + while (!ShuttingDown(self)) { + uint64_t sleep_time_ms; + if (first_iteration) { + // Sleep less long for the first iteration since we want to record loaded classes shortly + // after app launch. + sleep_time_ms = kInitialDelayMs; + } else { + const uint64_t random_sleep_delay_ms = rand() % kRandomDelayMaxMs; + sleep_time_ms = save_period_ms + random_sleep_delay_ms; + } { MutexLock mu(self, wait_lock_); period_condition_.TimedWait(self, sleep_time_ms, 0); @@ -81,13 +88,14 @@ void ProfileSaver::Run() { // Reset the period to the initial value as it's highly likely to JIT again. save_period_ms = kSavePeriodMs; } + first_iteration = false; } } bool ProfileSaver::ProcessProfilingInfo() { uint64_t last_update_time_ns = jit_code_cache_->GetLastUpdateTimeNs(); - if (last_update_time_ns - code_cache_last_update_time_ns_ - < kMinimumTimeBetweenCodeCacheUpdatesNs) { + if (!first_profile_ && last_update_time_ns - code_cache_last_update_time_ns_ + < kMinimumTimeBetweenCodeCacheUpdatesNs) { VLOG(profiler) << "Not enough time has passed since the last code cache update." << "Last update: " << last_update_time_ns << " Last save: " << code_cache_last_update_time_ns_; @@ -113,19 +121,27 @@ bool ProfileSaver::ProcessProfilingInfo() { ScopedObjectAccess soa(Thread::Current()); jit_code_cache_->GetCompiledArtMethods(locations, methods); } - if (methods.size() < kMinimumNrOrMethodsToSave) { + // Always save for the first one for loaded classes profile. + if (methods.size() < kMinimumNrOrMethodsToSave && !first_profile_) { VLOG(profiler) << "Not enough information to save to: " << filename <<" Nr of methods: " << methods.size(); return false; } - if (!ProfileCompilationInfo::SaveProfilingInfo(filename, methods)) { + std::set resolved_classes; + if (first_profile_) { + ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); + resolved_classes = class_linker->GetResolvedClasses(/*ignore boot classes*/true); + } + + if (!ProfileCompilationInfo::SaveProfilingInfo(filename, methods, resolved_classes)) { LOG(WARNING) << "Could not save profiling info to " << filename; return false; } VLOG(profiler) << "Profile process time: " << PrettyDuration(NanoTime() - start); } + first_profile_ = false; return true; } diff --git a/runtime/jit/profile_saver.h b/runtime/jit/profile_saver.h index 3342790e43..21017c1acd 100644 --- a/runtime/jit/profile_saver.h +++ b/runtime/jit/profile_saver.h @@ -74,6 +74,7 @@ class ProfileSaver { GUARDED_BY(Locks::profiler_lock_); uint64_t code_cache_last_update_time_ns_; bool shutting_down_ GUARDED_BY(Locks::profiler_lock_); + bool first_profile_ = true; // Save period condition support. Mutex wait_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; -- GitLab From bc4d218ce2ceef0c4f308d4ff42f7ec1ec43c40e Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Mon, 22 Feb 2016 10:03:12 -0800 Subject: [PATCH 061/204] ART: Add unstarted-runtime functions Add more functions to allow compile-time initialization of code. Bug: 27248115 (cherry picked from commit 0866f4ed6338faa4a193b7e819fc7cd72bd7b0ae) Change-Id: Iaf8d92deb73547ccd31c0d6dde68da3bc14c3985 --- runtime/atomic.cc | 4 +- runtime/atomic.h | 23 +-- runtime/base/mutex.h | 2 +- runtime/interpreter/unstarted_runtime.cc | 188 ++++++++++++++++++ runtime/interpreter/unstarted_runtime_list.h | 11 +- runtime/mirror/abstract_method.cc | 25 ++- runtime/mirror/abstract_method.h | 2 + runtime/mirror/class.cc | 84 ++++++++ runtime/mirror/class.h | 8 + runtime/mirror/method.cc | 6 +- runtime/mirror/method.h | 1 + runtime/native/java_lang_Class.cc | 69 +------ .../java_util_concurrent_atomic_AtomicLong.cc | 3 +- 13 files changed, 337 insertions(+), 89 deletions(-) diff --git a/runtime/atomic.cc b/runtime/atomic.cc index e766a8d77d..d5ae570c30 100644 --- a/runtime/atomic.cc +++ b/runtime/atomic.cc @@ -28,7 +28,7 @@ Mutex* QuasiAtomic::GetSwapMutex(const volatile int64_t* addr) { } void QuasiAtomic::Startup() { - if (kNeedSwapMutexes) { + if (NeedSwapMutexes(kRuntimeISA)) { gSwapMutexes = new std::vector; for (size_t i = 0; i < kSwapMutexCount; ++i) { gSwapMutexes->push_back(new Mutex("QuasiAtomic stripe", kSwapMutexesLock)); @@ -37,7 +37,7 @@ void QuasiAtomic::Startup() { } void QuasiAtomic::Shutdown() { - if (kNeedSwapMutexes) { + if (NeedSwapMutexes(kRuntimeISA)) { STLDeleteElements(gSwapMutexes); delete gSwapMutexes; } diff --git a/runtime/atomic.h b/runtime/atomic.h index d4a7f37bc6..e2a7259784 100644 --- a/runtime/atomic.h +++ b/runtime/atomic.h @@ -22,6 +22,7 @@ #include #include +#include "arch/instruction_set.h" #include "base/logging.h" #include "base/macros.h" @@ -44,14 +45,10 @@ class Mutex; // quasiatomic operations that are performed on partially-overlapping // memory. class QuasiAtomic { -#if defined(__mips__) && !defined(__LP64__) - static constexpr bool kNeedSwapMutexes = true; -#elif defined(__mips__) && defined(__LP64__) - // TODO - mips64 still need this for Cas64 ??? - static constexpr bool kNeedSwapMutexes = true; -#else - static constexpr bool kNeedSwapMutexes = false; -#endif + static constexpr bool NeedSwapMutexes(InstructionSet isa) { + // TODO - mips64 still need this for Cas64 ??? + return (isa == kMips) || (isa == kMips64); + } public: static void Startup(); @@ -60,7 +57,7 @@ class QuasiAtomic { // Reads the 64-bit value at "addr" without tearing. static int64_t Read64(volatile const int64_t* addr) { - if (!kNeedSwapMutexes) { + if (!NeedSwapMutexes(kRuntimeISA)) { int64_t value; #if defined(__LP64__) value = *addr; @@ -96,7 +93,7 @@ class QuasiAtomic { // Writes to the 64-bit value at "addr" without tearing. static void Write64(volatile int64_t* addr, int64_t value) { - if (!kNeedSwapMutexes) { + if (!NeedSwapMutexes(kRuntimeISA)) { #if defined(__LP64__) *addr = value; #else @@ -142,7 +139,7 @@ class QuasiAtomic { // at some point during the execution of Cas64, *addr was not equal to // old_value. static bool Cas64(int64_t old_value, int64_t new_value, volatile int64_t* addr) { - if (!kNeedSwapMutexes) { + if (!NeedSwapMutexes(kRuntimeISA)) { return __sync_bool_compare_and_swap(addr, old_value, new_value); } else { return SwapMutexCas64(old_value, new_value, addr); @@ -150,8 +147,8 @@ class QuasiAtomic { } // Does the architecture provide reasonable atomic long operations or do we fall back on mutexes? - static bool LongAtomicsUseMutexes() { - return kNeedSwapMutexes; + static bool LongAtomicsUseMutexes(InstructionSet isa) { + return NeedSwapMutexes(isa); } static void ThreadFenceAcquire() { diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h index e72f2a2e7b..293451c4bf 100644 --- a/runtime/base/mutex.h +++ b/runtime/base/mutex.h @@ -66,8 +66,8 @@ enum LockLevel { kRosAllocGlobalLock, kRosAllocBracketLock, kRosAllocBulkFreeLock, - kTransactionLogLock, kMarkSweepMarkStackLock, + kTransactionLogLock, kJniWeakGlobalsLock, kReferenceQueueSoftReferencesLock, kReferenceQueuePhantomReferencesLock, diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc index 0e175b85eb..b21f1ecc8b 100644 --- a/runtime/interpreter/unstarted_runtime.cc +++ b/runtime/interpreter/unstarted_runtime.cc @@ -22,11 +22,13 @@ #include "ScopedLocalRef.h" #include "art_method-inl.h" +#include "base/casts.h" #include "base/logging.h" #include "base/macros.h" #include "class_linker.h" #include "common_throws.h" #include "entrypoints/entrypoint_utils-inl.h" +#include "gc/reference_processor.h" #include "handle_scope-inl.h" #include "interpreter/interpreter_common.h" #include "mirror/array-inl.h" @@ -261,6 +263,25 @@ void UnstartedRuntime::UnstartedClassGetDeclaredField( } } +// This is required for Enum(Set) code, as that uses reflection to inspect enum classes. +void UnstartedRuntime::UnstartedClassGetDeclaredMethod( + Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) { + // Special managed code cut-out to allow method lookup in a un-started runtime. + mirror::Class* klass = shadow_frame->GetVRegReference(arg_offset)->AsClass(); + if (klass == nullptr) { + ThrowNullPointerExceptionForMethodAccess(shadow_frame->GetMethod(), InvokeType::kVirtual); + return; + } + mirror::String* name = shadow_frame->GetVRegReference(arg_offset + 1)->AsString(); + mirror::ObjectArray* args = + shadow_frame->GetVRegReference(arg_offset + 2)->AsObjectArray(); + if (Runtime::Current()->IsActiveTransaction()) { + result->SetL(mirror::Class::GetDeclaredMethodInternal(self, klass, name, args)); + } else { + result->SetL(mirror::Class::GetDeclaredMethodInternal(self, klass, name, args)); + } +} + void UnstartedRuntime::UnstartedClassGetEnclosingClass( Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) { StackHandleScope<1> hs(self); @@ -860,6 +881,155 @@ void UnstartedRuntime::UnstartedStringToCharArray( result->SetL(string->ToCharArray(self)); } +// This allows statically initializing ConcurrentHashMap and SynchronousQueue. +void UnstartedRuntime::UnstartedReferenceGetReferent( + Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) { + mirror::Reference* const ref = down_cast( + shadow_frame->GetVRegReference(arg_offset)); + if (ref == nullptr) { + AbortTransactionOrFail(self, "Reference.getReferent() with null object"); + return; + } + mirror::Object* const referent = + Runtime::Current()->GetHeap()->GetReferenceProcessor()->GetReferent(self, ref); + result->SetL(referent); +} + +// This allows statically initializing ConcurrentHashMap and SynchronousQueue. We use a somewhat +// conservative upper bound. We restrict the callers to SynchronousQueue and ConcurrentHashMap, +// where we can predict the behavior (somewhat). +// Note: this is required (instead of lazy initialization) as these classes are used in the static +// initialization of other classes, so will *use* the value. +void UnstartedRuntime::UnstartedRuntimeAvailableProcessors( + Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset ATTRIBUTE_UNUSED) { + std::string caller(PrettyMethod(shadow_frame->GetLink()->GetMethod())); + if (caller == "void java.util.concurrent.SynchronousQueue.()") { + // SynchronousQueue really only separates between single- and multiprocessor case. Return + // 8 as a conservative upper approximation. + result->SetI(8); + } else if (caller == "void java.util.concurrent.ConcurrentHashMap.()") { + // ConcurrentHashMap uses it for striding. 8 still seems an OK general value, as it's likely + // a good upper bound. + // TODO: Consider resetting in the zygote? + result->SetI(8); + } else { + // Not supported. + AbortTransactionOrFail(self, "Accessing availableProcessors not allowed"); + } +} + +// This allows accessing ConcurrentHashMap/SynchronousQueue. + +void UnstartedRuntime::UnstartedUnsafeCompareAndSwapLong( + Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) { + // Argument 0 is the Unsafe instance, skip. + mirror::Object* obj = shadow_frame->GetVRegReference(arg_offset + 1); + if (obj == nullptr) { + AbortTransactionOrFail(self, "Cannot access null object, retry at runtime."); + return; + } + int64_t offset = shadow_frame->GetVRegLong(arg_offset + 2); + int64_t expectedValue = shadow_frame->GetVRegLong(arg_offset + 4); + int64_t newValue = shadow_frame->GetVRegLong(arg_offset + 6); + + // Must use non transactional mode. + if (kUseReadBarrier) { + // Need to make sure the reference stored in the field is a to-space one before attempting the + // CAS or the CAS could fail incorrectly. + mirror::HeapReference* field_addr = + reinterpret_cast*>( + reinterpret_cast(obj) + static_cast(offset)); + ReadBarrier::Barrier( + obj, + MemberOffset(offset), + field_addr); + } + bool success; + // Check whether we're in a transaction, call accordingly. + if (Runtime::Current()->IsActiveTransaction()) { + success = obj->CasFieldStrongSequentiallyConsistent64(MemberOffset(offset), + expectedValue, + newValue); + } else { + success = obj->CasFieldStrongSequentiallyConsistent64(MemberOffset(offset), + expectedValue, + newValue); + } + result->SetZ(success ? 1 : 0); +} + +void UnstartedRuntime::UnstartedUnsafeCompareAndSwapObject( + Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) { + // Argument 0 is the Unsafe instance, skip. + mirror::Object* obj = shadow_frame->GetVRegReference(arg_offset + 1); + if (obj == nullptr) { + AbortTransactionOrFail(self, "Cannot access null object, retry at runtime."); + return; + } + int64_t offset = shadow_frame->GetVRegLong(arg_offset + 2); + mirror::Object* expected_value = shadow_frame->GetVRegReference(arg_offset + 4); + mirror::Object* newValue = shadow_frame->GetVRegReference(arg_offset + 5); + + // Must use non transactional mode. + if (kUseReadBarrier) { + // Need to make sure the reference stored in the field is a to-space one before attempting the + // CAS or the CAS could fail incorrectly. + mirror::HeapReference* field_addr = + reinterpret_cast*>( + reinterpret_cast(obj) + static_cast(offset)); + ReadBarrier::Barrier( + obj, + MemberOffset(offset), + field_addr); + } + bool success; + // Check whether we're in a transaction, call accordingly. + if (Runtime::Current()->IsActiveTransaction()) { + success = obj->CasFieldStrongSequentiallyConsistentObject(MemberOffset(offset), + expected_value, + newValue); + } else { + success = obj->CasFieldStrongSequentiallyConsistentObject(MemberOffset(offset), + expected_value, + newValue); + } + result->SetZ(success ? 1 : 0); +} + +void UnstartedRuntime::UnstartedUnsafeGetObjectVolatile( + Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) + SHARED_REQUIRES(Locks::mutator_lock_) { + // Argument 0 is the Unsafe instance, skip. + mirror::Object* obj = shadow_frame->GetVRegReference(arg_offset + 1); + if (obj == nullptr) { + AbortTransactionOrFail(self, "Cannot access null object, retry at runtime."); + return; + } + int64_t offset = shadow_frame->GetVRegLong(arg_offset + 2); + mirror::Object* value = obj->GetFieldObjectVolatile(MemberOffset(offset)); + result->SetL(value); +} + +void UnstartedRuntime::UnstartedUnsafePutOrderedObject( + Thread* self, ShadowFrame* shadow_frame, JValue* result ATTRIBUTE_UNUSED, size_t arg_offset) + SHARED_REQUIRES(Locks::mutator_lock_) { + // Argument 0 is the Unsafe instance, skip. + mirror::Object* obj = shadow_frame->GetVRegReference(arg_offset + 1); + if (obj == nullptr) { + AbortTransactionOrFail(self, "Cannot access null object, retry at runtime."); + return; + } + int64_t offset = shadow_frame->GetVRegLong(arg_offset + 2); + mirror::Object* newValue = shadow_frame->GetVRegReference(arg_offset + 4); + QuasiAtomic::ThreadFenceRelease(); + if (Runtime::Current()->IsActiveTransaction()) { + obj->SetFieldObject(MemberOffset(offset), newValue); + } else { + obj->SetFieldObject(MemberOffset(offset), newValue); + } +} + + void UnstartedRuntime::UnstartedJNIVMRuntimeNewUnpaddedArray( Thread* self, ArtMethod* method ATTRIBUTE_UNUSED, mirror::Object* receiver ATTRIBUTE_UNUSED, uint32_t* args, JValue* result) { @@ -906,6 +1076,17 @@ void UnstartedRuntime::UnstartedJNIMathExp( result->SetD(exp(value.GetD())); } +void UnstartedRuntime::UnstartedJNIAtomicLongVMSupportsCS8( + Thread* self ATTRIBUTE_UNUSED, + ArtMethod* method ATTRIBUTE_UNUSED, + mirror::Object* receiver ATTRIBUTE_UNUSED, + uint32_t* args ATTRIBUTE_UNUSED, + JValue* result) { + result->SetZ(QuasiAtomic::LongAtomicsUseMutexes(Runtime::Current()->GetInstructionSet()) + ? 0 + : 1); +} + void UnstartedRuntime::UnstartedJNIClassGetNameNative( Thread* self, ArtMethod* method ATTRIBUTE_UNUSED, mirror::Object* receiver, uint32_t* args ATTRIBUTE_UNUSED, JValue* result) { @@ -913,6 +1094,13 @@ void UnstartedRuntime::UnstartedJNIClassGetNameNative( result->SetL(mirror::Class::ComputeName(hs.NewHandle(receiver->AsClass()))); } +void UnstartedRuntime::UnstartedJNIDoubleLongBitsToDouble( + Thread* self ATTRIBUTE_UNUSED, ArtMethod* method ATTRIBUTE_UNUSED, + mirror::Object* receiver ATTRIBUTE_UNUSED, uint32_t* args, JValue* result) { + uint64_t long_input = args[0] | (static_cast(args[1]) << 32); + result->SetD(bit_cast(long_input)); +} + void UnstartedRuntime::UnstartedJNIFloatFloatToRawIntBits( Thread* self ATTRIBUTE_UNUSED, ArtMethod* method ATTRIBUTE_UNUSED, mirror::Object* receiver ATTRIBUTE_UNUSED, uint32_t* args, JValue* result) { diff --git a/runtime/interpreter/unstarted_runtime_list.h b/runtime/interpreter/unstarted_runtime_list.h index 6d4d711645..29f2197a03 100644 --- a/runtime/interpreter/unstarted_runtime_list.h +++ b/runtime/interpreter/unstarted_runtime_list.h @@ -24,6 +24,7 @@ V(ClassClassForName, "java.lang.Class java.lang.Class.classForName(java.lang.String, boolean, java.lang.ClassLoader)") \ V(ClassNewInstance, "java.lang.Object java.lang.Class.newInstance()") \ V(ClassGetDeclaredField, "java.lang.reflect.Field java.lang.Class.getDeclaredField(java.lang.String)") \ + V(ClassGetDeclaredMethod, "java.lang.reflect.Method java.lang.Class.getDeclaredMethodInternal(java.lang.String, java.lang.Class[])") \ V(ClassGetEnclosingClass, "java.lang.Class java.lang.Class.getEnclosingClass()") \ V(VmClassLoaderFindLoadedClass, "java.lang.Class java.lang.VMClassLoader.findLoadedClass(java.lang.ClassLoader, java.lang.String)") \ V(VoidLookupType, "java.lang.Class java.lang.Void.lookupType()") \ @@ -40,6 +41,8 @@ V(MemoryPeekInt, "int libcore.io.Memory.peekIntNative(long)") \ V(MemoryPeekLong, "long libcore.io.Memory.peekLongNative(long)") \ V(MemoryPeekByteArray, "void libcore.io.Memory.peekByteArray(long, byte[], int, int)") \ + V(ReferenceGetReferent, "java.lang.Object java.lang.ref.Reference.getReferent()") \ + V(RuntimeAvailableProcessors, "int java.lang.Runtime.availableProcessors()") \ V(SecurityGetSecurityPropertiesReader, "java.io.Reader java.security.Security.getSecurityPropertiesReader()") \ V(StringGetCharsNoCheck, "void java.lang.String.getCharsNoCheck(int, int, char[], int)") \ V(StringCharAt, "char java.lang.String.charAt(int)") \ @@ -47,7 +50,11 @@ V(StringFactoryNewStringFromChars, "java.lang.String java.lang.StringFactory.newStringFromChars(int, int, char[])") \ V(StringFactoryNewStringFromString, "java.lang.String java.lang.StringFactory.newStringFromString(java.lang.String)") \ V(StringFastSubstring, "java.lang.String java.lang.String.fastSubstring(int, int)") \ - V(StringToCharArray, "char[] java.lang.String.toCharArray()") + V(StringToCharArray, "char[] java.lang.String.toCharArray()") \ + V(UnsafeCompareAndSwapLong, "boolean sun.misc.Unsafe.compareAndSwapLong(java.lang.Object, long, long, long)") \ + V(UnsafeCompareAndSwapObject, "boolean sun.misc.Unsafe.compareAndSwapObject(java.lang.Object, long, java.lang.Object, java.lang.Object)") \ + V(UnsafeGetObjectVolatile, "java.lang.Object sun.misc.Unsafe.getObjectVolatile(java.lang.Object, long)") \ + V(UnsafePutOrderedObject, "void sun.misc.Unsafe.putOrderedObject(java.lang.Object, long, java.lang.Object)") // Methods that are native. #define UNSTARTED_RUNTIME_JNI_LIST(V) \ @@ -56,7 +63,9 @@ V(VMStackGetStackClass2, "java.lang.Class dalvik.system.VMStack.getStackClass2()") \ V(MathLog, "double java.lang.Math.log(double)") \ V(MathExp, "double java.lang.Math.exp(double)") \ + V(AtomicLongVMSupportsCS8, "boolean java.util.concurrent.atomic.AtomicLong.VMSupportsCS8()") \ V(ClassGetNameNative, "java.lang.String java.lang.Class.getNameNative()") \ + V(DoubleLongBitsToDouble, "double java.lang.Double.longBitsToDouble(long)") \ V(FloatFloatToRawIntBits, "int java.lang.Float.floatToRawIntBits(float)") \ V(FloatIntBitsToFloat, "float java.lang.Float.intBitsToFloat(int)") \ V(ObjectInternalClone, "java.lang.Object java.lang.Object.internalClone()") \ diff --git a/runtime/mirror/abstract_method.cc b/runtime/mirror/abstract_method.cc index 91a98707c4..5a07deee51 100644 --- a/runtime/mirror/abstract_method.cc +++ b/runtime/mirror/abstract_method.cc @@ -21,25 +21,36 @@ namespace art { namespace mirror { +template bool AbstractMethod::CreateFromArtMethod(ArtMethod* method) { - auto* interface_method = method->GetInterfaceMethodIfProxy(sizeof(void*)); - SetArtMethod(method); - SetFieldObject(DeclaringClassOffset(), method->GetDeclaringClass()); - SetFieldObject( + auto* interface_method = method->GetInterfaceMethodIfProxy( + kTransactionActive + ? Runtime::Current()->GetClassLinker()->GetImagePointerSize() + : sizeof(void*)); + SetArtMethod(method); + SetFieldObject(DeclaringClassOffset(), method->GetDeclaringClass()); + SetFieldObject( DeclaringClassOfOverriddenMethodOffset(), interface_method->GetDeclaringClass()); - SetField32(AccessFlagsOffset(), method->GetAccessFlags()); - SetField32(DexMethodIndexOffset(), method->GetDexMethodIndex()); + SetField32(AccessFlagsOffset(), method->GetAccessFlags()); + SetField32(DexMethodIndexOffset(), method->GetDexMethodIndex()); return true; } +template bool AbstractMethod::CreateFromArtMethod(ArtMethod* method); +template bool AbstractMethod::CreateFromArtMethod(ArtMethod* method); + ArtMethod* AbstractMethod::GetArtMethod() { return reinterpret_cast(GetField64(ArtMethodOffset())); } +template void AbstractMethod::SetArtMethod(ArtMethod* method) { - SetField64(ArtMethodOffset(), reinterpret_cast(method)); + SetField64(ArtMethodOffset(), reinterpret_cast(method)); } +template void AbstractMethod::SetArtMethod(ArtMethod* method); +template void AbstractMethod::SetArtMethod(ArtMethod* method); + mirror::Class* AbstractMethod::GetDeclaringClass() { return GetFieldObject(DeclaringClassOffset()); } diff --git a/runtime/mirror/abstract_method.h b/runtime/mirror/abstract_method.h index dc084be06e..a39f94d395 100644 --- a/runtime/mirror/abstract_method.h +++ b/runtime/mirror/abstract_method.h @@ -34,11 +34,13 @@ namespace mirror { class MANAGED AbstractMethod : public AccessibleObject { public: // Called from Constructor::CreateFromArtMethod, Method::CreateFromArtMethod. + template bool CreateFromArtMethod(ArtMethod* method) SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); ArtMethod* GetArtMethod() SHARED_REQUIRES(Locks::mutator_lock_); // Only used by the image writer. + template void SetArtMethod(ArtMethod* method) SHARED_REQUIRES(Locks::mutator_lock_); mirror::Class* GetDeclaringClass() SHARED_REQUIRES(Locks::mutator_lock_); diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc index 9190e44144..7900eac4e0 100644 --- a/runtime/mirror/class.cc +++ b/runtime/mirror/class.cc @@ -1054,5 +1054,89 @@ uint32_t Class::FindTypeIndexInOtherDexFile(const DexFile& dex_file) { return (type_id == nullptr) ? DexFile::kDexNoIndex : dex_file.GetIndexForTypeId(*type_id); } +template +mirror::Method* Class::GetDeclaredMethodInternal(Thread* self, + mirror::Class* klass, + mirror::String* name, + mirror::ObjectArray* args) { + // Covariant return types permit the class to define multiple + // methods with the same name and parameter types. Prefer to + // return a non-synthetic method in such situations. We may + // still return a synthetic method to handle situations like + // escalated visibility. We never return miranda methods that + // were synthesized by the runtime. + constexpr uint32_t kSkipModifiers = kAccMiranda | kAccSynthetic; + StackHandleScope<3> hs(self); + auto h_method_name = hs.NewHandle(name); + if (UNLIKELY(h_method_name.Get() == nullptr)) { + ThrowNullPointerException("name == null"); + return nullptr; + } + auto h_args = hs.NewHandle(args); + Handle h_klass = hs.NewHandle(klass); + ArtMethod* result = nullptr; + const size_t pointer_size = kTransactionActive + ? Runtime::Current()->GetClassLinker()->GetImagePointerSize() + : sizeof(void*); + for (auto& m : h_klass->GetDeclaredVirtualMethods(pointer_size)) { + auto* np_method = m.GetInterfaceMethodIfProxy(pointer_size); + // May cause thread suspension. + mirror::String* np_name = np_method->GetNameAsString(self); + if (!np_name->Equals(h_method_name.Get()) || !np_method->EqualParameters(h_args)) { + if (UNLIKELY(self->IsExceptionPending())) { + return nullptr; + } + continue; + } + auto modifiers = m.GetAccessFlags(); + if ((modifiers & kSkipModifiers) == 0) { + return mirror::Method::CreateFromArtMethod(self, &m); + } + if ((modifiers & kAccMiranda) == 0) { + result = &m; // Remember as potential result if it's not a miranda method. + } + } + if (result == nullptr) { + for (auto& m : h_klass->GetDirectMethods(pointer_size)) { + auto modifiers = m.GetAccessFlags(); + if ((modifiers & kAccConstructor) != 0) { + continue; + } + auto* np_method = m.GetInterfaceMethodIfProxy(pointer_size); + // May cause thread suspension. + mirror::String* np_name = np_method->GetNameAsString(self); + if (np_name == nullptr) { + self->AssertPendingException(); + return nullptr; + } + if (!np_name->Equals(h_method_name.Get()) || !np_method->EqualParameters(h_args)) { + if (UNLIKELY(self->IsExceptionPending())) { + return nullptr; + } + continue; + } + if ((modifiers & kSkipModifiers) == 0) { + return mirror::Method::CreateFromArtMethod(self, &m); + } + // Direct methods cannot be miranda methods, so this potential result must be synthetic. + result = &m; + } + } + return result != nullptr + ? mirror::Method::CreateFromArtMethod(self, result) + : nullptr; +} + +template +mirror::Method* Class::GetDeclaredMethodInternal(Thread* self, + mirror::Class* klass, + mirror::String* name, + mirror::ObjectArray* args); +template +mirror::Method* Class::GetDeclaredMethodInternal(Thread* self, + mirror::Class* klass, + mirror::String* name, + mirror::ObjectArray* args); + } // namespace mirror } // namespace art diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h index 6e3463c25c..7082c886aa 100644 --- a/runtime/mirror/class.h +++ b/runtime/mirror/class.h @@ -55,6 +55,7 @@ class ClassLoader; class Constructor; class DexCache; class IfTable; +class Method; // C++ mirror of java.lang.Class class MANAGED Class FINAL : public Object { @@ -759,6 +760,13 @@ class MANAGED Class FINAL : public Object { size_t pointer_size) SHARED_REQUIRES(Locks::mutator_lock_); + template + static Method* GetDeclaredMethodInternal(Thread* self, + mirror::Class* klass, + mirror::String* name, + mirror::ObjectArray* args) + SHARED_REQUIRES(Locks::mutator_lock_); + template ALWAYS_INLINE ArraySlice GetDeclaredVirtualMethodsSlice(size_t pointer_size) SHARED_REQUIRES(Locks::mutator_lock_); diff --git a/runtime/mirror/method.cc b/runtime/mirror/method.cc index 85c52e95df..97973e6277 100644 --- a/runtime/mirror/method.cc +++ b/runtime/mirror/method.cc @@ -51,15 +51,19 @@ void Method::ResetArrayClass() { array_class_ = GcRoot(nullptr); } +template Method* Method::CreateFromArtMethod(Thread* self, ArtMethod* method) { DCHECK(!method->IsConstructor()) << PrettyMethod(method); auto* ret = down_cast(StaticClass()->AllocObject(self)); if (LIKELY(ret != nullptr)) { - static_cast(ret)->CreateFromArtMethod(method); + static_cast(ret)->CreateFromArtMethod(method); } return ret; } +template Method* Method::CreateFromArtMethod(Thread* self, ArtMethod* method); +template Method* Method::CreateFromArtMethod(Thread* self, ArtMethod* method); + void Method::VisitRoots(RootVisitor* visitor) { static_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass)); array_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass)); diff --git a/runtime/mirror/method.h b/runtime/mirror/method.h index 0c28e4f580..12a72fe443 100644 --- a/runtime/mirror/method.h +++ b/runtime/mirror/method.h @@ -28,6 +28,7 @@ class Class; // C++ mirror of java.lang.reflect.Method. class MANAGED Method : public AbstractMethod { public: + template static Method* CreateFromArtMethod(Thread* self, ArtMethod* method) SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc index b5d859b6b8..bf24de5284 100644 --- a/runtime/native/java_lang_Class.cc +++ b/runtime/native/java_lang_Class.cc @@ -371,70 +371,13 @@ static jobjectArray Class_getDeclaredConstructorsInternal( static jobject Class_getDeclaredMethodInternal(JNIEnv* env, jobject javaThis, jobject name, jobjectArray args) { - // Covariant return types permit the class to define multiple - // methods with the same name and parameter types. Prefer to - // return a non-synthetic method in such situations. We may - // still return a synthetic method to handle situations like - // escalated visibility. We never return miranda methods that - // were synthesized by the runtime. - constexpr uint32_t kSkipModifiers = kAccMiranda | kAccSynthetic; ScopedFastNativeObjectAccess soa(env); - StackHandleScope<3> hs(soa.Self()); - auto h_method_name = hs.NewHandle(soa.Decode(name)); - if (UNLIKELY(h_method_name.Get() == nullptr)) { - ThrowNullPointerException("name == null"); - return nullptr; - } - auto h_args = hs.NewHandle(soa.Decode*>(args)); - Handle h_klass = hs.NewHandle(DecodeClass(soa, javaThis)); - ArtMethod* result = nullptr; - for (auto& m : h_klass->GetDeclaredVirtualMethods(sizeof(void*))) { - auto* np_method = m.GetInterfaceMethodIfProxy(sizeof(void*)); - // May cause thread suspension. - mirror::String* np_name = np_method->GetNameAsString(soa.Self()); - if (!np_name->Equals(h_method_name.Get()) || !np_method->EqualParameters(h_args)) { - if (UNLIKELY(soa.Self()->IsExceptionPending())) { - return nullptr; - } - continue; - } - auto modifiers = m.GetAccessFlags(); - if ((modifiers & kSkipModifiers) == 0) { - return soa.AddLocalReference(mirror::Method::CreateFromArtMethod(soa.Self(), &m)); - } - if ((modifiers & kAccMiranda) == 0) { - result = &m; // Remember as potential result if it's not a miranda method. - } - } - if (result == nullptr) { - for (auto& m : h_klass->GetDirectMethods(sizeof(void*))) { - auto modifiers = m.GetAccessFlags(); - if ((modifiers & kAccConstructor) != 0) { - continue; - } - auto* np_method = m.GetInterfaceMethodIfProxy(sizeof(void*)); - // May cause thread suspension. - mirror::String* np_name = np_method->GetNameAsString(soa.Self()); - if (np_name == nullptr) { - soa.Self()->AssertPendingException(); - return nullptr; - } - if (!np_name->Equals(h_method_name.Get()) || !np_method->EqualParameters(h_args)) { - if (UNLIKELY(soa.Self()->IsExceptionPending())) { - return nullptr; - } - continue; - } - if ((modifiers & kSkipModifiers) == 0) { - return soa.AddLocalReference(mirror::Method::CreateFromArtMethod(soa.Self(), &m)); - } - // Direct methods cannot be miranda methods, so this potential result must be synthetic. - result = &m; - } - } - return result != nullptr ? - soa.AddLocalReference(mirror::Method::CreateFromArtMethod(soa.Self(), result)) : - nullptr; + mirror::Method* result = mirror::Class::GetDeclaredMethodInternal( + soa.Self(), + DecodeClass(soa, javaThis), + soa.Decode(name), + soa.Decode*>(args)); + return soa.AddLocalReference(result); } static jobjectArray Class_getDeclaredMethodsUnchecked(JNIEnv* env, jobject javaThis, diff --git a/runtime/native/java_util_concurrent_atomic_AtomicLong.cc b/runtime/native/java_util_concurrent_atomic_AtomicLong.cc index 04f0ba0c19..4d2ea6794c 100644 --- a/runtime/native/java_util_concurrent_atomic_AtomicLong.cc +++ b/runtime/native/java_util_concurrent_atomic_AtomicLong.cc @@ -16,13 +16,14 @@ #include "java_util_concurrent_atomic_AtomicLong.h" +#include "arch/instruction_set.h" #include "atomic.h" #include "jni_internal.h" namespace art { static jboolean AtomicLong_VMSupportsCS8(JNIEnv*, jclass) { - return QuasiAtomic::LongAtomicsUseMutexes() ? JNI_FALSE : JNI_TRUE; + return QuasiAtomic::LongAtomicsUseMutexes(kRuntimeISA) ? JNI_FALSE : JNI_TRUE; } static JNINativeMethod gMethods[] = { -- GitLab From 17b8bce064fe4c0b29117abe489b7f8c2c950d43 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Mon, 22 Feb 2016 19:32:16 -0800 Subject: [PATCH 062/204] Improve immune spaces logic We now properly include the largest continuous region with the most image bytes. Oat bytes are considered as part of the region but are not counted when comparing. This can result in more image bytes in cases where large oat files were previously included for the immune region. Also added handling for adjacent oat files: [image][image][oat][oat][space] will now properly be a single region. Bug: 27136196 Change-Id: If2c002176dd32122e320e8a94551df46bd95256a --- runtime/gc/collector/immune_spaces.cc | 68 +++-- runtime/gc/collector/immune_spaces_test.cc | 325 ++++++++++++++++----- 2 files changed, 291 insertions(+), 102 deletions(-) diff --git a/runtime/gc/collector/immune_spaces.cc b/runtime/gc/collector/immune_spaces.cc index 26da4ca5a9..1e5f28382b 100644 --- a/runtime/gc/collector/immune_spaces.cc +++ b/runtime/gc/collector/immune_spaces.cc @@ -16,6 +16,9 @@ #include "immune_spaces.h" +#include +#include + #include "gc/space/space-inl.h" #include "mirror/object.h" #include "oat_file.h" @@ -32,11 +35,12 @@ void ImmuneSpaces::Reset() { void ImmuneSpaces::CreateLargestImmuneRegion() { uintptr_t best_begin = 0u; uintptr_t best_end = 0u; + uintptr_t best_heap_size = 0u; uintptr_t cur_begin = 0u; uintptr_t cur_end = 0u; - // TODO: If the last space is an image space, we may include its oat file in the immune region. - // This could potentially hide heap corruption bugs if there is invalid pointers that point into - // the boot oat code + uintptr_t cur_heap_size = 0u; + using Interval = std::tuple; + std::vector intervals; for (space::ContinuousSpace* space : GetSpaces()) { uintptr_t space_begin = reinterpret_cast(space->Begin()); uintptr_t space_end = reinterpret_cast(space->Limit()); @@ -50,29 +54,47 @@ void ImmuneSpaces::CreateLargestImmuneRegion() { // creation, the actual oat file could be somewhere else. const OatFile* const image_oat_file = image_space->GetOatFile(); if (image_oat_file != nullptr) { - uintptr_t oat_begin = reinterpret_cast(image_oat_file->Begin()); - uintptr_t oat_end = reinterpret_cast(image_oat_file->End()); - if (space_end == oat_begin) { - DCHECK_GE(oat_end, oat_begin); - space_end = oat_end; - } + intervals.push_back(Interval(reinterpret_cast(image_oat_file->Begin()), + reinterpret_cast(image_oat_file->End()), + /*image*/false)); } } - if (cur_begin == 0u) { - cur_begin = space_begin; - cur_end = space_end; - } else if (cur_end == space_begin) { - // Extend current region. - cur_end = space_end; - } else { - // Reset. - cur_begin = 0; - cur_end = 0; + intervals.push_back(Interval(space_begin, space_end, /*is_heap*/true)); + } + std::sort(intervals.begin(), intervals.end()); + // Intervals are already sorted by begin, if a new interval begins at the end of the current + // region then we append, otherwise we restart the current interval. To prevent starting an + // interval on an oat file, ignore oat files that are not extending an existing interval. + // If the total number of image bytes in the current interval is larger than the current best + // one, then we set the best one to be the current one. + for (const Interval& interval : intervals) { + const uintptr_t begin = std::get<0>(interval); + const uintptr_t end = std::get<1>(interval); + const bool is_heap = std::get<2>(interval); + VLOG(collector) << "Interval " << reinterpret_cast(begin) << "-" + << reinterpret_cast(end) << " is_heap=" << is_heap; + DCHECK_GE(end, begin); + DCHECK_GE(begin, cur_end); + // New interval is not at the end of the current one, start a new interval if we are a heap + // interval. Otherwise continue since we never start a new region with non image intervals. + if (begin != cur_end) { + if (!is_heap) { + continue; + } + // Not extending, reset the region. + cur_begin = begin; + cur_heap_size = 0; } - if (cur_end - cur_begin > best_end - best_begin) { - // Improvement, update the best range. - best_begin = cur_begin; - best_end = cur_end; + cur_end = end; + if (is_heap) { + // Only update if the total number of image bytes is greater than the current best one. + // We don't want to count the oat file bytes since these contain no java objects. + cur_heap_size += end - begin; + if (cur_heap_size > best_heap_size) { + best_begin = cur_begin; + best_end = cur_end; + best_heap_size = cur_heap_size; + } } } largest_immune_region_.SetBegin(reinterpret_cast(best_begin)); diff --git a/runtime/gc/collector/immune_spaces_test.cc b/runtime/gc/collector/immune_spaces_test.cc index 56838f5c06..75de6e600f 100644 --- a/runtime/gc/collector/immune_spaces_test.cc +++ b/runtime/gc/collector/immune_spaces_test.cc @@ -28,50 +28,6 @@ class Object; namespace gc { namespace collector { -class ImmuneSpacesTest : public CommonRuntimeTest {}; - -class DummySpace : public space::ContinuousSpace { - public: - DummySpace(uint8_t* begin, uint8_t* end) - : ContinuousSpace("DummySpace", - space::kGcRetentionPolicyNeverCollect, - begin, - end, - /*limit*/end) {} - - space::SpaceType GetType() const OVERRIDE { - return space::kSpaceTypeMallocSpace; - } - - bool CanMoveObjects() const OVERRIDE { - return false; - } - - accounting::ContinuousSpaceBitmap* GetLiveBitmap() const OVERRIDE { - return nullptr; - } - - accounting::ContinuousSpaceBitmap* GetMarkBitmap() const OVERRIDE { - return nullptr; - } -}; - -TEST_F(ImmuneSpacesTest, AppendBasic) { - ImmuneSpaces spaces; - uint8_t* const base = reinterpret_cast(0x1000); - DummySpace a(base, base + 45 * KB); - DummySpace b(a.Limit(), a.Limit() + 813 * KB); - { - WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); - spaces.AddSpace(&a); - spaces.AddSpace(&b); - } - EXPECT_TRUE(spaces.ContainsSpace(&a)); - EXPECT_TRUE(spaces.ContainsSpace(&b)); - EXPECT_EQ(reinterpret_cast(spaces.GetLargestImmuneRegion().Begin()), a.Begin()); - EXPECT_EQ(reinterpret_cast(spaces.GetLargestImmuneRegion().End()), b.Limit()); -} - class DummyOatFile : public OatFile { public: DummyOatFile(uint8_t* begin, uint8_t* end) : OatFile("Location", /*is_executable*/ false) { @@ -84,23 +40,50 @@ class DummyImageSpace : public space::ImageSpace { public: DummyImageSpace(MemMap* map, accounting::ContinuousSpaceBitmap* live_bitmap, - std::unique_ptr&& oat_file) + std::unique_ptr&& oat_file, + std::unique_ptr&& oat_map) : ImageSpace("DummyImageSpace", /*image_location*/"", map, live_bitmap, - map->End()) { + map->End()), + oat_map_(std::move(oat_map)) { oat_file_ = std::move(oat_file); oat_file_non_owned_ = oat_file_.get(); } - // Size is the size of the image space, oat offset is where the oat file is located - // after the end of image space. oat_size is the size of the oat file. - static DummyImageSpace* Create(size_t size, size_t oat_offset, size_t oat_size) { + private: + std::unique_ptr oat_map_; +}; + +class ImmuneSpacesTest : public CommonRuntimeTest { + static constexpr size_t kMaxBitmaps = 10; + + public: + ImmuneSpacesTest() {} + + void ReserveBitmaps() { + // Create a bunch of dummy bitmaps since these are required to create image spaces. The bitmaps + // do not need to cover the image spaces though. + for (size_t i = 0; i < kMaxBitmaps; ++i) { + std::unique_ptr bitmap( + accounting::ContinuousSpaceBitmap::Create("bitmap", + reinterpret_cast(kPageSize), + kPageSize)); + CHECK(bitmap != nullptr); + live_bitmaps_.push_back(std::move(bitmap)); + } + } + + // Create an image space, the oat file is optional. + DummyImageSpace* CreateImageSpace(uint8_t* image_begin, + size_t image_size, + uint8_t* oat_begin, + size_t oat_size) { std::string error_str; std::unique_ptr map(MemMap::MapAnonymous("DummyImageSpace", - nullptr, - size, + image_begin, + image_size, PROT_READ | PROT_WRITE, /*low_4gb*/true, /*reuse*/false, @@ -109,14 +92,21 @@ class DummyImageSpace : public space::ImageSpace { LOG(ERROR) << error_str; return nullptr; } - std::unique_ptr live_bitmap( - accounting::ContinuousSpaceBitmap::Create("bitmap", map->Begin(), map->Size())); - if (live_bitmap == nullptr) { + CHECK(!live_bitmaps_.empty()); + std::unique_ptr live_bitmap(std::move(live_bitmaps_.back())); + live_bitmaps_.pop_back(); + std::unique_ptr oat_map(MemMap::MapAnonymous("OatMap", + oat_begin, + oat_size, + PROT_READ | PROT_WRITE, + /*low_4gb*/true, + /*reuse*/false, + &error_str)); + if (oat_map == nullptr) { + LOG(ERROR) << error_str; return nullptr; } - // The actual mapped oat file may not be directly after the image for the app image case. - std::unique_ptr oat_file(new DummyOatFile(map->End() + oat_offset, - map->End() + oat_offset + oat_size)); + std::unique_ptr oat_file(new DummyOatFile(oat_map->Begin(), oat_map->End())); // Create image header. ImageSection sections[ImageHeader::kSectionCount]; new (map->Begin()) ImageHeader( @@ -126,10 +116,10 @@ class DummyImageSpace : public space::ImageSpace { /*image_roots*/PointerToLowMemUInt32(map->Begin()) + 1, /*oat_checksum*/0u, // The oat file data in the header is always right after the image space. - /*oat_file_begin*/PointerToLowMemUInt32(map->End()), - /*oat_data_begin*/PointerToLowMemUInt32(map->End()), - /*oat_data_end*/PointerToLowMemUInt32(map->End() + oat_size), - /*oat_file_end*/PointerToLowMemUInt32(map->End() + oat_size), + /*oat_file_begin*/PointerToLowMemUInt32(oat_begin), + /*oat_data_begin*/PointerToLowMemUInt32(oat_begin), + /*oat_data_end*/PointerToLowMemUInt32(oat_begin + oat_size), + /*oat_file_end*/PointerToLowMemUInt32(oat_begin + oat_size), /*boot_image_begin*/0u, /*boot_image_size*/0u, /*boot_oat_begin*/0u, @@ -139,27 +129,113 @@ class DummyImageSpace : public space::ImageSpace { /*is_pic*/false, ImageHeader::kStorageModeUncompressed, /*storage_size*/0u); - return new DummyImageSpace(map.release(), live_bitmap.release(), std::move(oat_file)); + return new DummyImageSpace(map.release(), + live_bitmap.release(), + std::move(oat_file), + std::move(oat_map)); } + + // Does not reserve the memory, the caller needs to be sure no other threads will map at the + // returned address. + static uint8_t* GetContinuousMemoryRegion(size_t size) { + std::string error_str; + std::unique_ptr map(MemMap::MapAnonymous("reserve", + nullptr, + size, + PROT_READ | PROT_WRITE, + /*low_4gb*/true, + /*reuse*/false, + &error_str)); + if (map == nullptr) { + LOG(ERROR) << "Failed to allocate memory region " << error_str; + return nullptr; + } + return map->Begin(); + } + + private: + // Bitmap pool for pre-allocated dummy bitmaps. We need to pre-allocate them since we don't want + // them to randomly get placed somewhere where we want an image space. + std::vector> live_bitmaps_; }; +class DummySpace : public space::ContinuousSpace { + public: + DummySpace(uint8_t* begin, uint8_t* end) + : ContinuousSpace("DummySpace", + space::kGcRetentionPolicyNeverCollect, + begin, + end, + /*limit*/end) {} + + space::SpaceType GetType() const OVERRIDE { + return space::kSpaceTypeMallocSpace; + } + + bool CanMoveObjects() const OVERRIDE { + return false; + } + + accounting::ContinuousSpaceBitmap* GetLiveBitmap() const OVERRIDE { + return nullptr; + } + + accounting::ContinuousSpaceBitmap* GetMarkBitmap() const OVERRIDE { + return nullptr; + } +}; + +TEST_F(ImmuneSpacesTest, AppendBasic) { + ImmuneSpaces spaces; + uint8_t* const base = reinterpret_cast(0x1000); + DummySpace a(base, base + 45 * KB); + DummySpace b(a.Limit(), a.Limit() + 813 * KB); + { + WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); + spaces.AddSpace(&a); + spaces.AddSpace(&b); + } + EXPECT_TRUE(spaces.ContainsSpace(&a)); + EXPECT_TRUE(spaces.ContainsSpace(&b)); + EXPECT_EQ(reinterpret_cast(spaces.GetLargestImmuneRegion().Begin()), a.Begin()); + EXPECT_EQ(reinterpret_cast(spaces.GetLargestImmuneRegion().End()), b.Limit()); +} + +// Tests [image][oat][space] producing a single large immune region. TEST_F(ImmuneSpacesTest, AppendAfterImage) { + ReserveBitmaps(); ImmuneSpaces spaces; - constexpr size_t image_size = 123 * kPageSize; - constexpr size_t image_oat_size = 321 * kPageSize; - std::unique_ptr image_space(DummyImageSpace::Create(image_size, - 0, - image_oat_size)); + constexpr size_t kImageSize = 123 * kPageSize; + constexpr size_t kImageOatSize = 321 * kPageSize; + constexpr size_t kOtherSpaceSize= 100 * kPageSize; + + uint8_t* memory = GetContinuousMemoryRegion(kImageSize + kImageOatSize + kOtherSpaceSize); + + std::unique_ptr image_space(CreateImageSpace(memory, + kImageSize, + memory + kImageSize, + kImageOatSize)); ASSERT_TRUE(image_space != nullptr); const ImageHeader& image_header = image_space->GetImageHeader(); - EXPECT_EQ(image_header.GetImageSize(), image_size); + DummySpace space(image_header.GetOatFileEnd(), image_header.GetOatFileEnd() + kOtherSpaceSize); + + EXPECT_EQ(image_header.GetImageSize(), kImageSize); EXPECT_EQ(static_cast(image_header.GetOatFileEnd() - image_header.GetOatFileBegin()), - image_oat_size); - DummySpace space(image_header.GetOatFileEnd(), image_header.GetOatFileEnd() + 813 * kPageSize); - EXPECT_NE(image_space->Limit(), space.Begin()); + kImageOatSize); + EXPECT_EQ(image_space->GetOatFile()->Size(), kImageOatSize); + // Check that we do not include the oat if there is no space after. { WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); spaces.AddSpace(image_space.get()); + } + EXPECT_EQ(reinterpret_cast(spaces.GetLargestImmuneRegion().Begin()), + image_space->Begin()); + EXPECT_EQ(reinterpret_cast(spaces.GetLargestImmuneRegion().End()), + image_space->Limit()); + // Add another space and ensure it gets appended. + EXPECT_NE(image_space->Limit(), space.Begin()); + { + WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); spaces.AddSpace(&space); } EXPECT_TRUE(spaces.ContainsSpace(image_space.get())); @@ -170,18 +246,109 @@ TEST_F(ImmuneSpacesTest, AppendAfterImage) { EXPECT_EQ(reinterpret_cast(spaces.GetLargestImmuneRegion().Begin()), image_space->Begin()); EXPECT_EQ(reinterpret_cast(spaces.GetLargestImmuneRegion().End()), space.Limit()); - // Check that appending with a gap between the map does not include the oat file. - image_space.reset(DummyImageSpace::Create(image_size, kPageSize, image_oat_size)); - spaces.Reset(); +} + +// Test [image1][image2][image1 oat][image2 oat][image3] producing a single large immune region. +TEST_F(ImmuneSpacesTest, MultiImage) { + ReserveBitmaps(); + // Image 2 needs to be smaller or else it may be chosen for immune region. + constexpr size_t kImage1Size = kPageSize * 17; + constexpr size_t kImage2Size = kPageSize * 13; + constexpr size_t kImage3Size = kPageSize * 3; + constexpr size_t kImage1OatSize = kPageSize * 5; + constexpr size_t kImage2OatSize = kPageSize * 8; + constexpr size_t kImage3OatSize = kPageSize; + constexpr size_t kImageBytes = kImage1Size + kImage2Size + kImage3Size; + constexpr size_t kMemorySize = kImageBytes + kImage1OatSize + kImage2OatSize + kImage3OatSize; + uint8_t* memory = GetContinuousMemoryRegion(kMemorySize); + uint8_t* space1_begin = memory; + memory += kImage1Size; + uint8_t* space2_begin = memory; + memory += kImage2Size; + uint8_t* space1_oat_begin = memory; + memory += kImage1OatSize; + uint8_t* space2_oat_begin = memory; + memory += kImage2OatSize; + uint8_t* space3_begin = memory; + + std::unique_ptr space1(CreateImageSpace(space1_begin, + kImage1Size, + space1_oat_begin, + kImage1OatSize)); + ASSERT_TRUE(space1 != nullptr); + + + std::unique_ptr space2(CreateImageSpace(space2_begin, + kImage2Size, + space2_oat_begin, + kImage2OatSize)); + ASSERT_TRUE(space2 != nullptr); + + // Finally put a 3rd image space. + std::unique_ptr space3(CreateImageSpace(space3_begin, + kImage3Size, + space3_begin + kImage3Size, + kImage3OatSize)); + ASSERT_TRUE(space3 != nullptr); + + // Check that we do not include the oat if there is no space after. + ImmuneSpaces spaces; { WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); - spaces.AddSpace(image_space.get()); + LOG(INFO) << "Adding space1 " << reinterpret_cast(space1->Begin()); + spaces.AddSpace(space1.get()); + LOG(INFO) << "Adding space2 " << reinterpret_cast(space2->Begin()); + spaces.AddSpace(space2.get()); } + // There are no more heap bytes, the immune region should only be the first 2 image spaces and + // should exclude the image oat files. EXPECT_EQ(reinterpret_cast(spaces.GetLargestImmuneRegion().Begin()), - image_space->Begin()); - // Size should be equal, we should not add the oat file since it is not adjacent to the image - // space. - EXPECT_EQ(spaces.GetLargestImmuneRegion().Size(), image_size); + space1->Begin()); + EXPECT_EQ(reinterpret_cast(spaces.GetLargestImmuneRegion().End()), + space2->Limit()); + + // Add another space after the oat files, now it should contain the entire memory region. + { + WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); + LOG(INFO) << "Adding space3 " << reinterpret_cast(space3->Begin()); + spaces.AddSpace(space3.get()); + } + EXPECT_EQ(reinterpret_cast(spaces.GetLargestImmuneRegion().Begin()), + space1->Begin()); + EXPECT_EQ(reinterpret_cast(spaces.GetLargestImmuneRegion().End()), + space3->Limit()); + + // Add a smaller non-adjacent space and ensure it is not part of the immune region. + uint8_t* memory2 = GetContinuousMemoryRegion(kImageBytes + kPageSize); + std::unique_ptr space4(CreateImageSpace(memory2 + kPageSize, + kImageBytes - kPageSize, + memory2 + kImageBytes, + kPageSize)); + ASSERT_TRUE(space4 != nullptr); + { + WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); + LOG(INFO) << "Adding space4 " << reinterpret_cast(space4->Begin()); + spaces.AddSpace(space4.get()); + } + EXPECT_EQ(reinterpret_cast(spaces.GetLargestImmuneRegion().Begin()), + space1->Begin()); + EXPECT_EQ(reinterpret_cast(spaces.GetLargestImmuneRegion().End()), + space3->Limit()); + + // Add a larger non-adjacent space and ensure it becomes the new largest immune region. + uint8_t* memory3 = GetContinuousMemoryRegion(kImageBytes + kPageSize * 3); + std::unique_ptr space5(CreateImageSpace(memory3 + kPageSize, + kImageBytes + kPageSize, + memory3 + kImageBytes + 2 * kPageSize, + kPageSize)); + ASSERT_TRUE(space5 != nullptr); + { + WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); + LOG(INFO) << "Adding space5 " << reinterpret_cast(space5->Begin()); + spaces.AddSpace(space5.get()); + } + EXPECT_EQ(reinterpret_cast(spaces.GetLargestImmuneRegion().Begin()), space5->Begin()); + EXPECT_EQ(reinterpret_cast(spaces.GetLargestImmuneRegion().End()), space5->Limit()); } } // namespace collector -- GitLab From 07dbbca0b42cb8da1811de8209b4a7d4becfc7b2 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Sat, 27 Feb 2016 11:30:25 +0000 Subject: [PATCH 063/204] Revert "Improve immune spaces logic" test is flaky. For example: [ RUN ] ImmuneSpacesTest.MultiImage art/runtime/gc/collector/immune_spaces_test.cc:351: Failure Value of: space5->Limit() Actual: 0xb6d72000 Expected: reinterpret_cast(spaces.GetLargestImmuneRegion().End()) Which is: 0xb6d93000 [ FAILED ] ImmuneSpacesTest.MultiImage (1076 ms) Bug: 27136196 This reverts commit 17b8bce064fe4c0b29117abe489b7f8c2c950d43. Change-Id: I9168421dd6ebabc271ed5c2cdbc5a27c211dcf5a --- runtime/gc/collector/immune_spaces.cc | 68 ++--- runtime/gc/collector/immune_spaces_test.cc | 325 +++++---------------- 2 files changed, 102 insertions(+), 291 deletions(-) diff --git a/runtime/gc/collector/immune_spaces.cc b/runtime/gc/collector/immune_spaces.cc index 1e5f28382b..26da4ca5a9 100644 --- a/runtime/gc/collector/immune_spaces.cc +++ b/runtime/gc/collector/immune_spaces.cc @@ -16,9 +16,6 @@ #include "immune_spaces.h" -#include -#include - #include "gc/space/space-inl.h" #include "mirror/object.h" #include "oat_file.h" @@ -35,12 +32,11 @@ void ImmuneSpaces::Reset() { void ImmuneSpaces::CreateLargestImmuneRegion() { uintptr_t best_begin = 0u; uintptr_t best_end = 0u; - uintptr_t best_heap_size = 0u; uintptr_t cur_begin = 0u; uintptr_t cur_end = 0u; - uintptr_t cur_heap_size = 0u; - using Interval = std::tuple; - std::vector intervals; + // TODO: If the last space is an image space, we may include its oat file in the immune region. + // This could potentially hide heap corruption bugs if there is invalid pointers that point into + // the boot oat code for (space::ContinuousSpace* space : GetSpaces()) { uintptr_t space_begin = reinterpret_cast(space->Begin()); uintptr_t space_end = reinterpret_cast(space->Limit()); @@ -54,47 +50,29 @@ void ImmuneSpaces::CreateLargestImmuneRegion() { // creation, the actual oat file could be somewhere else. const OatFile* const image_oat_file = image_space->GetOatFile(); if (image_oat_file != nullptr) { - intervals.push_back(Interval(reinterpret_cast(image_oat_file->Begin()), - reinterpret_cast(image_oat_file->End()), - /*image*/false)); + uintptr_t oat_begin = reinterpret_cast(image_oat_file->Begin()); + uintptr_t oat_end = reinterpret_cast(image_oat_file->End()); + if (space_end == oat_begin) { + DCHECK_GE(oat_end, oat_begin); + space_end = oat_end; + } } } - intervals.push_back(Interval(space_begin, space_end, /*is_heap*/true)); - } - std::sort(intervals.begin(), intervals.end()); - // Intervals are already sorted by begin, if a new interval begins at the end of the current - // region then we append, otherwise we restart the current interval. To prevent starting an - // interval on an oat file, ignore oat files that are not extending an existing interval. - // If the total number of image bytes in the current interval is larger than the current best - // one, then we set the best one to be the current one. - for (const Interval& interval : intervals) { - const uintptr_t begin = std::get<0>(interval); - const uintptr_t end = std::get<1>(interval); - const bool is_heap = std::get<2>(interval); - VLOG(collector) << "Interval " << reinterpret_cast(begin) << "-" - << reinterpret_cast(end) << " is_heap=" << is_heap; - DCHECK_GE(end, begin); - DCHECK_GE(begin, cur_end); - // New interval is not at the end of the current one, start a new interval if we are a heap - // interval. Otherwise continue since we never start a new region with non image intervals. - if (begin != cur_end) { - if (!is_heap) { - continue; - } - // Not extending, reset the region. - cur_begin = begin; - cur_heap_size = 0; + if (cur_begin == 0u) { + cur_begin = space_begin; + cur_end = space_end; + } else if (cur_end == space_begin) { + // Extend current region. + cur_end = space_end; + } else { + // Reset. + cur_begin = 0; + cur_end = 0; } - cur_end = end; - if (is_heap) { - // Only update if the total number of image bytes is greater than the current best one. - // We don't want to count the oat file bytes since these contain no java objects. - cur_heap_size += end - begin; - if (cur_heap_size > best_heap_size) { - best_begin = cur_begin; - best_end = cur_end; - best_heap_size = cur_heap_size; - } + if (cur_end - cur_begin > best_end - best_begin) { + // Improvement, update the best range. + best_begin = cur_begin; + best_end = cur_end; } } largest_immune_region_.SetBegin(reinterpret_cast(best_begin)); diff --git a/runtime/gc/collector/immune_spaces_test.cc b/runtime/gc/collector/immune_spaces_test.cc index 75de6e600f..56838f5c06 100644 --- a/runtime/gc/collector/immune_spaces_test.cc +++ b/runtime/gc/collector/immune_spaces_test.cc @@ -28,6 +28,50 @@ class Object; namespace gc { namespace collector { +class ImmuneSpacesTest : public CommonRuntimeTest {}; + +class DummySpace : public space::ContinuousSpace { + public: + DummySpace(uint8_t* begin, uint8_t* end) + : ContinuousSpace("DummySpace", + space::kGcRetentionPolicyNeverCollect, + begin, + end, + /*limit*/end) {} + + space::SpaceType GetType() const OVERRIDE { + return space::kSpaceTypeMallocSpace; + } + + bool CanMoveObjects() const OVERRIDE { + return false; + } + + accounting::ContinuousSpaceBitmap* GetLiveBitmap() const OVERRIDE { + return nullptr; + } + + accounting::ContinuousSpaceBitmap* GetMarkBitmap() const OVERRIDE { + return nullptr; + } +}; + +TEST_F(ImmuneSpacesTest, AppendBasic) { + ImmuneSpaces spaces; + uint8_t* const base = reinterpret_cast(0x1000); + DummySpace a(base, base + 45 * KB); + DummySpace b(a.Limit(), a.Limit() + 813 * KB); + { + WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); + spaces.AddSpace(&a); + spaces.AddSpace(&b); + } + EXPECT_TRUE(spaces.ContainsSpace(&a)); + EXPECT_TRUE(spaces.ContainsSpace(&b)); + EXPECT_EQ(reinterpret_cast(spaces.GetLargestImmuneRegion().Begin()), a.Begin()); + EXPECT_EQ(reinterpret_cast(spaces.GetLargestImmuneRegion().End()), b.Limit()); +} + class DummyOatFile : public OatFile { public: DummyOatFile(uint8_t* begin, uint8_t* end) : OatFile("Location", /*is_executable*/ false) { @@ -40,50 +84,23 @@ class DummyImageSpace : public space::ImageSpace { public: DummyImageSpace(MemMap* map, accounting::ContinuousSpaceBitmap* live_bitmap, - std::unique_ptr&& oat_file, - std::unique_ptr&& oat_map) + std::unique_ptr&& oat_file) : ImageSpace("DummyImageSpace", /*image_location*/"", map, live_bitmap, - map->End()), - oat_map_(std::move(oat_map)) { + map->End()) { oat_file_ = std::move(oat_file); oat_file_non_owned_ = oat_file_.get(); } - private: - std::unique_ptr oat_map_; -}; - -class ImmuneSpacesTest : public CommonRuntimeTest { - static constexpr size_t kMaxBitmaps = 10; - - public: - ImmuneSpacesTest() {} - - void ReserveBitmaps() { - // Create a bunch of dummy bitmaps since these are required to create image spaces. The bitmaps - // do not need to cover the image spaces though. - for (size_t i = 0; i < kMaxBitmaps; ++i) { - std::unique_ptr bitmap( - accounting::ContinuousSpaceBitmap::Create("bitmap", - reinterpret_cast(kPageSize), - kPageSize)); - CHECK(bitmap != nullptr); - live_bitmaps_.push_back(std::move(bitmap)); - } - } - - // Create an image space, the oat file is optional. - DummyImageSpace* CreateImageSpace(uint8_t* image_begin, - size_t image_size, - uint8_t* oat_begin, - size_t oat_size) { + // Size is the size of the image space, oat offset is where the oat file is located + // after the end of image space. oat_size is the size of the oat file. + static DummyImageSpace* Create(size_t size, size_t oat_offset, size_t oat_size) { std::string error_str; std::unique_ptr map(MemMap::MapAnonymous("DummyImageSpace", - image_begin, - image_size, + nullptr, + size, PROT_READ | PROT_WRITE, /*low_4gb*/true, /*reuse*/false, @@ -92,21 +109,14 @@ class ImmuneSpacesTest : public CommonRuntimeTest { LOG(ERROR) << error_str; return nullptr; } - CHECK(!live_bitmaps_.empty()); - std::unique_ptr live_bitmap(std::move(live_bitmaps_.back())); - live_bitmaps_.pop_back(); - std::unique_ptr oat_map(MemMap::MapAnonymous("OatMap", - oat_begin, - oat_size, - PROT_READ | PROT_WRITE, - /*low_4gb*/true, - /*reuse*/false, - &error_str)); - if (oat_map == nullptr) { - LOG(ERROR) << error_str; + std::unique_ptr live_bitmap( + accounting::ContinuousSpaceBitmap::Create("bitmap", map->Begin(), map->Size())); + if (live_bitmap == nullptr) { return nullptr; } - std::unique_ptr oat_file(new DummyOatFile(oat_map->Begin(), oat_map->End())); + // The actual mapped oat file may not be directly after the image for the app image case. + std::unique_ptr oat_file(new DummyOatFile(map->End() + oat_offset, + map->End() + oat_offset + oat_size)); // Create image header. ImageSection sections[ImageHeader::kSectionCount]; new (map->Begin()) ImageHeader( @@ -116,10 +126,10 @@ class ImmuneSpacesTest : public CommonRuntimeTest { /*image_roots*/PointerToLowMemUInt32(map->Begin()) + 1, /*oat_checksum*/0u, // The oat file data in the header is always right after the image space. - /*oat_file_begin*/PointerToLowMemUInt32(oat_begin), - /*oat_data_begin*/PointerToLowMemUInt32(oat_begin), - /*oat_data_end*/PointerToLowMemUInt32(oat_begin + oat_size), - /*oat_file_end*/PointerToLowMemUInt32(oat_begin + oat_size), + /*oat_file_begin*/PointerToLowMemUInt32(map->End()), + /*oat_data_begin*/PointerToLowMemUInt32(map->End()), + /*oat_data_end*/PointerToLowMemUInt32(map->End() + oat_size), + /*oat_file_end*/PointerToLowMemUInt32(map->End() + oat_size), /*boot_image_begin*/0u, /*boot_image_size*/0u, /*boot_oat_begin*/0u, @@ -129,113 +139,27 @@ class ImmuneSpacesTest : public CommonRuntimeTest { /*is_pic*/false, ImageHeader::kStorageModeUncompressed, /*storage_size*/0u); - return new DummyImageSpace(map.release(), - live_bitmap.release(), - std::move(oat_file), - std::move(oat_map)); + return new DummyImageSpace(map.release(), live_bitmap.release(), std::move(oat_file)); } - - // Does not reserve the memory, the caller needs to be sure no other threads will map at the - // returned address. - static uint8_t* GetContinuousMemoryRegion(size_t size) { - std::string error_str; - std::unique_ptr map(MemMap::MapAnonymous("reserve", - nullptr, - size, - PROT_READ | PROT_WRITE, - /*low_4gb*/true, - /*reuse*/false, - &error_str)); - if (map == nullptr) { - LOG(ERROR) << "Failed to allocate memory region " << error_str; - return nullptr; - } - return map->Begin(); - } - - private: - // Bitmap pool for pre-allocated dummy bitmaps. We need to pre-allocate them since we don't want - // them to randomly get placed somewhere where we want an image space. - std::vector> live_bitmaps_; }; -class DummySpace : public space::ContinuousSpace { - public: - DummySpace(uint8_t* begin, uint8_t* end) - : ContinuousSpace("DummySpace", - space::kGcRetentionPolicyNeverCollect, - begin, - end, - /*limit*/end) {} - - space::SpaceType GetType() const OVERRIDE { - return space::kSpaceTypeMallocSpace; - } - - bool CanMoveObjects() const OVERRIDE { - return false; - } - - accounting::ContinuousSpaceBitmap* GetLiveBitmap() const OVERRIDE { - return nullptr; - } - - accounting::ContinuousSpaceBitmap* GetMarkBitmap() const OVERRIDE { - return nullptr; - } -}; - -TEST_F(ImmuneSpacesTest, AppendBasic) { - ImmuneSpaces spaces; - uint8_t* const base = reinterpret_cast(0x1000); - DummySpace a(base, base + 45 * KB); - DummySpace b(a.Limit(), a.Limit() + 813 * KB); - { - WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); - spaces.AddSpace(&a); - spaces.AddSpace(&b); - } - EXPECT_TRUE(spaces.ContainsSpace(&a)); - EXPECT_TRUE(spaces.ContainsSpace(&b)); - EXPECT_EQ(reinterpret_cast(spaces.GetLargestImmuneRegion().Begin()), a.Begin()); - EXPECT_EQ(reinterpret_cast(spaces.GetLargestImmuneRegion().End()), b.Limit()); -} - -// Tests [image][oat][space] producing a single large immune region. TEST_F(ImmuneSpacesTest, AppendAfterImage) { - ReserveBitmaps(); ImmuneSpaces spaces; - constexpr size_t kImageSize = 123 * kPageSize; - constexpr size_t kImageOatSize = 321 * kPageSize; - constexpr size_t kOtherSpaceSize= 100 * kPageSize; - - uint8_t* memory = GetContinuousMemoryRegion(kImageSize + kImageOatSize + kOtherSpaceSize); - - std::unique_ptr image_space(CreateImageSpace(memory, - kImageSize, - memory + kImageSize, - kImageOatSize)); + constexpr size_t image_size = 123 * kPageSize; + constexpr size_t image_oat_size = 321 * kPageSize; + std::unique_ptr image_space(DummyImageSpace::Create(image_size, + 0, + image_oat_size)); ASSERT_TRUE(image_space != nullptr); const ImageHeader& image_header = image_space->GetImageHeader(); - DummySpace space(image_header.GetOatFileEnd(), image_header.GetOatFileEnd() + kOtherSpaceSize); - - EXPECT_EQ(image_header.GetImageSize(), kImageSize); + EXPECT_EQ(image_header.GetImageSize(), image_size); EXPECT_EQ(static_cast(image_header.GetOatFileEnd() - image_header.GetOatFileBegin()), - kImageOatSize); - EXPECT_EQ(image_space->GetOatFile()->Size(), kImageOatSize); - // Check that we do not include the oat if there is no space after. - { - WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); - spaces.AddSpace(image_space.get()); - } - EXPECT_EQ(reinterpret_cast(spaces.GetLargestImmuneRegion().Begin()), - image_space->Begin()); - EXPECT_EQ(reinterpret_cast(spaces.GetLargestImmuneRegion().End()), - image_space->Limit()); - // Add another space and ensure it gets appended. + image_oat_size); + DummySpace space(image_header.GetOatFileEnd(), image_header.GetOatFileEnd() + 813 * kPageSize); EXPECT_NE(image_space->Limit(), space.Begin()); { WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); + spaces.AddSpace(image_space.get()); spaces.AddSpace(&space); } EXPECT_TRUE(spaces.ContainsSpace(image_space.get())); @@ -246,109 +170,18 @@ TEST_F(ImmuneSpacesTest, AppendAfterImage) { EXPECT_EQ(reinterpret_cast(spaces.GetLargestImmuneRegion().Begin()), image_space->Begin()); EXPECT_EQ(reinterpret_cast(spaces.GetLargestImmuneRegion().End()), space.Limit()); -} - -// Test [image1][image2][image1 oat][image2 oat][image3] producing a single large immune region. -TEST_F(ImmuneSpacesTest, MultiImage) { - ReserveBitmaps(); - // Image 2 needs to be smaller or else it may be chosen for immune region. - constexpr size_t kImage1Size = kPageSize * 17; - constexpr size_t kImage2Size = kPageSize * 13; - constexpr size_t kImage3Size = kPageSize * 3; - constexpr size_t kImage1OatSize = kPageSize * 5; - constexpr size_t kImage2OatSize = kPageSize * 8; - constexpr size_t kImage3OatSize = kPageSize; - constexpr size_t kImageBytes = kImage1Size + kImage2Size + kImage3Size; - constexpr size_t kMemorySize = kImageBytes + kImage1OatSize + kImage2OatSize + kImage3OatSize; - uint8_t* memory = GetContinuousMemoryRegion(kMemorySize); - uint8_t* space1_begin = memory; - memory += kImage1Size; - uint8_t* space2_begin = memory; - memory += kImage2Size; - uint8_t* space1_oat_begin = memory; - memory += kImage1OatSize; - uint8_t* space2_oat_begin = memory; - memory += kImage2OatSize; - uint8_t* space3_begin = memory; - - std::unique_ptr space1(CreateImageSpace(space1_begin, - kImage1Size, - space1_oat_begin, - kImage1OatSize)); - ASSERT_TRUE(space1 != nullptr); - - - std::unique_ptr space2(CreateImageSpace(space2_begin, - kImage2Size, - space2_oat_begin, - kImage2OatSize)); - ASSERT_TRUE(space2 != nullptr); - - // Finally put a 3rd image space. - std::unique_ptr space3(CreateImageSpace(space3_begin, - kImage3Size, - space3_begin + kImage3Size, - kImage3OatSize)); - ASSERT_TRUE(space3 != nullptr); - - // Check that we do not include the oat if there is no space after. - ImmuneSpaces spaces; + // Check that appending with a gap between the map does not include the oat file. + image_space.reset(DummyImageSpace::Create(image_size, kPageSize, image_oat_size)); + spaces.Reset(); { WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); - LOG(INFO) << "Adding space1 " << reinterpret_cast(space1->Begin()); - spaces.AddSpace(space1.get()); - LOG(INFO) << "Adding space2 " << reinterpret_cast(space2->Begin()); - spaces.AddSpace(space2.get()); - } - // There are no more heap bytes, the immune region should only be the first 2 image spaces and - // should exclude the image oat files. - EXPECT_EQ(reinterpret_cast(spaces.GetLargestImmuneRegion().Begin()), - space1->Begin()); - EXPECT_EQ(reinterpret_cast(spaces.GetLargestImmuneRegion().End()), - space2->Limit()); - - // Add another space after the oat files, now it should contain the entire memory region. - { - WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); - LOG(INFO) << "Adding space3 " << reinterpret_cast(space3->Begin()); - spaces.AddSpace(space3.get()); - } - EXPECT_EQ(reinterpret_cast(spaces.GetLargestImmuneRegion().Begin()), - space1->Begin()); - EXPECT_EQ(reinterpret_cast(spaces.GetLargestImmuneRegion().End()), - space3->Limit()); - - // Add a smaller non-adjacent space and ensure it is not part of the immune region. - uint8_t* memory2 = GetContinuousMemoryRegion(kImageBytes + kPageSize); - std::unique_ptr space4(CreateImageSpace(memory2 + kPageSize, - kImageBytes - kPageSize, - memory2 + kImageBytes, - kPageSize)); - ASSERT_TRUE(space4 != nullptr); - { - WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); - LOG(INFO) << "Adding space4 " << reinterpret_cast(space4->Begin()); - spaces.AddSpace(space4.get()); + spaces.AddSpace(image_space.get()); } EXPECT_EQ(reinterpret_cast(spaces.GetLargestImmuneRegion().Begin()), - space1->Begin()); - EXPECT_EQ(reinterpret_cast(spaces.GetLargestImmuneRegion().End()), - space3->Limit()); - - // Add a larger non-adjacent space and ensure it becomes the new largest immune region. - uint8_t* memory3 = GetContinuousMemoryRegion(kImageBytes + kPageSize * 3); - std::unique_ptr space5(CreateImageSpace(memory3 + kPageSize, - kImageBytes + kPageSize, - memory3 + kImageBytes + 2 * kPageSize, - kPageSize)); - ASSERT_TRUE(space5 != nullptr); - { - WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); - LOG(INFO) << "Adding space5 " << reinterpret_cast(space5->Begin()); - spaces.AddSpace(space5.get()); - } - EXPECT_EQ(reinterpret_cast(spaces.GetLargestImmuneRegion().Begin()), space5->Begin()); - EXPECT_EQ(reinterpret_cast(spaces.GetLargestImmuneRegion().End()), space5->Limit()); + image_space->Begin()); + // Size should be equal, we should not add the oat file since it is not adjacent to the image + // space. + EXPECT_EQ(spaces.GetLargestImmuneRegion().Size(), image_size); } } // namespace collector -- GitLab From 95303c682bba971128950ef82c8f6c247917ffff Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Mon, 29 Feb 2016 09:35:26 +0000 Subject: [PATCH 064/204] Disable symbolizing traces on volantis. Change-Id: I1ba6b5a173e77e635d721dea8433cfcb8b0b787b --- tools/symbolize-buildbot-crashes.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tools/symbolize-buildbot-crashes.sh b/tools/symbolize-buildbot-crashes.sh index 8dc4e27885..505cf73fad 100755 --- a/tools/symbolize-buildbot-crashes.sh +++ b/tools/symbolize-buildbot-crashes.sh @@ -14,6 +14,12 @@ # See the License for the specific language governing permissions and # limitations under the License. +if [ "$TARGET_PRODUCT" = 'armv8' ]; then + echo "Symbolizing traces on Nexus9 with an L build is currently" + echo "bogus, as the system thinks crashes are arm32." + exit 0 +fi + # We push art and its dependencies to '/data/local/tmp', but the 'stack' # script expect things to be in '/'. So we just remove the # '/data/local/tmp' prefix. -- GitLab From 20adf2776c2d81facf7c5ba3f82f27d4ee58340d Mon Sep 17 00:00:00 2001 From: Goran Jakovljevic Date: Mon, 29 Feb 2016 12:15:34 +0100 Subject: [PATCH 065/204] Optimizing: Do not depend on sharpening in test 577 The sharpening may or may not remove the ArtMethod* parameter, so the test must not depend on its absence. This fixes the test 577 checker test on mips/mips64. Change-Id: I01a2fc1bd2755804326a2ac284ee81e99fc91aa6 --- test/577-checker-fp2int/src/Main.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/577-checker-fp2int/src/Main.java b/test/577-checker-fp2int/src/Main.java index e3f1230beb..ace956df2f 100644 --- a/test/577-checker-fp2int/src/Main.java +++ b/test/577-checker-fp2int/src/Main.java @@ -21,7 +21,8 @@ public class Main { /// CHECK-DAG: Return [<>] // /// CHECK-START: int Main.f2int(float) instruction_simplifier (after) - /// CHECK-DAG: <> InvokeStaticOrDirect [<>] intrinsic:FloatFloatToRawIntBits + // Note: The ArtMethod* (typed as int or long) is optional after sharpening. + /// CHECK-DAG: <> InvokeStaticOrDirect [<>{{(,[ij]\d+)?}}] intrinsic:FloatFloatToRawIntBits /// CHECK-DAG: <> NotEqual [<>,<>] /// CHECK-DAG: <> Select [<>,{{i\d+}},<>] /// CHECK-DAG: Return [<>] @@ -34,7 +35,8 @@ public class Main { /// CHECK-DAG: Return [<>] // /// CHECK-START: long Main.d2long(double) instruction_simplifier (after) - /// CHECK-DAG: <> InvokeStaticOrDirect [<>] intrinsic:DoubleDoubleToRawLongBits + // Note: The ArtMethod* (typed as int or long) is optional after sharpening. + /// CHECK-DAG: <> InvokeStaticOrDirect [<>{{(,[ij]\d+)?}}] intrinsic:DoubleDoubleToRawLongBits /// CHECK-DAG: <> NotEqual [<>,<>] /// CHECK-DAG: <> Select [<>,{{j\d+}},<>] /// CHECK-DAG: Return [<>] -- GitLab From a1d8ddfaf09545f99bc326dff97ab604d4574eb6 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Mon, 29 Feb 2016 11:46:58 +0000 Subject: [PATCH 066/204] Bug fix for polymorphic inlining. The code used to wrongly propagate try/catch information to new blocks. Since it has the same logic as Hraph::InlineInto, extract the code that updates loop and try/catch information to blocks to a shared method. bug:27330865 bug:27372101 bug:27360329 Change-Id: I4386f724d8d412bde5bcc04fda6955bc3bacf5a9 --- compiler/optimizing/inliner.cc | 33 ++---- compiler/optimizing/nodes.cc | 112 +++++++++----------- compiler/optimizing/nodes.h | 7 ++ test/578-polymorphic-inlining/expected.txt | 0 test/578-polymorphic-inlining/info.txt | 2 + test/578-polymorphic-inlining/src/Main.java | 56 ++++++++++ 6 files changed, 125 insertions(+), 85 deletions(-) create mode 100644 test/578-polymorphic-inlining/expected.txt create mode 100644 test/578-polymorphic-inlining/info.txt create mode 100644 test/578-polymorphic-inlining/src/Main.java diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index d55009554f..e14eb334db 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -562,31 +562,16 @@ void HInliner::CreateDiamondPatternForPolymorphicInline(HInstruction* compare, graph_->reverse_post_order_[++index] = otherwise; graph_->reverse_post_order_[++index] = merge; - // Set the loop information of the newly created blocks. - HLoopInformation* loop_info = cursor_block->GetLoopInformation(); - if (loop_info != nullptr) { - then->SetLoopInformation(cursor_block->GetLoopInformation()); - merge->SetLoopInformation(cursor_block->GetLoopInformation()); - otherwise->SetLoopInformation(cursor_block->GetLoopInformation()); - for (HLoopInformationOutwardIterator loop_it(*cursor_block); - !loop_it.Done(); - loop_it.Advance()) { - loop_it.Current()->Add(then); - loop_it.Current()->Add(merge); - loop_it.Current()->Add(otherwise); - } - // In case the original invoke location was a back edge, we need to update - // the loop to now have the merge block as a back edge. - if (loop_info->IsBackEdge(*original_invoke_block)) { - loop_info->RemoveBackEdge(original_invoke_block); - loop_info->AddBackEdge(merge); - } - } - // Set the try/catch information of the newly created blocks. - then->SetTryCatchInformation(cursor_block->GetTryCatchInformation()); - merge->SetTryCatchInformation(cursor_block->GetTryCatchInformation()); - otherwise->SetTryCatchInformation(cursor_block->GetTryCatchInformation()); + graph_->UpdateLoopAndTryInformationOfNewBlock( + then, original_invoke_block, /* replace_if_back_edge */ false); + graph_->UpdateLoopAndTryInformationOfNewBlock( + otherwise, original_invoke_block, /* replace_if_back_edge */ false); + + // In case the original invoke location was a back edge, we need to update + // the loop to now have the merge block as a back edge. + graph_->UpdateLoopAndTryInformationOfNewBlock( + merge, original_invoke_block, /* replace_if_back_edge */ true); } bool HInliner::TryInlinePolymorphicCallToSameTarget(HInvoke* invoke_instruction, diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index 27a5b97f5f..87b9c022df 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -1877,6 +1877,42 @@ void HGraph::DeleteDeadEmptyBlock(HBasicBlock* block) { blocks_[block->GetBlockId()] = nullptr; } +void HGraph::UpdateLoopAndTryInformationOfNewBlock(HBasicBlock* block, + HBasicBlock* reference, + bool replace_if_back_edge) { + if (block->IsLoopHeader()) { + // Clear the information of which blocks are contained in that loop. Since the + // information is stored as a bit vector based on block ids, we have to update + // it, as those block ids were specific to the callee graph and we are now adding + // these blocks to the caller graph. + block->GetLoopInformation()->ClearAllBlocks(); + } + + // If not already in a loop, update the loop information. + if (!block->IsInLoop()) { + block->SetLoopInformation(reference->GetLoopInformation()); + } + + // If the block is in a loop, update all its outward loops. + HLoopInformation* loop_info = block->GetLoopInformation(); + if (loop_info != nullptr) { + for (HLoopInformationOutwardIterator loop_it(*block); + !loop_it.Done(); + loop_it.Advance()) { + loop_it.Current()->Add(block); + } + if (replace_if_back_edge && loop_info->IsBackEdge(*reference)) { + loop_info->ReplaceBackEdge(reference, block); + } + } + + // Copy TryCatchInformation if `reference` is a try block, not if it is a catch block. + TryCatchInformation* try_catch_info = reference->IsTryBlock() + ? reference->GetTryCatchInformation() + : nullptr; + block->SetTryCatchInformation(try_catch_info); +} + HInstruction* HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) { DCHECK(HasExitBlock()) << "Unimplemented scenario"; // Update the environments in this graph to have the invoke's environment @@ -1991,10 +2027,6 @@ HInstruction* HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) { size_t index_of_at = IndexOfElement(outer_graph->reverse_post_order_, at); MakeRoomFor(&outer_graph->reverse_post_order_, blocks_added, index_of_at); - HLoopInformation* loop_info = at->GetLoopInformation(); - // Copy TryCatchInformation if `at` is a try block, not if it is a catch block. - TryCatchInformation* try_catch_info = at->IsTryBlock() ? at->GetTryCatchInformation() : nullptr; - // Do a reverse post order of the blocks in the callee and do (1), (2), (3) // and (4) to the blocks that apply. for (HReversePostOrderIterator it(*this); !it.Done(); it.Advance()) { @@ -2005,23 +2037,7 @@ HInstruction* HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) { current->SetGraph(outer_graph); outer_graph->AddBlock(current); outer_graph->reverse_post_order_[++index_of_at] = current; - if (!current->IsInLoop()) { - current->SetLoopInformation(loop_info); - } else if (current->IsLoopHeader()) { - // Clear the information of which blocks are contained in that loop. Since the - // information is stored as a bit vector based on block ids, we have to update - // it, as those block ids were specific to the callee graph and we are now adding - // these blocks to the caller graph. - current->GetLoopInformation()->ClearAllBlocks(); - } - if (current->IsInLoop()) { - for (HLoopInformationOutwardIterator loop_it(*current); - !loop_it.Done(); - loop_it.Advance()) { - loop_it.Current()->Add(current); - } - } - current->SetTryCatchInformation(try_catch_info); + UpdateLoopAndTryInformationOfNewBlock(current, at, /* replace_if_back_edge */ false); } } @@ -2029,20 +2045,9 @@ HInstruction* HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) { to->SetGraph(outer_graph); outer_graph->AddBlock(to); outer_graph->reverse_post_order_[++index_of_at] = to; - if (loop_info != nullptr) { - if (!to->IsInLoop()) { - to->SetLoopInformation(loop_info); - } - for (HLoopInformationOutwardIterator loop_it(*at); !loop_it.Done(); loop_it.Advance()) { - loop_it.Current()->Add(to); - } - if (loop_info->IsBackEdge(*at)) { - // Only `to` can become a back edge, as the inlined blocks - // are predecessors of `to`. - loop_info->ReplaceBackEdge(at, to); - } - } - to->SetTryCatchInformation(try_catch_info); + // Only `to` can become a back edge, as the inlined blocks + // are predecessors of `to`. + UpdateLoopAndTryInformationOfNewBlock(to, at, /* replace_if_back_edge */ true); } // Update the next instruction id of the outer graph, so that instructions @@ -2157,32 +2162,17 @@ void HGraph::TransformLoopHeaderForBCE(HBasicBlock* header) { reverse_post_order_[index_of_header++] = false_block; reverse_post_order_[index_of_header++] = new_pre_header; - // Fix loop information. - HLoopInformation* loop_info = old_pre_header->GetLoopInformation(); - if (loop_info != nullptr) { - if_block->SetLoopInformation(loop_info); - true_block->SetLoopInformation(loop_info); - false_block->SetLoopInformation(loop_info); - new_pre_header->SetLoopInformation(loop_info); - // Add blocks to all enveloping loops. - for (HLoopInformationOutwardIterator loop_it(*old_pre_header); - !loop_it.Done(); - loop_it.Advance()) { - loop_it.Current()->Add(if_block); - loop_it.Current()->Add(true_block); - loop_it.Current()->Add(false_block); - loop_it.Current()->Add(new_pre_header); - } - } - - // Fix try/catch information. - TryCatchInformation* try_catch_info = old_pre_header->IsTryBlock() - ? old_pre_header->GetTryCatchInformation() - : nullptr; - if_block->SetTryCatchInformation(try_catch_info); - true_block->SetTryCatchInformation(try_catch_info); - false_block->SetTryCatchInformation(try_catch_info); - new_pre_header->SetTryCatchInformation(try_catch_info); + // The pre_header can never be a back edge of a loop. + DCHECK((old_pre_header->GetLoopInformation() == nullptr) || + !old_pre_header->GetLoopInformation()->IsBackEdge(*old_pre_header)); + UpdateLoopAndTryInformationOfNewBlock( + if_block, old_pre_header, /* replace_if_back_edge */ false); + UpdateLoopAndTryInformationOfNewBlock( + true_block, old_pre_header, /* replace_if_back_edge */ false); + UpdateLoopAndTryInformationOfNewBlock( + false_block, old_pre_header, /* replace_if_back_edge */ false); + UpdateLoopAndTryInformationOfNewBlock( + new_pre_header, old_pre_header, /* replace_if_back_edge */ false); } static void CheckAgainstUpperBound(ReferenceTypeInfo rti, ReferenceTypeInfo upper_bound_rti) diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 9eddfc7e0e..e3dbe16547 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -353,6 +353,13 @@ class HGraph : public ArenaObject { // and removing the invoke instruction. HInstruction* InlineInto(HGraph* outer_graph, HInvoke* invoke); + // Update the loop and try membership of `block`, which was spawned from `reference`. + // In case `reference` is a back edge, `replace_if_back_edge` notifies whether `block` + // should be the new back edge. + void UpdateLoopAndTryInformationOfNewBlock(HBasicBlock* block, + HBasicBlock* reference, + bool replace_if_back_edge); + // Need to add a couple of blocks to test if the loop body is entered and // put deoptimization instructions, etc. void TransformLoopHeaderForBCE(HBasicBlock* header); diff --git a/test/578-polymorphic-inlining/expected.txt b/test/578-polymorphic-inlining/expected.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/578-polymorphic-inlining/info.txt b/test/578-polymorphic-inlining/info.txt new file mode 100644 index 0000000000..77ec49b347 --- /dev/null +++ b/test/578-polymorphic-inlining/info.txt @@ -0,0 +1,2 @@ +Regression test for polymorphic inlining that used to propagate +wrongly the try/catch information of new blocks. diff --git a/test/578-polymorphic-inlining/src/Main.java b/test/578-polymorphic-inlining/src/Main.java new file mode 100644 index 0000000000..22d33d083b --- /dev/null +++ b/test/578-polymorphic-inlining/src/Main.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Main { + public static void main(String[] args) { + for (int i = 0; i < 20000; ++i) { + $noinline$testInTryCatch(new Main(), i); + $noinline$testInTryCatch(new SubMain(), i); + } + } + + public static void $noinline$testInTryCatch(Main m, int i) { + final int value; + try { + throw new Exception(); + } catch (Exception e) { + // The polymorphic inlining of 'willInlineVoid' used to generate an + // incorrect graph, by setting the inlined blocks as catch blocks. + m.willInlineVoid(i); + return; + } + } + + public void willInlineVoid(int i) { + if (i == 0) { + $noinline$foo(); + } else { + $noinline$foo(); + $noinline$foo(); + } + } + + public static void $noinline$foo() { + if (doThrow) throw new Error(""); + } + + public static boolean doThrow; +} + +class SubMain extends Main { + public void willInlineVoid(int i) { + } +} -- GitLab From 4f97a212b6ff801eb9f7f88a87f44e0584339cca Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Thu, 25 Feb 2016 16:17:54 +0000 Subject: [PATCH 067/204] Cleanup polymorphic inlining to the same target. - Don't do expensive lookups, just fetch the ArtMethods from the imtable or vtable. - Don't perform the optimization if we may end up in the conflicting trampoline. bug:27398183 Change-Id: I327c9f7c3a85d570a057ff8f18602e8e52402fdc --- compiler/optimizing/inliner.cc | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index d55009554f..494a6ee9c3 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -603,6 +603,10 @@ bool HInliner::TryInlinePolymorphicCallToSameTarget(HInvoke* invoke_instruction, DCHECK(resolved_method != nullptr); ArtMethod* actual_method = nullptr; + size_t method_index = invoke_instruction->IsInvokeVirtual() + ? invoke_instruction->AsInvokeVirtual()->GetVTableIndex() + : invoke_instruction->AsInvokeInterface()->GetImtIndex(); + // Check whether we are actually calling the same method among // the different types seen. for (size_t i = 0; i < InlineCache::kIndividualCacheSize; ++i) { @@ -611,13 +615,18 @@ bool HInliner::TryInlinePolymorphicCallToSameTarget(HInvoke* invoke_instruction, } ArtMethod* new_method = nullptr; if (invoke_instruction->IsInvokeInterface()) { - new_method = ic.GetTypeAt(i)->FindVirtualMethodForInterface( - resolved_method, pointer_size); + new_method = ic.GetTypeAt(i)->GetEmbeddedImTableEntry( + method_index % mirror::Class::kImtSize, pointer_size); + if (new_method->IsRuntimeMethod()) { + // Bail out as soon as we see a conflict trampoline in one of the target's + // interface table. + return false; + } } else { DCHECK(invoke_instruction->IsInvokeVirtual()); - new_method = ic.GetTypeAt(i)->FindVirtualMethodForVirtual( - resolved_method, pointer_size); + new_method = ic.GetTypeAt(i)->GetEmbeddedVTableEntry(method_index, pointer_size); } + DCHECK(new_method != nullptr); if (actual_method == nullptr) { actual_method = new_method; } else if (actual_method != new_method) { @@ -641,10 +650,6 @@ bool HInliner::TryInlinePolymorphicCallToSameTarget(HInvoke* invoke_instruction, HInstanceFieldGet* receiver_class = BuildGetReceiverClass( class_linker, receiver, invoke_instruction->GetDexPc()); - size_t method_offset = invoke_instruction->IsInvokeVirtual() - ? actual_method->GetVtableIndex() - : invoke_instruction->AsInvokeInterface()->GetImtIndex(); - Primitive::Type type = Is64BitInstructionSet(graph_->GetInstructionSet()) ? Primitive::kPrimLong : Primitive::kPrimInt; @@ -653,7 +658,7 @@ bool HInliner::TryInlinePolymorphicCallToSameTarget(HInvoke* invoke_instruction, type, invoke_instruction->IsInvokeVirtual() ? HClassTableGet::TableKind::kVTable : HClassTableGet::TableKind::kIMTable, - method_offset, + method_index, invoke_instruction->GetDexPc()); HConstant* constant; -- GitLab From cc3839c15555a2751e13980638fc40e4d3da633e Mon Sep 17 00:00:00 2001 From: Roland Levillain Date: Mon, 29 Feb 2016 16:23:48 +0000 Subject: [PATCH 068/204] Improve documentation about StringFactory.newStringFromChars. Make it clear that the native method requires its third argument to be non-null, and therefore that the intrinsics do not need a null check for it. Bug: 27378573 Change-Id: Id2f78ceb0f7674f1066bc3f216b738358ca25542 --- compiler/dex/quick/gen_invoke.cc | 6 ++++++ compiler/optimizing/intrinsics_arm.cc | 6 ++++++ compiler/optimizing/intrinsics_arm64.cc | 6 ++++++ compiler/optimizing/intrinsics_mips64.cc | 10 ++++++++-- compiler/optimizing/intrinsics_x86.cc | 6 ++++++ compiler/optimizing/intrinsics_x86_64.cc | 6 ++++++ runtime/native/java_lang_StringFactory.cc | 2 ++ 7 files changed, 40 insertions(+), 2 deletions(-) diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc index 422d82ffa2..fa7c961e42 100755 --- a/compiler/dex/quick/gen_invoke.cc +++ b/compiler/dex/quick/gen_invoke.cc @@ -1140,6 +1140,12 @@ bool Mir2Lir::GenInlinedStringFactoryNewStringFromChars(CallInfo* info) { RegLocation rl_offset = info->args[0]; RegLocation rl_count = info->args[1]; RegLocation rl_data = info->args[2]; + // No need to emit code checking whether `rl_data` is a null + // pointer, as callers of the native method + // + // java.lang.StringFactory.newStringFromChars(int offset, int charCount, char[] data) + // + // all include a null check on `data` before calling that method. CallRuntimeHelperRegLocationRegLocationRegLocation( kQuickAllocStringFromChars, rl_offset, rl_count, rl_data, true); RegLocation rl_return = GetReturn(kRefReg); diff --git a/compiler/optimizing/intrinsics_arm.cc b/compiler/optimizing/intrinsics_arm.cc index 4ce919ee39..448b4251a5 100644 --- a/compiler/optimizing/intrinsics_arm.cc +++ b/compiler/optimizing/intrinsics_arm.cc @@ -1242,6 +1242,12 @@ void IntrinsicLocationsBuilderARM::VisitStringNewStringFromChars(HInvoke* invoke void IntrinsicCodeGeneratorARM::VisitStringNewStringFromChars(HInvoke* invoke) { ArmAssembler* assembler = GetAssembler(); + // No need to emit code checking whether `locations->InAt(2)` is a null + // pointer, as callers of the native method + // + // java.lang.StringFactory.newStringFromChars(int offset, int charCount, char[] data) + // + // all include a null check on `data` before calling that method. __ LoadFromOffset( kLoadWord, LR, TR, QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pAllocStringFromChars).Int32Value()); codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc index 4be1695a94..d05f5af157 100644 --- a/compiler/optimizing/intrinsics_arm64.cc +++ b/compiler/optimizing/intrinsics_arm64.cc @@ -1392,6 +1392,12 @@ void IntrinsicLocationsBuilderARM64::VisitStringNewStringFromChars(HInvoke* invo void IntrinsicCodeGeneratorARM64::VisitStringNewStringFromChars(HInvoke* invoke) { vixl::MacroAssembler* masm = GetVIXLAssembler(); + // No need to emit code checking whether `locations->InAt(2)` is a null + // pointer, as callers of the native method + // + // java.lang.StringFactory.newStringFromChars(int offset, int charCount, char[] data) + // + // all include a null check on `data` before calling that method. __ Ldr(lr, MemOperand(tr, QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, pAllocStringFromChars).Int32Value())); codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc index ca2652b74a..78b4360797 100644 --- a/compiler/optimizing/intrinsics_mips64.cc +++ b/compiler/optimizing/intrinsics_mips64.cc @@ -1685,7 +1685,7 @@ void IntrinsicCodeGeneratorMIPS64::VisitStringIndexOfAfter(HInvoke* invoke) { invoke, GetAssembler(), codegen_, GetAllocator(), /* start_at_zero */ false); } -// java.lang.String.String(byte[] bytes) +// java.lang.StringFactory.newStringFromBytes(byte[] data, int high, int offset, int byteCount) void IntrinsicLocationsBuilderMIPS64::VisitStringNewStringFromBytes(HInvoke* invoke) { LocationSummary* locations = new (arena_) LocationSummary(invoke, LocationSummary::kCall, @@ -1719,7 +1719,7 @@ void IntrinsicCodeGeneratorMIPS64::VisitStringNewStringFromBytes(HInvoke* invoke __ Bind(slow_path->GetExitLabel()); } -// java.lang.String.String(char[] value) +// java.lang.StringFactory.newStringFromChars(int offset, int charCount, char[] data) void IntrinsicLocationsBuilderMIPS64::VisitStringNewStringFromChars(HInvoke* invoke) { LocationSummary* locations = new (arena_) LocationSummary(invoke, LocationSummary::kCall, @@ -1735,6 +1735,12 @@ void IntrinsicLocationsBuilderMIPS64::VisitStringNewStringFromChars(HInvoke* inv void IntrinsicCodeGeneratorMIPS64::VisitStringNewStringFromChars(HInvoke* invoke) { Mips64Assembler* assembler = GetAssembler(); + // No need to emit code checking whether `locations->InAt(2)` is a null + // pointer, as callers of the native method + // + // java.lang.StringFactory.newStringFromChars(int offset, int charCount, char[] data) + // + // all include a null check on `data` before calling that method. __ LoadFromOffset(kLoadDoubleword, TMP, TR, diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc index 0df4553f56..bfa4e374f7 100644 --- a/compiler/optimizing/intrinsics_x86.cc +++ b/compiler/optimizing/intrinsics_x86.cc @@ -1564,6 +1564,12 @@ void IntrinsicLocationsBuilderX86::VisitStringNewStringFromChars(HInvoke* invoke void IntrinsicCodeGeneratorX86::VisitStringNewStringFromChars(HInvoke* invoke) { X86Assembler* assembler = GetAssembler(); + // No need to emit code checking whether `locations->InAt(2)` is a null + // pointer, as callers of the native method + // + // java.lang.StringFactory.newStringFromChars(int offset, int charCount, char[] data) + // + // all include a null check on `data` before calling that method. __ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pAllocStringFromChars))); codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); } diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc index 2a9e684d11..15c399712d 100644 --- a/compiler/optimizing/intrinsics_x86_64.cc +++ b/compiler/optimizing/intrinsics_x86_64.cc @@ -1659,6 +1659,12 @@ void IntrinsicLocationsBuilderX86_64::VisitStringNewStringFromChars(HInvoke* inv void IntrinsicCodeGeneratorX86_64::VisitStringNewStringFromChars(HInvoke* invoke) { X86_64Assembler* assembler = GetAssembler(); + // No need to emit code checking whether `locations->InAt(2)` is a null + // pointer, as callers of the native method + // + // java.lang.StringFactory.newStringFromChars(int offset, int charCount, char[] data) + // + // all include a null check on `data` before calling that method. __ gs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pAllocStringFromChars), /* no_rip */ true)); codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); diff --git a/runtime/native/java_lang_StringFactory.cc b/runtime/native/java_lang_StringFactory.cc index 34d6a37ab2..5a219efc7b 100644 --- a/runtime/native/java_lang_StringFactory.cc +++ b/runtime/native/java_lang_StringFactory.cc @@ -50,8 +50,10 @@ static jstring StringFactory_newStringFromBytes(JNIEnv* env, jclass, jbyteArray return soa.AddLocalReference(result); } +// The char array passed as `java_data` must not be a null reference. static jstring StringFactory_newStringFromChars(JNIEnv* env, jclass, jint offset, jint char_count, jcharArray java_data) { + DCHECK(java_data != nullptr); ScopedFastNativeObjectAccess soa(env); StackHandleScope<1> hs(soa.Self()); Handle char_array(hs.NewHandle(soa.Decode(java_data))); -- GitLab From 42ad288254e660ad091d03fad8c8fbad1d34ec89 Mon Sep 17 00:00:00 2001 From: Roland Levillain Date: Mon, 29 Feb 2016 18:26:54 +0000 Subject: [PATCH 069/204] Fix the signature of the IndexOf entry point. The IndexOf entry point was declared as taking four arguments (void*, uint32_t, uint32_t, uint32_t) whereas all actual implementations use three arguments (void*, uint32_t, uint32_t). As that fourth argument is not documented, drop it from the intrinsic declaration to have it match the implementations. Change-Id: I65d747033192025ccd2b9a5e8f8ed05b77a21941 --- compiler/dex/quick/gen_invoke.cc | 1 + compiler/optimizing/intrinsics_arm.cc | 1 + compiler/optimizing/intrinsics_arm64.cc | 1 + compiler/optimizing/intrinsics_mips64.cc | 1 + runtime/entrypoints/quick/quick_default_externs.h | 2 +- runtime/entrypoints/quick/quick_entrypoints_list.h | 2 +- 6 files changed, 6 insertions(+), 2 deletions(-) diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc index 422d82ffa2..fe85555bc4 100755 --- a/compiler/dex/quick/gen_invoke.cc +++ b/compiler/dex/quick/gen_invoke.cc @@ -1357,6 +1357,7 @@ bool Mir2Lir::GenInlinedIndexOf(CallInfo* info, bool zero_based) { LoadValueDirectFixed(rl_start, reg_start); } RegStorage r_tgt = LoadHelper(kQuickIndexOf); + CheckEntrypointTypes(); GenExplicitNullCheck(reg_ptr, info->opt_flags); LIR* high_code_point_branch = rl_char.is_const ? nullptr : OpCmpImmBranch(kCondGt, reg_char, 0xFFFF, nullptr); diff --git a/compiler/optimizing/intrinsics_arm.cc b/compiler/optimizing/intrinsics_arm.cc index 4ce919ee39..a9da5fa696 100644 --- a/compiler/optimizing/intrinsics_arm.cc +++ b/compiler/optimizing/intrinsics_arm.cc @@ -1151,6 +1151,7 @@ static void GenerateVisitStringIndexOf(HInvoke* invoke, __ LoadFromOffset(kLoadWord, LR, TR, QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pIndexOf).Int32Value()); + CheckEntrypointTypes(); __ blx(LR); if (slow_path != nullptr) { diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc index 4be1695a94..ce595c54e0 100644 --- a/compiler/optimizing/intrinsics_arm64.cc +++ b/compiler/optimizing/intrinsics_arm64.cc @@ -1301,6 +1301,7 @@ static void GenerateVisitStringIndexOf(HInvoke* invoke, } __ Ldr(lr, MemOperand(tr, QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, pIndexOf).Int32Value())); + CheckEntrypointTypes(); __ Blr(lr); if (slow_path != nullptr) { diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc index ca2652b74a..0f98549716 100644 --- a/compiler/optimizing/intrinsics_mips64.cc +++ b/compiler/optimizing/intrinsics_mips64.cc @@ -1636,6 +1636,7 @@ static void GenerateStringIndexOf(HInvoke* invoke, TMP, TR, QUICK_ENTRYPOINT_OFFSET(kMips64DoublewordSize, pIndexOf).Int32Value()); + CheckEntrypointTypes(); __ Jalr(TMP); __ Nop(); diff --git a/runtime/entrypoints/quick/quick_default_externs.h b/runtime/entrypoints/quick/quick_default_externs.h index fbf028d882..4e01d80312 100644 --- a/runtime/entrypoints/quick/quick_default_externs.h +++ b/runtime/entrypoints/quick/quick_default_externs.h @@ -93,7 +93,7 @@ extern "C" uint64_t art_quick_shr_long(uint64_t, uint32_t); extern "C" uint64_t art_quick_ushr_long(uint64_t, uint32_t); // Intrinsic entrypoints. -extern "C" int32_t art_quick_indexof(void*, uint32_t, uint32_t, uint32_t); +extern "C" int32_t art_quick_indexof(void*, uint32_t, uint32_t); extern "C" int32_t art_quick_string_compareto(void*, void*); extern "C" void* art_quick_memcpy(void*, const void*, size_t); diff --git a/runtime/entrypoints/quick/quick_entrypoints_list.h b/runtime/entrypoints/quick/quick_entrypoints_list.h index faa4747ec3..79d1c1377b 100644 --- a/runtime/entrypoints/quick/quick_entrypoints_list.h +++ b/runtime/entrypoints/quick/quick_entrypoints_list.h @@ -119,7 +119,7 @@ V(ShrLong, uint64_t, uint64_t, uint32_t) \ V(UshrLong, uint64_t, uint64_t, uint32_t) \ \ - V(IndexOf, int32_t, void*, uint32_t, uint32_t, uint32_t) \ + V(IndexOf, int32_t, void*, uint32_t, uint32_t) \ V(StringCompareTo, int32_t, void*, void*) \ V(Memcpy, void*, void*, const void*, size_t) \ \ -- GitLab From 032c0051eb47f1f62586602894e5833356f4f9ec Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Sun, 28 Feb 2016 13:10:58 -0800 Subject: [PATCH 070/204] Revert "Revert "Improve immune spaces logic"" Test was flaky due to missing guard page at the end of the region. Bug: 27136196 This reverts commit 07dbbca0b42cb8da1811de8209b4a7d4becfc7b2. Change-Id: I4eb345f34e66b8684cd71a7897e3a8caa2f84140 --- runtime/gc/collector/immune_spaces.cc | 68 +++-- runtime/gc/collector/immune_spaces_test.cc | 338 ++++++++++++++++----- 2 files changed, 304 insertions(+), 102 deletions(-) diff --git a/runtime/gc/collector/immune_spaces.cc b/runtime/gc/collector/immune_spaces.cc index 26da4ca5a9..1e5f28382b 100644 --- a/runtime/gc/collector/immune_spaces.cc +++ b/runtime/gc/collector/immune_spaces.cc @@ -16,6 +16,9 @@ #include "immune_spaces.h" +#include +#include + #include "gc/space/space-inl.h" #include "mirror/object.h" #include "oat_file.h" @@ -32,11 +35,12 @@ void ImmuneSpaces::Reset() { void ImmuneSpaces::CreateLargestImmuneRegion() { uintptr_t best_begin = 0u; uintptr_t best_end = 0u; + uintptr_t best_heap_size = 0u; uintptr_t cur_begin = 0u; uintptr_t cur_end = 0u; - // TODO: If the last space is an image space, we may include its oat file in the immune region. - // This could potentially hide heap corruption bugs if there is invalid pointers that point into - // the boot oat code + uintptr_t cur_heap_size = 0u; + using Interval = std::tuple; + std::vector intervals; for (space::ContinuousSpace* space : GetSpaces()) { uintptr_t space_begin = reinterpret_cast(space->Begin()); uintptr_t space_end = reinterpret_cast(space->Limit()); @@ -50,29 +54,47 @@ void ImmuneSpaces::CreateLargestImmuneRegion() { // creation, the actual oat file could be somewhere else. const OatFile* const image_oat_file = image_space->GetOatFile(); if (image_oat_file != nullptr) { - uintptr_t oat_begin = reinterpret_cast(image_oat_file->Begin()); - uintptr_t oat_end = reinterpret_cast(image_oat_file->End()); - if (space_end == oat_begin) { - DCHECK_GE(oat_end, oat_begin); - space_end = oat_end; - } + intervals.push_back(Interval(reinterpret_cast(image_oat_file->Begin()), + reinterpret_cast(image_oat_file->End()), + /*image*/false)); } } - if (cur_begin == 0u) { - cur_begin = space_begin; - cur_end = space_end; - } else if (cur_end == space_begin) { - // Extend current region. - cur_end = space_end; - } else { - // Reset. - cur_begin = 0; - cur_end = 0; + intervals.push_back(Interval(space_begin, space_end, /*is_heap*/true)); + } + std::sort(intervals.begin(), intervals.end()); + // Intervals are already sorted by begin, if a new interval begins at the end of the current + // region then we append, otherwise we restart the current interval. To prevent starting an + // interval on an oat file, ignore oat files that are not extending an existing interval. + // If the total number of image bytes in the current interval is larger than the current best + // one, then we set the best one to be the current one. + for (const Interval& interval : intervals) { + const uintptr_t begin = std::get<0>(interval); + const uintptr_t end = std::get<1>(interval); + const bool is_heap = std::get<2>(interval); + VLOG(collector) << "Interval " << reinterpret_cast(begin) << "-" + << reinterpret_cast(end) << " is_heap=" << is_heap; + DCHECK_GE(end, begin); + DCHECK_GE(begin, cur_end); + // New interval is not at the end of the current one, start a new interval if we are a heap + // interval. Otherwise continue since we never start a new region with non image intervals. + if (begin != cur_end) { + if (!is_heap) { + continue; + } + // Not extending, reset the region. + cur_begin = begin; + cur_heap_size = 0; } - if (cur_end - cur_begin > best_end - best_begin) { - // Improvement, update the best range. - best_begin = cur_begin; - best_end = cur_end; + cur_end = end; + if (is_heap) { + // Only update if the total number of image bytes is greater than the current best one. + // We don't want to count the oat file bytes since these contain no java objects. + cur_heap_size += end - begin; + if (cur_heap_size > best_heap_size) { + best_begin = cur_begin; + best_end = cur_end; + best_heap_size = cur_heap_size; + } } } largest_immune_region_.SetBegin(reinterpret_cast(best_begin)); diff --git a/runtime/gc/collector/immune_spaces_test.cc b/runtime/gc/collector/immune_spaces_test.cc index 56838f5c06..cf93ec614d 100644 --- a/runtime/gc/collector/immune_spaces_test.cc +++ b/runtime/gc/collector/immune_spaces_test.cc @@ -28,50 +28,6 @@ class Object; namespace gc { namespace collector { -class ImmuneSpacesTest : public CommonRuntimeTest {}; - -class DummySpace : public space::ContinuousSpace { - public: - DummySpace(uint8_t* begin, uint8_t* end) - : ContinuousSpace("DummySpace", - space::kGcRetentionPolicyNeverCollect, - begin, - end, - /*limit*/end) {} - - space::SpaceType GetType() const OVERRIDE { - return space::kSpaceTypeMallocSpace; - } - - bool CanMoveObjects() const OVERRIDE { - return false; - } - - accounting::ContinuousSpaceBitmap* GetLiveBitmap() const OVERRIDE { - return nullptr; - } - - accounting::ContinuousSpaceBitmap* GetMarkBitmap() const OVERRIDE { - return nullptr; - } -}; - -TEST_F(ImmuneSpacesTest, AppendBasic) { - ImmuneSpaces spaces; - uint8_t* const base = reinterpret_cast(0x1000); - DummySpace a(base, base + 45 * KB); - DummySpace b(a.Limit(), a.Limit() + 813 * KB); - { - WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); - spaces.AddSpace(&a); - spaces.AddSpace(&b); - } - EXPECT_TRUE(spaces.ContainsSpace(&a)); - EXPECT_TRUE(spaces.ContainsSpace(&b)); - EXPECT_EQ(reinterpret_cast(spaces.GetLargestImmuneRegion().Begin()), a.Begin()); - EXPECT_EQ(reinterpret_cast(spaces.GetLargestImmuneRegion().End()), b.Limit()); -} - class DummyOatFile : public OatFile { public: DummyOatFile(uint8_t* begin, uint8_t* end) : OatFile("Location", /*is_executable*/ false) { @@ -84,23 +40,50 @@ class DummyImageSpace : public space::ImageSpace { public: DummyImageSpace(MemMap* map, accounting::ContinuousSpaceBitmap* live_bitmap, - std::unique_ptr&& oat_file) + std::unique_ptr&& oat_file, + std::unique_ptr&& oat_map) : ImageSpace("DummyImageSpace", /*image_location*/"", map, live_bitmap, - map->End()) { + map->End()), + oat_map_(std::move(oat_map)) { oat_file_ = std::move(oat_file); oat_file_non_owned_ = oat_file_.get(); } - // Size is the size of the image space, oat offset is where the oat file is located - // after the end of image space. oat_size is the size of the oat file. - static DummyImageSpace* Create(size_t size, size_t oat_offset, size_t oat_size) { + private: + std::unique_ptr oat_map_; +}; + +class ImmuneSpacesTest : public CommonRuntimeTest { + static constexpr size_t kMaxBitmaps = 10; + + public: + ImmuneSpacesTest() {} + + void ReserveBitmaps() { + // Create a bunch of dummy bitmaps since these are required to create image spaces. The bitmaps + // do not need to cover the image spaces though. + for (size_t i = 0; i < kMaxBitmaps; ++i) { + std::unique_ptr bitmap( + accounting::ContinuousSpaceBitmap::Create("bitmap", + reinterpret_cast(kPageSize), + kPageSize)); + CHECK(bitmap != nullptr); + live_bitmaps_.push_back(std::move(bitmap)); + } + } + + // Create an image space, the oat file is optional. + DummyImageSpace* CreateImageSpace(uint8_t* image_begin, + size_t image_size, + uint8_t* oat_begin, + size_t oat_size) { std::string error_str; std::unique_ptr map(MemMap::MapAnonymous("DummyImageSpace", - nullptr, - size, + image_begin, + image_size, PROT_READ | PROT_WRITE, /*low_4gb*/true, /*reuse*/false, @@ -109,14 +92,21 @@ class DummyImageSpace : public space::ImageSpace { LOG(ERROR) << error_str; return nullptr; } - std::unique_ptr live_bitmap( - accounting::ContinuousSpaceBitmap::Create("bitmap", map->Begin(), map->Size())); - if (live_bitmap == nullptr) { + CHECK(!live_bitmaps_.empty()); + std::unique_ptr live_bitmap(std::move(live_bitmaps_.back())); + live_bitmaps_.pop_back(); + std::unique_ptr oat_map(MemMap::MapAnonymous("OatMap", + oat_begin, + oat_size, + PROT_READ | PROT_WRITE, + /*low_4gb*/true, + /*reuse*/false, + &error_str)); + if (oat_map == nullptr) { + LOG(ERROR) << error_str; return nullptr; } - // The actual mapped oat file may not be directly after the image for the app image case. - std::unique_ptr oat_file(new DummyOatFile(map->End() + oat_offset, - map->End() + oat_offset + oat_size)); + std::unique_ptr oat_file(new DummyOatFile(oat_map->Begin(), oat_map->End())); // Create image header. ImageSection sections[ImageHeader::kSectionCount]; new (map->Begin()) ImageHeader( @@ -126,10 +116,10 @@ class DummyImageSpace : public space::ImageSpace { /*image_roots*/PointerToLowMemUInt32(map->Begin()) + 1, /*oat_checksum*/0u, // The oat file data in the header is always right after the image space. - /*oat_file_begin*/PointerToLowMemUInt32(map->End()), - /*oat_data_begin*/PointerToLowMemUInt32(map->End()), - /*oat_data_end*/PointerToLowMemUInt32(map->End() + oat_size), - /*oat_file_end*/PointerToLowMemUInt32(map->End() + oat_size), + /*oat_file_begin*/PointerToLowMemUInt32(oat_begin), + /*oat_data_begin*/PointerToLowMemUInt32(oat_begin), + /*oat_data_end*/PointerToLowMemUInt32(oat_begin + oat_size), + /*oat_file_end*/PointerToLowMemUInt32(oat_begin + oat_size), /*boot_image_begin*/0u, /*boot_image_size*/0u, /*boot_oat_begin*/0u, @@ -139,27 +129,113 @@ class DummyImageSpace : public space::ImageSpace { /*is_pic*/false, ImageHeader::kStorageModeUncompressed, /*storage_size*/0u); - return new DummyImageSpace(map.release(), live_bitmap.release(), std::move(oat_file)); + return new DummyImageSpace(map.release(), + live_bitmap.release(), + std::move(oat_file), + std::move(oat_map)); } + + // Does not reserve the memory, the caller needs to be sure no other threads will map at the + // returned address. + static uint8_t* GetContinuousMemoryRegion(size_t size) { + std::string error_str; + std::unique_ptr map(MemMap::MapAnonymous("reserve", + nullptr, + size, + PROT_READ | PROT_WRITE, + /*low_4gb*/true, + /*reuse*/false, + &error_str)); + if (map == nullptr) { + LOG(ERROR) << "Failed to allocate memory region " << error_str; + return nullptr; + } + return map->Begin(); + } + + private: + // Bitmap pool for pre-allocated dummy bitmaps. We need to pre-allocate them since we don't want + // them to randomly get placed somewhere where we want an image space. + std::vector> live_bitmaps_; }; +class DummySpace : public space::ContinuousSpace { + public: + DummySpace(uint8_t* begin, uint8_t* end) + : ContinuousSpace("DummySpace", + space::kGcRetentionPolicyNeverCollect, + begin, + end, + /*limit*/end) {} + + space::SpaceType GetType() const OVERRIDE { + return space::kSpaceTypeMallocSpace; + } + + bool CanMoveObjects() const OVERRIDE { + return false; + } + + accounting::ContinuousSpaceBitmap* GetLiveBitmap() const OVERRIDE { + return nullptr; + } + + accounting::ContinuousSpaceBitmap* GetMarkBitmap() const OVERRIDE { + return nullptr; + } +}; + +TEST_F(ImmuneSpacesTest, AppendBasic) { + ImmuneSpaces spaces; + uint8_t* const base = reinterpret_cast(0x1000); + DummySpace a(base, base + 45 * KB); + DummySpace b(a.Limit(), a.Limit() + 813 * KB); + { + WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); + spaces.AddSpace(&a); + spaces.AddSpace(&b); + } + EXPECT_TRUE(spaces.ContainsSpace(&a)); + EXPECT_TRUE(spaces.ContainsSpace(&b)); + EXPECT_EQ(reinterpret_cast(spaces.GetLargestImmuneRegion().Begin()), a.Begin()); + EXPECT_EQ(reinterpret_cast(spaces.GetLargestImmuneRegion().End()), b.Limit()); +} + +// Tests [image][oat][space] producing a single large immune region. TEST_F(ImmuneSpacesTest, AppendAfterImage) { + ReserveBitmaps(); ImmuneSpaces spaces; - constexpr size_t image_size = 123 * kPageSize; - constexpr size_t image_oat_size = 321 * kPageSize; - std::unique_ptr image_space(DummyImageSpace::Create(image_size, - 0, - image_oat_size)); + constexpr size_t kImageSize = 123 * kPageSize; + constexpr size_t kImageOatSize = 321 * kPageSize; + constexpr size_t kOtherSpaceSize= 100 * kPageSize; + + uint8_t* memory = GetContinuousMemoryRegion(kImageSize + kImageOatSize + kOtherSpaceSize); + + std::unique_ptr image_space(CreateImageSpace(memory, + kImageSize, + memory + kImageSize, + kImageOatSize)); ASSERT_TRUE(image_space != nullptr); const ImageHeader& image_header = image_space->GetImageHeader(); - EXPECT_EQ(image_header.GetImageSize(), image_size); + DummySpace space(image_header.GetOatFileEnd(), image_header.GetOatFileEnd() + kOtherSpaceSize); + + EXPECT_EQ(image_header.GetImageSize(), kImageSize); EXPECT_EQ(static_cast(image_header.GetOatFileEnd() - image_header.GetOatFileBegin()), - image_oat_size); - DummySpace space(image_header.GetOatFileEnd(), image_header.GetOatFileEnd() + 813 * kPageSize); - EXPECT_NE(image_space->Limit(), space.Begin()); + kImageOatSize); + EXPECT_EQ(image_space->GetOatFile()->Size(), kImageOatSize); + // Check that we do not include the oat if there is no space after. { WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); spaces.AddSpace(image_space.get()); + } + EXPECT_EQ(reinterpret_cast(spaces.GetLargestImmuneRegion().Begin()), + image_space->Begin()); + EXPECT_EQ(reinterpret_cast(spaces.GetLargestImmuneRegion().End()), + image_space->Limit()); + // Add another space and ensure it gets appended. + EXPECT_NE(image_space->Limit(), space.Begin()); + { + WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); spaces.AddSpace(&space); } EXPECT_TRUE(spaces.ContainsSpace(image_space.get())); @@ -170,18 +246,122 @@ TEST_F(ImmuneSpacesTest, AppendAfterImage) { EXPECT_EQ(reinterpret_cast(spaces.GetLargestImmuneRegion().Begin()), image_space->Begin()); EXPECT_EQ(reinterpret_cast(spaces.GetLargestImmuneRegion().End()), space.Limit()); - // Check that appending with a gap between the map does not include the oat file. - image_space.reset(DummyImageSpace::Create(image_size, kPageSize, image_oat_size)); - spaces.Reset(); +} + +// Test [image1][image2][image1 oat][image2 oat][image3] producing a single large immune region. +TEST_F(ImmuneSpacesTest, MultiImage) { + ReserveBitmaps(); + // Image 2 needs to be smaller or else it may be chosen for immune region. + constexpr size_t kImage1Size = kPageSize * 17; + constexpr size_t kImage2Size = kPageSize * 13; + constexpr size_t kImage3Size = kPageSize * 3; + constexpr size_t kImage1OatSize = kPageSize * 5; + constexpr size_t kImage2OatSize = kPageSize * 8; + constexpr size_t kImage3OatSize = kPageSize; + constexpr size_t kImageBytes = kImage1Size + kImage2Size + kImage3Size; + constexpr size_t kMemorySize = kImageBytes + kImage1OatSize + kImage2OatSize + kImage3OatSize; + uint8_t* memory = GetContinuousMemoryRegion(kMemorySize); + uint8_t* space1_begin = memory; + memory += kImage1Size; + uint8_t* space2_begin = memory; + memory += kImage2Size; + uint8_t* space1_oat_begin = memory; + memory += kImage1OatSize; + uint8_t* space2_oat_begin = memory; + memory += kImage2OatSize; + uint8_t* space3_begin = memory; + + std::unique_ptr space1(CreateImageSpace(space1_begin, + kImage1Size, + space1_oat_begin, + kImage1OatSize)); + ASSERT_TRUE(space1 != nullptr); + + + std::unique_ptr space2(CreateImageSpace(space2_begin, + kImage2Size, + space2_oat_begin, + kImage2OatSize)); + ASSERT_TRUE(space2 != nullptr); + + // Finally put a 3rd image space. + std::unique_ptr space3(CreateImageSpace(space3_begin, + kImage3Size, + space3_begin + kImage3Size, + kImage3OatSize)); + ASSERT_TRUE(space3 != nullptr); + + // Check that we do not include the oat if there is no space after. + ImmuneSpaces spaces; { WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); - spaces.AddSpace(image_space.get()); + LOG(INFO) << "Adding space1 " << reinterpret_cast(space1->Begin()); + spaces.AddSpace(space1.get()); + LOG(INFO) << "Adding space2 " << reinterpret_cast(space2->Begin()); + spaces.AddSpace(space2.get()); } + // There are no more heap bytes, the immune region should only be the first 2 image spaces and + // should exclude the image oat files. EXPECT_EQ(reinterpret_cast(spaces.GetLargestImmuneRegion().Begin()), - image_space->Begin()); - // Size should be equal, we should not add the oat file since it is not adjacent to the image - // space. - EXPECT_EQ(spaces.GetLargestImmuneRegion().Size(), image_size); + space1->Begin()); + EXPECT_EQ(reinterpret_cast(spaces.GetLargestImmuneRegion().End()), + space2->Limit()); + + // Add another space after the oat files, now it should contain the entire memory region. + { + WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); + LOG(INFO) << "Adding space3 " << reinterpret_cast(space3->Begin()); + spaces.AddSpace(space3.get()); + } + EXPECT_EQ(reinterpret_cast(spaces.GetLargestImmuneRegion().Begin()), + space1->Begin()); + EXPECT_EQ(reinterpret_cast(spaces.GetLargestImmuneRegion().End()), + space3->Limit()); + + // Add a smaller non-adjacent space and ensure it does not become part of the immune region. + // Image size is kImageBytes - kPageSize + // Oat size is kPageSize. + // Guard pages to ensure it is not adjacent to an existing immune region. + // Layout: [guard page][image][oat][guard page] + constexpr size_t kGuardSize = kPageSize; + constexpr size_t kImage4Size = kImageBytes - kPageSize; + constexpr size_t kImage4OatSize = kPageSize; + uint8_t* memory2 = GetContinuousMemoryRegion(kImage4Size + kImage4OatSize + kGuardSize * 2); + std::unique_ptr space4(CreateImageSpace(memory2 + kGuardSize, + kImage4Size, + memory2 + kGuardSize + kImage4Size, + kImage4OatSize)); + ASSERT_TRUE(space4 != nullptr); + { + WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); + LOG(INFO) << "Adding space4 " << reinterpret_cast(space4->Begin()); + spaces.AddSpace(space4.get()); + } + EXPECT_EQ(reinterpret_cast(spaces.GetLargestImmuneRegion().Begin()), + space1->Begin()); + EXPECT_EQ(reinterpret_cast(spaces.GetLargestImmuneRegion().End()), + space3->Limit()); + + // Add a larger non-adjacent space and ensure it becomes the new largest immune region. + // Image size is kImageBytes + kPageSize + // Oat size is kPageSize. + // Guard pages to ensure it is not adjacent to an existing immune region. + // Layout: [guard page][image][oat][guard page] + constexpr size_t kImage5Size = kImageBytes + kPageSize; + constexpr size_t kImage5OatSize = kPageSize; + uint8_t* memory3 = GetContinuousMemoryRegion(kImage5Size + kImage5OatSize + kGuardSize * 2); + std::unique_ptr space5(CreateImageSpace(memory3 + kGuardSize, + kImage5Size, + memory3 + kGuardSize + kImage5Size, + kImage5OatSize)); + ASSERT_TRUE(space5 != nullptr); + { + WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); + LOG(INFO) << "Adding space5 " << reinterpret_cast(space5->Begin()); + spaces.AddSpace(space5.get()); + } + EXPECT_EQ(reinterpret_cast(spaces.GetLargestImmuneRegion().Begin()), space5->Begin()); + EXPECT_EQ(reinterpret_cast(spaces.GetLargestImmuneRegion().End()), space5->Limit()); } } // namespace collector -- GitLab From b6347b7a3dbf9f55cbd638c630e9d044d6d53ac6 Mon Sep 17 00:00:00 2001 From: Aart Bik Date: Mon, 29 Feb 2016 13:56:44 -0800 Subject: [PATCH 071/204] Fixed bug on incorrectly revisiting same block. Rationale: Aart's fuzz tester found this particular bug, where revisiting a block (after dynamic bce) would cause static array length based bce to feed into itself and thus incorrectly remove a needed bounds check. bug=27376274 Change-Id: I9163f283af355d444b4cec707f194fe2b67c2572 --- .../optimizing/bounds_check_elimination.cc | 13 ++-- test/449-checker-bce/src/Main.java | 3 +- test/530-checker-loops/src/Main.java | 4 ++ test/578-bce-visit/expected.txt | 2 + test/578-bce-visit/info.txt | 1 + test/578-bce-visit/src/Main.java | 60 +++++++++++++++++++ 6 files changed, 74 insertions(+), 9 deletions(-) create mode 100644 test/578-bce-visit/expected.txt create mode 100644 test/578-bce-visit/info.txt create mode 100644 test/578-bce-visit/src/Main.java diff --git a/compiler/optimizing/bounds_check_elimination.cc b/compiler/optimizing/bounds_check_elimination.cc index a7a1c0f2c4..288322e1c7 100644 --- a/compiler/optimizing/bounds_check_elimination.cc +++ b/compiler/optimizing/bounds_check_elimination.cc @@ -1711,21 +1711,18 @@ void BoundsCheckElimination::Run() { // that value dominated by that instruction fits in that range. Range of that // value can be narrowed further down in the dominator tree. BCEVisitor visitor(graph_, side_effects_, induction_analysis_); - HBasicBlock* last_visited_block = nullptr; for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) { HBasicBlock* current = it.Current(); - if (current == last_visited_block) { - // We may insert blocks into the reverse post order list when processing - // a loop header. Don't process it again. - DCHECK(current->IsLoopHeader()); - continue; - } if (visitor.IsAddedBlock(current)) { // Skip added blocks. Their effects are already taken care of. continue; } visitor.VisitBasicBlock(current); - last_visited_block = current; + // Skip forward to the current block in case new basic blocks were inserted + // (which always appear earlier in reverse post order) to avoid visiting the + // same basic block twice. + for ( ; !it.Done() && it.Current() != current; it.Advance()) { + } } // Perform cleanup. diff --git a/test/449-checker-bce/src/Main.java b/test/449-checker-bce/src/Main.java index 66e1d92cc2..39467fc626 100644 --- a/test/449-checker-bce/src/Main.java +++ b/test/449-checker-bce/src/Main.java @@ -1260,7 +1260,8 @@ public class Main { } else { assertIsManaged(); } - array[i] = (array[i-2] + array[i-1] + array[i] + array[i+1] + array[i+2]) / 5; + // TODO: order matters, make it not so. + array[i] = (array[i] + array[i-2] + array[i-1] + array[i+1] + array[i+2]) / 5; } } diff --git a/test/530-checker-loops/src/Main.java b/test/530-checker-loops/src/Main.java index d5111b0c14..2e5fd2534a 100644 --- a/test/530-checker-loops/src/Main.java +++ b/test/530-checker-loops/src/Main.java @@ -633,6 +633,10 @@ public class Main { } } + // + // Verifier. + // + public static void main(String[] args) { int[] empty = { }; int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; diff --git a/test/578-bce-visit/expected.txt b/test/578-bce-visit/expected.txt new file mode 100644 index 0000000000..28fca2cc12 --- /dev/null +++ b/test/578-bce-visit/expected.txt @@ -0,0 +1,2 @@ +exception caught +FUZZ result = 1001 16 diff --git a/test/578-bce-visit/info.txt b/test/578-bce-visit/info.txt new file mode 100644 index 0000000000..2462e1b8b0 --- /dev/null +++ b/test/578-bce-visit/info.txt @@ -0,0 +1 @@ +Fuzz test that exposed bug in bounds check elimination visiting of blocks. diff --git a/test/578-bce-visit/src/Main.java b/test/578-bce-visit/src/Main.java new file mode 100644 index 0000000000..b0e920e163 --- /dev/null +++ b/test/578-bce-visit/src/Main.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Automatically generated fuzz test that exposed bug in the way bounds + * check elimination visits basic blocks. If, after dynamic bce, the same + * block would be visited again, then static length based bce would incorrectly + * feed information back to itself and removed a necessary bounds check. + */ +public class Main { + + private static int[][][] mA = new int[10][10][10]; + + private static int mX = 17; + + private static int doit() { + int l0 = (((++mA[7][2][8]) <= mA[0][1][3]) ? (++mA[9][0][5]) : ((( -mA[0][7][0]) * ((mX == mX) ? 180 : mX)) + (mA[7][8][8]++))); + mA[1][0][4] -= mX; + int l1 = (((l0 >= ( ~mA[6][7][5])) && ((921 <= l0) && (mA[3][9][6] > l0))) ? mX : (l0--)); + int l2 = ( -384); + for (int i0 = 7 - 1; i0 >= 1; i0--) { + mA[6][0][0] -= ((((l0++) == ( -mX)) ? (((mA[3][i0][1] > 503) || (mX <= i0)) ? (--l0) : (l0--)) : mX) - ( ~(mX--))); + int l3 = 24; + int l4 = ((l2--) & mX); + for (int i1 = i0-2 - 1; i1 >= 3; i1--) { + for (int i2 = 2; i2 < i0; i2++) { + mA[i0][4][l3] >>= 1; + } + } + } + return 1; + } + + public static void main(String[] args) { + int k = 1; + for (int i0 = 0; i0 < 10; i0++) + for (int i1 = 0; i1 < 10; i1++) + for (int i2 = 0; i2 < 10; i2++) + mA[i0][i1][i2] = k++; + try { + k = doit(); + } catch (Exception e) { + System.out.println("exception caught"); + } + System.out.println("FUZZ result = " + k + " " + mX); + } +} -- GitLab From 2f0d3bc12f9ade80f4d3c21b7c4aad2cfe10f19e Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Tue, 1 Mar 2016 09:09:35 +0000 Subject: [PATCH 072/204] Disable flaky 130-hprof test. bug:27337759 Change-Id: I167bd42ef539bce46978b07f755efd57bc61b2f2 --- test/Android.run-test.mk | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index 167ad859d2..d6a05ea3dd 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -222,8 +222,10 @@ ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES), # Disable 097-duplicate-method while investigation (broken by latest Jack release, b/27358065) +# Disable 130-hprof, it has races (b/27337759) TEST_ART_BROKEN_ALL_TARGET_TESTS := \ - 097-duplicate-method + 097-duplicate-method \ + 130-hprof ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \ $(COMPILER_TYPES), $(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \ -- GitLab From 4866794d0e26299e5a3294e90206e6a3856ab6c1 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Tue, 1 Mar 2016 09:28:26 +0000 Subject: [PATCH 073/204] Revert "Disable symbolizing traces on volantis." Devices have been updated to M. This reverts commit 95303c682bba971128950ef82c8f6c247917ffff. Change-Id: Ib6620f88b939700e53af87d2003ea8bbe222e1bc --- tools/symbolize-buildbot-crashes.sh | 6 ------ 1 file changed, 6 deletions(-) diff --git a/tools/symbolize-buildbot-crashes.sh b/tools/symbolize-buildbot-crashes.sh index 505cf73fad..8dc4e27885 100755 --- a/tools/symbolize-buildbot-crashes.sh +++ b/tools/symbolize-buildbot-crashes.sh @@ -14,12 +14,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -if [ "$TARGET_PRODUCT" = 'armv8' ]; then - echo "Symbolizing traces on Nexus9 with an L build is currently" - echo "bogus, as the system thinks crashes are arm32." - exit 0 -fi - # We push art and its dependencies to '/data/local/tmp', but the 'stack' # script expect things to be in '/'. So we just remove the # '/data/local/tmp' prefix. -- GitLab From 6cbe0814952bd3bbb329c4ca4dc683ac87c2c2de Mon Sep 17 00:00:00 2001 From: Serguei Katkov Date: Tue, 1 Mar 2016 16:10:48 +0600 Subject: [PATCH 074/204] Revert "Revert "Fast ART x86_64 interpretator"" CFI is updated. Now unwinder can fall through fast interpreter. This reverts commit c4a575f58007036ff3408b15c2ec48108add20f3. Change-Id: Ibb0a2ef41e4d02ab0b4ecc4f030ba0e72971aa9d Signed-off-by: Serguei Katkov --- runtime/Android.mk | 3 +- runtime/interpreter/interpreter.cc | 4 +- runtime/interpreter/mterp/config_x86_64 | 516 +- runtime/interpreter/mterp/out/mterp_x86_64.S | 11959 ++++++++++++++++ runtime/interpreter/mterp/rebuild.sh | 2 +- runtime/interpreter/mterp/x86_64/alt_stub.S | 17 + runtime/interpreter/mterp/x86_64/bincmp.S | 28 + runtime/interpreter/mterp/x86_64/bindiv.S | 34 + .../interpreter/mterp/x86_64/bindiv2addr.S | 35 + .../interpreter/mterp/x86_64/bindivLit16.S | 27 + runtime/interpreter/mterp/x86_64/bindivLit8.S | 25 + runtime/interpreter/mterp/x86_64/binop.S | 17 + runtime/interpreter/mterp/x86_64/binop1.S | 19 + runtime/interpreter/mterp/x86_64/binop2addr.S | 19 + runtime/interpreter/mterp/x86_64/binopLit16.S | 19 + runtime/interpreter/mterp/x86_64/binopLit8.S | 18 + runtime/interpreter/mterp/x86_64/binopWide.S | 10 + .../interpreter/mterp/x86_64/binopWide2addr.S | 11 + runtime/interpreter/mterp/x86_64/cvtfp_int.S | 27 + runtime/interpreter/mterp/x86_64/entry.S | 71 + runtime/interpreter/mterp/x86_64/fallback.S | 3 + runtime/interpreter/mterp/x86_64/footer.S | 165 + runtime/interpreter/mterp/x86_64/fpcmp.S | 35 + runtime/interpreter/mterp/x86_64/fpcvt.S | 17 + runtime/interpreter/mterp/x86_64/header.S | 293 + runtime/interpreter/mterp/x86_64/invoke.S | 17 + .../interpreter/mterp/x86_64/op_add_double.S | 1 + .../mterp/x86_64/op_add_double_2addr.S | 1 + .../interpreter/mterp/x86_64/op_add_float.S | 1 + .../mterp/x86_64/op_add_float_2addr.S | 1 + runtime/interpreter/mterp/x86_64/op_add_int.S | 1 + .../mterp/x86_64/op_add_int_2addr.S | 1 + .../mterp/x86_64/op_add_int_lit16.S | 1 + .../mterp/x86_64/op_add_int_lit8.S | 1 + .../interpreter/mterp/x86_64/op_add_long.S | 1 + .../mterp/x86_64/op_add_long_2addr.S | 1 + runtime/interpreter/mterp/x86_64/op_aget.S | 24 + .../mterp/x86_64/op_aget_boolean.S | 1 + .../interpreter/mterp/x86_64/op_aget_byte.S | 1 + .../interpreter/mterp/x86_64/op_aget_char.S | 1 + .../interpreter/mterp/x86_64/op_aget_object.S | 16 + .../interpreter/mterp/x86_64/op_aget_short.S | 1 + .../interpreter/mterp/x86_64/op_aget_wide.S | 1 + runtime/interpreter/mterp/x86_64/op_and_int.S | 1 + .../mterp/x86_64/op_and_int_2addr.S | 1 + .../mterp/x86_64/op_and_int_lit16.S | 1 + .../mterp/x86_64/op_and_int_lit8.S | 1 + .../interpreter/mterp/x86_64/op_and_long.S | 1 + .../mterp/x86_64/op_and_long_2addr.S | 1 + runtime/interpreter/mterp/x86_64/op_aput.S | 23 + .../mterp/x86_64/op_aput_boolean.S | 1 + .../interpreter/mterp/x86_64/op_aput_byte.S | 1 + .../interpreter/mterp/x86_64/op_aput_char.S | 1 + .../interpreter/mterp/x86_64/op_aput_object.S | 13 + .../interpreter/mterp/x86_64/op_aput_short.S | 1 + .../interpreter/mterp/x86_64/op_aput_wide.S | 1 + .../mterp/x86_64/op_array_length.S | 12 + .../interpreter/mterp/x86_64/op_check_cast.S | 13 + .../interpreter/mterp/x86_64/op_cmp_long.S | 17 + .../interpreter/mterp/x86_64/op_cmpg_double.S | 1 + .../interpreter/mterp/x86_64/op_cmpg_float.S | 1 + .../interpreter/mterp/x86_64/op_cmpl_double.S | 1 + .../interpreter/mterp/x86_64/op_cmpl_float.S | 1 + runtime/interpreter/mterp/x86_64/op_const.S | 4 + .../interpreter/mterp/x86_64/op_const_16.S | 4 + runtime/interpreter/mterp/x86_64/op_const_4.S | 7 + .../interpreter/mterp/x86_64/op_const_class.S | 10 + .../mterp/x86_64/op_const_high16.S | 5 + .../mterp/x86_64/op_const_string.S | 10 + .../mterp/x86_64/op_const_string_jumbo.S | 10 + .../interpreter/mterp/x86_64/op_const_wide.S | 4 + .../mterp/x86_64/op_const_wide_16.S | 4 + .../mterp/x86_64/op_const_wide_32.S | 4 + .../mterp/x86_64/op_const_wide_high16.S | 5 + .../interpreter/mterp/x86_64/op_div_double.S | 1 + .../mterp/x86_64/op_div_double_2addr.S | 1 + .../interpreter/mterp/x86_64/op_div_float.S | 1 + .../mterp/x86_64/op_div_float_2addr.S | 1 + runtime/interpreter/mterp/x86_64/op_div_int.S | 1 + .../mterp/x86_64/op_div_int_2addr.S | 1 + .../mterp/x86_64/op_div_int_lit16.S | 1 + .../mterp/x86_64/op_div_int_lit8.S | 1 + .../interpreter/mterp/x86_64/op_div_long.S | 1 + .../mterp/x86_64/op_div_long_2addr.S | 1 + .../mterp/x86_64/op_double_to_float.S | 1 + .../mterp/x86_64/op_double_to_int.S | 1 + .../mterp/x86_64/op_double_to_long.S | 1 + .../mterp/x86_64/op_fill_array_data.S | 9 + .../mterp/x86_64/op_filled_new_array.S | 17 + .../mterp/x86_64/op_filled_new_array_range.S | 1 + .../mterp/x86_64/op_float_to_double.S | 1 + .../mterp/x86_64/op_float_to_int.S | 1 + .../mterp/x86_64/op_float_to_long.S | 1 + runtime/interpreter/mterp/x86_64/op_goto.S | 19 + runtime/interpreter/mterp/x86_64/op_goto_16.S | 19 + runtime/interpreter/mterp/x86_64/op_goto_32.S | 22 + runtime/interpreter/mterp/x86_64/op_if_eq.S | 1 + runtime/interpreter/mterp/x86_64/op_if_eqz.S | 1 + runtime/interpreter/mterp/x86_64/op_if_ge.S | 1 + runtime/interpreter/mterp/x86_64/op_if_gez.S | 1 + runtime/interpreter/mterp/x86_64/op_if_gt.S | 1 + runtime/interpreter/mterp/x86_64/op_if_gtz.S | 1 + runtime/interpreter/mterp/x86_64/op_if_le.S | 1 + runtime/interpreter/mterp/x86_64/op_if_lez.S | 1 + runtime/interpreter/mterp/x86_64/op_if_lt.S | 1 + runtime/interpreter/mterp/x86_64/op_if_ltz.S | 1 + runtime/interpreter/mterp/x86_64/op_if_ne.S | 1 + runtime/interpreter/mterp/x86_64/op_if_nez.S | 1 + runtime/interpreter/mterp/x86_64/op_iget.S | 27 + .../mterp/x86_64/op_iget_boolean.S | 1 + .../mterp/x86_64/op_iget_boolean_quick.S | 1 + .../interpreter/mterp/x86_64/op_iget_byte.S | 1 + .../mterp/x86_64/op_iget_byte_quick.S | 1 + .../interpreter/mterp/x86_64/op_iget_char.S | 1 + .../mterp/x86_64/op_iget_char_quick.S | 1 + .../interpreter/mterp/x86_64/op_iget_object.S | 1 + .../mterp/x86_64/op_iget_object_quick.S | 14 + .../interpreter/mterp/x86_64/op_iget_quick.S | 18 + .../interpreter/mterp/x86_64/op_iget_short.S | 1 + .../mterp/x86_64/op_iget_short_quick.S | 1 + .../interpreter/mterp/x86_64/op_iget_wide.S | 1 + .../mterp/x86_64/op_iget_wide_quick.S | 1 + .../interpreter/mterp/x86_64/op_instance_of.S | 21 + .../interpreter/mterp/x86_64/op_int_to_byte.S | 1 + .../interpreter/mterp/x86_64/op_int_to_char.S | 1 + .../mterp/x86_64/op_int_to_double.S | 1 + .../mterp/x86_64/op_int_to_float.S | 1 + .../interpreter/mterp/x86_64/op_int_to_long.S | 8 + .../mterp/x86_64/op_int_to_short.S | 1 + .../mterp/x86_64/op_invoke_direct.S | 1 + .../mterp/x86_64/op_invoke_direct_range.S | 1 + .../mterp/x86_64/op_invoke_interface.S | 8 + .../mterp/x86_64/op_invoke_interface_range.S | 1 + .../mterp/x86_64/op_invoke_static.S | 2 + .../mterp/x86_64/op_invoke_static_range.S | 1 + .../mterp/x86_64/op_invoke_super.S | 8 + .../mterp/x86_64/op_invoke_super_range.S | 1 + .../mterp/x86_64/op_invoke_virtual.S | 8 + .../mterp/x86_64/op_invoke_virtual_quick.S | 1 + .../mterp/x86_64/op_invoke_virtual_range.S | 1 + .../x86_64/op_invoke_virtual_range_quick.S | 1 + runtime/interpreter/mterp/x86_64/op_iput.S | 20 + .../mterp/x86_64/op_iput_boolean.S | 1 + .../mterp/x86_64/op_iput_boolean_quick.S | 1 + .../interpreter/mterp/x86_64/op_iput_byte.S | 1 + .../mterp/x86_64/op_iput_byte_quick.S | 1 + .../interpreter/mterp/x86_64/op_iput_char.S | 1 + .../mterp/x86_64/op_iput_char_quick.S | 1 + .../interpreter/mterp/x86_64/op_iput_object.S | 10 + .../mterp/x86_64/op_iput_object_quick.S | 9 + .../interpreter/mterp/x86_64/op_iput_quick.S | 13 + .../interpreter/mterp/x86_64/op_iput_short.S | 1 + .../mterp/x86_64/op_iput_short_quick.S | 1 + .../interpreter/mterp/x86_64/op_iput_wide.S | 14 + .../mterp/x86_64/op_iput_wide_quick.S | 12 + .../mterp/x86_64/op_long_to_double.S | 1 + .../mterp/x86_64/op_long_to_float.S | 1 + .../interpreter/mterp/x86_64/op_long_to_int.S | 2 + .../mterp/x86_64/op_monitor_enter.S | 11 + .../mterp/x86_64/op_monitor_exit.S | 15 + runtime/interpreter/mterp/x86_64/op_move.S | 13 + runtime/interpreter/mterp/x86_64/op_move_16.S | 12 + .../mterp/x86_64/op_move_exception.S | 5 + .../interpreter/mterp/x86_64/op_move_from16.S | 11 + .../interpreter/mterp/x86_64/op_move_object.S | 1 + .../mterp/x86_64/op_move_object_16.S | 1 + .../mterp/x86_64/op_move_object_from16.S | 1 + .../interpreter/mterp/x86_64/op_move_result.S | 11 + .../mterp/x86_64/op_move_result_object.S | 1 + .../mterp/x86_64/op_move_result_wide.S | 5 + .../interpreter/mterp/x86_64/op_move_wide.S | 8 + .../mterp/x86_64/op_move_wide_16.S | 7 + .../mterp/x86_64/op_move_wide_from16.S | 6 + .../interpreter/mterp/x86_64/op_mul_double.S | 1 + .../mterp/x86_64/op_mul_double_2addr.S | 1 + .../interpreter/mterp/x86_64/op_mul_float.S | 1 + .../mterp/x86_64/op_mul_float_2addr.S | 1 + runtime/interpreter/mterp/x86_64/op_mul_int.S | 1 + .../mterp/x86_64/op_mul_int_2addr.S | 8 + .../mterp/x86_64/op_mul_int_lit16.S | 1 + .../mterp/x86_64/op_mul_int_lit8.S | 1 + .../interpreter/mterp/x86_64/op_mul_long.S | 1 + .../mterp/x86_64/op_mul_long_2addr.S | 8 + .../interpreter/mterp/x86_64/op_neg_double.S | 1 + .../interpreter/mterp/x86_64/op_neg_float.S | 1 + runtime/interpreter/mterp/x86_64/op_neg_int.S | 1 + .../interpreter/mterp/x86_64/op_neg_long.S | 1 + .../interpreter/mterp/x86_64/op_new_array.S | 18 + .../mterp/x86_64/op_new_instance.S | 13 + runtime/interpreter/mterp/x86_64/op_nop.S | 1 + runtime/interpreter/mterp/x86_64/op_not_int.S | 1 + .../interpreter/mterp/x86_64/op_not_long.S | 1 + runtime/interpreter/mterp/x86_64/op_or_int.S | 1 + .../mterp/x86_64/op_or_int_2addr.S | 1 + .../mterp/x86_64/op_or_int_lit16.S | 1 + .../interpreter/mterp/x86_64/op_or_int_lit8.S | 1 + runtime/interpreter/mterp/x86_64/op_or_long.S | 1 + .../mterp/x86_64/op_or_long_2addr.S | 1 + .../mterp/x86_64/op_packed_switch.S | 27 + .../interpreter/mterp/x86_64/op_rem_double.S | 14 + .../mterp/x86_64/op_rem_double_2addr.S | 15 + .../interpreter/mterp/x86_64/op_rem_float.S | 14 + .../mterp/x86_64/op_rem_float_2addr.S | 15 + runtime/interpreter/mterp/x86_64/op_rem_int.S | 1 + .../mterp/x86_64/op_rem_int_2addr.S | 1 + .../mterp/x86_64/op_rem_int_lit16.S | 1 + .../mterp/x86_64/op_rem_int_lit8.S | 1 + .../interpreter/mterp/x86_64/op_rem_long.S | 1 + .../mterp/x86_64/op_rem_long_2addr.S | 1 + runtime/interpreter/mterp/x86_64/op_return.S | 15 + .../mterp/x86_64/op_return_object.S | 1 + .../interpreter/mterp/x86_64/op_return_void.S | 9 + .../mterp/x86_64/op_return_void_no_barrier.S | 7 + .../interpreter/mterp/x86_64/op_return_wide.S | 13 + .../interpreter/mterp/x86_64/op_rsub_int.S | 2 + .../mterp/x86_64/op_rsub_int_lit8.S | 1 + runtime/interpreter/mterp/x86_64/op_sget.S | 25 + .../mterp/x86_64/op_sget_boolean.S | 1 + .../interpreter/mterp/x86_64/op_sget_byte.S | 1 + .../interpreter/mterp/x86_64/op_sget_char.S | 1 + .../interpreter/mterp/x86_64/op_sget_object.S | 1 + .../interpreter/mterp/x86_64/op_sget_short.S | 1 + .../interpreter/mterp/x86_64/op_sget_wide.S | 1 + runtime/interpreter/mterp/x86_64/op_shl_int.S | 1 + .../mterp/x86_64/op_shl_int_2addr.S | 1 + .../mterp/x86_64/op_shl_int_lit8.S | 1 + .../interpreter/mterp/x86_64/op_shl_long.S | 1 + .../mterp/x86_64/op_shl_long_2addr.S | 1 + runtime/interpreter/mterp/x86_64/op_shr_int.S | 1 + .../mterp/x86_64/op_shr_int_2addr.S | 1 + .../mterp/x86_64/op_shr_int_lit8.S | 1 + .../interpreter/mterp/x86_64/op_shr_long.S | 1 + .../mterp/x86_64/op_shr_long_2addr.S | 1 + .../mterp/x86_64/op_sparse_switch.S | 1 + runtime/interpreter/mterp/x86_64/op_sput.S | 17 + .../mterp/x86_64/op_sput_boolean.S | 1 + .../interpreter/mterp/x86_64/op_sput_byte.S | 1 + .../interpreter/mterp/x86_64/op_sput_char.S | 1 + .../interpreter/mterp/x86_64/op_sput_object.S | 10 + .../interpreter/mterp/x86_64/op_sput_short.S | 1 + .../interpreter/mterp/x86_64/op_sput_wide.S | 15 + .../interpreter/mterp/x86_64/op_sub_double.S | 1 + .../mterp/x86_64/op_sub_double_2addr.S | 1 + .../interpreter/mterp/x86_64/op_sub_float.S | 1 + .../mterp/x86_64/op_sub_float_2addr.S | 1 + runtime/interpreter/mterp/x86_64/op_sub_int.S | 1 + .../mterp/x86_64/op_sub_int_2addr.S | 1 + .../interpreter/mterp/x86_64/op_sub_long.S | 1 + .../mterp/x86_64/op_sub_long_2addr.S | 1 + runtime/interpreter/mterp/x86_64/op_throw.S | 10 + .../interpreter/mterp/x86_64/op_unused_3e.S | 1 + .../interpreter/mterp/x86_64/op_unused_3f.S | 1 + .../interpreter/mterp/x86_64/op_unused_40.S | 1 + .../interpreter/mterp/x86_64/op_unused_41.S | 1 + .../interpreter/mterp/x86_64/op_unused_42.S | 1 + .../interpreter/mterp/x86_64/op_unused_43.S | 1 + .../interpreter/mterp/x86_64/op_unused_79.S | 1 + .../interpreter/mterp/x86_64/op_unused_7a.S | 1 + .../interpreter/mterp/x86_64/op_unused_f4.S | 1 + .../interpreter/mterp/x86_64/op_unused_fa.S | 1 + .../interpreter/mterp/x86_64/op_unused_fb.S | 1 + .../interpreter/mterp/x86_64/op_unused_fc.S | 1 + .../interpreter/mterp/x86_64/op_unused_fd.S | 1 + .../interpreter/mterp/x86_64/op_unused_fe.S | 1 + .../interpreter/mterp/x86_64/op_unused_ff.S | 1 + .../interpreter/mterp/x86_64/op_ushr_int.S | 1 + .../mterp/x86_64/op_ushr_int_2addr.S | 1 + .../mterp/x86_64/op_ushr_int_lit8.S | 1 + .../interpreter/mterp/x86_64/op_ushr_long.S | 1 + .../mterp/x86_64/op_ushr_long_2addr.S | 1 + runtime/interpreter/mterp/x86_64/op_xor_int.S | 1 + .../mterp/x86_64/op_xor_int_2addr.S | 1 + .../mterp/x86_64/op_xor_int_lit16.S | 1 + .../mterp/x86_64/op_xor_int_lit8.S | 1 + .../interpreter/mterp/x86_64/op_xor_long.S | 1 + .../mterp/x86_64/op_xor_long_2addr.S | 1 + runtime/interpreter/mterp/x86_64/shop2addr.S | 19 + runtime/interpreter/mterp/x86_64/sseBinop.S | 9 + .../interpreter/mterp/x86_64/sseBinop2Addr.S | 10 + runtime/interpreter/mterp/x86_64/unop.S | 22 + runtime/interpreter/mterp/x86_64/unused.S | 4 + runtime/interpreter/mterp/x86_64/zcmp.S | 24 + 282 files changed, 14224 insertions(+), 260 deletions(-) create mode 100644 runtime/interpreter/mterp/out/mterp_x86_64.S create mode 100644 runtime/interpreter/mterp/x86_64/alt_stub.S create mode 100644 runtime/interpreter/mterp/x86_64/bincmp.S create mode 100644 runtime/interpreter/mterp/x86_64/bindiv.S create mode 100644 runtime/interpreter/mterp/x86_64/bindiv2addr.S create mode 100644 runtime/interpreter/mterp/x86_64/bindivLit16.S create mode 100644 runtime/interpreter/mterp/x86_64/bindivLit8.S create mode 100644 runtime/interpreter/mterp/x86_64/binop.S create mode 100644 runtime/interpreter/mterp/x86_64/binop1.S create mode 100644 runtime/interpreter/mterp/x86_64/binop2addr.S create mode 100644 runtime/interpreter/mterp/x86_64/binopLit16.S create mode 100644 runtime/interpreter/mterp/x86_64/binopLit8.S create mode 100644 runtime/interpreter/mterp/x86_64/binopWide.S create mode 100644 runtime/interpreter/mterp/x86_64/binopWide2addr.S create mode 100644 runtime/interpreter/mterp/x86_64/cvtfp_int.S create mode 100644 runtime/interpreter/mterp/x86_64/entry.S create mode 100644 runtime/interpreter/mterp/x86_64/fallback.S create mode 100644 runtime/interpreter/mterp/x86_64/footer.S create mode 100644 runtime/interpreter/mterp/x86_64/fpcmp.S create mode 100644 runtime/interpreter/mterp/x86_64/fpcvt.S create mode 100644 runtime/interpreter/mterp/x86_64/header.S create mode 100644 runtime/interpreter/mterp/x86_64/invoke.S create mode 100644 runtime/interpreter/mterp/x86_64/op_add_double.S create mode 100644 runtime/interpreter/mterp/x86_64/op_add_double_2addr.S create mode 100644 runtime/interpreter/mterp/x86_64/op_add_float.S create mode 100644 runtime/interpreter/mterp/x86_64/op_add_float_2addr.S create mode 100644 runtime/interpreter/mterp/x86_64/op_add_int.S create mode 100644 runtime/interpreter/mterp/x86_64/op_add_int_2addr.S create mode 100644 runtime/interpreter/mterp/x86_64/op_add_int_lit16.S create mode 100644 runtime/interpreter/mterp/x86_64/op_add_int_lit8.S create mode 100644 runtime/interpreter/mterp/x86_64/op_add_long.S create mode 100644 runtime/interpreter/mterp/x86_64/op_add_long_2addr.S create mode 100644 runtime/interpreter/mterp/x86_64/op_aget.S create mode 100644 runtime/interpreter/mterp/x86_64/op_aget_boolean.S create mode 100644 runtime/interpreter/mterp/x86_64/op_aget_byte.S create mode 100644 runtime/interpreter/mterp/x86_64/op_aget_char.S create mode 100644 runtime/interpreter/mterp/x86_64/op_aget_object.S create mode 100644 runtime/interpreter/mterp/x86_64/op_aget_short.S create mode 100644 runtime/interpreter/mterp/x86_64/op_aget_wide.S create mode 100644 runtime/interpreter/mterp/x86_64/op_and_int.S create mode 100644 runtime/interpreter/mterp/x86_64/op_and_int_2addr.S create mode 100644 runtime/interpreter/mterp/x86_64/op_and_int_lit16.S create mode 100644 runtime/interpreter/mterp/x86_64/op_and_int_lit8.S create mode 100644 runtime/interpreter/mterp/x86_64/op_and_long.S create mode 100644 runtime/interpreter/mterp/x86_64/op_and_long_2addr.S create mode 100644 runtime/interpreter/mterp/x86_64/op_aput.S create mode 100644 runtime/interpreter/mterp/x86_64/op_aput_boolean.S create mode 100644 runtime/interpreter/mterp/x86_64/op_aput_byte.S create mode 100644 runtime/interpreter/mterp/x86_64/op_aput_char.S create mode 100644 runtime/interpreter/mterp/x86_64/op_aput_object.S create mode 100644 runtime/interpreter/mterp/x86_64/op_aput_short.S create mode 100644 runtime/interpreter/mterp/x86_64/op_aput_wide.S create mode 100644 runtime/interpreter/mterp/x86_64/op_array_length.S create mode 100644 runtime/interpreter/mterp/x86_64/op_check_cast.S create mode 100644 runtime/interpreter/mterp/x86_64/op_cmp_long.S create mode 100644 runtime/interpreter/mterp/x86_64/op_cmpg_double.S create mode 100644 runtime/interpreter/mterp/x86_64/op_cmpg_float.S create mode 100644 runtime/interpreter/mterp/x86_64/op_cmpl_double.S create mode 100644 runtime/interpreter/mterp/x86_64/op_cmpl_float.S create mode 100644 runtime/interpreter/mterp/x86_64/op_const.S create mode 100644 runtime/interpreter/mterp/x86_64/op_const_16.S create mode 100644 runtime/interpreter/mterp/x86_64/op_const_4.S create mode 100644 runtime/interpreter/mterp/x86_64/op_const_class.S create mode 100644 runtime/interpreter/mterp/x86_64/op_const_high16.S create mode 100644 runtime/interpreter/mterp/x86_64/op_const_string.S create mode 100644 runtime/interpreter/mterp/x86_64/op_const_string_jumbo.S create mode 100644 runtime/interpreter/mterp/x86_64/op_const_wide.S create mode 100644 runtime/interpreter/mterp/x86_64/op_const_wide_16.S create mode 100644 runtime/interpreter/mterp/x86_64/op_const_wide_32.S create mode 100644 runtime/interpreter/mterp/x86_64/op_const_wide_high16.S create mode 100644 runtime/interpreter/mterp/x86_64/op_div_double.S create mode 100644 runtime/interpreter/mterp/x86_64/op_div_double_2addr.S create mode 100644 runtime/interpreter/mterp/x86_64/op_div_float.S create mode 100644 runtime/interpreter/mterp/x86_64/op_div_float_2addr.S create mode 100644 runtime/interpreter/mterp/x86_64/op_div_int.S create mode 100644 runtime/interpreter/mterp/x86_64/op_div_int_2addr.S create mode 100644 runtime/interpreter/mterp/x86_64/op_div_int_lit16.S create mode 100644 runtime/interpreter/mterp/x86_64/op_div_int_lit8.S create mode 100644 runtime/interpreter/mterp/x86_64/op_div_long.S create mode 100644 runtime/interpreter/mterp/x86_64/op_div_long_2addr.S create mode 100644 runtime/interpreter/mterp/x86_64/op_double_to_float.S create mode 100644 runtime/interpreter/mterp/x86_64/op_double_to_int.S create mode 100644 runtime/interpreter/mterp/x86_64/op_double_to_long.S create mode 100644 runtime/interpreter/mterp/x86_64/op_fill_array_data.S create mode 100644 runtime/interpreter/mterp/x86_64/op_filled_new_array.S create mode 100644 runtime/interpreter/mterp/x86_64/op_filled_new_array_range.S create mode 100644 runtime/interpreter/mterp/x86_64/op_float_to_double.S create mode 100644 runtime/interpreter/mterp/x86_64/op_float_to_int.S create mode 100644 runtime/interpreter/mterp/x86_64/op_float_to_long.S create mode 100644 runtime/interpreter/mterp/x86_64/op_goto.S create mode 100644 runtime/interpreter/mterp/x86_64/op_goto_16.S create mode 100644 runtime/interpreter/mterp/x86_64/op_goto_32.S create mode 100644 runtime/interpreter/mterp/x86_64/op_if_eq.S create mode 100644 runtime/interpreter/mterp/x86_64/op_if_eqz.S create mode 100644 runtime/interpreter/mterp/x86_64/op_if_ge.S create mode 100644 runtime/interpreter/mterp/x86_64/op_if_gez.S create mode 100644 runtime/interpreter/mterp/x86_64/op_if_gt.S create mode 100644 runtime/interpreter/mterp/x86_64/op_if_gtz.S create mode 100644 runtime/interpreter/mterp/x86_64/op_if_le.S create mode 100644 runtime/interpreter/mterp/x86_64/op_if_lez.S create mode 100644 runtime/interpreter/mterp/x86_64/op_if_lt.S create mode 100644 runtime/interpreter/mterp/x86_64/op_if_ltz.S create mode 100644 runtime/interpreter/mterp/x86_64/op_if_ne.S create mode 100644 runtime/interpreter/mterp/x86_64/op_if_nez.S create mode 100644 runtime/interpreter/mterp/x86_64/op_iget.S create mode 100644 runtime/interpreter/mterp/x86_64/op_iget_boolean.S create mode 100644 runtime/interpreter/mterp/x86_64/op_iget_boolean_quick.S create mode 100644 runtime/interpreter/mterp/x86_64/op_iget_byte.S create mode 100644 runtime/interpreter/mterp/x86_64/op_iget_byte_quick.S create mode 100644 runtime/interpreter/mterp/x86_64/op_iget_char.S create mode 100644 runtime/interpreter/mterp/x86_64/op_iget_char_quick.S create mode 100644 runtime/interpreter/mterp/x86_64/op_iget_object.S create mode 100644 runtime/interpreter/mterp/x86_64/op_iget_object_quick.S create mode 100644 runtime/interpreter/mterp/x86_64/op_iget_quick.S create mode 100644 runtime/interpreter/mterp/x86_64/op_iget_short.S create mode 100644 runtime/interpreter/mterp/x86_64/op_iget_short_quick.S create mode 100644 runtime/interpreter/mterp/x86_64/op_iget_wide.S create mode 100644 runtime/interpreter/mterp/x86_64/op_iget_wide_quick.S create mode 100644 runtime/interpreter/mterp/x86_64/op_instance_of.S create mode 100644 runtime/interpreter/mterp/x86_64/op_int_to_byte.S create mode 100644 runtime/interpreter/mterp/x86_64/op_int_to_char.S create mode 100644 runtime/interpreter/mterp/x86_64/op_int_to_double.S create mode 100644 runtime/interpreter/mterp/x86_64/op_int_to_float.S create mode 100644 runtime/interpreter/mterp/x86_64/op_int_to_long.S create mode 100644 runtime/interpreter/mterp/x86_64/op_int_to_short.S create mode 100644 runtime/interpreter/mterp/x86_64/op_invoke_direct.S create mode 100644 runtime/interpreter/mterp/x86_64/op_invoke_direct_range.S create mode 100644 runtime/interpreter/mterp/x86_64/op_invoke_interface.S create mode 100644 runtime/interpreter/mterp/x86_64/op_invoke_interface_range.S create mode 100644 runtime/interpreter/mterp/x86_64/op_invoke_static.S create mode 100644 runtime/interpreter/mterp/x86_64/op_invoke_static_range.S create mode 100644 runtime/interpreter/mterp/x86_64/op_invoke_super.S create mode 100644 runtime/interpreter/mterp/x86_64/op_invoke_super_range.S create mode 100644 runtime/interpreter/mterp/x86_64/op_invoke_virtual.S create mode 100644 runtime/interpreter/mterp/x86_64/op_invoke_virtual_quick.S create mode 100644 runtime/interpreter/mterp/x86_64/op_invoke_virtual_range.S create mode 100644 runtime/interpreter/mterp/x86_64/op_invoke_virtual_range_quick.S create mode 100644 runtime/interpreter/mterp/x86_64/op_iput.S create mode 100644 runtime/interpreter/mterp/x86_64/op_iput_boolean.S create mode 100644 runtime/interpreter/mterp/x86_64/op_iput_boolean_quick.S create mode 100644 runtime/interpreter/mterp/x86_64/op_iput_byte.S create mode 100644 runtime/interpreter/mterp/x86_64/op_iput_byte_quick.S create mode 100644 runtime/interpreter/mterp/x86_64/op_iput_char.S create mode 100644 runtime/interpreter/mterp/x86_64/op_iput_char_quick.S create mode 100644 runtime/interpreter/mterp/x86_64/op_iput_object.S create mode 100644 runtime/interpreter/mterp/x86_64/op_iput_object_quick.S create mode 100644 runtime/interpreter/mterp/x86_64/op_iput_quick.S create mode 100644 runtime/interpreter/mterp/x86_64/op_iput_short.S create mode 100644 runtime/interpreter/mterp/x86_64/op_iput_short_quick.S create mode 100644 runtime/interpreter/mterp/x86_64/op_iput_wide.S create mode 100644 runtime/interpreter/mterp/x86_64/op_iput_wide_quick.S create mode 100644 runtime/interpreter/mterp/x86_64/op_long_to_double.S create mode 100644 runtime/interpreter/mterp/x86_64/op_long_to_float.S create mode 100644 runtime/interpreter/mterp/x86_64/op_long_to_int.S create mode 100644 runtime/interpreter/mterp/x86_64/op_monitor_enter.S create mode 100644 runtime/interpreter/mterp/x86_64/op_monitor_exit.S create mode 100644 runtime/interpreter/mterp/x86_64/op_move.S create mode 100644 runtime/interpreter/mterp/x86_64/op_move_16.S create mode 100644 runtime/interpreter/mterp/x86_64/op_move_exception.S create mode 100644 runtime/interpreter/mterp/x86_64/op_move_from16.S create mode 100644 runtime/interpreter/mterp/x86_64/op_move_object.S create mode 100644 runtime/interpreter/mterp/x86_64/op_move_object_16.S create mode 100644 runtime/interpreter/mterp/x86_64/op_move_object_from16.S create mode 100644 runtime/interpreter/mterp/x86_64/op_move_result.S create mode 100644 runtime/interpreter/mterp/x86_64/op_move_result_object.S create mode 100644 runtime/interpreter/mterp/x86_64/op_move_result_wide.S create mode 100644 runtime/interpreter/mterp/x86_64/op_move_wide.S create mode 100644 runtime/interpreter/mterp/x86_64/op_move_wide_16.S create mode 100644 runtime/interpreter/mterp/x86_64/op_move_wide_from16.S create mode 100644 runtime/interpreter/mterp/x86_64/op_mul_double.S create mode 100644 runtime/interpreter/mterp/x86_64/op_mul_double_2addr.S create mode 100644 runtime/interpreter/mterp/x86_64/op_mul_float.S create mode 100644 runtime/interpreter/mterp/x86_64/op_mul_float_2addr.S create mode 100644 runtime/interpreter/mterp/x86_64/op_mul_int.S create mode 100644 runtime/interpreter/mterp/x86_64/op_mul_int_2addr.S create mode 100644 runtime/interpreter/mterp/x86_64/op_mul_int_lit16.S create mode 100644 runtime/interpreter/mterp/x86_64/op_mul_int_lit8.S create mode 100644 runtime/interpreter/mterp/x86_64/op_mul_long.S create mode 100644 runtime/interpreter/mterp/x86_64/op_mul_long_2addr.S create mode 100644 runtime/interpreter/mterp/x86_64/op_neg_double.S create mode 100644 runtime/interpreter/mterp/x86_64/op_neg_float.S create mode 100644 runtime/interpreter/mterp/x86_64/op_neg_int.S create mode 100644 runtime/interpreter/mterp/x86_64/op_neg_long.S create mode 100644 runtime/interpreter/mterp/x86_64/op_new_array.S create mode 100644 runtime/interpreter/mterp/x86_64/op_new_instance.S create mode 100644 runtime/interpreter/mterp/x86_64/op_nop.S create mode 100644 runtime/interpreter/mterp/x86_64/op_not_int.S create mode 100644 runtime/interpreter/mterp/x86_64/op_not_long.S create mode 100644 runtime/interpreter/mterp/x86_64/op_or_int.S create mode 100644 runtime/interpreter/mterp/x86_64/op_or_int_2addr.S create mode 100644 runtime/interpreter/mterp/x86_64/op_or_int_lit16.S create mode 100644 runtime/interpreter/mterp/x86_64/op_or_int_lit8.S create mode 100644 runtime/interpreter/mterp/x86_64/op_or_long.S create mode 100644 runtime/interpreter/mterp/x86_64/op_or_long_2addr.S create mode 100644 runtime/interpreter/mterp/x86_64/op_packed_switch.S create mode 100644 runtime/interpreter/mterp/x86_64/op_rem_double.S create mode 100644 runtime/interpreter/mterp/x86_64/op_rem_double_2addr.S create mode 100644 runtime/interpreter/mterp/x86_64/op_rem_float.S create mode 100644 runtime/interpreter/mterp/x86_64/op_rem_float_2addr.S create mode 100644 runtime/interpreter/mterp/x86_64/op_rem_int.S create mode 100644 runtime/interpreter/mterp/x86_64/op_rem_int_2addr.S create mode 100644 runtime/interpreter/mterp/x86_64/op_rem_int_lit16.S create mode 100644 runtime/interpreter/mterp/x86_64/op_rem_int_lit8.S create mode 100644 runtime/interpreter/mterp/x86_64/op_rem_long.S create mode 100644 runtime/interpreter/mterp/x86_64/op_rem_long_2addr.S create mode 100644 runtime/interpreter/mterp/x86_64/op_return.S create mode 100644 runtime/interpreter/mterp/x86_64/op_return_object.S create mode 100644 runtime/interpreter/mterp/x86_64/op_return_void.S create mode 100644 runtime/interpreter/mterp/x86_64/op_return_void_no_barrier.S create mode 100644 runtime/interpreter/mterp/x86_64/op_return_wide.S create mode 100644 runtime/interpreter/mterp/x86_64/op_rsub_int.S create mode 100644 runtime/interpreter/mterp/x86_64/op_rsub_int_lit8.S create mode 100644 runtime/interpreter/mterp/x86_64/op_sget.S create mode 100644 runtime/interpreter/mterp/x86_64/op_sget_boolean.S create mode 100644 runtime/interpreter/mterp/x86_64/op_sget_byte.S create mode 100644 runtime/interpreter/mterp/x86_64/op_sget_char.S create mode 100644 runtime/interpreter/mterp/x86_64/op_sget_object.S create mode 100644 runtime/interpreter/mterp/x86_64/op_sget_short.S create mode 100644 runtime/interpreter/mterp/x86_64/op_sget_wide.S create mode 100644 runtime/interpreter/mterp/x86_64/op_shl_int.S create mode 100644 runtime/interpreter/mterp/x86_64/op_shl_int_2addr.S create mode 100644 runtime/interpreter/mterp/x86_64/op_shl_int_lit8.S create mode 100644 runtime/interpreter/mterp/x86_64/op_shl_long.S create mode 100644 runtime/interpreter/mterp/x86_64/op_shl_long_2addr.S create mode 100644 runtime/interpreter/mterp/x86_64/op_shr_int.S create mode 100644 runtime/interpreter/mterp/x86_64/op_shr_int_2addr.S create mode 100644 runtime/interpreter/mterp/x86_64/op_shr_int_lit8.S create mode 100644 runtime/interpreter/mterp/x86_64/op_shr_long.S create mode 100644 runtime/interpreter/mterp/x86_64/op_shr_long_2addr.S create mode 100644 runtime/interpreter/mterp/x86_64/op_sparse_switch.S create mode 100644 runtime/interpreter/mterp/x86_64/op_sput.S create mode 100644 runtime/interpreter/mterp/x86_64/op_sput_boolean.S create mode 100644 runtime/interpreter/mterp/x86_64/op_sput_byte.S create mode 100644 runtime/interpreter/mterp/x86_64/op_sput_char.S create mode 100644 runtime/interpreter/mterp/x86_64/op_sput_object.S create mode 100644 runtime/interpreter/mterp/x86_64/op_sput_short.S create mode 100644 runtime/interpreter/mterp/x86_64/op_sput_wide.S create mode 100644 runtime/interpreter/mterp/x86_64/op_sub_double.S create mode 100644 runtime/interpreter/mterp/x86_64/op_sub_double_2addr.S create mode 100644 runtime/interpreter/mterp/x86_64/op_sub_float.S create mode 100644 runtime/interpreter/mterp/x86_64/op_sub_float_2addr.S create mode 100644 runtime/interpreter/mterp/x86_64/op_sub_int.S create mode 100644 runtime/interpreter/mterp/x86_64/op_sub_int_2addr.S create mode 100644 runtime/interpreter/mterp/x86_64/op_sub_long.S create mode 100644 runtime/interpreter/mterp/x86_64/op_sub_long_2addr.S create mode 100644 runtime/interpreter/mterp/x86_64/op_throw.S create mode 100644 runtime/interpreter/mterp/x86_64/op_unused_3e.S create mode 100644 runtime/interpreter/mterp/x86_64/op_unused_3f.S create mode 100644 runtime/interpreter/mterp/x86_64/op_unused_40.S create mode 100644 runtime/interpreter/mterp/x86_64/op_unused_41.S create mode 100644 runtime/interpreter/mterp/x86_64/op_unused_42.S create mode 100644 runtime/interpreter/mterp/x86_64/op_unused_43.S create mode 100644 runtime/interpreter/mterp/x86_64/op_unused_79.S create mode 100644 runtime/interpreter/mterp/x86_64/op_unused_7a.S create mode 100644 runtime/interpreter/mterp/x86_64/op_unused_f4.S create mode 100644 runtime/interpreter/mterp/x86_64/op_unused_fa.S create mode 100644 runtime/interpreter/mterp/x86_64/op_unused_fb.S create mode 100644 runtime/interpreter/mterp/x86_64/op_unused_fc.S create mode 100644 runtime/interpreter/mterp/x86_64/op_unused_fd.S create mode 100644 runtime/interpreter/mterp/x86_64/op_unused_fe.S create mode 100644 runtime/interpreter/mterp/x86_64/op_unused_ff.S create mode 100644 runtime/interpreter/mterp/x86_64/op_ushr_int.S create mode 100644 runtime/interpreter/mterp/x86_64/op_ushr_int_2addr.S create mode 100644 runtime/interpreter/mterp/x86_64/op_ushr_int_lit8.S create mode 100644 runtime/interpreter/mterp/x86_64/op_ushr_long.S create mode 100644 runtime/interpreter/mterp/x86_64/op_ushr_long_2addr.S create mode 100644 runtime/interpreter/mterp/x86_64/op_xor_int.S create mode 100644 runtime/interpreter/mterp/x86_64/op_xor_int_2addr.S create mode 100644 runtime/interpreter/mterp/x86_64/op_xor_int_lit16.S create mode 100644 runtime/interpreter/mterp/x86_64/op_xor_int_lit8.S create mode 100644 runtime/interpreter/mterp/x86_64/op_xor_long.S create mode 100644 runtime/interpreter/mterp/x86_64/op_xor_long_2addr.S create mode 100644 runtime/interpreter/mterp/x86_64/shop2addr.S create mode 100644 runtime/interpreter/mterp/x86_64/sseBinop.S create mode 100644 runtime/interpreter/mterp/x86_64/sseBinop2Addr.S create mode 100644 runtime/interpreter/mterp/x86_64/unop.S create mode 100644 runtime/interpreter/mterp/x86_64/unused.S create mode 100644 runtime/interpreter/mterp/x86_64/zcmp.S diff --git a/runtime/Android.mk b/runtime/Android.mk index deee3a641e..88118501a0 100644 --- a/runtime/Android.mk +++ b/runtime/Android.mk @@ -292,7 +292,8 @@ LIBART_TARGET_SRC_FILES_x86 := \ # Note that the fault_handler_x86.cc is not a mistake. This file is # shared between the x86 and x86_64 architectures. LIBART_SRC_FILES_x86_64 := \ - interpreter/mterp/mterp_stub.cc \ + interpreter/mterp/mterp.cc \ + interpreter/mterp/out/mterp_x86_64.S \ arch/x86_64/context_x86_64.cc \ arch/x86_64/entrypoints_init_x86_64.cc \ arch/x86_64/jni_entrypoints_x86_64.S \ diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc index 8b72d7189e..bfb1f9de06 100644 --- a/runtime/interpreter/interpreter.cc +++ b/runtime/interpreter/interpreter.cc @@ -241,7 +241,7 @@ static std::ostream& operator<<(std::ostream& os, const InterpreterImplKind& rhs } #if !defined(__clang__) -#if (defined(__arm__) || defined(__i386__) || defined(__aarch64__) || (defined(__mips__) && !defined(__LP64__))) +#if (defined(__arm__) || defined(__i386__) || defined(__aarch64__) || defined(__x86_64__) || (defined(__mips__) && !defined(__LP64__))) // TODO: remove when all targets implemented. static constexpr InterpreterImplKind kInterpreterImplKind = kMterpImplKind; #else @@ -249,7 +249,7 @@ static constexpr InterpreterImplKind kInterpreterImplKind = kComputedGotoImplKin #endif #else // Clang 3.4 fails to build the goto interpreter implementation. -#if (defined(__arm__) || defined(__i386__) || defined(__aarch64__) || (defined(__mips__) && !defined(__LP64__))) +#if (defined(__arm__) || defined(__i386__) || defined(__aarch64__) || defined(__x86_64__) || (defined(__mips__) && !defined(__LP64__))) static constexpr InterpreterImplKind kInterpreterImplKind = kMterpImplKind; #else static constexpr InterpreterImplKind kInterpreterImplKind = kSwitchImplKind; diff --git a/runtime/interpreter/mterp/config_x86_64 b/runtime/interpreter/mterp/config_x86_64 index a002dc2873..1d7eb038bb 100644 --- a/runtime/interpreter/mterp/config_x86_64 +++ b/runtime/interpreter/mterp/config_x86_64 @@ -19,6 +19,10 @@ handler-style computed-goto handler-size 128 +function-type-format FUNCTION_TYPE(%s) +function-size-format SIZE(%s,%s) +global-name-format SYMBOL(%s) + # source for alternate entry stub asm-alt-stub x86_64/alt_stub.S @@ -36,262 +40,262 @@ op-start x86_64 # (override example:) op OP_SUB_FLOAT_2ADDR arm-vfp # (fallback example:) op OP_SUB_FLOAT_2ADDR FALLBACK - op op_nop FALLBACK - op op_move FALLBACK - op op_move_from16 FALLBACK - op op_move_16 FALLBACK - op op_move_wide FALLBACK - op op_move_wide_from16 FALLBACK - op op_move_wide_16 FALLBACK - op op_move_object FALLBACK - op op_move_object_from16 FALLBACK - op op_move_object_16 FALLBACK - op op_move_result FALLBACK - op op_move_result_wide FALLBACK - op op_move_result_object FALLBACK - op op_move_exception FALLBACK - op op_return_void FALLBACK - op op_return FALLBACK - op op_return_wide FALLBACK - op op_return_object FALLBACK - op op_const_4 FALLBACK - op op_const_16 FALLBACK - op op_const FALLBACK - op op_const_high16 FALLBACK - op op_const_wide_16 FALLBACK - op op_const_wide_32 FALLBACK - op op_const_wide FALLBACK - op op_const_wide_high16 FALLBACK - op op_const_string FALLBACK - op op_const_string_jumbo FALLBACK - op op_const_class FALLBACK - op op_monitor_enter FALLBACK - op op_monitor_exit FALLBACK - op op_check_cast FALLBACK - op op_instance_of FALLBACK - op op_array_length FALLBACK - op op_new_instance FALLBACK - op op_new_array FALLBACK - op op_filled_new_array FALLBACK - op op_filled_new_array_range FALLBACK - op op_fill_array_data FALLBACK - op op_throw FALLBACK - op op_goto FALLBACK - op op_goto_16 FALLBACK - op op_goto_32 FALLBACK - op op_packed_switch FALLBACK - op op_sparse_switch FALLBACK - op op_cmpl_float FALLBACK - op op_cmpg_float FALLBACK - op op_cmpl_double FALLBACK - op op_cmpg_double FALLBACK - op op_cmp_long FALLBACK - op op_if_eq FALLBACK - op op_if_ne FALLBACK - op op_if_lt FALLBACK - op op_if_ge FALLBACK - op op_if_gt FALLBACK - op op_if_le FALLBACK - op op_if_eqz FALLBACK - op op_if_nez FALLBACK - op op_if_ltz FALLBACK - op op_if_gez FALLBACK - op op_if_gtz FALLBACK - op op_if_lez FALLBACK - op_unused_3e FALLBACK - op_unused_3f FALLBACK - op_unused_40 FALLBACK - op_unused_41 FALLBACK - op_unused_42 FALLBACK - op_unused_43 FALLBACK - op op_aget FALLBACK - op op_aget_wide FALLBACK - op op_aget_object FALLBACK - op op_aget_boolean FALLBACK - op op_aget_byte FALLBACK - op op_aget_char FALLBACK - op op_aget_short FALLBACK - op op_aput FALLBACK - op op_aput_wide FALLBACK - op op_aput_object FALLBACK - op op_aput_boolean FALLBACK - op op_aput_byte FALLBACK - op op_aput_char FALLBACK - op op_aput_short FALLBACK - op op_iget FALLBACK - op op_iget_wide FALLBACK - op op_iget_object FALLBACK - op op_iget_boolean FALLBACK - op op_iget_byte FALLBACK - op op_iget_char FALLBACK - op op_iget_short FALLBACK - op op_iput FALLBACK - op op_iput_wide FALLBACK - op op_iput_object FALLBACK - op op_iput_boolean FALLBACK - op op_iput_byte FALLBACK - op op_iput_char FALLBACK - op op_iput_short FALLBACK - op op_sget FALLBACK - op op_sget_wide FALLBACK - op op_sget_object FALLBACK - op op_sget_boolean FALLBACK - op op_sget_byte FALLBACK - op op_sget_char FALLBACK - op op_sget_short FALLBACK - op op_sput FALLBACK - op op_sput_wide FALLBACK - op op_sput_object FALLBACK - op op_sput_boolean FALLBACK - op op_sput_byte FALLBACK - op op_sput_char FALLBACK - op op_sput_short FALLBACK - op op_invoke_virtual FALLBACK - op op_invoke_super FALLBACK - op op_invoke_direct FALLBACK - op op_invoke_static FALLBACK - op op_invoke_interface FALLBACK - op op_return_void_no_barrier FALLBACK - op op_invoke_virtual_range FALLBACK - op op_invoke_super_range FALLBACK - op op_invoke_direct_range FALLBACK - op op_invoke_static_range FALLBACK - op op_invoke_interface_range FALLBACK - op_unused_79 FALLBACK - op_unused_7a FALLBACK - op op_neg_int FALLBACK - op op_not_int FALLBACK - op op_neg_long FALLBACK - op op_not_long FALLBACK - op op_neg_float FALLBACK - op op_neg_double FALLBACK - op op_int_to_long FALLBACK - op op_int_to_float FALLBACK - op op_int_to_double FALLBACK - op op_long_to_int FALLBACK - op op_long_to_float FALLBACK - op op_long_to_double FALLBACK - op op_float_to_int FALLBACK - op op_float_to_long FALLBACK - op op_float_to_double FALLBACK - op op_double_to_int FALLBACK - op op_double_to_long FALLBACK - op op_double_to_float FALLBACK - op op_int_to_byte FALLBACK - op op_int_to_char FALLBACK - op op_int_to_short FALLBACK - op op_add_int FALLBACK - op op_sub_int FALLBACK - op op_mul_int FALLBACK - op op_div_int FALLBACK - op op_rem_int FALLBACK - op op_and_int FALLBACK - op op_or_int FALLBACK - op op_xor_int FALLBACK - op op_shl_int FALLBACK - op op_shr_int FALLBACK - op op_ushr_int FALLBACK - op op_add_long FALLBACK - op op_sub_long FALLBACK - op op_mul_long FALLBACK - op op_div_long FALLBACK - op op_rem_long FALLBACK - op op_and_long FALLBACK - op op_or_long FALLBACK - op op_xor_long FALLBACK - op op_shl_long FALLBACK - op op_shr_long FALLBACK - op op_ushr_long FALLBACK - op op_add_float FALLBACK - op op_sub_float FALLBACK - op op_mul_float FALLBACK - op op_div_float FALLBACK - op op_rem_float FALLBACK - op op_add_double FALLBACK - op op_sub_double FALLBACK - op op_mul_double FALLBACK - op op_div_double FALLBACK - op op_rem_double FALLBACK - op op_add_int_2addr FALLBACK - op op_sub_int_2addr FALLBACK - op op_mul_int_2addr FALLBACK - op op_div_int_2addr FALLBACK - op op_rem_int_2addr FALLBACK - op op_and_int_2addr FALLBACK - op op_or_int_2addr FALLBACK - op op_xor_int_2addr FALLBACK - op op_shl_int_2addr FALLBACK - op op_shr_int_2addr FALLBACK - op op_ushr_int_2addr FALLBACK - op op_add_long_2addr FALLBACK - op op_sub_long_2addr FALLBACK - op op_mul_long_2addr FALLBACK - op op_div_long_2addr FALLBACK - op op_rem_long_2addr FALLBACK - op op_and_long_2addr FALLBACK - op op_or_long_2addr FALLBACK - op op_xor_long_2addr FALLBACK - op op_shl_long_2addr FALLBACK - op op_shr_long_2addr FALLBACK - op op_ushr_long_2addr FALLBACK - op op_add_float_2addr FALLBACK - op op_sub_float_2addr FALLBACK - op op_mul_float_2addr FALLBACK - op op_div_float_2addr FALLBACK - op op_rem_float_2addr FALLBACK - op op_add_double_2addr FALLBACK - op op_sub_double_2addr FALLBACK - op op_mul_double_2addr FALLBACK - op op_div_double_2addr FALLBACK - op op_rem_double_2addr FALLBACK - op op_add_int_lit16 FALLBACK - op op_rsub_int FALLBACK - op op_mul_int_lit16 FALLBACK - op op_div_int_lit16 FALLBACK - op op_rem_int_lit16 FALLBACK - op op_and_int_lit16 FALLBACK - op op_or_int_lit16 FALLBACK - op op_xor_int_lit16 FALLBACK - op op_add_int_lit8 FALLBACK - op op_rsub_int_lit8 FALLBACK - op op_mul_int_lit8 FALLBACK - op op_div_int_lit8 FALLBACK - op op_rem_int_lit8 FALLBACK - op op_and_int_lit8 FALLBACK - op op_or_int_lit8 FALLBACK - op op_xor_int_lit8 FALLBACK - op op_shl_int_lit8 FALLBACK - op op_shr_int_lit8 FALLBACK - op op_ushr_int_lit8 FALLBACK - op op_iget_quick FALLBACK - op op_iget_wide_quick FALLBACK - op op_iget_object_quick FALLBACK - op op_iput_quick FALLBACK - op op_iput_wide_quick FALLBACK - op op_iput_object_quick FALLBACK - op op_invoke_virtual_quick FALLBACK - op op_invoke_virtual_range_quick FALLBACK - op op_iput_boolean_quick FALLBACK - op op_iput_byte_quick FALLBACK - op op_iput_char_quick FALLBACK - op op_iput_short_quick FALLBACK - op op_iget_boolean_quick FALLBACK - op op_iget_byte_quick FALLBACK - op op_iget_char_quick FALLBACK - op op_iget_short_quick FALLBACK - op_unused_f3 FALLBACK - op_unused_f4 FALLBACK - op_unused_f5 FALLBACK - op_unused_f6 FALLBACK - op_unused_f7 FALLBACK - op_unused_f8 FALLBACK - op_unused_f9 FALLBACK - op_unused_fa FALLBACK - op_unused_fb FALLBACK - op_unused_fc FALLBACK - op_unused_fd FALLBACK - op_unused_fe FALLBACK - op_unused_ff FALLBACK + # op op_nop FALLBACK + # op op_move FALLBACK + # op op_move_from16 FALLBACK + # op op_move_16 FALLBACK + # op op_move_wide FALLBACK + # op op_move_wide_from16 FALLBACK + # op op_move_wide_16 FALLBACK + # op op_move_object FALLBACK + # op op_move_object_from16 FALLBACK + # op op_move_object_16 FALLBACK + # op op_move_result FALLBACK + # op op_move_result_wide FALLBACK + # op op_move_result_object FALLBACK + # op op_move_exception FALLBACK + # op op_return_void FALLBACK + # op op_return FALLBACK + # op op_return_wide FALLBACK + # op op_return_object FALLBACK + # op op_const_4 FALLBACK + # op op_const_16 FALLBACK + # op op_const FALLBACK + # op op_const_high16 FALLBACK + # op op_const_wide_16 FALLBACK + # op op_const_wide_32 FALLBACK + # op op_const_wide FALLBACK + # op op_const_wide_high16 FALLBACK + # op op_const_string FALLBACK + # op op_const_string_jumbo FALLBACK + # op op_const_class FALLBACK + # op op_monitor_enter FALLBACK + # op op_monitor_exit FALLBACK + # op op_check_cast FALLBACK + # op op_instance_of FALLBACK + # op op_array_length FALLBACK + # op op_new_instance FALLBACK + # op op_new_array FALLBACK + # op op_filled_new_array FALLBACK + # op op_filled_new_array_range FALLBACK + # op op_fill_array_data FALLBACK + # op op_throw FALLBACK + # op op_goto FALLBACK + # op op_goto_16 FALLBACK + # op op_goto_32 FALLBACK + # op op_packed_switch FALLBACK + # op op_sparse_switch FALLBACK + # op op_cmpl_float FALLBACK + # op op_cmpg_float FALLBACK + # op op_cmpl_double FALLBACK + # op op_cmpg_double FALLBACK + # op op_cmp_long FALLBACK + # op op_if_eq FALLBACK + # op op_if_ne FALLBACK + # op op_if_lt FALLBACK + # op op_if_ge FALLBACK + # op op_if_gt FALLBACK + # op op_if_le FALLBACK + # op op_if_eqz FALLBACK + # op op_if_nez FALLBACK + # op op_if_ltz FALLBACK + # op op_if_gez FALLBACK + # op op_if_gtz FALLBACK + # op op_if_lez FALLBACK + # op op_unused_3e FALLBACK + # op op_unused_3f FALLBACK + # op op_unused_40 FALLBACK + # op op_unused_41 FALLBACK + # op op_unused_42 FALLBACK + # op op_unused_43 FALLBACK + # op op_aget FALLBACK + # op op_aget_wide FALLBACK + # op op_aget_object FALLBACK + # op op_aget_boolean FALLBACK + # op op_aget_byte FALLBACK + # op op_aget_char FALLBACK + # op op_aget_short FALLBACK + # op op_aput FALLBACK + # op op_aput_wide FALLBACK + # op op_aput_object FALLBACK + # op op_aput_boolean FALLBACK + # op op_aput_byte FALLBACK + # op op_aput_char FALLBACK + # op op_aput_short FALLBACK + # op op_iget FALLBACK + # op op_iget_wide FALLBACK + # op op_iget_object FALLBACK + # op op_iget_boolean FALLBACK + # op op_iget_byte FALLBACK + # op op_iget_char FALLBACK + # op op_iget_short FALLBACK + # op op_iput FALLBACK + # op op_iput_wide FALLBACK + # op op_iput_object FALLBACK + # op op_iput_boolean FALLBACK + # op op_iput_byte FALLBACK + # op op_iput_char FALLBACK + # op op_iput_short FALLBACK + # op op_sget FALLBACK + # op op_sget_wide FALLBACK + # op op_sget_object FALLBACK + # op op_sget_boolean FALLBACK + # op op_sget_byte FALLBACK + # op op_sget_char FALLBACK + # op op_sget_short FALLBACK + # op op_sput FALLBACK + # op op_sput_wide FALLBACK + # op op_sput_object FALLBACK + # op op_sput_boolean FALLBACK + # op op_sput_byte FALLBACK + # op op_sput_char FALLBACK + # op op_sput_short FALLBACK + # op op_invoke_virtual FALLBACK + # op op_invoke_super FALLBACK + # op op_invoke_direct FALLBACK + # op op_invoke_static FALLBACK + # op op_invoke_interface FALLBACK + # op op_return_void_no_barrier FALLBACK + # op op_invoke_virtual_range FALLBACK + # op op_invoke_super_range FALLBACK + # op op_invoke_direct_range FALLBACK + # op op_invoke_static_range FALLBACK + # op op_invoke_interface_range FALLBACK + # op op_unused_79 FALLBACK + # op op_unused_7a FALLBACK + # op op_neg_int FALLBACK + # op op_not_int FALLBACK + # op op_neg_long FALLBACK + # op op_not_long FALLBACK + # op op_neg_float FALLBACK + # op op_neg_double FALLBACK + # op op_int_to_long FALLBACK + # op op_int_to_float FALLBACK + # op op_int_to_double FALLBACK + # op op_long_to_int FALLBACK + # op op_long_to_float FALLBACK + # op op_long_to_double FALLBACK + # op op_float_to_int FALLBACK + # op op_float_to_long FALLBACK + # op op_float_to_double FALLBACK + # op op_double_to_int FALLBACK + # op op_double_to_long FALLBACK + # op op_double_to_float FALLBACK + # op op_int_to_byte FALLBACK + # op op_int_to_char FALLBACK + # op op_int_to_short FALLBACK + # op op_add_int FALLBACK + # op op_sub_int FALLBACK + # op op_mul_int FALLBACK + # op op_div_int FALLBACK + # op op_rem_int FALLBACK + # op op_and_int FALLBACK + # op op_or_int FALLBACK + # op op_xor_int FALLBACK + # op op_shl_int FALLBACK + # op op_shr_int FALLBACK + # op op_ushr_int FALLBACK + # op op_add_long FALLBACK + # op op_sub_long FALLBACK + # op op_mul_long FALLBACK + # op op_div_long FALLBACK + # op op_rem_long FALLBACK + # op op_and_long FALLBACK + # op op_or_long FALLBACK + # op op_xor_long FALLBACK + # op op_shl_long FALLBACK + # op op_shr_long FALLBACK + # op op_ushr_long FALLBACK + # op op_add_float FALLBACK + # op op_sub_float FALLBACK + # op op_mul_float FALLBACK + # op op_div_float FALLBACK + # op op_rem_float FALLBACK + # op op_add_double FALLBACK + # op op_sub_double FALLBACK + # op op_mul_double FALLBACK + # op op_div_double FALLBACK + # op op_rem_double FALLBACK + # op op_add_int_2addr FALLBACK + # op op_sub_int_2addr FALLBACK + # op op_mul_int_2addr FALLBACK + # op op_div_int_2addr FALLBACK + # op op_rem_int_2addr FALLBACK + # op op_and_int_2addr FALLBACK + # op op_or_int_2addr FALLBACK + # op op_xor_int_2addr FALLBACK + # op op_shl_int_2addr FALLBACK + # op op_shr_int_2addr FALLBACK + # op op_ushr_int_2addr FALLBACK + # op op_add_long_2addr FALLBACK + # op op_sub_long_2addr FALLBACK + # op op_mul_long_2addr FALLBACK + # op op_div_long_2addr FALLBACK + # op op_rem_long_2addr FALLBACK + # op op_and_long_2addr FALLBACK + # op op_or_long_2addr FALLBACK + # op op_xor_long_2addr FALLBACK + # op op_shl_long_2addr FALLBACK + # op op_shr_long_2addr FALLBACK + # op op_ushr_long_2addr FALLBACK + # op op_add_float_2addr FALLBACK + # op op_sub_float_2addr FALLBACK + # op op_mul_float_2addr FALLBACK + # op op_div_float_2addr FALLBACK + # op op_rem_float_2addr FALLBACK + # op op_add_double_2addr FALLBACK + # op op_sub_double_2addr FALLBACK + # op op_mul_double_2addr FALLBACK + # op op_div_double_2addr FALLBACK + # op op_rem_double_2addr FALLBACK + # op op_add_int_lit16 FALLBACK + # op op_rsub_int FALLBACK + # op op_mul_int_lit16 FALLBACK + # op op_div_int_lit16 FALLBACK + # op op_rem_int_lit16 FALLBACK + # op op_and_int_lit16 FALLBACK + # op op_or_int_lit16 FALLBACK + # op op_xor_int_lit16 FALLBACK + # op op_add_int_lit8 FALLBACK + # op op_rsub_int_lit8 FALLBACK + # op op_mul_int_lit8 FALLBACK + # op op_div_int_lit8 FALLBACK + # op op_rem_int_lit8 FALLBACK + # op op_and_int_lit8 FALLBACK + # op op_or_int_lit8 FALLBACK + # op op_xor_int_lit8 FALLBACK + # op op_shl_int_lit8 FALLBACK + # op op_shr_int_lit8 FALLBACK + # op op_ushr_int_lit8 FALLBACK + # op op_iget_quick FALLBACK + # op op_iget_wide_quick FALLBACK + # op op_iget_object_quick FALLBACK + # op op_iput_quick FALLBACK + # op op_iput_wide_quick FALLBACK + # op op_iput_object_quick FALLBACK + # op op_invoke_virtual_quick FALLBACK + # op op_invoke_virtual_range_quick FALLBACK + # op op_iput_boolean_quick FALLBACK + # op op_iput_byte_quick FALLBACK + # op op_iput_char_quick FALLBACK + # op op_iput_short_quick FALLBACK + # op op_iget_boolean_quick FALLBACK + # op op_iget_byte_quick FALLBACK + # op op_iget_char_quick FALLBACK + # op op_iget_short_quick FALLBACK + op op_invoke_lambda FALLBACK + # op op_unused_f4 FALLBACK + op op_capture_variable FALLBACK + op op_create_lambda FALLBACK + op op_liberate_variable FALLBACK + op op_box_lambda FALLBACK + op op_unbox_lambda FALLBACK + # op op_unused_fa FALLBACK + # op op_unused_fb FALLBACK + # op op_unused_fc FALLBACK + # op op_unused_fd FALLBACK + # op op_unused_fe FALLBACK + # op op_unused_ff FALLBACK op-end # common subroutines for asm diff --git a/runtime/interpreter/mterp/out/mterp_x86_64.S b/runtime/interpreter/mterp/out/mterp_x86_64.S new file mode 100644 index 0000000000..53fa50fc23 --- /dev/null +++ b/runtime/interpreter/mterp/out/mterp_x86_64.S @@ -0,0 +1,11959 @@ +/* + * This file was generated automatically by gen-mterp.py for 'x86_64'. + * + * --> DO NOT EDIT <-- + */ + +/* File: x86_64/header.S */ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + Art assembly interpreter notes: + + First validate assembly code by implementing ExecuteXXXImpl() style body (doesn't + handle invoke, allows higher-level code to create frame & shadow frame. + + Once that's working, support direct entry code & eliminate shadow frame (and + excess locals allocation. + + Some (hopefully) temporary ugliness. We'll treat rFP as pointing to the + base of the vreg array within the shadow frame. Access the other fields, + dex_pc_, method_ and number_of_vregs_ via negative offsets. For now, we'll continue + the shadow frame mechanism of double-storing object references - via rFP & + number_of_vregs_. + + */ + +/* +x86_64 ABI general notes: + +Caller save set: + rax, rdx, rcx, rsi, rdi, r8-r11, st(0)-st(7) +Callee save set: + rbx, rbp, r12-r15 +Return regs: + 32-bit in eax + 64-bit in rax + fp on xmm0 + +First 8 fp parameters came in xmm0-xmm7. +First 6 non-fp parameters came in rdi, rsi, rdx, rcx, r8, r9. +Other parameters passed on stack, pushed right-to-left. On entry to target, first +param is at 8(%esp). Traditional entry code is: + +Stack must be 16-byte aligned to support SSE in native code. + +If we're not doing variable stack allocation (alloca), the frame pointer can be +eliminated and all arg references adjusted to be esp relative. +*/ + +/* +Mterp and x86_64 notes: + +Some key interpreter variables will be assigned to registers. + + nick reg purpose + rSELF rbp pointer to ThreadSelf. + rPC r12 interpreted program counter, used for fetching instructions + rFP r13 interpreted frame pointer, used for accessing locals and args + rINSTw bx first 16-bit code of current instruction + rINSTbl bl opcode portion of instruction word + rINSTbh bh high byte of inst word, usually contains src/tgt reg names + rIBASE r14 base of instruction handler table + rREFS r15 base of object references in shadow frame. + +Notes: + o High order 16 bits of ebx must be zero on entry to handler + o rPC, rFP, rINSTw/rINSTbl valid on handler entry and exit + o eax and ecx are scratch, rINSTw/ebx sometimes scratch + +Macros are provided for common operations. Each macro MUST emit only +one instruction to make instruction-counting easier. They MUST NOT alter +unspecified registers or condition codes. +*/ + +/* + * This is a #include, not a %include, because we want the C pre-processor + * to expand the macros into assembler assignment statements. + */ +#include "asm_support.h" + +/* + * Handle mac compiler specific + */ +#if defined(__APPLE__) + #define MACRO_LITERAL(value) $(value) + #define FUNCTION_TYPE(name) + #define SIZE(start,end) + // Mac OS' symbols have an _ prefix. + #define SYMBOL(name) _ ## name +#else + #define MACRO_LITERAL(value) $value + #define FUNCTION_TYPE(name) .type name, @function + #define SIZE(start,end) .size start, .-end + #define SYMBOL(name) name +#endif + +.macro PUSH _reg + pushq \_reg + .cfi_adjust_cfa_offset 8 + .cfi_rel_offset \_reg, 0 +.endm + +.macro POP _reg + popq \_reg + .cfi_adjust_cfa_offset -8 + .cfi_restore \_reg +.endm + +/* Frame size must be 16-byte aligned. + * Remember about 8 bytes for return address + 6 * 8 for spills. + */ +#define FRAME_SIZE 8 + +/* Frame diagram while executing ExecuteMterpImpl, high to low addresses */ +#define IN_ARG3 %rcx +#define IN_ARG2 %rdx +#define IN_ARG1 %rsi +#define IN_ARG0 %rdi +/* Out Args */ +#define OUT_ARG3 %rcx +#define OUT_ARG2 %rdx +#define OUT_ARG1 %rsi +#define OUT_ARG0 %rdi +#define OUT_32_ARG3 %ecx +#define OUT_32_ARG2 %edx +#define OUT_32_ARG1 %esi +#define OUT_32_ARG0 %edi +#define OUT_FP_ARG1 %xmm1 +#define OUT_FP_ARG0 %xmm0 + +/* During bringup, we'll use the shadow frame model instead of rFP */ +/* single-purpose registers, given names for clarity */ +#define rSELF %rbp +#define rPC %r12 +#define rFP %r13 +#define rINST %ebx +#define rINSTq %rbx +#define rINSTw %bx +#define rINSTbh %bh +#define rINSTbl %bl +#define rIBASE %r14 +#define rREFS %r15 + +/* + * Instead of holding a pointer to the shadow frame, we keep rFP at the base of the vregs. So, + * to access other shadow frame fields, we need to use a backwards offset. Define those here. + */ +#define OFF_FP(a) (a - SHADOWFRAME_VREGS_OFFSET) +#define OFF_FP_NUMBER_OF_VREGS OFF_FP(SHADOWFRAME_NUMBER_OF_VREGS_OFFSET) +#define OFF_FP_DEX_PC OFF_FP(SHADOWFRAME_DEX_PC_OFFSET) +#define OFF_FP_LINK OFF_FP(SHADOWFRAME_LINK_OFFSET) +#define OFF_FP_METHOD OFF_FP(SHADOWFRAME_METHOD_OFFSET) +#define OFF_FP_RESULT_REGISTER OFF_FP(SHADOWFRAME_RESULT_REGISTER_OFFSET) +#define OFF_FP_DEX_PC_PTR OFF_FP(SHADOWFRAME_DEX_PC_PTR_OFFSET) +#define OFF_FP_CODE_ITEM OFF_FP(SHADOWFRAME_CODE_ITEM_OFFSET) +#define OFF_FP_SHADOWFRAME (-SHADOWFRAME_VREGS_OFFSET) + +/* + * + * The reference interpreter performs explicit suspect checks, which is somewhat wasteful. + * Dalvik's interpreter folded suspend checks into the jump table mechanism, and eventually + * mterp should do so as well. + */ +#define MTERP_SUSPEND 0 + +/* + * "export" the PC to dex_pc field in the shadow frame, f/b/o future exception objects. Must + * be done *before* something throws. + * + * It's okay to do this more than once. + * + * NOTE: the fast interpreter keeps track of dex pc as a direct pointer to the mapped + * dex byte codes. However, the rest of the runtime expects dex pc to be an instruction + * offset into the code_items_[] array. For effiency, we will "export" the + * current dex pc as a direct pointer using the EXPORT_PC macro, and rely on GetDexPC + * to convert to a dex pc when needed. + */ +.macro EXPORT_PC + movq rPC, OFF_FP_DEX_PC_PTR(rFP) +.endm + +/* + * Refresh handler table. + * IBase handles uses the caller save register so we must restore it after each call. + * Also it is used as a result of some 64-bit operations (like imul) and we should + * restore it in such cases also. + * + */ +.macro REFRESH_IBASE + movq THREAD_CURRENT_IBASE_OFFSET(rSELF), rIBASE +.endm + +/* + * Refresh rINST. + * At enter to handler rINST does not contain the opcode number. + * However some utilities require the full value, so this macro + * restores the opcode number. + */ +.macro REFRESH_INST _opnum + movb rINSTbl, rINSTbh + movb $\_opnum, rINSTbl +.endm + +/* + * Fetch the next instruction from rPC into rINSTw. Does not advance rPC. + */ +.macro FETCH_INST + movzwq (rPC), rINSTq +.endm + +/* + * Remove opcode from rINST, compute the address of handler and jump to it. + */ +.macro GOTO_NEXT + movzx rINSTbl,%eax + movzbl rINSTbh,rINST + shll MACRO_LITERAL(7), %eax + addq rIBASE, %rax + jmp *%rax +.endm + +/* + * Advance rPC by instruction count. + */ +.macro ADVANCE_PC _count + leaq 2*\_count(rPC), rPC +.endm + +/* + * Advance rPC by instruction count, fetch instruction and jump to handler. + */ +.macro ADVANCE_PC_FETCH_AND_GOTO_NEXT _count + ADVANCE_PC \_count + FETCH_INST + GOTO_NEXT +.endm + +/* + * Get/set the 32-bit value from a Dalvik register. + */ +#define VREG_ADDRESS(_vreg) (rFP,_vreg,4) +#define VREG_REF_ADDRESS(_vreg) (rREFS,_vreg,4) + +.macro GET_VREG _reg _vreg + movl (rFP,\_vreg,4), \_reg +.endm + +/* Read wide value. */ +.macro GET_WIDE_VREG _reg _vreg + movq (rFP,\_vreg,4), \_reg +.endm + +.macro SET_VREG _reg _vreg + movl \_reg, (rFP,\_vreg,4) + movl MACRO_LITERAL(0), (rREFS,\_vreg,4) +.endm + +/* Write wide value. reg is clobbered. */ +.macro SET_WIDE_VREG _reg _vreg + movq \_reg, (rFP,\_vreg,4) + xorq \_reg, \_reg + movq \_reg, (rREFS,\_vreg,4) +.endm + +.macro SET_VREG_OBJECT _reg _vreg + movl \_reg, (rFP,\_vreg,4) + movl \_reg, (rREFS,\_vreg,4) +.endm + +.macro GET_VREG_HIGH _reg _vreg + movl 4(rFP,\_vreg,4), \_reg +.endm + +.macro SET_VREG_HIGH _reg _vreg + movl \_reg, 4(rFP,\_vreg,4) + movl MACRO_LITERAL(0), 4(rREFS,\_vreg,4) +.endm + +.macro CLEAR_REF _vreg + movl MACRO_LITERAL(0), (rREFS,\_vreg,4) +.endm + +.macro CLEAR_WIDE_REF _vreg + movl MACRO_LITERAL(0), (rREFS,\_vreg,4) + movl MACRO_LITERAL(0), 4(rREFS,\_vreg,4) +.endm + +/* File: x86_64/entry.S */ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * Interpreter entry point. + */ + + .text + .global SYMBOL(ExecuteMterpImpl) + FUNCTION_TYPE(ExecuteMterpImpl) + +/* + * On entry: + * 0 Thread* self + * 1 code_item + * 2 ShadowFrame + * 3 JValue* result_register + * + */ + +SYMBOL(ExecuteMterpImpl): + .cfi_startproc + .cfi_def_cfa rsp, 8 + + /* Spill callee save regs */ + PUSH %rbx + PUSH %rbp + PUSH %r12 + PUSH %r13 + PUSH %r14 + PUSH %r15 + + /* Allocate frame */ + subq $FRAME_SIZE, %rsp + .cfi_adjust_cfa_offset FRAME_SIZE + + /* Remember the return register */ + movq IN_ARG3, SHADOWFRAME_RESULT_REGISTER_OFFSET(IN_ARG2) + + /* Remember the code_item */ + movq IN_ARG1, SHADOWFRAME_CODE_ITEM_OFFSET(IN_ARG2) + + /* set up "named" registers */ + movl SHADOWFRAME_NUMBER_OF_VREGS_OFFSET(IN_ARG2), %eax + leaq SHADOWFRAME_VREGS_OFFSET(IN_ARG2), rFP + leaq (rFP, %rax, 4), rREFS + movl SHADOWFRAME_DEX_PC_OFFSET(IN_ARG2), %eax + leaq CODEITEM_INSNS_OFFSET(IN_ARG1), rPC + leaq (rPC, %rax, 2), rPC + EXPORT_PC + + /* Starting ibase */ + movq IN_ARG0, rSELF + REFRESH_IBASE + + /* start executing the instruction at rPC */ + FETCH_INST + GOTO_NEXT + /* NOTE: no fallthrough */ + + + .global SYMBOL(artMterpAsmInstructionStart) + FUNCTION_TYPE(SYMBOL(artMterpAsmInstructionStart)) +SYMBOL(artMterpAsmInstructionStart) = .L_op_nop + .text + +/* ------------------------------ */ + .balign 128 +.L_op_nop: /* 0x00 */ +/* File: x86_64/op_nop.S */ + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + +/* ------------------------------ */ + .balign 128 +.L_op_move: /* 0x01 */ +/* File: x86_64/op_move.S */ + /* for move, move-object, long-to-int */ + /* op vA, vB */ + movl rINST, %eax # eax <- BA + andb $0xf, %al # eax <- A + shrl $4, rINST # rINST <- B + GET_VREG %edx, rINSTq + .if 0 + SET_VREG_OBJECT %edx, %rax # fp[A] <- fp[B] + .else + SET_VREG %edx, %rax # fp[A] <- fp[B] + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + +/* ------------------------------ */ + .balign 128 +.L_op_move_from16: /* 0x02 */ +/* File: x86_64/op_move_from16.S */ + /* for: move/from16, move-object/from16 */ + /* op vAA, vBBBB */ + movzwq 2(rPC), %rax # eax <- BBBB + GET_VREG %edx, %rax # edx <- fp[BBBB] + .if 0 + SET_VREG_OBJECT %edx, rINSTq # fp[A] <- fp[B] + .else + SET_VREG %edx, rINSTq # fp[A] <- fp[B] + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_move_16: /* 0x03 */ +/* File: x86_64/op_move_16.S */ + /* for: move/16, move-object/16 */ + /* op vAAAA, vBBBB */ + movzwq 4(rPC), %rcx # ecx <- BBBB + movzwq 2(rPC), %rax # eax <- AAAA + GET_VREG %edx, %rcx + .if 0 + SET_VREG_OBJECT %edx, %rax # fp[A] <- fp[B] + .else + SET_VREG %edx, %rax # fp[A] <- fp[B] + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + +/* ------------------------------ */ + .balign 128 +.L_op_move_wide: /* 0x04 */ +/* File: x86_64/op_move_wide.S */ + /* move-wide vA, vB */ + /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */ + movl rINST, %ecx # ecx <- BA + sarl $4, rINST # rINST <- B + andb $0xf, %cl # ecx <- A + GET_WIDE_VREG %rdx, rINSTq # rdx <- v[B] + SET_WIDE_VREG %rdx, %rcx # v[A] <- rdx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + +/* ------------------------------ */ + .balign 128 +.L_op_move_wide_from16: /* 0x05 */ +/* File: x86_64/op_move_wide_from16.S */ + /* move-wide/from16 vAA, vBBBB */ + /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */ + movzwl 2(rPC), %ecx # ecx <- BBBB + GET_WIDE_VREG %rdx, %rcx # rdx <- v[B] + SET_WIDE_VREG %rdx, rINSTq # v[A] <- rdx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_move_wide_16: /* 0x06 */ +/* File: x86_64/op_move_wide_16.S */ + /* move-wide/16 vAAAA, vBBBB */ + /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */ + movzwq 4(rPC), %rcx # ecx<- BBBB + movzwq 2(rPC), %rax # eax<- AAAA + GET_WIDE_VREG %rdx, %rcx # rdx <- v[B] + SET_WIDE_VREG %rdx, %rax # v[A] <- rdx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + +/* ------------------------------ */ + .balign 128 +.L_op_move_object: /* 0x07 */ +/* File: x86_64/op_move_object.S */ +/* File: x86_64/op_move.S */ + /* for move, move-object, long-to-int */ + /* op vA, vB */ + movl rINST, %eax # eax <- BA + andb $0xf, %al # eax <- A + shrl $4, rINST # rINST <- B + GET_VREG %edx, rINSTq + .if 1 + SET_VREG_OBJECT %edx, %rax # fp[A] <- fp[B] + .else + SET_VREG %edx, %rax # fp[A] <- fp[B] + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_move_object_from16: /* 0x08 */ +/* File: x86_64/op_move_object_from16.S */ +/* File: x86_64/op_move_from16.S */ + /* for: move/from16, move-object/from16 */ + /* op vAA, vBBBB */ + movzwq 2(rPC), %rax # eax <- BBBB + GET_VREG %edx, %rax # edx <- fp[BBBB] + .if 1 + SET_VREG_OBJECT %edx, rINSTq # fp[A] <- fp[B] + .else + SET_VREG %edx, rINSTq # fp[A] <- fp[B] + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_move_object_16: /* 0x09 */ +/* File: x86_64/op_move_object_16.S */ +/* File: x86_64/op_move_16.S */ + /* for: move/16, move-object/16 */ + /* op vAAAA, vBBBB */ + movzwq 4(rPC), %rcx # ecx <- BBBB + movzwq 2(rPC), %rax # eax <- AAAA + GET_VREG %edx, %rcx + .if 1 + SET_VREG_OBJECT %edx, %rax # fp[A] <- fp[B] + .else + SET_VREG %edx, %rax # fp[A] <- fp[B] + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + + +/* ------------------------------ */ + .balign 128 +.L_op_move_result: /* 0x0a */ +/* File: x86_64/op_move_result.S */ + /* for: move-result, move-result-object */ + /* op vAA */ + movq OFF_FP_RESULT_REGISTER(rFP), %rax # get pointer to result JType. + movl (%rax), %eax # r0 <- result.i. + .if 0 + SET_VREG_OBJECT %eax, rINSTq # fp[A] <- fp[B] + .else + SET_VREG %eax, rINSTq # fp[A] <- fp[B] + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + +/* ------------------------------ */ + .balign 128 +.L_op_move_result_wide: /* 0x0b */ +/* File: x86_64/op_move_result_wide.S */ + /* move-result-wide vAA */ + movq OFF_FP_RESULT_REGISTER(rFP), %rax # get pointer to result JType. + movq (%rax), %rdx # Get wide + SET_WIDE_VREG %rdx, rINSTq # v[AA] <- rdx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + +/* ------------------------------ */ + .balign 128 +.L_op_move_result_object: /* 0x0c */ +/* File: x86_64/op_move_result_object.S */ +/* File: x86_64/op_move_result.S */ + /* for: move-result, move-result-object */ + /* op vAA */ + movq OFF_FP_RESULT_REGISTER(rFP), %rax # get pointer to result JType. + movl (%rax), %eax # r0 <- result.i. + .if 1 + SET_VREG_OBJECT %eax, rINSTq # fp[A] <- fp[B] + .else + SET_VREG %eax, rINSTq # fp[A] <- fp[B] + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_move_exception: /* 0x0d */ +/* File: x86_64/op_move_exception.S */ + /* move-exception vAA */ + movl THREAD_EXCEPTION_OFFSET(rSELF), %eax + SET_VREG_OBJECT %eax, rINSTq # fp[AA] <- exception object + movl $0, THREAD_EXCEPTION_OFFSET(rSELF) + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + +/* ------------------------------ */ + .balign 128 +.L_op_return_void: /* 0x0e */ +/* File: x86_64/op_return_void.S */ + .extern MterpThreadFenceForConstructor + call SYMBOL(MterpThreadFenceForConstructor) + testl $(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(rSELF) + jz 1f + movq rSELF, OUT_ARG0 + call SYMBOL(MterpSuspendCheck) +1: + xorq %rax, %rax + jmp MterpReturn + +/* ------------------------------ */ + .balign 128 +.L_op_return: /* 0x0f */ +/* File: x86_64/op_return.S */ +/* + * Return a 32-bit value. + * + * for: return, return-object + */ + /* op vAA */ + .extern MterpThreadFenceForConstructor + call SYMBOL(MterpThreadFenceForConstructor) + testl $(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(rSELF) + jz 1f + movq rSELF, OUT_ARG0 + call SYMBOL(MterpSuspendCheck) +1: + GET_VREG %eax, rINSTq # eax <- vAA + jmp MterpReturn + +/* ------------------------------ */ + .balign 128 +.L_op_return_wide: /* 0x10 */ +/* File: x86_64/op_return_wide.S */ +/* + * Return a 64-bit value. + */ + /* return-wide vAA */ + .extern MterpThreadFenceForConstructor + call SYMBOL(MterpThreadFenceForConstructor) + testl $(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(rSELF) + jz 1f + movq rSELF, OUT_ARG0 + call SYMBOL(MterpSuspendCheck) +1: + GET_WIDE_VREG %rax, rINSTq # eax <- v[AA] + jmp MterpReturn + +/* ------------------------------ */ + .balign 128 +.L_op_return_object: /* 0x11 */ +/* File: x86_64/op_return_object.S */ +/* File: x86_64/op_return.S */ +/* + * Return a 32-bit value. + * + * for: return, return-object + */ + /* op vAA */ + .extern MterpThreadFenceForConstructor + call SYMBOL(MterpThreadFenceForConstructor) + testl $(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(rSELF) + jz 1f + movq rSELF, OUT_ARG0 + call SYMBOL(MterpSuspendCheck) +1: + GET_VREG %eax, rINSTq # eax <- vAA + jmp MterpReturn + + +/* ------------------------------ */ + .balign 128 +.L_op_const_4: /* 0x12 */ +/* File: x86_64/op_const_4.S */ + /* const/4 vA, #+B */ + movsbl rINSTbl, %eax # eax <-ssssssBx + movl $0xf, rINST + andl %eax, rINST # rINST <- A + sarl $4, %eax + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + +/* ------------------------------ */ + .balign 128 +.L_op_const_16: /* 0x13 */ +/* File: x86_64/op_const_16.S */ + /* const/16 vAA, #+BBBB */ + movswl 2(rPC), %ecx # ecx <- ssssBBBB + SET_VREG %ecx, rINSTq # vAA <- ssssBBBB + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_const: /* 0x14 */ +/* File: x86_64/op_const.S */ + /* const vAA, #+BBBBbbbb */ + movl 2(rPC), %eax # grab all 32 bits at once + SET_VREG %eax, rINSTq # vAA<- eax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + +/* ------------------------------ */ + .balign 128 +.L_op_const_high16: /* 0x15 */ +/* File: x86_64/op_const_high16.S */ + /* const/high16 vAA, #+BBBB0000 */ + movzwl 2(rPC), %eax # eax <- 0000BBBB + sall $16, %eax # eax <- BBBB0000 + SET_VREG %eax, rINSTq # vAA <- eax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_const_wide_16: /* 0x16 */ +/* File: x86_64/op_const_wide_16.S */ + /* const-wide/16 vAA, #+BBBB */ + movswq 2(rPC), %rax # rax <- ssssBBBB + SET_WIDE_VREG %rax, rINSTq # store + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_const_wide_32: /* 0x17 */ +/* File: x86_64/op_const_wide_32.S */ + /* const-wide/32 vAA, #+BBBBbbbb */ + movslq 2(rPC), %rax # eax <- ssssssssBBBBbbbb + SET_WIDE_VREG %rax, rINSTq # store + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + +/* ------------------------------ */ + .balign 128 +.L_op_const_wide: /* 0x18 */ +/* File: x86_64/op_const_wide.S */ + /* const-wide vAA, #+HHHHhhhhBBBBbbbb */ + movq 2(rPC), %rax # rax <- HHHHhhhhBBBBbbbb + SET_WIDE_VREG %rax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 5 + +/* ------------------------------ */ + .balign 128 +.L_op_const_wide_high16: /* 0x19 */ +/* File: x86_64/op_const_wide_high16.S */ + /* const-wide/high16 vAA, #+BBBB000000000000 */ + movzwq 2(rPC), %rax # eax <- 0000BBBB + salq $48, %rax # eax <- BBBB0000 + SET_WIDE_VREG %rax, rINSTq # v[AA+0] <- eax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_const_string: /* 0x1a */ +/* File: x86_64/op_const_string.S */ + /* const/string vAA, String@BBBB */ + EXPORT_PC + movzwq 2(rPC), OUT_ARG0 # OUT_ARG0 <- BBBB + movq rINSTq, OUT_ARG1 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG2 + movq rSELF, OUT_ARG3 + call SYMBOL(MterpConstString) # (index, tgt_reg, shadow_frame, self) + testb %al, %al + jnz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_const_string_jumbo: /* 0x1b */ +/* File: x86_64/op_const_string_jumbo.S */ + /* const/string vAA, String@BBBBBBBB */ + EXPORT_PC + movl 2(rPC), OUT_32_ARG0 # OUT_32_ARG0 <- BBBB + movq rINSTq, OUT_ARG1 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG2 + movq rSELF, OUT_ARG3 + call SYMBOL(MterpConstString) # (index, tgt_reg, shadow_frame, self) + testb %al, %al + jnz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + +/* ------------------------------ */ + .balign 128 +.L_op_const_class: /* 0x1c */ +/* File: x86_64/op_const_class.S */ + /* const/class vAA, Class@BBBB */ + EXPORT_PC + movzwq 2(rPC), OUT_ARG0 # eax <- OUT_ARG0 + movq rINSTq, OUT_ARG1 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG2 + movq rSELF, OUT_ARG3 + call SYMBOL(MterpConstClass) # (index, tgt_reg, shadow_frame, self) + testb %al, %al + jnz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_monitor_enter: /* 0x1d */ +/* File: x86_64/op_monitor_enter.S */ +/* + * Synchronize on an object. + */ + /* monitor-enter vAA */ + EXPORT_PC + GET_VREG OUT_32_ARG0, rINSTq + movq rSELF, OUT_ARG1 + call SYMBOL(artLockObjectFromCode) # (object, self) + testq %rax, %rax + jnz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + +/* ------------------------------ */ + .balign 128 +.L_op_monitor_exit: /* 0x1e */ +/* File: x86_64/op_monitor_exit.S */ +/* + * Unlock an object. + * + * Exceptions that occur when unlocking a monitor need to appear as + * if they happened at the following instruction. See the Dalvik + * instruction spec. + */ + /* monitor-exit vAA */ + EXPORT_PC + GET_VREG OUT_32_ARG0, rINSTq + movq rSELF, OUT_ARG1 + call SYMBOL(artUnlockObjectFromCode) # (object, self) + testq %rax, %rax + jnz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + +/* ------------------------------ */ + .balign 128 +.L_op_check_cast: /* 0x1f */ +/* File: x86_64/op_check_cast.S */ +/* + * Check to see if a cast from one class to another is allowed. + */ + /* check-cast vAA, class@BBBB */ + EXPORT_PC + movzwq 2(rPC), OUT_ARG0 # OUT_ARG0 <- BBBB + leaq VREG_ADDRESS(rINSTq), OUT_ARG1 + movq OFF_FP_METHOD(rFP), OUT_ARG2 + movq rSELF, OUT_ARG3 + call SYMBOL(MterpCheckCast) # (index, &obj, method, self) + testb %al, %al + jnz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_instance_of: /* 0x20 */ +/* File: x86_64/op_instance_of.S */ +/* + * Check to see if an object reference is an instance of a class. + * + * Most common situation is a non-null object, being compared against + * an already-resolved class. + */ + /* instance-of vA, vB, class@CCCC */ + EXPORT_PC + movzwl 2(rPC), OUT_32_ARG0 # OUT_32_ARG0 <- CCCC + movl rINST, %eax # eax <- BA + sarl $4, %eax # eax <- B + leaq VREG_ADDRESS(%rax), OUT_ARG1 # Get object address + movq OFF_FP_METHOD(rFP), OUT_ARG2 + movq rSELF, OUT_ARG3 + call SYMBOL(MterpInstanceOf) # (index, &obj, method, self) + movsbl %al, %eax + cmpq $0, THREAD_EXCEPTION_OFFSET(rSELF) + jnz MterpException + andb $0xf, rINSTbl # rINSTbl <- A + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_array_length: /* 0x21 */ +/* File: x86_64/op_array_length.S */ +/* + * Return the length of an array. + */ + movl rINST, %eax # eax <- BA + sarl $4, rINST # rINST <- B + GET_VREG %ecx, rINSTq # ecx <- vB (object ref) + testl %ecx, %ecx # is null? + je common_errNullObject + andb $0xf, %al # eax <- A + movl MIRROR_ARRAY_LENGTH_OFFSET(%rcx), rINST + SET_VREG rINST, %rax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + +/* ------------------------------ */ + .balign 128 +.L_op_new_instance: /* 0x22 */ +/* File: x86_64/op_new_instance.S */ +/* + * Create a new instance of a class. + */ + /* new-instance vAA, class@BBBB */ + EXPORT_PC + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG0 + movq rSELF, OUT_ARG1 + REFRESH_INST 34 + movq rINSTq, OUT_ARG2 + call SYMBOL(MterpNewInstance) + testb %al, %al # 0 means an exception is thrown + jz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_new_array: /* 0x23 */ +/* File: x86_64/op_new_array.S */ +/* + * Allocate an array of objects, specified with the array class + * and a count. + * + * The verifier guarantees that this is an array class, so we don't + * check for it here. + */ + /* new-array vA, vB, class@CCCC */ + EXPORT_PC + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG0 + movq rPC, OUT_ARG1 + REFRESH_INST 35 + movq rINSTq, OUT_ARG2 + movq rSELF, OUT_ARG3 + call SYMBOL(MterpNewArray) + testb %al, %al # 0 means an exception is thrown + jz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_filled_new_array: /* 0x24 */ +/* File: x86_64/op_filled_new_array.S */ +/* + * Create a new array with elements filled from registers. + * + * for: filled-new-array, filled-new-array/range + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, type@BBBB */ + .extern MterpFilledNewArray + EXPORT_PC + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG0 + movq rPC, OUT_ARG1 + movq rSELF, OUT_ARG2 + call SYMBOL(MterpFilledNewArray) + testb %al, %al # 0 means an exception is thrown + jz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + +/* ------------------------------ */ + .balign 128 +.L_op_filled_new_array_range: /* 0x25 */ +/* File: x86_64/op_filled_new_array_range.S */ +/* File: x86_64/op_filled_new_array.S */ +/* + * Create a new array with elements filled from registers. + * + * for: filled-new-array, filled-new-array/range + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, type@BBBB */ + .extern MterpFilledNewArrayRange + EXPORT_PC + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG0 + movq rPC, OUT_ARG1 + movq rSELF, OUT_ARG2 + call SYMBOL(MterpFilledNewArrayRange) + testb %al, %al # 0 means an exception is thrown + jz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + + +/* ------------------------------ */ + .balign 128 +.L_op_fill_array_data: /* 0x26 */ +/* File: x86_64/op_fill_array_data.S */ + /* fill-array-data vAA, +BBBBBBBB */ + EXPORT_PC + movl 2(rPC), %ecx # ecx <- BBBBbbbb + leaq (rPC,%rcx,2), OUT_ARG1 # OUT_ARG1 <- PC + BBBBbbbb*2 + GET_VREG OUT_32_ARG0, rINSTq # OUT_ARG0 <- vAA (array object) + call SYMBOL(MterpFillArrayData) # (obj, payload) + testb %al, %al # 0 means an exception is thrown + jz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + +/* ------------------------------ */ + .balign 128 +.L_op_throw: /* 0x27 */ +/* File: x86_64/op_throw.S */ +/* + * Throw an exception object in the current thread. + */ + /* throw vAA */ + EXPORT_PC + GET_VREG %eax, rINSTq # eax<- vAA (exception object) + testb %al, %al + jz common_errNullObject + movq %rax, THREAD_EXCEPTION_OFFSET(rSELF) + jmp MterpException + +/* ------------------------------ */ + .balign 128 +.L_op_goto: /* 0x28 */ +/* File: x86_64/op_goto.S */ +/* + * Unconditional branch, 8-bit offset. + * + * The branch distance is a signed code-unit offset, which we need to + * double to get a byte offset. + */ + /* goto +AA */ + movsbq rINSTbl, %rax # rax <- ssssssAA + addq %rax, %rax # rax <- AA * 2 + leaq (rPC, %rax), rPC + FETCH_INST + jg 1f # AA * 2 > 0 => no suspend check +#if MTERP_SUSPEND + REFRESH_IBASE +#else + jmp MterpCheckSuspendAndContinue +#endif +1: + GOTO_NEXT + +/* ------------------------------ */ + .balign 128 +.L_op_goto_16: /* 0x29 */ +/* File: x86_64/op_goto_16.S */ +/* + * Unconditional branch, 16-bit offset. + * + * The branch distance is a signed code-unit offset, which we need to + * double to get a byte offset. + */ + /* goto/16 +AAAA */ + movswq 2(rPC), %rax # rax <- ssssAAAA + addq %rax, %rax # rax <- AA * 2 + leaq (rPC, %rax), rPC + FETCH_INST + jg 1f # AA * 2 > 0 => no suspend check +#if MTERP_SUSPEND + REFRESH_IBASE +#else + jmp MterpCheckSuspendAndContinue +#endif +1: + GOTO_NEXT + +/* ------------------------------ */ + .balign 128 +.L_op_goto_32: /* 0x2a */ +/* File: x86_64/op_goto_32.S */ +/* + * Unconditional branch, 32-bit offset. + * + * The branch distance is a signed code-unit offset, which we need to + * double to get a byte offset. + * + * Because we need the SF bit set, we'll use an adds + * to convert from Dalvik offset to byte offset. + */ + /* goto/32 +AAAAAAAA */ + movslq 2(rPC), %rax # rax <- AAAAAAAA + addq %rax, %rax # rax <- AA * 2 + leaq (rPC, %rax), rPC + FETCH_INST + jg 1f # AA * 2 > 0 => no suspend check +#if MTERP_SUSPEND + REFRESH_IBASE +#else + jmp MterpCheckSuspendAndContinue +#endif +1: + GOTO_NEXT + +/* ------------------------------ */ + .balign 128 +.L_op_packed_switch: /* 0x2b */ +/* File: x86_64/op_packed_switch.S */ +/* + * Handle a packed-switch or sparse-switch instruction. In both cases + * we decode it and hand it off to a helper function. + * + * We don't really expect backward branches in a switch statement, but + * they're perfectly legal, so we check for them here. + * + * for: packed-switch, sparse-switch + */ + /* op vAA, +BBBB */ + movslq 2(rPC), OUT_ARG0 # rcx <- BBBBbbbb + leaq (rPC,OUT_ARG0,2), OUT_ARG0 # rcx <- PC + BBBBbbbb*2 + GET_VREG OUT_32_ARG1, rINSTq # eax <- vAA + call SYMBOL(MterpDoPackedSwitch) + addl %eax, %eax + movslq %eax, %rax + leaq (rPC, %rax), rPC + FETCH_INST + jg 1f +#if MTERP_SUSPEND + REFRESH_IBASE +#else + jmp MterpCheckSuspendAndContinue +#endif +1: + GOTO_NEXT + +/* ------------------------------ */ + .balign 128 +.L_op_sparse_switch: /* 0x2c */ +/* File: x86_64/op_sparse_switch.S */ +/* File: x86_64/op_packed_switch.S */ +/* + * Handle a packed-switch or sparse-switch instruction. In both cases + * we decode it and hand it off to a helper function. + * + * We don't really expect backward branches in a switch statement, but + * they're perfectly legal, so we check for them here. + * + * for: packed-switch, sparse-switch + */ + /* op vAA, +BBBB */ + movslq 2(rPC), OUT_ARG0 # rcx <- BBBBbbbb + leaq (rPC,OUT_ARG0,2), OUT_ARG0 # rcx <- PC + BBBBbbbb*2 + GET_VREG OUT_32_ARG1, rINSTq # eax <- vAA + call SYMBOL(MterpDoSparseSwitch) + addl %eax, %eax + movslq %eax, %rax + leaq (rPC, %rax), rPC + FETCH_INST + jg 1f +#if MTERP_SUSPEND + REFRESH_IBASE +#else + jmp MterpCheckSuspendAndContinue +#endif +1: + GOTO_NEXT + + +/* ------------------------------ */ + .balign 128 +.L_op_cmpl_float: /* 0x2d */ +/* File: x86_64/op_cmpl_float.S */ +/* File: x86_64/fpcmp.S */ +/* + * Compare two floating-point values. Puts 0, 1, or -1 into the + * destination register based on the results of the comparison. + * + * int compare(x, y) { + * if (x == y) { + * return 0; + * } else if (x < y) { + * return -1; + * } else if (x > y) { + * return 1; + * } else { + * return nanval ? 1 : -1; + * } + * } + */ + /* op vAA, vBB, vCC */ + movzbq 3(rPC), %rcx # ecx<- CC + movzbq 2(rPC), %rax # eax<- BB + movss VREG_ADDRESS(%rax), %xmm0 + xor %eax, %eax + ucomiss VREG_ADDRESS(%rcx), %xmm0 + jp .Lop_cmpl_float_nan_is_neg + je .Lop_cmpl_float_finish + jb .Lop_cmpl_float_less +.Lop_cmpl_float_nan_is_pos: + addb $1, %al + jmp .Lop_cmpl_float_finish +.Lop_cmpl_float_nan_is_neg: +.Lop_cmpl_float_less: + movl $-1, %eax +.Lop_cmpl_float_finish: + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_cmpg_float: /* 0x2e */ +/* File: x86_64/op_cmpg_float.S */ +/* File: x86_64/fpcmp.S */ +/* + * Compare two floating-point values. Puts 0, 1, or -1 into the + * destination register based on the results of the comparison. + * + * int compare(x, y) { + * if (x == y) { + * return 0; + * } else if (x < y) { + * return -1; + * } else if (x > y) { + * return 1; + * } else { + * return nanval ? 1 : -1; + * } + * } + */ + /* op vAA, vBB, vCC */ + movzbq 3(rPC), %rcx # ecx<- CC + movzbq 2(rPC), %rax # eax<- BB + movss VREG_ADDRESS(%rax), %xmm0 + xor %eax, %eax + ucomiss VREG_ADDRESS(%rcx), %xmm0 + jp .Lop_cmpg_float_nan_is_pos + je .Lop_cmpg_float_finish + jb .Lop_cmpg_float_less +.Lop_cmpg_float_nan_is_pos: + addb $1, %al + jmp .Lop_cmpg_float_finish +.Lop_cmpg_float_nan_is_neg: +.Lop_cmpg_float_less: + movl $-1, %eax +.Lop_cmpg_float_finish: + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_cmpl_double: /* 0x2f */ +/* File: x86_64/op_cmpl_double.S */ +/* File: x86_64/fpcmp.S */ +/* + * Compare two floating-point values. Puts 0, 1, or -1 into the + * destination register based on the results of the comparison. + * + * int compare(x, y) { + * if (x == y) { + * return 0; + * } else if (x < y) { + * return -1; + * } else if (x > y) { + * return 1; + * } else { + * return nanval ? 1 : -1; + * } + * } + */ + /* op vAA, vBB, vCC */ + movzbq 3(rPC), %rcx # ecx<- CC + movzbq 2(rPC), %rax # eax<- BB + movsd VREG_ADDRESS(%rax), %xmm0 + xor %eax, %eax + ucomisd VREG_ADDRESS(%rcx), %xmm0 + jp .Lop_cmpl_double_nan_is_neg + je .Lop_cmpl_double_finish + jb .Lop_cmpl_double_less +.Lop_cmpl_double_nan_is_pos: + addb $1, %al + jmp .Lop_cmpl_double_finish +.Lop_cmpl_double_nan_is_neg: +.Lop_cmpl_double_less: + movl $-1, %eax +.Lop_cmpl_double_finish: + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_cmpg_double: /* 0x30 */ +/* File: x86_64/op_cmpg_double.S */ +/* File: x86_64/fpcmp.S */ +/* + * Compare two floating-point values. Puts 0, 1, or -1 into the + * destination register based on the results of the comparison. + * + * int compare(x, y) { + * if (x == y) { + * return 0; + * } else if (x < y) { + * return -1; + * } else if (x > y) { + * return 1; + * } else { + * return nanval ? 1 : -1; + * } + * } + */ + /* op vAA, vBB, vCC */ + movzbq 3(rPC), %rcx # ecx<- CC + movzbq 2(rPC), %rax # eax<- BB + movsd VREG_ADDRESS(%rax), %xmm0 + xor %eax, %eax + ucomisd VREG_ADDRESS(%rcx), %xmm0 + jp .Lop_cmpg_double_nan_is_pos + je .Lop_cmpg_double_finish + jb .Lop_cmpg_double_less +.Lop_cmpg_double_nan_is_pos: + addb $1, %al + jmp .Lop_cmpg_double_finish +.Lop_cmpg_double_nan_is_neg: +.Lop_cmpg_double_less: + movl $-1, %eax +.Lop_cmpg_double_finish: + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_cmp_long: /* 0x31 */ +/* File: x86_64/op_cmp_long.S */ +/* + * Compare two 64-bit values. Puts 0, 1, or -1 into the destination + * register based on the results of the comparison. + */ + /* cmp-long vAA, vBB, vCC */ + movzbq 2(rPC), %rdx # edx <- BB + movzbq 3(rPC), %rcx # ecx <- CC + GET_WIDE_VREG %rdx, %rdx # rdx <- v[BB] + xorl %eax, %eax + xorl %edi, %edi + addb $1, %al + movl $-1, %esi + cmpq VREG_ADDRESS(%rcx), %rdx + cmovl %esi, %edi + cmovg %eax, %edi + SET_VREG %edi, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_if_eq: /* 0x32 */ +/* File: x86_64/op_if_eq.S */ +/* File: x86_64/bincmp.S */ +/* + * Generic two-operand compare-and-branch operation. Provide a "revcmp" + * fragment that specifies the *reverse* comparison to perform, e.g. + * for "if-le" you would use "gt". + * + * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le + */ + /* if-cmp vA, vB, +CCCC */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # rcx <- A + GET_VREG %eax, %rcx # eax <- vA + cmpl VREG_ADDRESS(rINSTq), %eax # compare (vA, vB) + movl $2, %eax # assume not taken + jne 1f + movswq 2(rPC),%rax # Get signed branch offset +1: + addq %rax, %rax # rax <- AA * 2 + leaq (rPC, %rax), rPC + FETCH_INST + jg 2f # AA * 2 > 0 => no suspend check +#if MTERP_SUSPEND + REFRESH_IBASE +#else + jmp MterpCheckSuspendAndContinue +#endif +2: + GOTO_NEXT + + +/* ------------------------------ */ + .balign 128 +.L_op_if_ne: /* 0x33 */ +/* File: x86_64/op_if_ne.S */ +/* File: x86_64/bincmp.S */ +/* + * Generic two-operand compare-and-branch operation. Provide a "revcmp" + * fragment that specifies the *reverse* comparison to perform, e.g. + * for "if-le" you would use "gt". + * + * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le + */ + /* if-cmp vA, vB, +CCCC */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # rcx <- A + GET_VREG %eax, %rcx # eax <- vA + cmpl VREG_ADDRESS(rINSTq), %eax # compare (vA, vB) + movl $2, %eax # assume not taken + je 1f + movswq 2(rPC),%rax # Get signed branch offset +1: + addq %rax, %rax # rax <- AA * 2 + leaq (rPC, %rax), rPC + FETCH_INST + jg 2f # AA * 2 > 0 => no suspend check +#if MTERP_SUSPEND + REFRESH_IBASE +#else + jmp MterpCheckSuspendAndContinue +#endif +2: + GOTO_NEXT + + +/* ------------------------------ */ + .balign 128 +.L_op_if_lt: /* 0x34 */ +/* File: x86_64/op_if_lt.S */ +/* File: x86_64/bincmp.S */ +/* + * Generic two-operand compare-and-branch operation. Provide a "revcmp" + * fragment that specifies the *reverse* comparison to perform, e.g. + * for "if-le" you would use "gt". + * + * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le + */ + /* if-cmp vA, vB, +CCCC */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # rcx <- A + GET_VREG %eax, %rcx # eax <- vA + cmpl VREG_ADDRESS(rINSTq), %eax # compare (vA, vB) + movl $2, %eax # assume not taken + jge 1f + movswq 2(rPC),%rax # Get signed branch offset +1: + addq %rax, %rax # rax <- AA * 2 + leaq (rPC, %rax), rPC + FETCH_INST + jg 2f # AA * 2 > 0 => no suspend check +#if MTERP_SUSPEND + REFRESH_IBASE +#else + jmp MterpCheckSuspendAndContinue +#endif +2: + GOTO_NEXT + + +/* ------------------------------ */ + .balign 128 +.L_op_if_ge: /* 0x35 */ +/* File: x86_64/op_if_ge.S */ +/* File: x86_64/bincmp.S */ +/* + * Generic two-operand compare-and-branch operation. Provide a "revcmp" + * fragment that specifies the *reverse* comparison to perform, e.g. + * for "if-le" you would use "gt". + * + * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le + */ + /* if-cmp vA, vB, +CCCC */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # rcx <- A + GET_VREG %eax, %rcx # eax <- vA + cmpl VREG_ADDRESS(rINSTq), %eax # compare (vA, vB) + movl $2, %eax # assume not taken + jl 1f + movswq 2(rPC),%rax # Get signed branch offset +1: + addq %rax, %rax # rax <- AA * 2 + leaq (rPC, %rax), rPC + FETCH_INST + jg 2f # AA * 2 > 0 => no suspend check +#if MTERP_SUSPEND + REFRESH_IBASE +#else + jmp MterpCheckSuspendAndContinue +#endif +2: + GOTO_NEXT + + +/* ------------------------------ */ + .balign 128 +.L_op_if_gt: /* 0x36 */ +/* File: x86_64/op_if_gt.S */ +/* File: x86_64/bincmp.S */ +/* + * Generic two-operand compare-and-branch operation. Provide a "revcmp" + * fragment that specifies the *reverse* comparison to perform, e.g. + * for "if-le" you would use "gt". + * + * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le + */ + /* if-cmp vA, vB, +CCCC */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # rcx <- A + GET_VREG %eax, %rcx # eax <- vA + cmpl VREG_ADDRESS(rINSTq), %eax # compare (vA, vB) + movl $2, %eax # assume not taken + jle 1f + movswq 2(rPC),%rax # Get signed branch offset +1: + addq %rax, %rax # rax <- AA * 2 + leaq (rPC, %rax), rPC + FETCH_INST + jg 2f # AA * 2 > 0 => no suspend check +#if MTERP_SUSPEND + REFRESH_IBASE +#else + jmp MterpCheckSuspendAndContinue +#endif +2: + GOTO_NEXT + + +/* ------------------------------ */ + .balign 128 +.L_op_if_le: /* 0x37 */ +/* File: x86_64/op_if_le.S */ +/* File: x86_64/bincmp.S */ +/* + * Generic two-operand compare-and-branch operation. Provide a "revcmp" + * fragment that specifies the *reverse* comparison to perform, e.g. + * for "if-le" you would use "gt". + * + * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le + */ + /* if-cmp vA, vB, +CCCC */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # rcx <- A + GET_VREG %eax, %rcx # eax <- vA + cmpl VREG_ADDRESS(rINSTq), %eax # compare (vA, vB) + movl $2, %eax # assume not taken + jg 1f + movswq 2(rPC),%rax # Get signed branch offset +1: + addq %rax, %rax # rax <- AA * 2 + leaq (rPC, %rax), rPC + FETCH_INST + jg 2f # AA * 2 > 0 => no suspend check +#if MTERP_SUSPEND + REFRESH_IBASE +#else + jmp MterpCheckSuspendAndContinue +#endif +2: + GOTO_NEXT + + +/* ------------------------------ */ + .balign 128 +.L_op_if_eqz: /* 0x38 */ +/* File: x86_64/op_if_eqz.S */ +/* File: x86_64/zcmp.S */ +/* + * Generic one-operand compare-and-branch operation. Provide a "revcmp" + * fragment that specifies the *reverse* comparison to perform, e.g. + * for "if-le" you would use "gt". + * + * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez + */ + /* if-cmp vAA, +BBBB */ + cmpl $0, VREG_ADDRESS(rINSTq) # compare (vA, 0) + movl $2, %eax # assume branch not taken + jne 1f + movswq 2(rPC),%rax # fetch signed displacement +1: + addq %rax, %rax # eax <- AA * 2 + leaq (rPC, %rax), rPC + FETCH_INST + jg 2f # AA * 2 > 0 => no suspend check +#if MTERP_SUSPEND + REFRESH_IBASE +#else + jmp MterpCheckSuspendAndContinue +#endif +2: + GOTO_NEXT + + +/* ------------------------------ */ + .balign 128 +.L_op_if_nez: /* 0x39 */ +/* File: x86_64/op_if_nez.S */ +/* File: x86_64/zcmp.S */ +/* + * Generic one-operand compare-and-branch operation. Provide a "revcmp" + * fragment that specifies the *reverse* comparison to perform, e.g. + * for "if-le" you would use "gt". + * + * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez + */ + /* if-cmp vAA, +BBBB */ + cmpl $0, VREG_ADDRESS(rINSTq) # compare (vA, 0) + movl $2, %eax # assume branch not taken + je 1f + movswq 2(rPC),%rax # fetch signed displacement +1: + addq %rax, %rax # eax <- AA * 2 + leaq (rPC, %rax), rPC + FETCH_INST + jg 2f # AA * 2 > 0 => no suspend check +#if MTERP_SUSPEND + REFRESH_IBASE +#else + jmp MterpCheckSuspendAndContinue +#endif +2: + GOTO_NEXT + + +/* ------------------------------ */ + .balign 128 +.L_op_if_ltz: /* 0x3a */ +/* File: x86_64/op_if_ltz.S */ +/* File: x86_64/zcmp.S */ +/* + * Generic one-operand compare-and-branch operation. Provide a "revcmp" + * fragment that specifies the *reverse* comparison to perform, e.g. + * for "if-le" you would use "gt". + * + * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez + */ + /* if-cmp vAA, +BBBB */ + cmpl $0, VREG_ADDRESS(rINSTq) # compare (vA, 0) + movl $2, %eax # assume branch not taken + jge 1f + movswq 2(rPC),%rax # fetch signed displacement +1: + addq %rax, %rax # eax <- AA * 2 + leaq (rPC, %rax), rPC + FETCH_INST + jg 2f # AA * 2 > 0 => no suspend check +#if MTERP_SUSPEND + REFRESH_IBASE +#else + jmp MterpCheckSuspendAndContinue +#endif +2: + GOTO_NEXT + + +/* ------------------------------ */ + .balign 128 +.L_op_if_gez: /* 0x3b */ +/* File: x86_64/op_if_gez.S */ +/* File: x86_64/zcmp.S */ +/* + * Generic one-operand compare-and-branch operation. Provide a "revcmp" + * fragment that specifies the *reverse* comparison to perform, e.g. + * for "if-le" you would use "gt". + * + * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez + */ + /* if-cmp vAA, +BBBB */ + cmpl $0, VREG_ADDRESS(rINSTq) # compare (vA, 0) + movl $2, %eax # assume branch not taken + jl 1f + movswq 2(rPC),%rax # fetch signed displacement +1: + addq %rax, %rax # eax <- AA * 2 + leaq (rPC, %rax), rPC + FETCH_INST + jg 2f # AA * 2 > 0 => no suspend check +#if MTERP_SUSPEND + REFRESH_IBASE +#else + jmp MterpCheckSuspendAndContinue +#endif +2: + GOTO_NEXT + + +/* ------------------------------ */ + .balign 128 +.L_op_if_gtz: /* 0x3c */ +/* File: x86_64/op_if_gtz.S */ +/* File: x86_64/zcmp.S */ +/* + * Generic one-operand compare-and-branch operation. Provide a "revcmp" + * fragment that specifies the *reverse* comparison to perform, e.g. + * for "if-le" you would use "gt". + * + * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez + */ + /* if-cmp vAA, +BBBB */ + cmpl $0, VREG_ADDRESS(rINSTq) # compare (vA, 0) + movl $2, %eax # assume branch not taken + jle 1f + movswq 2(rPC),%rax # fetch signed displacement +1: + addq %rax, %rax # eax <- AA * 2 + leaq (rPC, %rax), rPC + FETCH_INST + jg 2f # AA * 2 > 0 => no suspend check +#if MTERP_SUSPEND + REFRESH_IBASE +#else + jmp MterpCheckSuspendAndContinue +#endif +2: + GOTO_NEXT + + +/* ------------------------------ */ + .balign 128 +.L_op_if_lez: /* 0x3d */ +/* File: x86_64/op_if_lez.S */ +/* File: x86_64/zcmp.S */ +/* + * Generic one-operand compare-and-branch operation. Provide a "revcmp" + * fragment that specifies the *reverse* comparison to perform, e.g. + * for "if-le" you would use "gt". + * + * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez + */ + /* if-cmp vAA, +BBBB */ + cmpl $0, VREG_ADDRESS(rINSTq) # compare (vA, 0) + movl $2, %eax # assume branch not taken + jg 1f + movswq 2(rPC),%rax # fetch signed displacement +1: + addq %rax, %rax # eax <- AA * 2 + leaq (rPC, %rax), rPC + FETCH_INST + jg 2f # AA * 2 > 0 => no suspend check +#if MTERP_SUSPEND + REFRESH_IBASE +#else + jmp MterpCheckSuspendAndContinue +#endif +2: + GOTO_NEXT + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_3e: /* 0x3e */ +/* File: x86_64/op_unused_3e.S */ +/* File: x86_64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + jmp MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_3f: /* 0x3f */ +/* File: x86_64/op_unused_3f.S */ +/* File: x86_64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + jmp MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_40: /* 0x40 */ +/* File: x86_64/op_unused_40.S */ +/* File: x86_64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + jmp MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_41: /* 0x41 */ +/* File: x86_64/op_unused_41.S */ +/* File: x86_64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + jmp MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_42: /* 0x42 */ +/* File: x86_64/op_unused_42.S */ +/* File: x86_64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + jmp MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_43: /* 0x43 */ +/* File: x86_64/op_unused_43.S */ +/* File: x86_64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + jmp MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_aget: /* 0x44 */ +/* File: x86_64/op_aget.S */ +/* + * Array get, 32 bits or less. vAA <- vBB[vCC]. + * + * for: aget, aget-boolean, aget-byte, aget-char, aget-short, aget-wide + * + */ + /* op vAA, vBB, vCC */ + movzbq 2(rPC), %rax # eax <- BB + movzbq 3(rPC), %rcx # ecx <- CC + GET_VREG %eax, %rax # eax <- vBB (array object) + GET_VREG %ecx, %rcx # ecx <- vCC (requested index) + testl %eax, %eax # null array object? + je common_errNullObject # bail if so + cmpl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ecx + jae common_errArrayIndex # index >= length, bail. + .if 0 + movq MIRROR_INT_ARRAY_DATA_OFFSET(%rax,%rcx,8), %rax + SET_WIDE_VREG %rax, rINSTq + .else + movl MIRROR_INT_ARRAY_DATA_OFFSET(%rax,%rcx,4), %eax + SET_VREG %eax, rINSTq + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_aget_wide: /* 0x45 */ +/* File: x86_64/op_aget_wide.S */ +/* File: x86_64/op_aget.S */ +/* + * Array get, 32 bits or less. vAA <- vBB[vCC]. + * + * for: aget, aget-boolean, aget-byte, aget-char, aget-short, aget-wide + * + */ + /* op vAA, vBB, vCC */ + movzbq 2(rPC), %rax # eax <- BB + movzbq 3(rPC), %rcx # ecx <- CC + GET_VREG %eax, %rax # eax <- vBB (array object) + GET_VREG %ecx, %rcx # ecx <- vCC (requested index) + testl %eax, %eax # null array object? + je common_errNullObject # bail if so + cmpl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ecx + jae common_errArrayIndex # index >= length, bail. + .if 1 + movq MIRROR_WIDE_ARRAY_DATA_OFFSET(%rax,%rcx,8), %rax + SET_WIDE_VREG %rax, rINSTq + .else + movq MIRROR_WIDE_ARRAY_DATA_OFFSET(%rax,%rcx,8), %eax + SET_VREG %eax, rINSTq + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_aget_object: /* 0x46 */ +/* File: x86_64/op_aget_object.S */ +/* + * Array object get. vAA <- vBB[vCC]. + * + * for: aget-object + */ + /* op vAA, vBB, vCC */ + movzbq 2(rPC), %rax # rax <- BB + movzbq 3(rPC), %rcx # rcx <- CC + GET_VREG OUT_32_ARG0, %rax # eax <- vBB (array object) + GET_VREG OUT_32_ARG1, %rcx # ecx <- vCC (requested index) + EXPORT_PC + call SYMBOL(artAGetObjectFromMterp) # (array, index) + cmpq $0, THREAD_EXCEPTION_OFFSET(rSELF) + jnz MterpException + SET_VREG_OBJECT %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_aget_boolean: /* 0x47 */ +/* File: x86_64/op_aget_boolean.S */ +/* File: x86_64/op_aget.S */ +/* + * Array get, 32 bits or less. vAA <- vBB[vCC]. + * + * for: aget, aget-boolean, aget-byte, aget-char, aget-short, aget-wide + * + */ + /* op vAA, vBB, vCC */ + movzbq 2(rPC), %rax # eax <- BB + movzbq 3(rPC), %rcx # ecx <- CC + GET_VREG %eax, %rax # eax <- vBB (array object) + GET_VREG %ecx, %rcx # ecx <- vCC (requested index) + testl %eax, %eax # null array object? + je common_errNullObject # bail if so + cmpl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ecx + jae common_errArrayIndex # index >= length, bail. + .if 0 + movq MIRROR_BOOLEAN_ARRAY_DATA_OFFSET(%rax,%rcx,8), %rax + SET_WIDE_VREG %rax, rINSTq + .else + movzbl MIRROR_BOOLEAN_ARRAY_DATA_OFFSET(%rax,%rcx,1), %eax + SET_VREG %eax, rINSTq + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_aget_byte: /* 0x48 */ +/* File: x86_64/op_aget_byte.S */ +/* File: x86_64/op_aget.S */ +/* + * Array get, 32 bits or less. vAA <- vBB[vCC]. + * + * for: aget, aget-boolean, aget-byte, aget-char, aget-short, aget-wide + * + */ + /* op vAA, vBB, vCC */ + movzbq 2(rPC), %rax # eax <- BB + movzbq 3(rPC), %rcx # ecx <- CC + GET_VREG %eax, %rax # eax <- vBB (array object) + GET_VREG %ecx, %rcx # ecx <- vCC (requested index) + testl %eax, %eax # null array object? + je common_errNullObject # bail if so + cmpl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ecx + jae common_errArrayIndex # index >= length, bail. + .if 0 + movq MIRROR_BYTE_ARRAY_DATA_OFFSET(%rax,%rcx,8), %rax + SET_WIDE_VREG %rax, rINSTq + .else + movsbl MIRROR_BYTE_ARRAY_DATA_OFFSET(%rax,%rcx,1), %eax + SET_VREG %eax, rINSTq + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_aget_char: /* 0x49 */ +/* File: x86_64/op_aget_char.S */ +/* File: x86_64/op_aget.S */ +/* + * Array get, 32 bits or less. vAA <- vBB[vCC]. + * + * for: aget, aget-boolean, aget-byte, aget-char, aget-short, aget-wide + * + */ + /* op vAA, vBB, vCC */ + movzbq 2(rPC), %rax # eax <- BB + movzbq 3(rPC), %rcx # ecx <- CC + GET_VREG %eax, %rax # eax <- vBB (array object) + GET_VREG %ecx, %rcx # ecx <- vCC (requested index) + testl %eax, %eax # null array object? + je common_errNullObject # bail if so + cmpl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ecx + jae common_errArrayIndex # index >= length, bail. + .if 0 + movq MIRROR_CHAR_ARRAY_DATA_OFFSET(%rax,%rcx,8), %rax + SET_WIDE_VREG %rax, rINSTq + .else + movzwl MIRROR_CHAR_ARRAY_DATA_OFFSET(%rax,%rcx,2), %eax + SET_VREG %eax, rINSTq + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_aget_short: /* 0x4a */ +/* File: x86_64/op_aget_short.S */ +/* File: x86_64/op_aget.S */ +/* + * Array get, 32 bits or less. vAA <- vBB[vCC]. + * + * for: aget, aget-boolean, aget-byte, aget-char, aget-short, aget-wide + * + */ + /* op vAA, vBB, vCC */ + movzbq 2(rPC), %rax # eax <- BB + movzbq 3(rPC), %rcx # ecx <- CC + GET_VREG %eax, %rax # eax <- vBB (array object) + GET_VREG %ecx, %rcx # ecx <- vCC (requested index) + testl %eax, %eax # null array object? + je common_errNullObject # bail if so + cmpl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ecx + jae common_errArrayIndex # index >= length, bail. + .if 0 + movq MIRROR_SHORT_ARRAY_DATA_OFFSET(%rax,%rcx,8), %rax + SET_WIDE_VREG %rax, rINSTq + .else + movswl MIRROR_SHORT_ARRAY_DATA_OFFSET(%rax,%rcx,2), %eax + SET_VREG %eax, rINSTq + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_aput: /* 0x4b */ +/* File: x86_64/op_aput.S */ +/* + * Array put, 32 bits or less. vBB[vCC] <- vAA. + * + * for: aput, aput-boolean, aput-byte, aput-char, aput-short, aput-wide + * + */ + /* op vAA, vBB, vCC */ + movzbq 2(rPC), %rax # rax <- BB + movzbq 3(rPC), %rcx # rcx <- CC + GET_VREG %eax, %rax # eax <- vBB (array object) + GET_VREG %ecx, %rcx # ecx <- vCC (requested index) + testl %eax, %eax # null array object? + je common_errNullObject # bail if so + cmpl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ecx + jae common_errArrayIndex # index >= length, bail. + .if 0 + GET_WIDE_VREG rINSTq, rINSTq + .else + GET_VREG rINST, rINSTq + .endif + movl rINST, MIRROR_INT_ARRAY_DATA_OFFSET(%rax,%rcx,4) + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_aput_wide: /* 0x4c */ +/* File: x86_64/op_aput_wide.S */ +/* File: x86_64/op_aput.S */ +/* + * Array put, 32 bits or less. vBB[vCC] <- vAA. + * + * for: aput, aput-boolean, aput-byte, aput-char, aput-short, aput-wide + * + */ + /* op vAA, vBB, vCC */ + movzbq 2(rPC), %rax # rax <- BB + movzbq 3(rPC), %rcx # rcx <- CC + GET_VREG %eax, %rax # eax <- vBB (array object) + GET_VREG %ecx, %rcx # ecx <- vCC (requested index) + testl %eax, %eax # null array object? + je common_errNullObject # bail if so + cmpl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ecx + jae common_errArrayIndex # index >= length, bail. + .if 1 + GET_WIDE_VREG rINSTq, rINSTq + .else + GET_VREG rINST, rINSTq + .endif + movq rINSTq, MIRROR_WIDE_ARRAY_DATA_OFFSET(%rax,%rcx,8) + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_aput_object: /* 0x4d */ +/* File: x86_64/op_aput_object.S */ +/* + * Store an object into an array. vBB[vCC] <- vAA. + */ + /* op vAA, vBB, vCC */ + EXPORT_PC + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG0 + movq rPC, OUT_ARG1 + REFRESH_INST 77 + movq rINSTq, OUT_ARG2 + call SYMBOL(MterpAputObject) # (array, index) + testb %al, %al + jz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_aput_boolean: /* 0x4e */ +/* File: x86_64/op_aput_boolean.S */ +/* File: x86_64/op_aput.S */ +/* + * Array put, 32 bits or less. vBB[vCC] <- vAA. + * + * for: aput, aput-boolean, aput-byte, aput-char, aput-short, aput-wide + * + */ + /* op vAA, vBB, vCC */ + movzbq 2(rPC), %rax # rax <- BB + movzbq 3(rPC), %rcx # rcx <- CC + GET_VREG %eax, %rax # eax <- vBB (array object) + GET_VREG %ecx, %rcx # ecx <- vCC (requested index) + testl %eax, %eax # null array object? + je common_errNullObject # bail if so + cmpl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ecx + jae common_errArrayIndex # index >= length, bail. + .if 0 + GET_WIDE_VREG rINSTq, rINSTq + .else + GET_VREG rINST, rINSTq + .endif + movb rINSTbl, MIRROR_BOOLEAN_ARRAY_DATA_OFFSET(%rax,%rcx,1) + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_aput_byte: /* 0x4f */ +/* File: x86_64/op_aput_byte.S */ +/* File: x86_64/op_aput.S */ +/* + * Array put, 32 bits or less. vBB[vCC] <- vAA. + * + * for: aput, aput-boolean, aput-byte, aput-char, aput-short, aput-wide + * + */ + /* op vAA, vBB, vCC */ + movzbq 2(rPC), %rax # rax <- BB + movzbq 3(rPC), %rcx # rcx <- CC + GET_VREG %eax, %rax # eax <- vBB (array object) + GET_VREG %ecx, %rcx # ecx <- vCC (requested index) + testl %eax, %eax # null array object? + je common_errNullObject # bail if so + cmpl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ecx + jae common_errArrayIndex # index >= length, bail. + .if 0 + GET_WIDE_VREG rINSTq, rINSTq + .else + GET_VREG rINST, rINSTq + .endif + movb rINSTbl, MIRROR_BYTE_ARRAY_DATA_OFFSET(%rax,%rcx,1) + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_aput_char: /* 0x50 */ +/* File: x86_64/op_aput_char.S */ +/* File: x86_64/op_aput.S */ +/* + * Array put, 32 bits or less. vBB[vCC] <- vAA. + * + * for: aput, aput-boolean, aput-byte, aput-char, aput-short, aput-wide + * + */ + /* op vAA, vBB, vCC */ + movzbq 2(rPC), %rax # rax <- BB + movzbq 3(rPC), %rcx # rcx <- CC + GET_VREG %eax, %rax # eax <- vBB (array object) + GET_VREG %ecx, %rcx # ecx <- vCC (requested index) + testl %eax, %eax # null array object? + je common_errNullObject # bail if so + cmpl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ecx + jae common_errArrayIndex # index >= length, bail. + .if 0 + GET_WIDE_VREG rINSTq, rINSTq + .else + GET_VREG rINST, rINSTq + .endif + movw rINSTw, MIRROR_CHAR_ARRAY_DATA_OFFSET(%rax,%rcx,2) + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_aput_short: /* 0x51 */ +/* File: x86_64/op_aput_short.S */ +/* File: x86_64/op_aput.S */ +/* + * Array put, 32 bits or less. vBB[vCC] <- vAA. + * + * for: aput, aput-boolean, aput-byte, aput-char, aput-short, aput-wide + * + */ + /* op vAA, vBB, vCC */ + movzbq 2(rPC), %rax # rax <- BB + movzbq 3(rPC), %rcx # rcx <- CC + GET_VREG %eax, %rax # eax <- vBB (array object) + GET_VREG %ecx, %rcx # ecx <- vCC (requested index) + testl %eax, %eax # null array object? + je common_errNullObject # bail if so + cmpl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ecx + jae common_errArrayIndex # index >= length, bail. + .if 0 + GET_WIDE_VREG rINSTq, rINSTq + .else + GET_VREG rINST, rINSTq + .endif + movw rINSTw, MIRROR_SHORT_ARRAY_DATA_OFFSET(%rax,%rcx,2) + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iget: /* 0x52 */ +/* File: x86_64/op_iget.S */ +/* + * General instance field get. + * + * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short, iget-wide + */ + EXPORT_PC + movzbq rINSTbl, %rcx # rcx <- BA + movzwl 2(rPC), OUT_32_ARG0 # eax <- field ref CCCC + sarl $4, %ecx # ecx <- B + GET_VREG OUT_32_ARG1, %rcx # the object pointer + movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer + movq rSELF, OUT_ARG3 + call SYMBOL(artGet32InstanceFromCode) + cmpq $0, THREAD_EXCEPTION_OFFSET(rSELF) + jnz MterpException # bail out + andb $0xf, rINSTbl # rINST <- A + .if 0 + SET_VREG_OBJECT %eax, rINSTq # fp[A] <-value + .else + .if 0 + SET_WIDE_VREG %rax, rINSTq # fp[A] <-value + .else + SET_VREG %eax, rINSTq # fp[A] <-value + .endif + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_iget_wide: /* 0x53 */ +/* File: x86_64/op_iget_wide.S */ +/* File: x86_64/op_iget.S */ +/* + * General instance field get. + * + * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short, iget-wide + */ + EXPORT_PC + movzbq rINSTbl, %rcx # rcx <- BA + movzwl 2(rPC), OUT_32_ARG0 # eax <- field ref CCCC + sarl $4, %ecx # ecx <- B + GET_VREG OUT_32_ARG1, %rcx # the object pointer + movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer + movq rSELF, OUT_ARG3 + call SYMBOL(artGet64InstanceFromCode) + cmpq $0, THREAD_EXCEPTION_OFFSET(rSELF) + jnz MterpException # bail out + andb $0xf, rINSTbl # rINST <- A + .if 0 + SET_VREG_OBJECT %eax, rINSTq # fp[A] <-value + .else + .if 1 + SET_WIDE_VREG %rax, rINSTq # fp[A] <-value + .else + SET_VREG %eax, rINSTq # fp[A] <-value + .endif + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_object: /* 0x54 */ +/* File: x86_64/op_iget_object.S */ +/* File: x86_64/op_iget.S */ +/* + * General instance field get. + * + * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short, iget-wide + */ + EXPORT_PC + movzbq rINSTbl, %rcx # rcx <- BA + movzwl 2(rPC), OUT_32_ARG0 # eax <- field ref CCCC + sarl $4, %ecx # ecx <- B + GET_VREG OUT_32_ARG1, %rcx # the object pointer + movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer + movq rSELF, OUT_ARG3 + call SYMBOL(artGetObjInstanceFromCode) + cmpq $0, THREAD_EXCEPTION_OFFSET(rSELF) + jnz MterpException # bail out + andb $0xf, rINSTbl # rINST <- A + .if 1 + SET_VREG_OBJECT %eax, rINSTq # fp[A] <-value + .else + .if 0 + SET_WIDE_VREG %rax, rINSTq # fp[A] <-value + .else + SET_VREG %eax, rINSTq # fp[A] <-value + .endif + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_boolean: /* 0x55 */ +/* File: x86_64/op_iget_boolean.S */ +/* File: x86_64/op_iget.S */ +/* + * General instance field get. + * + * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short, iget-wide + */ + EXPORT_PC + movzbq rINSTbl, %rcx # rcx <- BA + movzwl 2(rPC), OUT_32_ARG0 # eax <- field ref CCCC + sarl $4, %ecx # ecx <- B + GET_VREG OUT_32_ARG1, %rcx # the object pointer + movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer + movq rSELF, OUT_ARG3 + call SYMBOL(artGetBooleanInstanceFromCode) + cmpq $0, THREAD_EXCEPTION_OFFSET(rSELF) + jnz MterpException # bail out + andb $0xf, rINSTbl # rINST <- A + .if 0 + SET_VREG_OBJECT %eax, rINSTq # fp[A] <-value + .else + .if 0 + SET_WIDE_VREG %rax, rINSTq # fp[A] <-value + .else + SET_VREG %eax, rINSTq # fp[A] <-value + .endif + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_byte: /* 0x56 */ +/* File: x86_64/op_iget_byte.S */ +/* File: x86_64/op_iget.S */ +/* + * General instance field get. + * + * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short, iget-wide + */ + EXPORT_PC + movzbq rINSTbl, %rcx # rcx <- BA + movzwl 2(rPC), OUT_32_ARG0 # eax <- field ref CCCC + sarl $4, %ecx # ecx <- B + GET_VREG OUT_32_ARG1, %rcx # the object pointer + movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer + movq rSELF, OUT_ARG3 + call SYMBOL(artGetByteInstanceFromCode) + cmpq $0, THREAD_EXCEPTION_OFFSET(rSELF) + jnz MterpException # bail out + andb $0xf, rINSTbl # rINST <- A + .if 0 + SET_VREG_OBJECT %eax, rINSTq # fp[A] <-value + .else + .if 0 + SET_WIDE_VREG %rax, rINSTq # fp[A] <-value + .else + SET_VREG %eax, rINSTq # fp[A] <-value + .endif + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_char: /* 0x57 */ +/* File: x86_64/op_iget_char.S */ +/* File: x86_64/op_iget.S */ +/* + * General instance field get. + * + * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short, iget-wide + */ + EXPORT_PC + movzbq rINSTbl, %rcx # rcx <- BA + movzwl 2(rPC), OUT_32_ARG0 # eax <- field ref CCCC + sarl $4, %ecx # ecx <- B + GET_VREG OUT_32_ARG1, %rcx # the object pointer + movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer + movq rSELF, OUT_ARG3 + call SYMBOL(artGetCharInstanceFromCode) + cmpq $0, THREAD_EXCEPTION_OFFSET(rSELF) + jnz MterpException # bail out + andb $0xf, rINSTbl # rINST <- A + .if 0 + SET_VREG_OBJECT %eax, rINSTq # fp[A] <-value + .else + .if 0 + SET_WIDE_VREG %rax, rINSTq # fp[A] <-value + .else + SET_VREG %eax, rINSTq # fp[A] <-value + .endif + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_short: /* 0x58 */ +/* File: x86_64/op_iget_short.S */ +/* File: x86_64/op_iget.S */ +/* + * General instance field get. + * + * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short, iget-wide + */ + EXPORT_PC + movzbq rINSTbl, %rcx # rcx <- BA + movzwl 2(rPC), OUT_32_ARG0 # eax <- field ref CCCC + sarl $4, %ecx # ecx <- B + GET_VREG OUT_32_ARG1, %rcx # the object pointer + movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer + movq rSELF, OUT_ARG3 + call SYMBOL(artGetShortInstanceFromCode) + cmpq $0, THREAD_EXCEPTION_OFFSET(rSELF) + jnz MterpException # bail out + andb $0xf, rINSTbl # rINST <- A + .if 0 + SET_VREG_OBJECT %eax, rINSTq # fp[A] <-value + .else + .if 0 + SET_WIDE_VREG %rax, rINSTq # fp[A] <-value + .else + SET_VREG %eax, rINSTq # fp[A] <-value + .endif + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iput: /* 0x59 */ +/* File: x86_64/op_iput.S */ +/* + * General 32-bit instance field put. + * + * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short + */ + /* op vA, vB, field@CCCC */ + .extern artSet32InstanceFromMterp + EXPORT_PC + movzwl 2(rPC), OUT_32_ARG0 # field ref <- 0000CCCC + movzbq rINSTbl, %rcx # rcx<- BA + sarl $4, %ecx # ecx<- B + GET_VREG OUT_32_ARG1, %rcx # the object pointer + andb $0xf, rINSTbl # rINST<- A + GET_VREG OUT_32_ARG2, rINSTq # fp[A] + movq OFF_FP_METHOD(rFP), OUT_ARG3 # referrer + call SYMBOL(artSet32InstanceFromMterp) + testb %al, %al + jnz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_iput_wide: /* 0x5a */ +/* File: x86_64/op_iput_wide.S */ + /* iput-wide vA, vB, field@CCCC */ + .extern artSet64InstanceFromMterp + EXPORT_PC + movzwq 2(rPC), OUT_ARG0 # field ref CCCC + movzbq rINSTbl, %rcx # rcx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG OUT_32_ARG1, %rcx # the object pointer + andb $0xf, rINSTbl # rINST <- A + leaq VREG_ADDRESS(rINSTq), OUT_ARG2 # &fp[A] + movq OFF_FP_METHOD(rFP), OUT_ARG3 # referrer + call SYMBOL(artSet64InstanceFromMterp) + testb %al, %al + jnz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_iput_object: /* 0x5b */ +/* File: x86_64/op_iput_object.S */ + EXPORT_PC + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG0 + movq rPC, OUT_ARG1 + REFRESH_INST 91 + movl rINST, OUT_32_ARG2 + movq rSELF, OUT_ARG3 + call SYMBOL(MterpIputObject) + testb %al, %al + jz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_iput_boolean: /* 0x5c */ +/* File: x86_64/op_iput_boolean.S */ +/* File: x86_64/op_iput.S */ +/* + * General 32-bit instance field put. + * + * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short + */ + /* op vA, vB, field@CCCC */ + .extern artSet8InstanceFromMterp + EXPORT_PC + movzwl 2(rPC), OUT_32_ARG0 # field ref <- 0000CCCC + movzbq rINSTbl, %rcx # rcx<- BA + sarl $4, %ecx # ecx<- B + GET_VREG OUT_32_ARG1, %rcx # the object pointer + andb $0xf, rINSTbl # rINST<- A + GET_VREG OUT_32_ARG2, rINSTq # fp[A] + movq OFF_FP_METHOD(rFP), OUT_ARG3 # referrer + call SYMBOL(artSet8InstanceFromMterp) + testb %al, %al + jnz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iput_byte: /* 0x5d */ +/* File: x86_64/op_iput_byte.S */ +/* File: x86_64/op_iput.S */ +/* + * General 32-bit instance field put. + * + * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short + */ + /* op vA, vB, field@CCCC */ + .extern artSet8InstanceFromMterp + EXPORT_PC + movzwl 2(rPC), OUT_32_ARG0 # field ref <- 0000CCCC + movzbq rINSTbl, %rcx # rcx<- BA + sarl $4, %ecx # ecx<- B + GET_VREG OUT_32_ARG1, %rcx # the object pointer + andb $0xf, rINSTbl # rINST<- A + GET_VREG OUT_32_ARG2, rINSTq # fp[A] + movq OFF_FP_METHOD(rFP), OUT_ARG3 # referrer + call SYMBOL(artSet8InstanceFromMterp) + testb %al, %al + jnz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iput_char: /* 0x5e */ +/* File: x86_64/op_iput_char.S */ +/* File: x86_64/op_iput.S */ +/* + * General 32-bit instance field put. + * + * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short + */ + /* op vA, vB, field@CCCC */ + .extern artSet16InstanceFromMterp + EXPORT_PC + movzwl 2(rPC), OUT_32_ARG0 # field ref <- 0000CCCC + movzbq rINSTbl, %rcx # rcx<- BA + sarl $4, %ecx # ecx<- B + GET_VREG OUT_32_ARG1, %rcx # the object pointer + andb $0xf, rINSTbl # rINST<- A + GET_VREG OUT_32_ARG2, rINSTq # fp[A] + movq OFF_FP_METHOD(rFP), OUT_ARG3 # referrer + call SYMBOL(artSet16InstanceFromMterp) + testb %al, %al + jnz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iput_short: /* 0x5f */ +/* File: x86_64/op_iput_short.S */ +/* File: x86_64/op_iput.S */ +/* + * General 32-bit instance field put. + * + * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short + */ + /* op vA, vB, field@CCCC */ + .extern artSet16InstanceFromMterp + EXPORT_PC + movzwl 2(rPC), OUT_32_ARG0 # field ref <- 0000CCCC + movzbq rINSTbl, %rcx # rcx<- BA + sarl $4, %ecx # ecx<- B + GET_VREG OUT_32_ARG1, %rcx # the object pointer + andb $0xf, rINSTbl # rINST<- A + GET_VREG OUT_32_ARG2, rINSTq # fp[A] + movq OFF_FP_METHOD(rFP), OUT_ARG3 # referrer + call SYMBOL(artSet16InstanceFromMterp) + testb %al, %al + jnz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_sget: /* 0x60 */ +/* File: x86_64/op_sget.S */ +/* + * General SGET handler wrapper. + * + * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short, sget-wide + */ + /* op vAA, field@BBBB */ + .extern artGet32StaticFromCode + EXPORT_PC + movzwq 2(rPC), OUT_ARG0 # field ref CCCC + movq OFF_FP_METHOD(rFP), OUT_ARG1 # referrer + movq rSELF, OUT_ARG2 # self + call SYMBOL(artGet32StaticFromCode) + cmpl $0, THREAD_EXCEPTION_OFFSET(rSELF) + jnz MterpException + .if 0 + SET_VREG_OBJECT %eax, rINSTq # fp[A] <- value + .else + .if 0 + SET_WIDE_VREG %rax, rINSTq # fp[A] <- value + .else + SET_VREG %eax, rINSTq # fp[A] <- value + .endif + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_sget_wide: /* 0x61 */ +/* File: x86_64/op_sget_wide.S */ +/* File: x86_64/op_sget.S */ +/* + * General SGET handler wrapper. + * + * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short, sget-wide + */ + /* op vAA, field@BBBB */ + .extern artGet64StaticFromCode + EXPORT_PC + movzwq 2(rPC), OUT_ARG0 # field ref CCCC + movq OFF_FP_METHOD(rFP), OUT_ARG1 # referrer + movq rSELF, OUT_ARG2 # self + call SYMBOL(artGet64StaticFromCode) + cmpl $0, THREAD_EXCEPTION_OFFSET(rSELF) + jnz MterpException + .if 0 + SET_VREG_OBJECT %eax, rINSTq # fp[A] <- value + .else + .if 1 + SET_WIDE_VREG %rax, rINSTq # fp[A] <- value + .else + SET_VREG %eax, rINSTq # fp[A] <- value + .endif + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_sget_object: /* 0x62 */ +/* File: x86_64/op_sget_object.S */ +/* File: x86_64/op_sget.S */ +/* + * General SGET handler wrapper. + * + * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short, sget-wide + */ + /* op vAA, field@BBBB */ + .extern artGetObjStaticFromCode + EXPORT_PC + movzwq 2(rPC), OUT_ARG0 # field ref CCCC + movq OFF_FP_METHOD(rFP), OUT_ARG1 # referrer + movq rSELF, OUT_ARG2 # self + call SYMBOL(artGetObjStaticFromCode) + cmpl $0, THREAD_EXCEPTION_OFFSET(rSELF) + jnz MterpException + .if 1 + SET_VREG_OBJECT %eax, rINSTq # fp[A] <- value + .else + .if 0 + SET_WIDE_VREG %rax, rINSTq # fp[A] <- value + .else + SET_VREG %eax, rINSTq # fp[A] <- value + .endif + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_sget_boolean: /* 0x63 */ +/* File: x86_64/op_sget_boolean.S */ +/* File: x86_64/op_sget.S */ +/* + * General SGET handler wrapper. + * + * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short, sget-wide + */ + /* op vAA, field@BBBB */ + .extern artGetBooleanStaticFromCode + EXPORT_PC + movzwq 2(rPC), OUT_ARG0 # field ref CCCC + movq OFF_FP_METHOD(rFP), OUT_ARG1 # referrer + movq rSELF, OUT_ARG2 # self + call SYMBOL(artGetBooleanStaticFromCode) + cmpl $0, THREAD_EXCEPTION_OFFSET(rSELF) + jnz MterpException + .if 0 + SET_VREG_OBJECT %eax, rINSTq # fp[A] <- value + .else + .if 0 + SET_WIDE_VREG %rax, rINSTq # fp[A] <- value + .else + SET_VREG %eax, rINSTq # fp[A] <- value + .endif + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_sget_byte: /* 0x64 */ +/* File: x86_64/op_sget_byte.S */ +/* File: x86_64/op_sget.S */ +/* + * General SGET handler wrapper. + * + * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short, sget-wide + */ + /* op vAA, field@BBBB */ + .extern artGetByteStaticFromCode + EXPORT_PC + movzwq 2(rPC), OUT_ARG0 # field ref CCCC + movq OFF_FP_METHOD(rFP), OUT_ARG1 # referrer + movq rSELF, OUT_ARG2 # self + call SYMBOL(artGetByteStaticFromCode) + cmpl $0, THREAD_EXCEPTION_OFFSET(rSELF) + jnz MterpException + .if 0 + SET_VREG_OBJECT %eax, rINSTq # fp[A] <- value + .else + .if 0 + SET_WIDE_VREG %rax, rINSTq # fp[A] <- value + .else + SET_VREG %eax, rINSTq # fp[A] <- value + .endif + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_sget_char: /* 0x65 */ +/* File: x86_64/op_sget_char.S */ +/* File: x86_64/op_sget.S */ +/* + * General SGET handler wrapper. + * + * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short, sget-wide + */ + /* op vAA, field@BBBB */ + .extern artGetCharStaticFromCode + EXPORT_PC + movzwq 2(rPC), OUT_ARG0 # field ref CCCC + movq OFF_FP_METHOD(rFP), OUT_ARG1 # referrer + movq rSELF, OUT_ARG2 # self + call SYMBOL(artGetCharStaticFromCode) + cmpl $0, THREAD_EXCEPTION_OFFSET(rSELF) + jnz MterpException + .if 0 + SET_VREG_OBJECT %eax, rINSTq # fp[A] <- value + .else + .if 0 + SET_WIDE_VREG %rax, rINSTq # fp[A] <- value + .else + SET_VREG %eax, rINSTq # fp[A] <- value + .endif + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_sget_short: /* 0x66 */ +/* File: x86_64/op_sget_short.S */ +/* File: x86_64/op_sget.S */ +/* + * General SGET handler wrapper. + * + * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short, sget-wide + */ + /* op vAA, field@BBBB */ + .extern artGetShortStaticFromCode + EXPORT_PC + movzwq 2(rPC), OUT_ARG0 # field ref CCCC + movq OFF_FP_METHOD(rFP), OUT_ARG1 # referrer + movq rSELF, OUT_ARG2 # self + call SYMBOL(artGetShortStaticFromCode) + cmpl $0, THREAD_EXCEPTION_OFFSET(rSELF) + jnz MterpException + .if 0 + SET_VREG_OBJECT %eax, rINSTq # fp[A] <- value + .else + .if 0 + SET_WIDE_VREG %rax, rINSTq # fp[A] <- value + .else + SET_VREG %eax, rINSTq # fp[A] <- value + .endif + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_sput: /* 0x67 */ +/* File: x86_64/op_sput.S */ +/* + * General SPUT handler wrapper. + * + * for: sput, sput-boolean, sput-byte, sput-char, sput-short + */ + /* op vAA, field@BBBB */ + .extern artSet32StaticFromCode + EXPORT_PC + movzwq 2(rPC), OUT_ARG0 # field ref BBBB + GET_VREG OUT_32_ARG1, rINSTq # fp[AA] + movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer + movq rSELF, OUT_ARG3 # self + call SYMBOL(artSet32StaticFromCode) + testb %al, %al + jnz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_sput_wide: /* 0x68 */ +/* File: x86_64/op_sput_wide.S */ +/* + * SPUT_WIDE handler wrapper. + * + */ + /* sput-wide vAA, field@BBBB */ + .extern artSet64IndirectStaticFromMterp + EXPORT_PC + movzwq 2(rPC), OUT_ARG0 # field ref BBBB + movq OFF_FP_METHOD(rFP), OUT_ARG1 # referrer + leaq VREG_ADDRESS(rINSTq), OUT_ARG2 # &fp[AA] + movq rSELF, OUT_ARG3 # self + call SYMBOL(artSet64IndirectStaticFromMterp) + testb %al, %al + jnz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_sput_object: /* 0x69 */ +/* File: x86_64/op_sput_object.S */ + EXPORT_PC + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG0 + movq rPC, OUT_ARG1 + REFRESH_INST 105 + movq rINSTq, OUT_ARG2 + movq rSELF, OUT_ARG3 + call SYMBOL(MterpSputObject) + testb %al, %al + jz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_sput_boolean: /* 0x6a */ +/* File: x86_64/op_sput_boolean.S */ +/* File: x86_64/op_sput.S */ +/* + * General SPUT handler wrapper. + * + * for: sput, sput-boolean, sput-byte, sput-char, sput-short + */ + /* op vAA, field@BBBB */ + .extern artSet8StaticFromCode + EXPORT_PC + movzwq 2(rPC), OUT_ARG0 # field ref BBBB + GET_VREG OUT_32_ARG1, rINSTq # fp[AA] + movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer + movq rSELF, OUT_ARG3 # self + call SYMBOL(artSet8StaticFromCode) + testb %al, %al + jnz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_sput_byte: /* 0x6b */ +/* File: x86_64/op_sput_byte.S */ +/* File: x86_64/op_sput.S */ +/* + * General SPUT handler wrapper. + * + * for: sput, sput-boolean, sput-byte, sput-char, sput-short + */ + /* op vAA, field@BBBB */ + .extern artSet8StaticFromCode + EXPORT_PC + movzwq 2(rPC), OUT_ARG0 # field ref BBBB + GET_VREG OUT_32_ARG1, rINSTq # fp[AA] + movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer + movq rSELF, OUT_ARG3 # self + call SYMBOL(artSet8StaticFromCode) + testb %al, %al + jnz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_sput_char: /* 0x6c */ +/* File: x86_64/op_sput_char.S */ +/* File: x86_64/op_sput.S */ +/* + * General SPUT handler wrapper. + * + * for: sput, sput-boolean, sput-byte, sput-char, sput-short + */ + /* op vAA, field@BBBB */ + .extern artSet16StaticFromCode + EXPORT_PC + movzwq 2(rPC), OUT_ARG0 # field ref BBBB + GET_VREG OUT_32_ARG1, rINSTq # fp[AA] + movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer + movq rSELF, OUT_ARG3 # self + call SYMBOL(artSet16StaticFromCode) + testb %al, %al + jnz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_sput_short: /* 0x6d */ +/* File: x86_64/op_sput_short.S */ +/* File: x86_64/op_sput.S */ +/* + * General SPUT handler wrapper. + * + * for: sput, sput-boolean, sput-byte, sput-char, sput-short + */ + /* op vAA, field@BBBB */ + .extern artSet16StaticFromCode + EXPORT_PC + movzwq 2(rPC), OUT_ARG0 # field ref BBBB + GET_VREG OUT_32_ARG1, rINSTq # fp[AA] + movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer + movq rSELF, OUT_ARG3 # self + call SYMBOL(artSet16StaticFromCode) + testb %al, %al + jnz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_virtual: /* 0x6e */ +/* File: x86_64/op_invoke_virtual.S */ +/* File: x86_64/invoke.S */ +/* + * Generic invoke handler wrapper. + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + .extern MterpInvokeVirtual + EXPORT_PC + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + movq rPC, OUT_ARG2 + REFRESH_INST 110 + movl rINST, OUT_32_ARG3 + call SYMBOL(MterpInvokeVirtual) + testb %al, %al + jz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + +/* + * Handle a virtual method call. + * + * for: invoke-virtual, invoke-virtual/range + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_super: /* 0x6f */ +/* File: x86_64/op_invoke_super.S */ +/* File: x86_64/invoke.S */ +/* + * Generic invoke handler wrapper. + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + .extern MterpInvokeSuper + EXPORT_PC + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + movq rPC, OUT_ARG2 + REFRESH_INST 111 + movl rINST, OUT_32_ARG3 + call SYMBOL(MterpInvokeSuper) + testb %al, %al + jz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + +/* + * Handle a "super" method call. + * + * for: invoke-super, invoke-super/range + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_direct: /* 0x70 */ +/* File: x86_64/op_invoke_direct.S */ +/* File: x86_64/invoke.S */ +/* + * Generic invoke handler wrapper. + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + .extern MterpInvokeDirect + EXPORT_PC + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + movq rPC, OUT_ARG2 + REFRESH_INST 112 + movl rINST, OUT_32_ARG3 + call SYMBOL(MterpInvokeDirect) + testb %al, %al + jz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_static: /* 0x71 */ +/* File: x86_64/op_invoke_static.S */ +/* File: x86_64/invoke.S */ +/* + * Generic invoke handler wrapper. + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + .extern MterpInvokeStatic + EXPORT_PC + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + movq rPC, OUT_ARG2 + REFRESH_INST 113 + movl rINST, OUT_32_ARG3 + call SYMBOL(MterpInvokeStatic) + testb %al, %al + jz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_interface: /* 0x72 */ +/* File: x86_64/op_invoke_interface.S */ +/* File: x86_64/invoke.S */ +/* + * Generic invoke handler wrapper. + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + .extern MterpInvokeInterface + EXPORT_PC + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + movq rPC, OUT_ARG2 + REFRESH_INST 114 + movl rINST, OUT_32_ARG3 + call SYMBOL(MterpInvokeInterface) + testb %al, %al + jz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + +/* + * Handle an interface method call. + * + * for: invoke-interface, invoke-interface/range + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + +/* ------------------------------ */ + .balign 128 +.L_op_return_void_no_barrier: /* 0x73 */ +/* File: x86_64/op_return_void_no_barrier.S */ + testl $(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(rSELF) + jz 1f + movq rSELF, OUT_ARG0 + call SYMBOL(MterpSuspendCheck) +1: + xorq %rax, %rax + jmp MterpReturn + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_virtual_range: /* 0x74 */ +/* File: x86_64/op_invoke_virtual_range.S */ +/* File: x86_64/invoke.S */ +/* + * Generic invoke handler wrapper. + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + .extern MterpInvokeVirtualRange + EXPORT_PC + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + movq rPC, OUT_ARG2 + REFRESH_INST 116 + movl rINST, OUT_32_ARG3 + call SYMBOL(MterpInvokeVirtualRange) + testb %al, %al + jz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_super_range: /* 0x75 */ +/* File: x86_64/op_invoke_super_range.S */ +/* File: x86_64/invoke.S */ +/* + * Generic invoke handler wrapper. + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + .extern MterpInvokeSuperRange + EXPORT_PC + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + movq rPC, OUT_ARG2 + REFRESH_INST 117 + movl rINST, OUT_32_ARG3 + call SYMBOL(MterpInvokeSuperRange) + testb %al, %al + jz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_direct_range: /* 0x76 */ +/* File: x86_64/op_invoke_direct_range.S */ +/* File: x86_64/invoke.S */ +/* + * Generic invoke handler wrapper. + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + .extern MterpInvokeDirectRange + EXPORT_PC + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + movq rPC, OUT_ARG2 + REFRESH_INST 118 + movl rINST, OUT_32_ARG3 + call SYMBOL(MterpInvokeDirectRange) + testb %al, %al + jz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_static_range: /* 0x77 */ +/* File: x86_64/op_invoke_static_range.S */ +/* File: x86_64/invoke.S */ +/* + * Generic invoke handler wrapper. + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + .extern MterpInvokeStaticRange + EXPORT_PC + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + movq rPC, OUT_ARG2 + REFRESH_INST 119 + movl rINST, OUT_32_ARG3 + call SYMBOL(MterpInvokeStaticRange) + testb %al, %al + jz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_interface_range: /* 0x78 */ +/* File: x86_64/op_invoke_interface_range.S */ +/* File: x86_64/invoke.S */ +/* + * Generic invoke handler wrapper. + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + .extern MterpInvokeInterfaceRange + EXPORT_PC + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + movq rPC, OUT_ARG2 + REFRESH_INST 120 + movl rINST, OUT_32_ARG3 + call SYMBOL(MterpInvokeInterfaceRange) + testb %al, %al + jz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_79: /* 0x79 */ +/* File: x86_64/op_unused_79.S */ +/* File: x86_64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + jmp MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_7a: /* 0x7a */ +/* File: x86_64/op_unused_7a.S */ +/* File: x86_64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + jmp MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_neg_int: /* 0x7b */ +/* File: x86_64/op_neg_int.S */ +/* File: x86_64/unop.S */ +/* + * Generic 32/64-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "result = op eax". + */ + /* unop vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4,rINST # rINST <- B + .if 0 + GET_WIDE_VREG %rax, rINSTq # rax <- vB + .else + GET_VREG %eax, rINSTq # eax <- vB + .endif + andb $0xf,%cl # ecx <- A + + negl %eax + .if 0 + SET_WIDE_VREG %rax, %rcx + .else + SET_VREG %eax, %rcx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_not_int: /* 0x7c */ +/* File: x86_64/op_not_int.S */ +/* File: x86_64/unop.S */ +/* + * Generic 32/64-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "result = op eax". + */ + /* unop vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4,rINST # rINST <- B + .if 0 + GET_WIDE_VREG %rax, rINSTq # rax <- vB + .else + GET_VREG %eax, rINSTq # eax <- vB + .endif + andb $0xf,%cl # ecx <- A + + notl %eax + .if 0 + SET_WIDE_VREG %rax, %rcx + .else + SET_VREG %eax, %rcx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_neg_long: /* 0x7d */ +/* File: x86_64/op_neg_long.S */ +/* File: x86_64/unop.S */ +/* + * Generic 32/64-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "result = op eax". + */ + /* unop vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4,rINST # rINST <- B + .if 1 + GET_WIDE_VREG %rax, rINSTq # rax <- vB + .else + GET_VREG %eax, rINSTq # eax <- vB + .endif + andb $0xf,%cl # ecx <- A + + negq %rax + .if 1 + SET_WIDE_VREG %rax, %rcx + .else + SET_VREG %eax, %rcx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_not_long: /* 0x7e */ +/* File: x86_64/op_not_long.S */ +/* File: x86_64/unop.S */ +/* + * Generic 32/64-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "result = op eax". + */ + /* unop vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4,rINST # rINST <- B + .if 1 + GET_WIDE_VREG %rax, rINSTq # rax <- vB + .else + GET_VREG %eax, rINSTq # eax <- vB + .endif + andb $0xf,%cl # ecx <- A + + notq %rax + .if 1 + SET_WIDE_VREG %rax, %rcx + .else + SET_VREG %eax, %rcx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_neg_float: /* 0x7f */ +/* File: x86_64/op_neg_float.S */ +/* File: x86_64/unop.S */ +/* + * Generic 32/64-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "result = op eax". + */ + /* unop vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4,rINST # rINST <- B + .if 0 + GET_WIDE_VREG %rax, rINSTq # rax <- vB + .else + GET_VREG %eax, rINSTq # eax <- vB + .endif + andb $0xf,%cl # ecx <- A + + xorl $0x80000000, %eax + .if 0 + SET_WIDE_VREG %rax, %rcx + .else + SET_VREG %eax, %rcx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_neg_double: /* 0x80 */ +/* File: x86_64/op_neg_double.S */ +/* File: x86_64/unop.S */ +/* + * Generic 32/64-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "result = op eax". + */ + /* unop vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4,rINST # rINST <- B + .if 1 + GET_WIDE_VREG %rax, rINSTq # rax <- vB + .else + GET_VREG %eax, rINSTq # eax <- vB + .endif + andb $0xf,%cl # ecx <- A + movq $0x8000000000000000, %rsi + xorq %rsi, %rax + .if 1 + SET_WIDE_VREG %rax, %rcx + .else + SET_VREG %eax, %rcx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_int_to_long: /* 0x81 */ +/* File: x86_64/op_int_to_long.S */ + /* int to long vA, vB */ + movzbq rINSTbl, %rax # rax <- +A + sarl $4, %eax # eax <- B + andb $0xf, rINSTbl # rINST <- A + movslq VREG_ADDRESS(%rax), %rax + SET_WIDE_VREG %rax, rINSTq # v[A] <- %rax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_int_to_float: /* 0x82 */ +/* File: x86_64/op_int_to_float.S */ +/* File: x86_64/fpcvt.S */ +/* + * Generic 32-bit FP conversion operation. + */ + /* unop vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # ecx <- A + cvtsi2ssl VREG_ADDRESS(rINSTq), %xmm0 + .if 0 + movsd %xmm0, VREG_ADDRESS(%rcx) + CLEAR_WIDE_REF %rcx + .else + movss %xmm0, VREG_ADDRESS(%rcx) + CLEAR_REF %rcx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_int_to_double: /* 0x83 */ +/* File: x86_64/op_int_to_double.S */ +/* File: x86_64/fpcvt.S */ +/* + * Generic 32-bit FP conversion operation. + */ + /* unop vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # ecx <- A + cvtsi2sdl VREG_ADDRESS(rINSTq), %xmm0 + .if 1 + movsd %xmm0, VREG_ADDRESS(%rcx) + CLEAR_WIDE_REF %rcx + .else + movss %xmm0, VREG_ADDRESS(%rcx) + CLEAR_REF %rcx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_long_to_int: /* 0x84 */ +/* File: x86_64/op_long_to_int.S */ +/* we ignore the high word, making this equivalent to a 32-bit reg move */ +/* File: x86_64/op_move.S */ + /* for move, move-object, long-to-int */ + /* op vA, vB */ + movl rINST, %eax # eax <- BA + andb $0xf, %al # eax <- A + shrl $4, rINST # rINST <- B + GET_VREG %edx, rINSTq + .if 0 + SET_VREG_OBJECT %edx, %rax # fp[A] <- fp[B] + .else + SET_VREG %edx, %rax # fp[A] <- fp[B] + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_long_to_float: /* 0x85 */ +/* File: x86_64/op_long_to_float.S */ +/* File: x86_64/fpcvt.S */ +/* + * Generic 32-bit FP conversion operation. + */ + /* unop vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # ecx <- A + cvtsi2ssq VREG_ADDRESS(rINSTq), %xmm0 + .if 0 + movsd %xmm0, VREG_ADDRESS(%rcx) + CLEAR_WIDE_REF %rcx + .else + movss %xmm0, VREG_ADDRESS(%rcx) + CLEAR_REF %rcx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_long_to_double: /* 0x86 */ +/* File: x86_64/op_long_to_double.S */ +/* File: x86_64/fpcvt.S */ +/* + * Generic 32-bit FP conversion operation. + */ + /* unop vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # ecx <- A + cvtsi2sdq VREG_ADDRESS(rINSTq), %xmm0 + .if 1 + movsd %xmm0, VREG_ADDRESS(%rcx) + CLEAR_WIDE_REF %rcx + .else + movss %xmm0, VREG_ADDRESS(%rcx) + CLEAR_REF %rcx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_float_to_int: /* 0x87 */ +/* File: x86_64/op_float_to_int.S */ +/* File: x86_64/cvtfp_int.S */ +/* On fp to int conversions, Java requires that + * if the result > maxint, it should be clamped to maxint. If it is less + * than minint, it should be clamped to minint. If it is a nan, the result + * should be zero. Further, the rounding mode is to truncate. + */ + /* float/double to int/long vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # ecx <- A + movss VREG_ADDRESS(rINSTq), %xmm0 + movl $0x7fffffff, %eax + cvtsi2ssl %eax, %xmm1 + comiss %xmm1, %xmm0 + jae 1f + jp 2f + cvttss2sil %xmm0, %eax + jmp 1f +2: + xorl %eax, %eax +1: + .if 0 + SET_WIDE_VREG %eax, %rcx + .else + SET_VREG %eax, %rcx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_float_to_long: /* 0x88 */ +/* File: x86_64/op_float_to_long.S */ +/* File: x86_64/cvtfp_int.S */ +/* On fp to int conversions, Java requires that + * if the result > maxint, it should be clamped to maxint. If it is less + * than minint, it should be clamped to minint. If it is a nan, the result + * should be zero. Further, the rounding mode is to truncate. + */ + /* float/double to int/long vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # ecx <- A + movss VREG_ADDRESS(rINSTq), %xmm0 + movq $0x7fffffffffffffff, %rax + cvtsi2ssq %rax, %xmm1 + comiss %xmm1, %xmm0 + jae 1f + jp 2f + cvttss2siq %xmm0, %rax + jmp 1f +2: + xorq %rax, %rax +1: + .if 1 + SET_WIDE_VREG %rax, %rcx + .else + SET_VREG %rax, %rcx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_float_to_double: /* 0x89 */ +/* File: x86_64/op_float_to_double.S */ +/* File: x86_64/fpcvt.S */ +/* + * Generic 32-bit FP conversion operation. + */ + /* unop vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # ecx <- A + cvtss2sd VREG_ADDRESS(rINSTq), %xmm0 + .if 1 + movsd %xmm0, VREG_ADDRESS(%rcx) + CLEAR_WIDE_REF %rcx + .else + movss %xmm0, VREG_ADDRESS(%rcx) + CLEAR_REF %rcx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_double_to_int: /* 0x8a */ +/* File: x86_64/op_double_to_int.S */ +/* File: x86_64/cvtfp_int.S */ +/* On fp to int conversions, Java requires that + * if the result > maxint, it should be clamped to maxint. If it is less + * than minint, it should be clamped to minint. If it is a nan, the result + * should be zero. Further, the rounding mode is to truncate. + */ + /* float/double to int/long vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # ecx <- A + movsd VREG_ADDRESS(rINSTq), %xmm0 + movl $0x7fffffff, %eax + cvtsi2sdl %eax, %xmm1 + comisd %xmm1, %xmm0 + jae 1f + jp 2f + cvttsd2sil %xmm0, %eax + jmp 1f +2: + xorl %eax, %eax +1: + .if 0 + SET_WIDE_VREG %eax, %rcx + .else + SET_VREG %eax, %rcx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_double_to_long: /* 0x8b */ +/* File: x86_64/op_double_to_long.S */ +/* File: x86_64/cvtfp_int.S */ +/* On fp to int conversions, Java requires that + * if the result > maxint, it should be clamped to maxint. If it is less + * than minint, it should be clamped to minint. If it is a nan, the result + * should be zero. Further, the rounding mode is to truncate. + */ + /* float/double to int/long vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # ecx <- A + movsd VREG_ADDRESS(rINSTq), %xmm0 + movq $0x7fffffffffffffff, %rax + cvtsi2sdq %rax, %xmm1 + comisd %xmm1, %xmm0 + jae 1f + jp 2f + cvttsd2siq %xmm0, %rax + jmp 1f +2: + xorq %rax, %rax +1: + .if 1 + SET_WIDE_VREG %rax, %rcx + .else + SET_VREG %rax, %rcx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_double_to_float: /* 0x8c */ +/* File: x86_64/op_double_to_float.S */ +/* File: x86_64/fpcvt.S */ +/* + * Generic 32-bit FP conversion operation. + */ + /* unop vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # ecx <- A + cvtsd2ss VREG_ADDRESS(rINSTq), %xmm0 + .if 0 + movsd %xmm0, VREG_ADDRESS(%rcx) + CLEAR_WIDE_REF %rcx + .else + movss %xmm0, VREG_ADDRESS(%rcx) + CLEAR_REF %rcx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_int_to_byte: /* 0x8d */ +/* File: x86_64/op_int_to_byte.S */ +/* File: x86_64/unop.S */ +/* + * Generic 32/64-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "result = op eax". + */ + /* unop vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4,rINST # rINST <- B + .if 0 + GET_WIDE_VREG %rax, rINSTq # rax <- vB + .else + GET_VREG %eax, rINSTq # eax <- vB + .endif + andb $0xf,%cl # ecx <- A + +movsbl %al, %eax + .if 0 + SET_WIDE_VREG %rax, %rcx + .else + SET_VREG %eax, %rcx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_int_to_char: /* 0x8e */ +/* File: x86_64/op_int_to_char.S */ +/* File: x86_64/unop.S */ +/* + * Generic 32/64-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "result = op eax". + */ + /* unop vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4,rINST # rINST <- B + .if 0 + GET_WIDE_VREG %rax, rINSTq # rax <- vB + .else + GET_VREG %eax, rINSTq # eax <- vB + .endif + andb $0xf,%cl # ecx <- A + +movzwl %ax,%eax + .if 0 + SET_WIDE_VREG %rax, %rcx + .else + SET_VREG %eax, %rcx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_int_to_short: /* 0x8f */ +/* File: x86_64/op_int_to_short.S */ +/* File: x86_64/unop.S */ +/* + * Generic 32/64-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "result = op eax". + */ + /* unop vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4,rINST # rINST <- B + .if 0 + GET_WIDE_VREG %rax, rINSTq # rax <- vB + .else + GET_VREG %eax, rINSTq # eax <- vB + .endif + andb $0xf,%cl # ecx <- A + +movswl %ax, %eax + .if 0 + SET_WIDE_VREG %rax, %rcx + .else + SET_VREG %eax, %rcx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_add_int: /* 0x90 */ +/* File: x86_64/op_add_int.S */ +/* File: x86_64/binop.S */ +/* + * Generic 32-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = eax op (rFP,%ecx,4)". + * This could be an x86 instruction or a function call. (If the result + * comes back in a register other than eax, you can override "result".) + * + * For: add-int, sub-int, and-int, or-int, + * xor-int, shl-int, shr-int, ushr-int + */ + /* binop vAA, vBB, vCC */ + movzbq 2(rPC), %rax # rax <- BB + movzbq 3(rPC), %rcx # rcx <- CC + GET_VREG %eax, %rax # eax <- vBB + addl (rFP,%rcx,4), %eax # ex: addl (rFP,%rcx,4),%eax + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_sub_int: /* 0x91 */ +/* File: x86_64/op_sub_int.S */ +/* File: x86_64/binop.S */ +/* + * Generic 32-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = eax op (rFP,%ecx,4)". + * This could be an x86 instruction or a function call. (If the result + * comes back in a register other than eax, you can override "result".) + * + * For: add-int, sub-int, and-int, or-int, + * xor-int, shl-int, shr-int, ushr-int + */ + /* binop vAA, vBB, vCC */ + movzbq 2(rPC), %rax # rax <- BB + movzbq 3(rPC), %rcx # rcx <- CC + GET_VREG %eax, %rax # eax <- vBB + subl (rFP,%rcx,4), %eax # ex: addl (rFP,%rcx,4),%eax + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_int: /* 0x92 */ +/* File: x86_64/op_mul_int.S */ +/* File: x86_64/binop.S */ +/* + * Generic 32-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = eax op (rFP,%ecx,4)". + * This could be an x86 instruction or a function call. (If the result + * comes back in a register other than eax, you can override "result".) + * + * For: add-int, sub-int, and-int, or-int, + * xor-int, shl-int, shr-int, ushr-int + */ + /* binop vAA, vBB, vCC */ + movzbq 2(rPC), %rax # rax <- BB + movzbq 3(rPC), %rcx # rcx <- CC + GET_VREG %eax, %rax # eax <- vBB + imull (rFP,%rcx,4), %eax # ex: addl (rFP,%rcx,4),%eax + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_div_int: /* 0x93 */ +/* File: x86_64/op_div_int.S */ +/* File: x86_64/bindiv.S */ +/* + * 32-bit binary div/rem operation. Handles special case of op1=-1. + */ + /* div/rem vAA, vBB, vCC */ + movzbq 2(rPC), %rax # rax <- BB + movzbq 3(rPC), %rcx # rcx <- CC + .if 0 + GET_WIDE_VREG %rax, %rax # eax <- vBB + GET_WIDE_VREG %ecx, %rcx # ecx <- vCC + .else + GET_VREG %eax, %rax # eax <- vBB + GET_VREG %ecx, %rcx # ecx <- vCC + .endif + testl %ecx, %ecx + jz common_errDivideByZero + cmpl $-1, %ecx + je 2f + cdq # rdx:rax <- sign-extended of rax + idivl %ecx +1: + .if 0 + SET_WIDE_VREG %eax, rINSTq # eax <- vBB + .else + SET_VREG %eax, rINSTq # eax <- vBB + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 +2: + .if 0 + xorl %eax, %eax + .else + negl %eax + .endif + jmp 1b + + +/* ------------------------------ */ + .balign 128 +.L_op_rem_int: /* 0x94 */ +/* File: x86_64/op_rem_int.S */ +/* File: x86_64/bindiv.S */ +/* + * 32-bit binary div/rem operation. Handles special case of op1=-1. + */ + /* div/rem vAA, vBB, vCC */ + movzbq 2(rPC), %rax # rax <- BB + movzbq 3(rPC), %rcx # rcx <- CC + .if 0 + GET_WIDE_VREG %rax, %rax # eax <- vBB + GET_WIDE_VREG %ecx, %rcx # ecx <- vCC + .else + GET_VREG %eax, %rax # eax <- vBB + GET_VREG %ecx, %rcx # ecx <- vCC + .endif + testl %ecx, %ecx + jz common_errDivideByZero + cmpl $-1, %ecx + je 2f + cdq # rdx:rax <- sign-extended of rax + idivl %ecx +1: + .if 0 + SET_WIDE_VREG %edx, rINSTq # eax <- vBB + .else + SET_VREG %edx, rINSTq # eax <- vBB + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 +2: + .if 1 + xorl %edx, %edx + .else + negl %edx + .endif + jmp 1b + + +/* ------------------------------ */ + .balign 128 +.L_op_and_int: /* 0x95 */ +/* File: x86_64/op_and_int.S */ +/* File: x86_64/binop.S */ +/* + * Generic 32-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = eax op (rFP,%ecx,4)". + * This could be an x86 instruction or a function call. (If the result + * comes back in a register other than eax, you can override "result".) + * + * For: add-int, sub-int, and-int, or-int, + * xor-int, shl-int, shr-int, ushr-int + */ + /* binop vAA, vBB, vCC */ + movzbq 2(rPC), %rax # rax <- BB + movzbq 3(rPC), %rcx # rcx <- CC + GET_VREG %eax, %rax # eax <- vBB + andl (rFP,%rcx,4), %eax # ex: addl (rFP,%rcx,4),%eax + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_or_int: /* 0x96 */ +/* File: x86_64/op_or_int.S */ +/* File: x86_64/binop.S */ +/* + * Generic 32-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = eax op (rFP,%ecx,4)". + * This could be an x86 instruction or a function call. (If the result + * comes back in a register other than eax, you can override "result".) + * + * For: add-int, sub-int, and-int, or-int, + * xor-int, shl-int, shr-int, ushr-int + */ + /* binop vAA, vBB, vCC */ + movzbq 2(rPC), %rax # rax <- BB + movzbq 3(rPC), %rcx # rcx <- CC + GET_VREG %eax, %rax # eax <- vBB + orl (rFP,%rcx,4), %eax # ex: addl (rFP,%rcx,4),%eax + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_xor_int: /* 0x97 */ +/* File: x86_64/op_xor_int.S */ +/* File: x86_64/binop.S */ +/* + * Generic 32-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = eax op (rFP,%ecx,4)". + * This could be an x86 instruction or a function call. (If the result + * comes back in a register other than eax, you can override "result".) + * + * For: add-int, sub-int, and-int, or-int, + * xor-int, shl-int, shr-int, ushr-int + */ + /* binop vAA, vBB, vCC */ + movzbq 2(rPC), %rax # rax <- BB + movzbq 3(rPC), %rcx # rcx <- CC + GET_VREG %eax, %rax # eax <- vBB + xorl (rFP,%rcx,4), %eax # ex: addl (rFP,%rcx,4),%eax + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_shl_int: /* 0x98 */ +/* File: x86_64/op_shl_int.S */ +/* File: x86_64/binop1.S */ +/* + * Generic 32-bit binary operation in which both operands loaded to + * registers (op0 in eax, op1 in ecx). + */ + /* binop vAA, vBB, vCC */ + movzbq 2(rPC), %rax # eax <- BB + movzbq 3(rPC), %rcx # ecx <- CC + GET_VREG %ecx, %rcx # eax <- vCC + .if 0 + GET_WIDE_VREG %rax, %rax # rax <- vBB + sall %cl, %eax # ex: addl %ecx,%eax + SET_WIDE_VREG %rax, rINSTq + .else + GET_VREG %eax, %rax # eax <- vBB + sall %cl, %eax # ex: addl %ecx,%eax + SET_VREG %eax, rINSTq + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_shr_int: /* 0x99 */ +/* File: x86_64/op_shr_int.S */ +/* File: x86_64/binop1.S */ +/* + * Generic 32-bit binary operation in which both operands loaded to + * registers (op0 in eax, op1 in ecx). + */ + /* binop vAA, vBB, vCC */ + movzbq 2(rPC), %rax # eax <- BB + movzbq 3(rPC), %rcx # ecx <- CC + GET_VREG %ecx, %rcx # eax <- vCC + .if 0 + GET_WIDE_VREG %rax, %rax # rax <- vBB + sarl %cl, %eax # ex: addl %ecx,%eax + SET_WIDE_VREG %rax, rINSTq + .else + GET_VREG %eax, %rax # eax <- vBB + sarl %cl, %eax # ex: addl %ecx,%eax + SET_VREG %eax, rINSTq + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_ushr_int: /* 0x9a */ +/* File: x86_64/op_ushr_int.S */ +/* File: x86_64/binop1.S */ +/* + * Generic 32-bit binary operation in which both operands loaded to + * registers (op0 in eax, op1 in ecx). + */ + /* binop vAA, vBB, vCC */ + movzbq 2(rPC), %rax # eax <- BB + movzbq 3(rPC), %rcx # ecx <- CC + GET_VREG %ecx, %rcx # eax <- vCC + .if 0 + GET_WIDE_VREG %rax, %rax # rax <- vBB + shrl %cl, %eax # ex: addl %ecx,%eax + SET_WIDE_VREG %rax, rINSTq + .else + GET_VREG %eax, %rax # eax <- vBB + shrl %cl, %eax # ex: addl %ecx,%eax + SET_VREG %eax, rINSTq + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_add_long: /* 0x9b */ +/* File: x86_64/op_add_long.S */ +/* File: x86_64/binopWide.S */ +/* + * Generic 64-bit binary operation. + */ + /* binop vAA, vBB, vCC */ + movzbq 2(rPC), %rax # eax <- BB + movzbq 3(rPC), %rcx # ecx <- CC + GET_WIDE_VREG %rax, %rax # rax <- v[BB] + addq (rFP,%rcx,4), %rax # ex: addq (rFP,%rcx,4),%rax + SET_WIDE_VREG %rax, rINSTq # v[AA] <- rax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_sub_long: /* 0x9c */ +/* File: x86_64/op_sub_long.S */ +/* File: x86_64/binopWide.S */ +/* + * Generic 64-bit binary operation. + */ + /* binop vAA, vBB, vCC */ + movzbq 2(rPC), %rax # eax <- BB + movzbq 3(rPC), %rcx # ecx <- CC + GET_WIDE_VREG %rax, %rax # rax <- v[BB] + subq (rFP,%rcx,4), %rax # ex: addq (rFP,%rcx,4),%rax + SET_WIDE_VREG %rax, rINSTq # v[AA] <- rax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_long: /* 0x9d */ +/* File: x86_64/op_mul_long.S */ +/* File: x86_64/binopWide.S */ +/* + * Generic 64-bit binary operation. + */ + /* binop vAA, vBB, vCC */ + movzbq 2(rPC), %rax # eax <- BB + movzbq 3(rPC), %rcx # ecx <- CC + GET_WIDE_VREG %rax, %rax # rax <- v[BB] + imulq (rFP,%rcx,4), %rax # ex: addq (rFP,%rcx,4),%rax + SET_WIDE_VREG %rax, rINSTq # v[AA] <- rax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_div_long: /* 0x9e */ +/* File: x86_64/op_div_long.S */ +/* File: x86_64/bindiv.S */ +/* + * 32-bit binary div/rem operation. Handles special case of op1=-1. + */ + /* div/rem vAA, vBB, vCC */ + movzbq 2(rPC), %rax # rax <- BB + movzbq 3(rPC), %rcx # rcx <- CC + .if 1 + GET_WIDE_VREG %rax, %rax # eax <- vBB + GET_WIDE_VREG %rcx, %rcx # ecx <- vCC + .else + GET_VREG %eax, %rax # eax <- vBB + GET_VREG %rcx, %rcx # ecx <- vCC + .endif + testq %rcx, %rcx + jz common_errDivideByZero + cmpq $-1, %rcx + je 2f + cqo # rdx:rax <- sign-extended of rax + idivq %rcx +1: + .if 1 + SET_WIDE_VREG %rax, rINSTq # eax <- vBB + .else + SET_VREG %rax, rINSTq # eax <- vBB + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 +2: + .if 0 + xorq %rax, %rax + .else + negq %rax + .endif + jmp 1b + + +/* ------------------------------ */ + .balign 128 +.L_op_rem_long: /* 0x9f */ +/* File: x86_64/op_rem_long.S */ +/* File: x86_64/bindiv.S */ +/* + * 32-bit binary div/rem operation. Handles special case of op1=-1. + */ + /* div/rem vAA, vBB, vCC */ + movzbq 2(rPC), %rax # rax <- BB + movzbq 3(rPC), %rcx # rcx <- CC + .if 1 + GET_WIDE_VREG %rax, %rax # eax <- vBB + GET_WIDE_VREG %rcx, %rcx # ecx <- vCC + .else + GET_VREG %eax, %rax # eax <- vBB + GET_VREG %rcx, %rcx # ecx <- vCC + .endif + testq %rcx, %rcx + jz common_errDivideByZero + cmpq $-1, %rcx + je 2f + cqo # rdx:rax <- sign-extended of rax + idivq %rcx +1: + .if 1 + SET_WIDE_VREG %rdx, rINSTq # eax <- vBB + .else + SET_VREG %rdx, rINSTq # eax <- vBB + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 +2: + .if 1 + xorq %rdx, %rdx + .else + negq %rdx + .endif + jmp 1b + + +/* ------------------------------ */ + .balign 128 +.L_op_and_long: /* 0xa0 */ +/* File: x86_64/op_and_long.S */ +/* File: x86_64/binopWide.S */ +/* + * Generic 64-bit binary operation. + */ + /* binop vAA, vBB, vCC */ + movzbq 2(rPC), %rax # eax <- BB + movzbq 3(rPC), %rcx # ecx <- CC + GET_WIDE_VREG %rax, %rax # rax <- v[BB] + andq (rFP,%rcx,4), %rax # ex: addq (rFP,%rcx,4),%rax + SET_WIDE_VREG %rax, rINSTq # v[AA] <- rax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_or_long: /* 0xa1 */ +/* File: x86_64/op_or_long.S */ +/* File: x86_64/binopWide.S */ +/* + * Generic 64-bit binary operation. + */ + /* binop vAA, vBB, vCC */ + movzbq 2(rPC), %rax # eax <- BB + movzbq 3(rPC), %rcx # ecx <- CC + GET_WIDE_VREG %rax, %rax # rax <- v[BB] + orq (rFP,%rcx,4), %rax # ex: addq (rFP,%rcx,4),%rax + SET_WIDE_VREG %rax, rINSTq # v[AA] <- rax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_xor_long: /* 0xa2 */ +/* File: x86_64/op_xor_long.S */ +/* File: x86_64/binopWide.S */ +/* + * Generic 64-bit binary operation. + */ + /* binop vAA, vBB, vCC */ + movzbq 2(rPC), %rax # eax <- BB + movzbq 3(rPC), %rcx # ecx <- CC + GET_WIDE_VREG %rax, %rax # rax <- v[BB] + xorq (rFP,%rcx,4), %rax # ex: addq (rFP,%rcx,4),%rax + SET_WIDE_VREG %rax, rINSTq # v[AA] <- rax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_shl_long: /* 0xa3 */ +/* File: x86_64/op_shl_long.S */ +/* File: x86_64/binop1.S */ +/* + * Generic 32-bit binary operation in which both operands loaded to + * registers (op0 in eax, op1 in ecx). + */ + /* binop vAA, vBB, vCC */ + movzbq 2(rPC), %rax # eax <- BB + movzbq 3(rPC), %rcx # ecx <- CC + GET_VREG %ecx, %rcx # eax <- vCC + .if 1 + GET_WIDE_VREG %rax, %rax # rax <- vBB + salq %cl, %rax # ex: addl %ecx,%eax + SET_WIDE_VREG %rax, rINSTq + .else + GET_VREG %eax, %rax # eax <- vBB + salq %cl, %rax # ex: addl %ecx,%eax + SET_VREG %eax, rINSTq + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_shr_long: /* 0xa4 */ +/* File: x86_64/op_shr_long.S */ +/* File: x86_64/binop1.S */ +/* + * Generic 32-bit binary operation in which both operands loaded to + * registers (op0 in eax, op1 in ecx). + */ + /* binop vAA, vBB, vCC */ + movzbq 2(rPC), %rax # eax <- BB + movzbq 3(rPC), %rcx # ecx <- CC + GET_VREG %ecx, %rcx # eax <- vCC + .if 1 + GET_WIDE_VREG %rax, %rax # rax <- vBB + sarq %cl, %rax # ex: addl %ecx,%eax + SET_WIDE_VREG %rax, rINSTq + .else + GET_VREG %eax, %rax # eax <- vBB + sarq %cl, %rax # ex: addl %ecx,%eax + SET_VREG %eax, rINSTq + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_ushr_long: /* 0xa5 */ +/* File: x86_64/op_ushr_long.S */ +/* File: x86_64/binop1.S */ +/* + * Generic 32-bit binary operation in which both operands loaded to + * registers (op0 in eax, op1 in ecx). + */ + /* binop vAA, vBB, vCC */ + movzbq 2(rPC), %rax # eax <- BB + movzbq 3(rPC), %rcx # ecx <- CC + GET_VREG %ecx, %rcx # eax <- vCC + .if 1 + GET_WIDE_VREG %rax, %rax # rax <- vBB + shrq %cl, %rax # ex: addl %ecx,%eax + SET_WIDE_VREG %rax, rINSTq + .else + GET_VREG %eax, %rax # eax <- vBB + shrq %cl, %rax # ex: addl %ecx,%eax + SET_VREG %eax, rINSTq + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_add_float: /* 0xa6 */ +/* File: x86_64/op_add_float.S */ +/* File: x86_64/sseBinop.S */ + movzbq 2(rPC), %rcx # ecx <- BB + movzbq 3(rPC), %rax # eax <- CC + movss VREG_ADDRESS(%rcx), %xmm0 # %xmm0 <- 1st src + addss VREG_ADDRESS(%rax), %xmm0 + movss %xmm0, VREG_ADDRESS(rINSTq) # vAA <- %xmm0 + pxor %xmm0, %xmm0 + movss %xmm0, VREG_REF_ADDRESS(rINSTq) # clear ref + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_sub_float: /* 0xa7 */ +/* File: x86_64/op_sub_float.S */ +/* File: x86_64/sseBinop.S */ + movzbq 2(rPC), %rcx # ecx <- BB + movzbq 3(rPC), %rax # eax <- CC + movss VREG_ADDRESS(%rcx), %xmm0 # %xmm0 <- 1st src + subss VREG_ADDRESS(%rax), %xmm0 + movss %xmm0, VREG_ADDRESS(rINSTq) # vAA <- %xmm0 + pxor %xmm0, %xmm0 + movss %xmm0, VREG_REF_ADDRESS(rINSTq) # clear ref + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_float: /* 0xa8 */ +/* File: x86_64/op_mul_float.S */ +/* File: x86_64/sseBinop.S */ + movzbq 2(rPC), %rcx # ecx <- BB + movzbq 3(rPC), %rax # eax <- CC + movss VREG_ADDRESS(%rcx), %xmm0 # %xmm0 <- 1st src + mulss VREG_ADDRESS(%rax), %xmm0 + movss %xmm0, VREG_ADDRESS(rINSTq) # vAA <- %xmm0 + pxor %xmm0, %xmm0 + movss %xmm0, VREG_REF_ADDRESS(rINSTq) # clear ref + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_div_float: /* 0xa9 */ +/* File: x86_64/op_div_float.S */ +/* File: x86_64/sseBinop.S */ + movzbq 2(rPC), %rcx # ecx <- BB + movzbq 3(rPC), %rax # eax <- CC + movss VREG_ADDRESS(%rcx), %xmm0 # %xmm0 <- 1st src + divss VREG_ADDRESS(%rax), %xmm0 + movss %xmm0, VREG_ADDRESS(rINSTq) # vAA <- %xmm0 + pxor %xmm0, %xmm0 + movss %xmm0, VREG_REF_ADDRESS(rINSTq) # clear ref + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_rem_float: /* 0xaa */ +/* File: x86_64/op_rem_float.S */ + /* rem_float vAA, vBB, vCC */ + movzbq 3(rPC), %rcx # ecx <- BB + movzbq 2(rPC), %rax # eax <- CC + flds VREG_ADDRESS(%rcx) # vBB to fp stack + flds VREG_ADDRESS(%rax) # vCC to fp stack +1: + fprem + fstsw %ax + sahf + jp 1b + fstp %st(1) + fstps VREG_ADDRESS(rINSTq) # %st to vAA + CLEAR_REF rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_add_double: /* 0xab */ +/* File: x86_64/op_add_double.S */ +/* File: x86_64/sseBinop.S */ + movzbq 2(rPC), %rcx # ecx <- BB + movzbq 3(rPC), %rax # eax <- CC + movsd VREG_ADDRESS(%rcx), %xmm0 # %xmm0 <- 1st src + addsd VREG_ADDRESS(%rax), %xmm0 + movsd %xmm0, VREG_ADDRESS(rINSTq) # vAA <- %xmm0 + pxor %xmm0, %xmm0 + movsd %xmm0, VREG_REF_ADDRESS(rINSTq) # clear ref + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_sub_double: /* 0xac */ +/* File: x86_64/op_sub_double.S */ +/* File: x86_64/sseBinop.S */ + movzbq 2(rPC), %rcx # ecx <- BB + movzbq 3(rPC), %rax # eax <- CC + movsd VREG_ADDRESS(%rcx), %xmm0 # %xmm0 <- 1st src + subsd VREG_ADDRESS(%rax), %xmm0 + movsd %xmm0, VREG_ADDRESS(rINSTq) # vAA <- %xmm0 + pxor %xmm0, %xmm0 + movsd %xmm0, VREG_REF_ADDRESS(rINSTq) # clear ref + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_double: /* 0xad */ +/* File: x86_64/op_mul_double.S */ +/* File: x86_64/sseBinop.S */ + movzbq 2(rPC), %rcx # ecx <- BB + movzbq 3(rPC), %rax # eax <- CC + movsd VREG_ADDRESS(%rcx), %xmm0 # %xmm0 <- 1st src + mulsd VREG_ADDRESS(%rax), %xmm0 + movsd %xmm0, VREG_ADDRESS(rINSTq) # vAA <- %xmm0 + pxor %xmm0, %xmm0 + movsd %xmm0, VREG_REF_ADDRESS(rINSTq) # clear ref + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_div_double: /* 0xae */ +/* File: x86_64/op_div_double.S */ +/* File: x86_64/sseBinop.S */ + movzbq 2(rPC), %rcx # ecx <- BB + movzbq 3(rPC), %rax # eax <- CC + movsd VREG_ADDRESS(%rcx), %xmm0 # %xmm0 <- 1st src + divsd VREG_ADDRESS(%rax), %xmm0 + movsd %xmm0, VREG_ADDRESS(rINSTq) # vAA <- %xmm0 + pxor %xmm0, %xmm0 + movsd %xmm0, VREG_REF_ADDRESS(rINSTq) # clear ref + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_rem_double: /* 0xaf */ +/* File: x86_64/op_rem_double.S */ + /* rem_double vAA, vBB, vCC */ + movzbq 3(rPC), %rcx # ecx <- BB + movzbq 2(rPC), %rax # eax <- CC + fldl VREG_ADDRESS(%rcx) # %st1 <- fp[vBB] + fldl VREG_ADDRESS(%rax) # %st0 <- fp[vCC] +1: + fprem + fstsw %ax + sahf + jp 1b + fstp %st(1) + fstpl VREG_ADDRESS(rINSTq) # fp[vAA] <- %st + CLEAR_WIDE_REF rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_add_int_2addr: /* 0xb0 */ +/* File: x86_64/op_add_int_2addr.S */ +/* File: x86_64/binop2addr.S */ +/* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = r0 op r1". + * This could be an instruction or a function call. + * + * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, + * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, + * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr, + * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr + */ + /* binop/2addr vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # ecx <- A + GET_VREG %eax, rINSTq # eax <- vB + addl %eax, (rFP,%rcx,4) # for ex: addl %eax,(rFP,%ecx,4) + CLEAR_REF %rcx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_sub_int_2addr: /* 0xb1 */ +/* File: x86_64/op_sub_int_2addr.S */ +/* File: x86_64/binop2addr.S */ +/* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = r0 op r1". + * This could be an instruction or a function call. + * + * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, + * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, + * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr, + * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr + */ + /* binop/2addr vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # ecx <- A + GET_VREG %eax, rINSTq # eax <- vB + subl %eax, (rFP,%rcx,4) # for ex: addl %eax,(rFP,%ecx,4) + CLEAR_REF %rcx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_int_2addr: /* 0xb2 */ +/* File: x86_64/op_mul_int_2addr.S */ + /* mul vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # ecx <- A + GET_VREG %eax, %rcx # eax <- vA + imull (rFP,rINSTq,4), %eax + SET_VREG %eax, %rcx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + +/* ------------------------------ */ + .balign 128 +.L_op_div_int_2addr: /* 0xb3 */ +/* File: x86_64/op_div_int_2addr.S */ +/* File: x86_64/bindiv2addr.S */ +/* + * 32-bit binary div/rem operation. Handles special case of op1=-1. + */ + /* div/rem/2addr vA, vB */ + movl rINST, %ecx # rcx <- BA + sarl $4, %ecx # rcx <- B + andb $0xf, rINSTbl # rINST <- A + .if 0 + GET_WIDE_VREG %rax, rINSTq # eax <- vA + GET_WIDE_VREG %ecx, %rcx # ecx <- vB + .else + GET_VREG %eax, rINSTq # eax <- vA + GET_VREG %ecx, %rcx # ecx <- vB + .endif + testl %ecx, %ecx + jz common_errDivideByZero + cmpl $-1, %ecx + je 2f + cdq # rdx:rax <- sign-extended of rax + idivl %ecx +1: + .if 0 + SET_WIDE_VREG %eax, rINSTq # vA <- result + .else + SET_VREG %eax, rINSTq # vA <- result + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 +2: + .if 0 + xorl %eax, %eax + .else + negl %eax + .endif + jmp 1b + + +/* ------------------------------ */ + .balign 128 +.L_op_rem_int_2addr: /* 0xb4 */ +/* File: x86_64/op_rem_int_2addr.S */ +/* File: x86_64/bindiv2addr.S */ +/* + * 32-bit binary div/rem operation. Handles special case of op1=-1. + */ + /* div/rem/2addr vA, vB */ + movl rINST, %ecx # rcx <- BA + sarl $4, %ecx # rcx <- B + andb $0xf, rINSTbl # rINST <- A + .if 0 + GET_WIDE_VREG %rax, rINSTq # eax <- vA + GET_WIDE_VREG %ecx, %rcx # ecx <- vB + .else + GET_VREG %eax, rINSTq # eax <- vA + GET_VREG %ecx, %rcx # ecx <- vB + .endif + testl %ecx, %ecx + jz common_errDivideByZero + cmpl $-1, %ecx + je 2f + cdq # rdx:rax <- sign-extended of rax + idivl %ecx +1: + .if 0 + SET_WIDE_VREG %edx, rINSTq # vA <- result + .else + SET_VREG %edx, rINSTq # vA <- result + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 +2: + .if 1 + xorl %edx, %edx + .else + negl %edx + .endif + jmp 1b + + +/* ------------------------------ */ + .balign 128 +.L_op_and_int_2addr: /* 0xb5 */ +/* File: x86_64/op_and_int_2addr.S */ +/* File: x86_64/binop2addr.S */ +/* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = r0 op r1". + * This could be an instruction or a function call. + * + * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, + * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, + * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr, + * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr + */ + /* binop/2addr vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # ecx <- A + GET_VREG %eax, rINSTq # eax <- vB + andl %eax, (rFP,%rcx,4) # for ex: addl %eax,(rFP,%ecx,4) + CLEAR_REF %rcx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_or_int_2addr: /* 0xb6 */ +/* File: x86_64/op_or_int_2addr.S */ +/* File: x86_64/binop2addr.S */ +/* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = r0 op r1". + * This could be an instruction or a function call. + * + * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, + * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, + * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr, + * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr + */ + /* binop/2addr vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # ecx <- A + GET_VREG %eax, rINSTq # eax <- vB + orl %eax, (rFP,%rcx,4) # for ex: addl %eax,(rFP,%ecx,4) + CLEAR_REF %rcx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_xor_int_2addr: /* 0xb7 */ +/* File: x86_64/op_xor_int_2addr.S */ +/* File: x86_64/binop2addr.S */ +/* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = r0 op r1". + * This could be an instruction or a function call. + * + * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, + * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, + * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr, + * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr + */ + /* binop/2addr vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # ecx <- A + GET_VREG %eax, rINSTq # eax <- vB + xorl %eax, (rFP,%rcx,4) # for ex: addl %eax,(rFP,%ecx,4) + CLEAR_REF %rcx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_shl_int_2addr: /* 0xb8 */ +/* File: x86_64/op_shl_int_2addr.S */ +/* File: x86_64/shop2addr.S */ +/* + * Generic 32-bit "shift/2addr" operation. + */ + /* shift/2addr vA, vB */ + movl rINST, %ecx # ecx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG %ecx, %rcx # ecx <- vBB + andb $0xf, rINSTbl # rINST <- A + .if 0 + GET_WIDE_VREG %rax, rINSTq # rax <- vAA + sall %cl, %eax # ex: sarl %cl, %eax + SET_WIDE_VREG %rax, rINSTq + .else + GET_VREG %eax, rINSTq # eax <- vAA + sall %cl, %eax # ex: sarl %cl, %eax + SET_VREG %eax, rINSTq + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_shr_int_2addr: /* 0xb9 */ +/* File: x86_64/op_shr_int_2addr.S */ +/* File: x86_64/shop2addr.S */ +/* + * Generic 32-bit "shift/2addr" operation. + */ + /* shift/2addr vA, vB */ + movl rINST, %ecx # ecx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG %ecx, %rcx # ecx <- vBB + andb $0xf, rINSTbl # rINST <- A + .if 0 + GET_WIDE_VREG %rax, rINSTq # rax <- vAA + sarl %cl, %eax # ex: sarl %cl, %eax + SET_WIDE_VREG %rax, rINSTq + .else + GET_VREG %eax, rINSTq # eax <- vAA + sarl %cl, %eax # ex: sarl %cl, %eax + SET_VREG %eax, rINSTq + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_ushr_int_2addr: /* 0xba */ +/* File: x86_64/op_ushr_int_2addr.S */ +/* File: x86_64/shop2addr.S */ +/* + * Generic 32-bit "shift/2addr" operation. + */ + /* shift/2addr vA, vB */ + movl rINST, %ecx # ecx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG %ecx, %rcx # ecx <- vBB + andb $0xf, rINSTbl # rINST <- A + .if 0 + GET_WIDE_VREG %rax, rINSTq # rax <- vAA + shrl %cl, %eax # ex: sarl %cl, %eax + SET_WIDE_VREG %rax, rINSTq + .else + GET_VREG %eax, rINSTq # eax <- vAA + shrl %cl, %eax # ex: sarl %cl, %eax + SET_VREG %eax, rINSTq + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_add_long_2addr: /* 0xbb */ +/* File: x86_64/op_add_long_2addr.S */ +/* File: x86_64/binopWide2addr.S */ +/* + * Generic 64-bit binary operation. + */ + /* binop/2addr vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # ecx <- A + GET_WIDE_VREG %rax, rINSTq # rax <- vB + addq %rax, (rFP,%rcx,4) # for ex: addq %rax,(rFP,%rcx,4) + CLEAR_WIDE_REF %rcx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_sub_long_2addr: /* 0xbc */ +/* File: x86_64/op_sub_long_2addr.S */ +/* File: x86_64/binopWide2addr.S */ +/* + * Generic 64-bit binary operation. + */ + /* binop/2addr vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # ecx <- A + GET_WIDE_VREG %rax, rINSTq # rax <- vB + subq %rax, (rFP,%rcx,4) # for ex: addq %rax,(rFP,%rcx,4) + CLEAR_WIDE_REF %rcx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_long_2addr: /* 0xbd */ +/* File: x86_64/op_mul_long_2addr.S */ + /* mul vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # ecx <- A + GET_WIDE_VREG %rax, %rcx # rax <- vA + imulq (rFP,rINSTq,4), %rax + SET_WIDE_VREG %rax, %rcx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + +/* ------------------------------ */ + .balign 128 +.L_op_div_long_2addr: /* 0xbe */ +/* File: x86_64/op_div_long_2addr.S */ +/* File: x86_64/bindiv2addr.S */ +/* + * 32-bit binary div/rem operation. Handles special case of op1=-1. + */ + /* div/rem/2addr vA, vB */ + movl rINST, %ecx # rcx <- BA + sarl $4, %ecx # rcx <- B + andb $0xf, rINSTbl # rINST <- A + .if 1 + GET_WIDE_VREG %rax, rINSTq # eax <- vA + GET_WIDE_VREG %rcx, %rcx # ecx <- vB + .else + GET_VREG %eax, rINSTq # eax <- vA + GET_VREG %rcx, %rcx # ecx <- vB + .endif + testq %rcx, %rcx + jz common_errDivideByZero + cmpq $-1, %rcx + je 2f + cqo # rdx:rax <- sign-extended of rax + idivq %rcx +1: + .if 1 + SET_WIDE_VREG %rax, rINSTq # vA <- result + .else + SET_VREG %rax, rINSTq # vA <- result + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 +2: + .if 0 + xorq %rax, %rax + .else + negq %rax + .endif + jmp 1b + + +/* ------------------------------ */ + .balign 128 +.L_op_rem_long_2addr: /* 0xbf */ +/* File: x86_64/op_rem_long_2addr.S */ +/* File: x86_64/bindiv2addr.S */ +/* + * 32-bit binary div/rem operation. Handles special case of op1=-1. + */ + /* div/rem/2addr vA, vB */ + movl rINST, %ecx # rcx <- BA + sarl $4, %ecx # rcx <- B + andb $0xf, rINSTbl # rINST <- A + .if 1 + GET_WIDE_VREG %rax, rINSTq # eax <- vA + GET_WIDE_VREG %rcx, %rcx # ecx <- vB + .else + GET_VREG %eax, rINSTq # eax <- vA + GET_VREG %rcx, %rcx # ecx <- vB + .endif + testq %rcx, %rcx + jz common_errDivideByZero + cmpq $-1, %rcx + je 2f + cqo # rdx:rax <- sign-extended of rax + idivq %rcx +1: + .if 1 + SET_WIDE_VREG %rdx, rINSTq # vA <- result + .else + SET_VREG %rdx, rINSTq # vA <- result + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 +2: + .if 1 + xorq %rdx, %rdx + .else + negq %rdx + .endif + jmp 1b + + +/* ------------------------------ */ + .balign 128 +.L_op_and_long_2addr: /* 0xc0 */ +/* File: x86_64/op_and_long_2addr.S */ +/* File: x86_64/binopWide2addr.S */ +/* + * Generic 64-bit binary operation. + */ + /* binop/2addr vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # ecx <- A + GET_WIDE_VREG %rax, rINSTq # rax <- vB + andq %rax, (rFP,%rcx,4) # for ex: addq %rax,(rFP,%rcx,4) + CLEAR_WIDE_REF %rcx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_or_long_2addr: /* 0xc1 */ +/* File: x86_64/op_or_long_2addr.S */ +/* File: x86_64/binopWide2addr.S */ +/* + * Generic 64-bit binary operation. + */ + /* binop/2addr vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # ecx <- A + GET_WIDE_VREG %rax, rINSTq # rax <- vB + orq %rax, (rFP,%rcx,4) # for ex: addq %rax,(rFP,%rcx,4) + CLEAR_WIDE_REF %rcx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_xor_long_2addr: /* 0xc2 */ +/* File: x86_64/op_xor_long_2addr.S */ +/* File: x86_64/binopWide2addr.S */ +/* + * Generic 64-bit binary operation. + */ + /* binop/2addr vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # ecx <- A + GET_WIDE_VREG %rax, rINSTq # rax <- vB + xorq %rax, (rFP,%rcx,4) # for ex: addq %rax,(rFP,%rcx,4) + CLEAR_WIDE_REF %rcx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_shl_long_2addr: /* 0xc3 */ +/* File: x86_64/op_shl_long_2addr.S */ +/* File: x86_64/shop2addr.S */ +/* + * Generic 32-bit "shift/2addr" operation. + */ + /* shift/2addr vA, vB */ + movl rINST, %ecx # ecx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG %ecx, %rcx # ecx <- vBB + andb $0xf, rINSTbl # rINST <- A + .if 1 + GET_WIDE_VREG %rax, rINSTq # rax <- vAA + salq %cl, %rax # ex: sarl %cl, %eax + SET_WIDE_VREG %rax, rINSTq + .else + GET_VREG %eax, rINSTq # eax <- vAA + salq %cl, %rax # ex: sarl %cl, %eax + SET_VREG %eax, rINSTq + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_shr_long_2addr: /* 0xc4 */ +/* File: x86_64/op_shr_long_2addr.S */ +/* File: x86_64/shop2addr.S */ +/* + * Generic 32-bit "shift/2addr" operation. + */ + /* shift/2addr vA, vB */ + movl rINST, %ecx # ecx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG %ecx, %rcx # ecx <- vBB + andb $0xf, rINSTbl # rINST <- A + .if 1 + GET_WIDE_VREG %rax, rINSTq # rax <- vAA + sarq %cl, %rax # ex: sarl %cl, %eax + SET_WIDE_VREG %rax, rINSTq + .else + GET_VREG %eax, rINSTq # eax <- vAA + sarq %cl, %rax # ex: sarl %cl, %eax + SET_VREG %eax, rINSTq + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_ushr_long_2addr: /* 0xc5 */ +/* File: x86_64/op_ushr_long_2addr.S */ +/* File: x86_64/shop2addr.S */ +/* + * Generic 32-bit "shift/2addr" operation. + */ + /* shift/2addr vA, vB */ + movl rINST, %ecx # ecx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG %ecx, %rcx # ecx <- vBB + andb $0xf, rINSTbl # rINST <- A + .if 1 + GET_WIDE_VREG %rax, rINSTq # rax <- vAA + shrq %cl, %rax # ex: sarl %cl, %eax + SET_WIDE_VREG %rax, rINSTq + .else + GET_VREG %eax, rINSTq # eax <- vAA + shrq %cl, %rax # ex: sarl %cl, %eax + SET_VREG %eax, rINSTq + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_add_float_2addr: /* 0xc6 */ +/* File: x86_64/op_add_float_2addr.S */ +/* File: x86_64/sseBinop2Addr.S */ + movl rINST, %ecx # ecx <- A+ + andl $0xf, %ecx # ecx <- A + movss VREG_ADDRESS(%rcx), %xmm0 # %xmm0 <- 1st src + sarl $4, rINST # rINST<- B + addss VREG_ADDRESS(rINSTq), %xmm0 + movss %xmm0, VREG_ADDRESS(%rcx) # vAA<- %xmm0 + pxor %xmm0, %xmm0 + movss %xmm0, VREG_REF_ADDRESS(rINSTq) # clear ref + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_sub_float_2addr: /* 0xc7 */ +/* File: x86_64/op_sub_float_2addr.S */ +/* File: x86_64/sseBinop2Addr.S */ + movl rINST, %ecx # ecx <- A+ + andl $0xf, %ecx # ecx <- A + movss VREG_ADDRESS(%rcx), %xmm0 # %xmm0 <- 1st src + sarl $4, rINST # rINST<- B + subss VREG_ADDRESS(rINSTq), %xmm0 + movss %xmm0, VREG_ADDRESS(%rcx) # vAA<- %xmm0 + pxor %xmm0, %xmm0 + movss %xmm0, VREG_REF_ADDRESS(rINSTq) # clear ref + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_float_2addr: /* 0xc8 */ +/* File: x86_64/op_mul_float_2addr.S */ +/* File: x86_64/sseBinop2Addr.S */ + movl rINST, %ecx # ecx <- A+ + andl $0xf, %ecx # ecx <- A + movss VREG_ADDRESS(%rcx), %xmm0 # %xmm0 <- 1st src + sarl $4, rINST # rINST<- B + mulss VREG_ADDRESS(rINSTq), %xmm0 + movss %xmm0, VREG_ADDRESS(%rcx) # vAA<- %xmm0 + pxor %xmm0, %xmm0 + movss %xmm0, VREG_REF_ADDRESS(rINSTq) # clear ref + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_div_float_2addr: /* 0xc9 */ +/* File: x86_64/op_div_float_2addr.S */ +/* File: x86_64/sseBinop2Addr.S */ + movl rINST, %ecx # ecx <- A+ + andl $0xf, %ecx # ecx <- A + movss VREG_ADDRESS(%rcx), %xmm0 # %xmm0 <- 1st src + sarl $4, rINST # rINST<- B + divss VREG_ADDRESS(rINSTq), %xmm0 + movss %xmm0, VREG_ADDRESS(%rcx) # vAA<- %xmm0 + pxor %xmm0, %xmm0 + movss %xmm0, VREG_REF_ADDRESS(rINSTq) # clear ref + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_rem_float_2addr: /* 0xca */ +/* File: x86_64/op_rem_float_2addr.S */ + /* rem_float/2addr vA, vB */ + movzbq rINSTbl, %rcx # ecx <- A+ + sarl $4, rINST # rINST <- B + flds VREG_ADDRESS(rINSTq) # vB to fp stack + andb $0xf, %cl # ecx <- A + flds VREG_ADDRESS(%rcx) # vA to fp stack +1: + fprem + fstsw %ax + sahf + jp 1b + fstp %st(1) + fstps VREG_ADDRESS(%rcx) # %st to vA + CLEAR_REF %rcx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + +/* ------------------------------ */ + .balign 128 +.L_op_add_double_2addr: /* 0xcb */ +/* File: x86_64/op_add_double_2addr.S */ +/* File: x86_64/sseBinop2Addr.S */ + movl rINST, %ecx # ecx <- A+ + andl $0xf, %ecx # ecx <- A + movsd VREG_ADDRESS(%rcx), %xmm0 # %xmm0 <- 1st src + sarl $4, rINST # rINST<- B + addsd VREG_ADDRESS(rINSTq), %xmm0 + movsd %xmm0, VREG_ADDRESS(%rcx) # vAA<- %xmm0 + pxor %xmm0, %xmm0 + movsd %xmm0, VREG_REF_ADDRESS(rINSTq) # clear ref + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_sub_double_2addr: /* 0xcc */ +/* File: x86_64/op_sub_double_2addr.S */ +/* File: x86_64/sseBinop2Addr.S */ + movl rINST, %ecx # ecx <- A+ + andl $0xf, %ecx # ecx <- A + movsd VREG_ADDRESS(%rcx), %xmm0 # %xmm0 <- 1st src + sarl $4, rINST # rINST<- B + subsd VREG_ADDRESS(rINSTq), %xmm0 + movsd %xmm0, VREG_ADDRESS(%rcx) # vAA<- %xmm0 + pxor %xmm0, %xmm0 + movsd %xmm0, VREG_REF_ADDRESS(rINSTq) # clear ref + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_double_2addr: /* 0xcd */ +/* File: x86_64/op_mul_double_2addr.S */ +/* File: x86_64/sseBinop2Addr.S */ + movl rINST, %ecx # ecx <- A+ + andl $0xf, %ecx # ecx <- A + movsd VREG_ADDRESS(%rcx), %xmm0 # %xmm0 <- 1st src + sarl $4, rINST # rINST<- B + mulsd VREG_ADDRESS(rINSTq), %xmm0 + movsd %xmm0, VREG_ADDRESS(%rcx) # vAA<- %xmm0 + pxor %xmm0, %xmm0 + movsd %xmm0, VREG_REF_ADDRESS(rINSTq) # clear ref + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_div_double_2addr: /* 0xce */ +/* File: x86_64/op_div_double_2addr.S */ +/* File: x86_64/sseBinop2Addr.S */ + movl rINST, %ecx # ecx <- A+ + andl $0xf, %ecx # ecx <- A + movsd VREG_ADDRESS(%rcx), %xmm0 # %xmm0 <- 1st src + sarl $4, rINST # rINST<- B + divsd VREG_ADDRESS(rINSTq), %xmm0 + movsd %xmm0, VREG_ADDRESS(%rcx) # vAA<- %xmm0 + pxor %xmm0, %xmm0 + movsd %xmm0, VREG_REF_ADDRESS(rINSTq) # clear ref + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_rem_double_2addr: /* 0xcf */ +/* File: x86_64/op_rem_double_2addr.S */ + /* rem_double/2addr vA, vB */ + movzbq rINSTbl, %rcx # ecx <- A+ + sarl $4, rINST # rINST <- B + fldl VREG_ADDRESS(rINSTq) # vB to fp stack + andb $0xf, %cl # ecx <- A + fldl VREG_ADDRESS(%rcx) # vA to fp stack +1: + fprem + fstsw %ax + sahf + jp 1b + fstp %st(1) + fstpl VREG_ADDRESS(%rcx) # %st to vA + CLEAR_WIDE_REF %rcx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + +/* ------------------------------ */ + .balign 128 +.L_op_add_int_lit16: /* 0xd0 */ +/* File: x86_64/op_add_int_lit16.S */ +/* File: x86_64/binopLit16.S */ +/* + * Generic 32-bit "lit16" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = eax op ecx". + * This could be an x86 instruction or a function call. (If the result + * comes back in a register other than eax, you can override "result".) + * + * For: add-int/lit16, rsub-int, + * and-int/lit16, or-int/lit16, xor-int/lit16 + */ + /* binop/lit16 vA, vB, #+CCCC */ + movl rINST, %eax # rax <- 000000BA + sarl $4, %eax # eax <- B + GET_VREG %eax, %rax # eax <- vB + andb $0xf, rINSTbl # rINST <- A + movswl 2(rPC), %ecx # ecx <- ssssCCCC + addl %ecx, %eax # for example: addl %ecx, %eax + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_rsub_int: /* 0xd1 */ +/* File: x86_64/op_rsub_int.S */ +/* this op is "rsub-int", but can be thought of as "rsub-int/lit16" */ +/* File: x86_64/binopLit16.S */ +/* + * Generic 32-bit "lit16" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = eax op ecx". + * This could be an x86 instruction or a function call. (If the result + * comes back in a register other than eax, you can override "result".) + * + * For: add-int/lit16, rsub-int, + * and-int/lit16, or-int/lit16, xor-int/lit16 + */ + /* binop/lit16 vA, vB, #+CCCC */ + movl rINST, %eax # rax <- 000000BA + sarl $4, %eax # eax <- B + GET_VREG %eax, %rax # eax <- vB + andb $0xf, rINSTbl # rINST <- A + movswl 2(rPC), %ecx # ecx <- ssssCCCC + subl %eax, %ecx # for example: addl %ecx, %eax + SET_VREG %ecx, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_int_lit16: /* 0xd2 */ +/* File: x86_64/op_mul_int_lit16.S */ +/* File: x86_64/binopLit16.S */ +/* + * Generic 32-bit "lit16" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = eax op ecx". + * This could be an x86 instruction or a function call. (If the result + * comes back in a register other than eax, you can override "result".) + * + * For: add-int/lit16, rsub-int, + * and-int/lit16, or-int/lit16, xor-int/lit16 + */ + /* binop/lit16 vA, vB, #+CCCC */ + movl rINST, %eax # rax <- 000000BA + sarl $4, %eax # eax <- B + GET_VREG %eax, %rax # eax <- vB + andb $0xf, rINSTbl # rINST <- A + movswl 2(rPC), %ecx # ecx <- ssssCCCC + imull %ecx, %eax # for example: addl %ecx, %eax + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_div_int_lit16: /* 0xd3 */ +/* File: x86_64/op_div_int_lit16.S */ +/* File: x86_64/bindivLit16.S */ +/* + * 32-bit binary div/rem operation. Handles special case of op1=-1. + */ + /* div/rem/lit16 vA, vB, #+CCCC */ + /* Need A in rINST, ssssCCCC in ecx, vB in eax */ + movl rINST, %eax # rax <- 000000BA + sarl $4, %eax # eax <- B + GET_VREG %eax, %rax # eax <- vB + movswl 2(rPC), %ecx # ecx <- ssssCCCC + andb $0xf, rINSTbl # rINST <- A + testl %ecx, %ecx + jz common_errDivideByZero + cmpl $-1, %ecx + je 2f + cdq # rax <- sign-extended of eax + idivl %ecx +1: + SET_VREG %eax, rINSTq # vA <- result + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 +2: + .if 0 + xorl %eax, %eax + .else + negl %eax + .endif + jmp 1b + + +/* ------------------------------ */ + .balign 128 +.L_op_rem_int_lit16: /* 0xd4 */ +/* File: x86_64/op_rem_int_lit16.S */ +/* File: x86_64/bindivLit16.S */ +/* + * 32-bit binary div/rem operation. Handles special case of op1=-1. + */ + /* div/rem/lit16 vA, vB, #+CCCC */ + /* Need A in rINST, ssssCCCC in ecx, vB in eax */ + movl rINST, %eax # rax <- 000000BA + sarl $4, %eax # eax <- B + GET_VREG %eax, %rax # eax <- vB + movswl 2(rPC), %ecx # ecx <- ssssCCCC + andb $0xf, rINSTbl # rINST <- A + testl %ecx, %ecx + jz common_errDivideByZero + cmpl $-1, %ecx + je 2f + cdq # rax <- sign-extended of eax + idivl %ecx +1: + SET_VREG %edx, rINSTq # vA <- result + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 +2: + .if 1 + xorl %edx, %edx + .else + negl %edx + .endif + jmp 1b + + +/* ------------------------------ */ + .balign 128 +.L_op_and_int_lit16: /* 0xd5 */ +/* File: x86_64/op_and_int_lit16.S */ +/* File: x86_64/binopLit16.S */ +/* + * Generic 32-bit "lit16" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = eax op ecx". + * This could be an x86 instruction or a function call. (If the result + * comes back in a register other than eax, you can override "result".) + * + * For: add-int/lit16, rsub-int, + * and-int/lit16, or-int/lit16, xor-int/lit16 + */ + /* binop/lit16 vA, vB, #+CCCC */ + movl rINST, %eax # rax <- 000000BA + sarl $4, %eax # eax <- B + GET_VREG %eax, %rax # eax <- vB + andb $0xf, rINSTbl # rINST <- A + movswl 2(rPC), %ecx # ecx <- ssssCCCC + andl %ecx, %eax # for example: addl %ecx, %eax + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_or_int_lit16: /* 0xd6 */ +/* File: x86_64/op_or_int_lit16.S */ +/* File: x86_64/binopLit16.S */ +/* + * Generic 32-bit "lit16" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = eax op ecx". + * This could be an x86 instruction or a function call. (If the result + * comes back in a register other than eax, you can override "result".) + * + * For: add-int/lit16, rsub-int, + * and-int/lit16, or-int/lit16, xor-int/lit16 + */ + /* binop/lit16 vA, vB, #+CCCC */ + movl rINST, %eax # rax <- 000000BA + sarl $4, %eax # eax <- B + GET_VREG %eax, %rax # eax <- vB + andb $0xf, rINSTbl # rINST <- A + movswl 2(rPC), %ecx # ecx <- ssssCCCC + orl %ecx, %eax # for example: addl %ecx, %eax + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_xor_int_lit16: /* 0xd7 */ +/* File: x86_64/op_xor_int_lit16.S */ +/* File: x86_64/binopLit16.S */ +/* + * Generic 32-bit "lit16" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = eax op ecx". + * This could be an x86 instruction or a function call. (If the result + * comes back in a register other than eax, you can override "result".) + * + * For: add-int/lit16, rsub-int, + * and-int/lit16, or-int/lit16, xor-int/lit16 + */ + /* binop/lit16 vA, vB, #+CCCC */ + movl rINST, %eax # rax <- 000000BA + sarl $4, %eax # eax <- B + GET_VREG %eax, %rax # eax <- vB + andb $0xf, rINSTbl # rINST <- A + movswl 2(rPC), %ecx # ecx <- ssssCCCC + xorl %ecx, %eax # for example: addl %ecx, %eax + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_add_int_lit8: /* 0xd8 */ +/* File: x86_64/op_add_int_lit8.S */ +/* File: x86_64/binopLit8.S */ +/* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = eax op ecx". + * This could be an x86 instruction or a function call. (If the result + * comes back in a register other than r0, you can override "result".) + * + * For: add-int/lit8, rsub-int/lit8 + * and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + /* binop/lit8 vAA, vBB, #+CC */ + movzbq 2(rPC), %rax # rax <- BB + movsbl 3(rPC), %ecx # rcx <- ssssssCC + GET_VREG %eax, %rax # eax <- rBB + addl %ecx, %eax # ex: addl %ecx,%eax + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_rsub_int_lit8: /* 0xd9 */ +/* File: x86_64/op_rsub_int_lit8.S */ +/* File: x86_64/binopLit8.S */ +/* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = eax op ecx". + * This could be an x86 instruction or a function call. (If the result + * comes back in a register other than r0, you can override "result".) + * + * For: add-int/lit8, rsub-int/lit8 + * and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + /* binop/lit8 vAA, vBB, #+CC */ + movzbq 2(rPC), %rax # rax <- BB + movsbl 3(rPC), %ecx # rcx <- ssssssCC + GET_VREG %eax, %rax # eax <- rBB + subl %eax, %ecx # ex: addl %ecx,%eax + SET_VREG %ecx, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_int_lit8: /* 0xda */ +/* File: x86_64/op_mul_int_lit8.S */ +/* File: x86_64/binopLit8.S */ +/* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = eax op ecx". + * This could be an x86 instruction or a function call. (If the result + * comes back in a register other than r0, you can override "result".) + * + * For: add-int/lit8, rsub-int/lit8 + * and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + /* binop/lit8 vAA, vBB, #+CC */ + movzbq 2(rPC), %rax # rax <- BB + movsbl 3(rPC), %ecx # rcx <- ssssssCC + GET_VREG %eax, %rax # eax <- rBB + imull %ecx, %eax # ex: addl %ecx,%eax + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_div_int_lit8: /* 0xdb */ +/* File: x86_64/op_div_int_lit8.S */ +/* File: x86_64/bindivLit8.S */ +/* + * 32-bit div/rem "lit8" binary operation. Handles special case of + * op0=minint & op1=-1 + */ + /* div/rem/lit8 vAA, vBB, #+CC */ + movzbq 2(rPC), %rax # eax <- BB + movsbl 3(rPC), %ecx # ecx <- ssssssCC + GET_VREG %eax, %rax # eax <- rBB + testl %ecx, %ecx + je common_errDivideByZero + cmpl $-1, %ecx + je 2f + cdq # rax <- sign-extended of eax + idivl %ecx +1: + SET_VREG %eax, rINSTq # vA <- result + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 +2: + .if 0 + xorl %eax, %eax + .else + negl %eax + .endif + jmp 1b + + +/* ------------------------------ */ + .balign 128 +.L_op_rem_int_lit8: /* 0xdc */ +/* File: x86_64/op_rem_int_lit8.S */ +/* File: x86_64/bindivLit8.S */ +/* + * 32-bit div/rem "lit8" binary operation. Handles special case of + * op0=minint & op1=-1 + */ + /* div/rem/lit8 vAA, vBB, #+CC */ + movzbq 2(rPC), %rax # eax <- BB + movsbl 3(rPC), %ecx # ecx <- ssssssCC + GET_VREG %eax, %rax # eax <- rBB + testl %ecx, %ecx + je common_errDivideByZero + cmpl $-1, %ecx + je 2f + cdq # rax <- sign-extended of eax + idivl %ecx +1: + SET_VREG %edx, rINSTq # vA <- result + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 +2: + .if 1 + xorl %edx, %edx + .else + negl %edx + .endif + jmp 1b + + +/* ------------------------------ */ + .balign 128 +.L_op_and_int_lit8: /* 0xdd */ +/* File: x86_64/op_and_int_lit8.S */ +/* File: x86_64/binopLit8.S */ +/* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = eax op ecx". + * This could be an x86 instruction or a function call. (If the result + * comes back in a register other than r0, you can override "result".) + * + * For: add-int/lit8, rsub-int/lit8 + * and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + /* binop/lit8 vAA, vBB, #+CC */ + movzbq 2(rPC), %rax # rax <- BB + movsbl 3(rPC), %ecx # rcx <- ssssssCC + GET_VREG %eax, %rax # eax <- rBB + andl %ecx, %eax # ex: addl %ecx,%eax + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_or_int_lit8: /* 0xde */ +/* File: x86_64/op_or_int_lit8.S */ +/* File: x86_64/binopLit8.S */ +/* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = eax op ecx". + * This could be an x86 instruction or a function call. (If the result + * comes back in a register other than r0, you can override "result".) + * + * For: add-int/lit8, rsub-int/lit8 + * and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + /* binop/lit8 vAA, vBB, #+CC */ + movzbq 2(rPC), %rax # rax <- BB + movsbl 3(rPC), %ecx # rcx <- ssssssCC + GET_VREG %eax, %rax # eax <- rBB + orl %ecx, %eax # ex: addl %ecx,%eax + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_xor_int_lit8: /* 0xdf */ +/* File: x86_64/op_xor_int_lit8.S */ +/* File: x86_64/binopLit8.S */ +/* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = eax op ecx". + * This could be an x86 instruction or a function call. (If the result + * comes back in a register other than r0, you can override "result".) + * + * For: add-int/lit8, rsub-int/lit8 + * and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + /* binop/lit8 vAA, vBB, #+CC */ + movzbq 2(rPC), %rax # rax <- BB + movsbl 3(rPC), %ecx # rcx <- ssssssCC + GET_VREG %eax, %rax # eax <- rBB + xorl %ecx, %eax # ex: addl %ecx,%eax + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_shl_int_lit8: /* 0xe0 */ +/* File: x86_64/op_shl_int_lit8.S */ +/* File: x86_64/binopLit8.S */ +/* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = eax op ecx". + * This could be an x86 instruction or a function call. (If the result + * comes back in a register other than r0, you can override "result".) + * + * For: add-int/lit8, rsub-int/lit8 + * and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + /* binop/lit8 vAA, vBB, #+CC */ + movzbq 2(rPC), %rax # rax <- BB + movsbl 3(rPC), %ecx # rcx <- ssssssCC + GET_VREG %eax, %rax # eax <- rBB + sall %cl, %eax # ex: addl %ecx,%eax + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_shr_int_lit8: /* 0xe1 */ +/* File: x86_64/op_shr_int_lit8.S */ +/* File: x86_64/binopLit8.S */ +/* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = eax op ecx". + * This could be an x86 instruction or a function call. (If the result + * comes back in a register other than r0, you can override "result".) + * + * For: add-int/lit8, rsub-int/lit8 + * and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + /* binop/lit8 vAA, vBB, #+CC */ + movzbq 2(rPC), %rax # rax <- BB + movsbl 3(rPC), %ecx # rcx <- ssssssCC + GET_VREG %eax, %rax # eax <- rBB + sarl %cl, %eax # ex: addl %ecx,%eax + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_ushr_int_lit8: /* 0xe2 */ +/* File: x86_64/op_ushr_int_lit8.S */ +/* File: x86_64/binopLit8.S */ +/* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = eax op ecx". + * This could be an x86 instruction or a function call. (If the result + * comes back in a register other than r0, you can override "result".) + * + * For: add-int/lit8, rsub-int/lit8 + * and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + /* binop/lit8 vAA, vBB, #+CC */ + movzbq 2(rPC), %rax # rax <- BB + movsbl 3(rPC), %ecx # rcx <- ssssssCC + GET_VREG %eax, %rax # eax <- rBB + shrl %cl, %eax # ex: addl %ecx,%eax + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_quick: /* 0xe3 */ +/* File: x86_64/op_iget_quick.S */ + /* For: iget-quick, iget-boolean-quick, iget-byte-quick, iget-char-quick, iget-short-quick, iget-wide-quick */ + /* op vA, vB, offset@CCCC */ + movl rINST, %ecx # rcx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG %ecx, %rcx # vB (object we're operating on) + movzwq 2(rPC), %rax # eax <- field byte offset + testl %ecx, %ecx # is object null? + je common_errNullObject + andb $0xf,rINSTbl # rINST <- A + .if 0 + movq (%rcx,%rax,1), %rax + SET_WIDE_VREG %rax, rINSTq # fp[A] <- value + .else + movl (%rcx,%rax,1), %eax + SET_VREG %eax, rINSTq # fp[A] <- value + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_iget_wide_quick: /* 0xe4 */ +/* File: x86_64/op_iget_wide_quick.S */ +/* File: x86_64/op_iget_quick.S */ + /* For: iget-quick, iget-boolean-quick, iget-byte-quick, iget-char-quick, iget-short-quick, iget-wide-quick */ + /* op vA, vB, offset@CCCC */ + movl rINST, %ecx # rcx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG %ecx, %rcx # vB (object we're operating on) + movzwq 2(rPC), %rax # eax <- field byte offset + testl %ecx, %ecx # is object null? + je common_errNullObject + andb $0xf,rINSTbl # rINST <- A + .if 1 + movq (%rcx,%rax,1), %rax + SET_WIDE_VREG %rax, rINSTq # fp[A] <- value + .else + movswl (%rcx,%rax,1), %eax + SET_VREG %eax, rINSTq # fp[A] <- value + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_object_quick: /* 0xe5 */ +/* File: x86_64/op_iget_object_quick.S */ + /* For: iget-object-quick */ + /* op vA, vB, offset@CCCC */ + .extern artIGetObjectFromMterp + movzbq rINSTbl, %rcx # rcx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG OUT_32_ARG0, %rcx # vB (object we're operating on) + movzwl 2(rPC), OUT_32_ARG1 # eax <- field byte offset + EXPORT_PC + callq SYMBOL(artIGetObjectFromMterp) # (obj, offset) + cmpq $0, THREAD_EXCEPTION_OFFSET(rSELF) + jnz MterpException # bail out + andb $0xf, rINSTbl # rINST <- A + SET_VREG_OBJECT %eax, rINSTq # fp[A] <- value + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_iput_quick: /* 0xe6 */ +/* File: x86_64/op_iput_quick.S */ + /* For: iput-quick, iput-object-quick */ + /* op vA, vB, offset@CCCC */ + movzbq rINSTbl, %rcx # rcx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG %ecx, %rcx # vB (object we're operating on) + testl %ecx, %ecx # is object null? + je common_errNullObject + andb $0xf, rINSTbl # rINST <- A + GET_VREG rINST, rINSTq # rINST <- v[A] + movzwq 2(rPC), %rax # rax <- field byte offset + movl rINST, (%rcx,%rax,1) + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_iput_wide_quick: /* 0xe7 */ +/* File: x86_64/op_iput_wide_quick.S */ + /* iput-wide-quick vA, vB, offset@CCCC */ + movzbq rINSTbl, %rcx # rcx<- BA + sarl $4, %ecx # ecx<- B + GET_VREG %ecx, %rcx # vB (object we're operating on) + testl %ecx, %ecx # is object null? + je common_errNullObject + movzwq 2(rPC), %rax # rax<- field byte offset + leaq (%rcx,%rax,1), %rcx # ecx<- Address of 64-bit target + andb $0xf, rINSTbl # rINST<- A + GET_WIDE_VREG %rax, rINSTq # rax<- fp[A]/fp[A+1] + movq %rax, (%rcx) # obj.field<- r0/r1 + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_iput_object_quick: /* 0xe8 */ +/* File: x86_64/op_iput_object_quick.S */ + EXPORT_PC + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG0 + movq rPC, OUT_ARG1 + REFRESH_INST 232 + movl rINST, OUT_32_ARG2 + call SYMBOL(MterpIputObjectQuick) + testb %al, %al + jz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_virtual_quick: /* 0xe9 */ +/* File: x86_64/op_invoke_virtual_quick.S */ +/* File: x86_64/invoke.S */ +/* + * Generic invoke handler wrapper. + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + .extern MterpInvokeVirtualQuick + EXPORT_PC + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + movq rPC, OUT_ARG2 + REFRESH_INST 233 + movl rINST, OUT_32_ARG3 + call SYMBOL(MterpInvokeVirtualQuick) + testb %al, %al + jz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_virtual_range_quick: /* 0xea */ +/* File: x86_64/op_invoke_virtual_range_quick.S */ +/* File: x86_64/invoke.S */ +/* + * Generic invoke handler wrapper. + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + .extern MterpInvokeVirtualQuickRange + EXPORT_PC + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + movq rPC, OUT_ARG2 + REFRESH_INST 234 + movl rINST, OUT_32_ARG3 + call SYMBOL(MterpInvokeVirtualQuickRange) + testb %al, %al + jz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + + +/* ------------------------------ */ + .balign 128 +.L_op_iput_boolean_quick: /* 0xeb */ +/* File: x86_64/op_iput_boolean_quick.S */ +/* File: x86_64/op_iput_quick.S */ + /* For: iput-quick, iput-object-quick */ + /* op vA, vB, offset@CCCC */ + movzbq rINSTbl, %rcx # rcx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG %ecx, %rcx # vB (object we're operating on) + testl %ecx, %ecx # is object null? + je common_errNullObject + andb $0xf, rINSTbl # rINST <- A + GET_VREG rINST, rINSTq # rINST <- v[A] + movzwq 2(rPC), %rax # rax <- field byte offset + movb rINSTbl, (%rcx,%rax,1) + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iput_byte_quick: /* 0xec */ +/* File: x86_64/op_iput_byte_quick.S */ +/* File: x86_64/op_iput_quick.S */ + /* For: iput-quick, iput-object-quick */ + /* op vA, vB, offset@CCCC */ + movzbq rINSTbl, %rcx # rcx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG %ecx, %rcx # vB (object we're operating on) + testl %ecx, %ecx # is object null? + je common_errNullObject + andb $0xf, rINSTbl # rINST <- A + GET_VREG rINST, rINSTq # rINST <- v[A] + movzwq 2(rPC), %rax # rax <- field byte offset + movb rINSTbl, (%rcx,%rax,1) + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iput_char_quick: /* 0xed */ +/* File: x86_64/op_iput_char_quick.S */ +/* File: x86_64/op_iput_quick.S */ + /* For: iput-quick, iput-object-quick */ + /* op vA, vB, offset@CCCC */ + movzbq rINSTbl, %rcx # rcx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG %ecx, %rcx # vB (object we're operating on) + testl %ecx, %ecx # is object null? + je common_errNullObject + andb $0xf, rINSTbl # rINST <- A + GET_VREG rINST, rINSTq # rINST <- v[A] + movzwq 2(rPC), %rax # rax <- field byte offset + movw rINSTw, (%rcx,%rax,1) + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iput_short_quick: /* 0xee */ +/* File: x86_64/op_iput_short_quick.S */ +/* File: x86_64/op_iput_quick.S */ + /* For: iput-quick, iput-object-quick */ + /* op vA, vB, offset@CCCC */ + movzbq rINSTbl, %rcx # rcx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG %ecx, %rcx # vB (object we're operating on) + testl %ecx, %ecx # is object null? + je common_errNullObject + andb $0xf, rINSTbl # rINST <- A + GET_VREG rINST, rINSTq # rINST <- v[A] + movzwq 2(rPC), %rax # rax <- field byte offset + movw rINSTw, (%rcx,%rax,1) + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_boolean_quick: /* 0xef */ +/* File: x86_64/op_iget_boolean_quick.S */ +/* File: x86_64/op_iget_quick.S */ + /* For: iget-quick, iget-boolean-quick, iget-byte-quick, iget-char-quick, iget-short-quick, iget-wide-quick */ + /* op vA, vB, offset@CCCC */ + movl rINST, %ecx # rcx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG %ecx, %rcx # vB (object we're operating on) + movzwq 2(rPC), %rax # eax <- field byte offset + testl %ecx, %ecx # is object null? + je common_errNullObject + andb $0xf,rINSTbl # rINST <- A + .if 0 + movq (%rcx,%rax,1), %rax + SET_WIDE_VREG %rax, rINSTq # fp[A] <- value + .else + movsbl (%rcx,%rax,1), %eax + SET_VREG %eax, rINSTq # fp[A] <- value + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_byte_quick: /* 0xf0 */ +/* File: x86_64/op_iget_byte_quick.S */ +/* File: x86_64/op_iget_quick.S */ + /* For: iget-quick, iget-boolean-quick, iget-byte-quick, iget-char-quick, iget-short-quick, iget-wide-quick */ + /* op vA, vB, offset@CCCC */ + movl rINST, %ecx # rcx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG %ecx, %rcx # vB (object we're operating on) + movzwq 2(rPC), %rax # eax <- field byte offset + testl %ecx, %ecx # is object null? + je common_errNullObject + andb $0xf,rINSTbl # rINST <- A + .if 0 + movq (%rcx,%rax,1), %rax + SET_WIDE_VREG %rax, rINSTq # fp[A] <- value + .else + movsbl (%rcx,%rax,1), %eax + SET_VREG %eax, rINSTq # fp[A] <- value + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_char_quick: /* 0xf1 */ +/* File: x86_64/op_iget_char_quick.S */ +/* File: x86_64/op_iget_quick.S */ + /* For: iget-quick, iget-boolean-quick, iget-byte-quick, iget-char-quick, iget-short-quick, iget-wide-quick */ + /* op vA, vB, offset@CCCC */ + movl rINST, %ecx # rcx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG %ecx, %rcx # vB (object we're operating on) + movzwq 2(rPC), %rax # eax <- field byte offset + testl %ecx, %ecx # is object null? + je common_errNullObject + andb $0xf,rINSTbl # rINST <- A + .if 0 + movq (%rcx,%rax,1), %rax + SET_WIDE_VREG %rax, rINSTq # fp[A] <- value + .else + movzwl (%rcx,%rax,1), %eax + SET_VREG %eax, rINSTq # fp[A] <- value + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_short_quick: /* 0xf2 */ +/* File: x86_64/op_iget_short_quick.S */ +/* File: x86_64/op_iget_quick.S */ + /* For: iget-quick, iget-boolean-quick, iget-byte-quick, iget-char-quick, iget-short-quick, iget-wide-quick */ + /* op vA, vB, offset@CCCC */ + movl rINST, %ecx # rcx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG %ecx, %rcx # vB (object we're operating on) + movzwq 2(rPC), %rax # eax <- field byte offset + testl %ecx, %ecx # is object null? + je common_errNullObject + andb $0xf,rINSTbl # rINST <- A + .if 0 + movq (%rcx,%rax,1), %rax + SET_WIDE_VREG %rax, rINSTq # fp[A] <- value + .else + movswl (%rcx,%rax,1), %eax + SET_VREG %eax, rINSTq # fp[A] <- value + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_lambda: /* 0xf3 */ +/* Transfer stub to alternate interpreter */ + jmp MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_f4: /* 0xf4 */ +/* File: x86_64/op_unused_f4.S */ +/* File: x86_64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + jmp MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_capture_variable: /* 0xf5 */ +/* Transfer stub to alternate interpreter */ + jmp MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_create_lambda: /* 0xf6 */ +/* Transfer stub to alternate interpreter */ + jmp MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_liberate_variable: /* 0xf7 */ +/* Transfer stub to alternate interpreter */ + jmp MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_box_lambda: /* 0xf8 */ +/* Transfer stub to alternate interpreter */ + jmp MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unbox_lambda: /* 0xf9 */ +/* Transfer stub to alternate interpreter */ + jmp MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_fa: /* 0xfa */ +/* File: x86_64/op_unused_fa.S */ +/* File: x86_64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + jmp MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_fb: /* 0xfb */ +/* File: x86_64/op_unused_fb.S */ +/* File: x86_64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + jmp MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_fc: /* 0xfc */ +/* File: x86_64/op_unused_fc.S */ +/* File: x86_64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + jmp MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_fd: /* 0xfd */ +/* File: x86_64/op_unused_fd.S */ +/* File: x86_64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + jmp MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_fe: /* 0xfe */ +/* File: x86_64/op_unused_fe.S */ +/* File: x86_64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + jmp MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_ff: /* 0xff */ +/* File: x86_64/op_unused_ff.S */ +/* File: x86_64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + jmp MterpFallback + + + .balign 128 + SIZE(SYMBOL(artMterpAsmInstructionStart),SYMBOL(artMterpAsmInstructionStart)) + .global SYMBOL(artMterpAsmInstructionEnd) +SYMBOL(artMterpAsmInstructionEnd): + +/* + * =========================================================================== + * Sister implementations + * =========================================================================== + */ + .global SYMBOL(artMterpAsmSisterStart) + FUNCTION_TYPE(SYMBOL(artMterpAsmSisterStart)) + .text + .balign 4 +SYMBOL(artMterpAsmSisterStart): + + SIZE(SYMBOL(artMterpAsmSisterStart),SYMBOL(artMterpAsmSisterStart)) + .global SYMBOL(artMterpAsmSisterEnd) +SYMBOL(artMterpAsmSisterEnd): + + + .global SYMBOL(artMterpAsmAltInstructionStart) + FUNCTION_TYPE(SYMBOL(artMterpAsmAltInstructionStart)) + .text + +SYMBOL(artMterpAsmAltInstructionStart) = .L_ALT_op_nop +/* ------------------------------ */ + .balign 128 +.L_ALT_op_nop: /* 0x00 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(0*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move: /* 0x01 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(1*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_from16: /* 0x02 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(2*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_16: /* 0x03 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(3*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_wide: /* 0x04 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(4*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_wide_from16: /* 0x05 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(5*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_wide_16: /* 0x06 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(6*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_object: /* 0x07 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(7*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_object_from16: /* 0x08 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(8*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_object_16: /* 0x09 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(9*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_result: /* 0x0a */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(10*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_result_wide: /* 0x0b */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(11*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_result_object: /* 0x0c */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(12*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_exception: /* 0x0d */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(13*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_return_void: /* 0x0e */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(14*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_return: /* 0x0f */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(15*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_return_wide: /* 0x10 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(16*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_return_object: /* 0x11 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(17*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_4: /* 0x12 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(18*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_16: /* 0x13 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(19*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const: /* 0x14 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(20*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_high16: /* 0x15 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(21*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_wide_16: /* 0x16 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(22*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_wide_32: /* 0x17 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(23*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_wide: /* 0x18 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(24*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_wide_high16: /* 0x19 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(25*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_string: /* 0x1a */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(26*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_string_jumbo: /* 0x1b */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(27*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_class: /* 0x1c */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(28*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_monitor_enter: /* 0x1d */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(29*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_monitor_exit: /* 0x1e */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(30*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_check_cast: /* 0x1f */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(31*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_instance_of: /* 0x20 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(32*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_array_length: /* 0x21 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(33*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_new_instance: /* 0x22 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(34*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_new_array: /* 0x23 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(35*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_filled_new_array: /* 0x24 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(36*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_filled_new_array_range: /* 0x25 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(37*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_fill_array_data: /* 0x26 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(38*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_throw: /* 0x27 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(39*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_goto: /* 0x28 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(40*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_goto_16: /* 0x29 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(41*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_goto_32: /* 0x2a */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(42*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_packed_switch: /* 0x2b */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(43*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sparse_switch: /* 0x2c */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(44*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_cmpl_float: /* 0x2d */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(45*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_cmpg_float: /* 0x2e */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(46*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_cmpl_double: /* 0x2f */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(47*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_cmpg_double: /* 0x30 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(48*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_cmp_long: /* 0x31 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(49*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_eq: /* 0x32 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(50*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_ne: /* 0x33 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(51*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_lt: /* 0x34 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(52*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_ge: /* 0x35 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(53*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_gt: /* 0x36 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(54*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_le: /* 0x37 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(55*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_eqz: /* 0x38 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(56*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_nez: /* 0x39 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(57*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_ltz: /* 0x3a */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(58*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_gez: /* 0x3b */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(59*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_gtz: /* 0x3c */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(60*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_lez: /* 0x3d */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(61*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_3e: /* 0x3e */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(62*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_3f: /* 0x3f */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(63*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_40: /* 0x40 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(64*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_41: /* 0x41 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(65*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_42: /* 0x42 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(66*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_43: /* 0x43 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(67*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aget: /* 0x44 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(68*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aget_wide: /* 0x45 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(69*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aget_object: /* 0x46 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(70*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aget_boolean: /* 0x47 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(71*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aget_byte: /* 0x48 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(72*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aget_char: /* 0x49 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(73*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aget_short: /* 0x4a */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(74*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aput: /* 0x4b */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(75*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aput_wide: /* 0x4c */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(76*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aput_object: /* 0x4d */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(77*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aput_boolean: /* 0x4e */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(78*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aput_byte: /* 0x4f */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(79*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aput_char: /* 0x50 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(80*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aput_short: /* 0x51 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(81*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget: /* 0x52 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(82*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_wide: /* 0x53 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(83*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_object: /* 0x54 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(84*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_boolean: /* 0x55 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(85*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_byte: /* 0x56 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(86*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_char: /* 0x57 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(87*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_short: /* 0x58 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(88*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput: /* 0x59 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(89*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_wide: /* 0x5a */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(90*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_object: /* 0x5b */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(91*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_boolean: /* 0x5c */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(92*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_byte: /* 0x5d */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(93*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_char: /* 0x5e */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(94*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_short: /* 0x5f */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(95*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sget: /* 0x60 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(96*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sget_wide: /* 0x61 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(97*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sget_object: /* 0x62 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(98*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sget_boolean: /* 0x63 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(99*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sget_byte: /* 0x64 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(100*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sget_char: /* 0x65 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(101*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sget_short: /* 0x66 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(102*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sput: /* 0x67 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(103*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sput_wide: /* 0x68 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(104*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sput_object: /* 0x69 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(105*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sput_boolean: /* 0x6a */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(106*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sput_byte: /* 0x6b */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(107*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sput_char: /* 0x6c */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(108*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sput_short: /* 0x6d */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(109*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_virtual: /* 0x6e */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(110*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_super: /* 0x6f */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(111*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_direct: /* 0x70 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(112*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_static: /* 0x71 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(113*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_interface: /* 0x72 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(114*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_return_void_no_barrier: /* 0x73 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(115*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_virtual_range: /* 0x74 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(116*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_super_range: /* 0x75 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(117*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_direct_range: /* 0x76 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(118*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_static_range: /* 0x77 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(119*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_interface_range: /* 0x78 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(120*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_79: /* 0x79 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(121*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_7a: /* 0x7a */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(122*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_neg_int: /* 0x7b */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(123*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_not_int: /* 0x7c */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(124*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_neg_long: /* 0x7d */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(125*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_not_long: /* 0x7e */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(126*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_neg_float: /* 0x7f */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(127*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_neg_double: /* 0x80 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(128*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_int_to_long: /* 0x81 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(129*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_int_to_float: /* 0x82 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(130*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_int_to_double: /* 0x83 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(131*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_long_to_int: /* 0x84 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(132*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_long_to_float: /* 0x85 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(133*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_long_to_double: /* 0x86 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(134*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_float_to_int: /* 0x87 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(135*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_float_to_long: /* 0x88 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(136*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_float_to_double: /* 0x89 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(137*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_double_to_int: /* 0x8a */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(138*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_double_to_long: /* 0x8b */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(139*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_double_to_float: /* 0x8c */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(140*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_int_to_byte: /* 0x8d */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(141*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_int_to_char: /* 0x8e */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(142*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_int_to_short: /* 0x8f */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(143*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_int: /* 0x90 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(144*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sub_int: /* 0x91 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(145*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_int: /* 0x92 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(146*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_int: /* 0x93 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(147*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_int: /* 0x94 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(148*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_and_int: /* 0x95 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(149*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_or_int: /* 0x96 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(150*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_xor_int: /* 0x97 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(151*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shl_int: /* 0x98 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(152*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shr_int: /* 0x99 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(153*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_ushr_int: /* 0x9a */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(154*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_long: /* 0x9b */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(155*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sub_long: /* 0x9c */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(156*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_long: /* 0x9d */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(157*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_long: /* 0x9e */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(158*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_long: /* 0x9f */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(159*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_and_long: /* 0xa0 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(160*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_or_long: /* 0xa1 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(161*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_xor_long: /* 0xa2 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(162*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shl_long: /* 0xa3 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(163*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shr_long: /* 0xa4 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(164*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_ushr_long: /* 0xa5 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(165*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_float: /* 0xa6 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(166*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sub_float: /* 0xa7 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(167*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_float: /* 0xa8 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(168*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_float: /* 0xa9 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(169*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_float: /* 0xaa */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(170*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_double: /* 0xab */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(171*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sub_double: /* 0xac */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(172*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_double: /* 0xad */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(173*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_double: /* 0xae */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(174*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_double: /* 0xaf */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(175*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_int_2addr: /* 0xb0 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(176*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sub_int_2addr: /* 0xb1 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(177*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_int_2addr: /* 0xb2 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(178*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_int_2addr: /* 0xb3 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(179*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_int_2addr: /* 0xb4 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(180*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_and_int_2addr: /* 0xb5 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(181*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_or_int_2addr: /* 0xb6 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(182*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_xor_int_2addr: /* 0xb7 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(183*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shl_int_2addr: /* 0xb8 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(184*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shr_int_2addr: /* 0xb9 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(185*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_ushr_int_2addr: /* 0xba */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(186*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_long_2addr: /* 0xbb */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(187*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sub_long_2addr: /* 0xbc */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(188*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_long_2addr: /* 0xbd */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(189*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_long_2addr: /* 0xbe */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(190*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_long_2addr: /* 0xbf */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(191*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_and_long_2addr: /* 0xc0 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(192*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_or_long_2addr: /* 0xc1 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(193*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_xor_long_2addr: /* 0xc2 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(194*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shl_long_2addr: /* 0xc3 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(195*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shr_long_2addr: /* 0xc4 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(196*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_ushr_long_2addr: /* 0xc5 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(197*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_float_2addr: /* 0xc6 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(198*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sub_float_2addr: /* 0xc7 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(199*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_float_2addr: /* 0xc8 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(200*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_float_2addr: /* 0xc9 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(201*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_float_2addr: /* 0xca */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(202*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_double_2addr: /* 0xcb */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(203*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sub_double_2addr: /* 0xcc */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(204*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_double_2addr: /* 0xcd */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(205*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_double_2addr: /* 0xce */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(206*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_double_2addr: /* 0xcf */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(207*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_int_lit16: /* 0xd0 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(208*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rsub_int: /* 0xd1 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(209*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_int_lit16: /* 0xd2 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(210*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_int_lit16: /* 0xd3 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(211*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_int_lit16: /* 0xd4 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(212*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_and_int_lit16: /* 0xd5 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(213*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_or_int_lit16: /* 0xd6 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(214*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_xor_int_lit16: /* 0xd7 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(215*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_int_lit8: /* 0xd8 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(216*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rsub_int_lit8: /* 0xd9 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(217*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_int_lit8: /* 0xda */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(218*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_int_lit8: /* 0xdb */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(219*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_int_lit8: /* 0xdc */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(220*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_and_int_lit8: /* 0xdd */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(221*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_or_int_lit8: /* 0xde */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(222*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_xor_int_lit8: /* 0xdf */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(223*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shl_int_lit8: /* 0xe0 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(224*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shr_int_lit8: /* 0xe1 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(225*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_ushr_int_lit8: /* 0xe2 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(226*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_quick: /* 0xe3 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(227*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_wide_quick: /* 0xe4 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(228*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_object_quick: /* 0xe5 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(229*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_quick: /* 0xe6 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(230*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_wide_quick: /* 0xe7 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(231*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_object_quick: /* 0xe8 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(232*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_virtual_quick: /* 0xe9 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(233*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_virtual_range_quick: /* 0xea */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(234*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_boolean_quick: /* 0xeb */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(235*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_byte_quick: /* 0xec */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(236*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_char_quick: /* 0xed */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(237*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_short_quick: /* 0xee */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(238*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_boolean_quick: /* 0xef */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(239*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_byte_quick: /* 0xf0 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(240*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_char_quick: /* 0xf1 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(241*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_short_quick: /* 0xf2 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(242*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_lambda: /* 0xf3 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(243*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_f4: /* 0xf4 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(244*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_capture_variable: /* 0xf5 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(245*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_create_lambda: /* 0xf6 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(246*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_liberate_variable: /* 0xf7 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(247*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_box_lambda: /* 0xf8 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(248*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unbox_lambda: /* 0xf9 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(249*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_fa: /* 0xfa */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(250*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_fb: /* 0xfb */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(251*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_fc: /* 0xfc */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(252*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_fd: /* 0xfd */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(253*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_fe: /* 0xfe */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(254*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_ff: /* 0xff */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(255*128) + + .balign 128 + SIZE(SYMBOL(artMterpAsmAltInstructionStart),SYMBOL(artMterpAsmAltInstructionStart)) + .global SYMBOL(artMterpAsmAltInstructionEnd) +SYMBOL(artMterpAsmAltInstructionEnd): +/* File: x86_64/footer.S */ +/* + * =========================================================================== + * Common subroutines and data + * =========================================================================== + */ + + .text + .align 2 + +/* + * We've detected a condition that will result in an exception, but the exception + * has not yet been thrown. Just bail out to the reference interpreter to deal with it. + * TUNING: for consistency, we may want to just go ahead and handle these here. + */ +#define MTERP_LOGGING 0 +common_errDivideByZero: + EXPORT_PC +#if MTERP_LOGGING + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpLogDivideByZeroException) +#endif + jmp MterpCommonFallback + +common_errArrayIndex: + EXPORT_PC +#if MTERP_LOGGING + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpLogArrayIndexException) +#endif + jmp MterpCommonFallback + +common_errNegativeArraySize: + EXPORT_PC +#if MTERP_LOGGING + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpLogNegativeArraySizeException) +#endif + jmp MterpCommonFallback + +common_errNoSuchMethod: + EXPORT_PC +#if MTERP_LOGGING + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpLogNoSuchMethodException) +#endif + jmp MterpCommonFallback + +common_errNullObject: + EXPORT_PC +#if MTERP_LOGGING + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpLogNullObjectException) +#endif + jmp MterpCommonFallback + +common_exceptionThrown: + EXPORT_PC +#if MTERP_LOGGING + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpLogExceptionThrownException) +#endif + jmp MterpCommonFallback + +MterpSuspendFallback: + EXPORT_PC +#if MTERP_LOGGING + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + movl THREAD_FLAGS_OFFSET(rSELF), OUT_32_ARG2 + call SYMBOL(MterpLogSuspendFallback) +#endif + jmp MterpCommonFallback + +/* + * If we're here, something is out of the ordinary. If there is a pending + * exception, handle it. Otherwise, roll back and retry with the reference + * interpreter. + */ +MterpPossibleException: + cmpq $0, THREAD_EXCEPTION_OFFSET(rSELF) + jz MterpFallback + /* intentional fallthrough - handle pending exception. */ + +/* + * On return from a runtime helper routine, we've found a pending exception. + * Can we handle it here - or need to bail out to caller? + * + */ +MterpException: + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpHandleException) + testb %al, %al + jz MterpExceptionReturn + REFRESH_IBASE + movq OFF_FP_CODE_ITEM(rFP), %rax + mov OFF_FP_DEX_PC(rFP), %ecx + leaq CODEITEM_INSNS_OFFSET(%rax), rPC + leaq (rPC, %rcx, 2), rPC + movq rPC, OFF_FP_DEX_PC_PTR(rFP) + /* resume execution at catch block */ + FETCH_INST + GOTO_NEXT + /* NOTE: no fallthrough */ + +/* + * Check for suspend check request. Assumes rINST already loaded, rPC advanced and + * still needs to get the opcode and branch to it, and flags are in lr. + */ +MterpCheckSuspendAndContinue: + REFRESH_IBASE + testl $(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(rSELF) + jz 1f + EXPORT_PC + movq rSELF, OUT_ARG0 + call SYMBOL(MterpSuspendCheck) +1: + GOTO_NEXT + +/* + * Bail out to reference interpreter. + */ +MterpFallback: + EXPORT_PC +#if MTERP_LOGGING + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpLogFallback) +#endif +MterpCommonFallback: + xorl %eax, %eax + jmp MterpDone + +/* + * On entry: + * uint32_t* rFP (should still be live, pointer to base of vregs) + */ +MterpExceptionReturn: + movl $1, %eax + jmp MterpDone +MterpReturn: + movq OFF_FP_RESULT_REGISTER(rFP), %rdx + movq %rax, (%rdx) + movl $1, %eax +MterpDone: + /* pop up frame */ + addq $FRAME_SIZE, %rsp + .cfi_adjust_cfa_offset -FRAME_SIZE + + /* Restore callee save register */ + POP %r15 + POP %r14 + POP %r13 + POP %r12 + POP %rbp + POP %rbx + ret + .cfi_endproc + SIZE(ExecuteMterpImpl,ExecuteMterpImpl) + diff --git a/runtime/interpreter/mterp/rebuild.sh b/runtime/interpreter/mterp/rebuild.sh index 2b5f339869..e3f043749f 100755 --- a/runtime/interpreter/mterp/rebuild.sh +++ b/runtime/interpreter/mterp/rebuild.sh @@ -21,4 +21,4 @@ set -e # for arch in arm x86 mips arm64 x86_64 mips64; do TARGET_ARCH_EXT=$arch make -f Makefile_mterp; done -for arch in arm x86 mips arm64 ; do TARGET_ARCH_EXT=$arch make -f Makefile_mterp; done +for arch in arm x86 mips arm64 x86_64 ; do TARGET_ARCH_EXT=$arch make -f Makefile_mterp; done diff --git a/runtime/interpreter/mterp/x86_64/alt_stub.S b/runtime/interpreter/mterp/x86_64/alt_stub.S new file mode 100644 index 0000000000..6fcebbba3c --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/alt_stub.S @@ -0,0 +1,17 @@ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(${opnum}*${handler_size_bytes}) diff --git a/runtime/interpreter/mterp/x86_64/bincmp.S b/runtime/interpreter/mterp/x86_64/bincmp.S new file mode 100644 index 0000000000..5e4225fd86 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/bincmp.S @@ -0,0 +1,28 @@ +/* + * Generic two-operand compare-and-branch operation. Provide a "revcmp" + * fragment that specifies the *reverse* comparison to perform, e.g. + * for "if-le" you would use "gt". + * + * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le + */ + /* if-cmp vA, vB, +CCCC */ + movl rINST, %ecx # rcx <- A+ + sarl $$4, rINST # rINST <- B + andb $$0xf, %cl # rcx <- A + GET_VREG %eax, %rcx # eax <- vA + cmpl VREG_ADDRESS(rINSTq), %eax # compare (vA, vB) + movl $$2, %eax # assume not taken + j${revcmp} 1f + movswq 2(rPC),%rax # Get signed branch offset +1: + addq %rax, %rax # rax <- AA * 2 + leaq (rPC, %rax), rPC + FETCH_INST + jg 2f # AA * 2 > 0 => no suspend check +#if MTERP_SUSPEND + REFRESH_IBASE +#else + jmp MterpCheckSuspendAndContinue +#endif +2: + GOTO_NEXT diff --git a/runtime/interpreter/mterp/x86_64/bindiv.S b/runtime/interpreter/mterp/x86_64/bindiv.S new file mode 100644 index 0000000000..e10d1dc4b1 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/bindiv.S @@ -0,0 +1,34 @@ +%default {"result":"","second":"","wide":"","suffix":"","rem":"0","ext":"cdq"} +/* + * 32-bit binary div/rem operation. Handles special case of op1=-1. + */ + /* div/rem vAA, vBB, vCC */ + movzbq 2(rPC), %rax # rax <- BB + movzbq 3(rPC), %rcx # rcx <- CC + .if $wide + GET_WIDE_VREG %rax, %rax # eax <- vBB + GET_WIDE_VREG $second, %rcx # ecx <- vCC + .else + GET_VREG %eax, %rax # eax <- vBB + GET_VREG $second, %rcx # ecx <- vCC + .endif + test${suffix} $second, $second + jz common_errDivideByZero + cmp${suffix} $$-1, $second + je 2f + $ext # rdx:rax <- sign-extended of rax + idiv${suffix} $second +1: + .if $wide + SET_WIDE_VREG $result, rINSTq # eax <- vBB + .else + SET_VREG $result, rINSTq # eax <- vBB + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 +2: + .if $rem + xor${suffix} $result, $result + .else + neg${suffix} $result + .endif + jmp 1b diff --git a/runtime/interpreter/mterp/x86_64/bindiv2addr.S b/runtime/interpreter/mterp/x86_64/bindiv2addr.S new file mode 100644 index 0000000000..8b9bc953d2 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/bindiv2addr.S @@ -0,0 +1,35 @@ +%default {"result":"","second":"","wide":"","suffix":"","rem":"0","ext":"cdq"} +/* + * 32-bit binary div/rem operation. Handles special case of op1=-1. + */ + /* div/rem/2addr vA, vB */ + movl rINST, %ecx # rcx <- BA + sarl $$4, %ecx # rcx <- B + andb $$0xf, rINSTbl # rINST <- A + .if $wide + GET_WIDE_VREG %rax, rINSTq # eax <- vA + GET_WIDE_VREG $second, %rcx # ecx <- vB + .else + GET_VREG %eax, rINSTq # eax <- vA + GET_VREG $second, %rcx # ecx <- vB + .endif + test${suffix} $second, $second + jz common_errDivideByZero + cmp${suffix} $$-1, $second + je 2f + $ext # rdx:rax <- sign-extended of rax + idiv${suffix} $second +1: + .if $wide + SET_WIDE_VREG $result, rINSTq # vA <- result + .else + SET_VREG $result, rINSTq # vA <- result + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 +2: + .if $rem + xor${suffix} $result, $result + .else + neg${suffix} $result + .endif + jmp 1b diff --git a/runtime/interpreter/mterp/x86_64/bindivLit16.S b/runtime/interpreter/mterp/x86_64/bindivLit16.S new file mode 100644 index 0000000000..80dbce2975 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/bindivLit16.S @@ -0,0 +1,27 @@ +%default {"result":"","rem":"0"} +/* + * 32-bit binary div/rem operation. Handles special case of op1=-1. + */ + /* div/rem/lit16 vA, vB, #+CCCC */ + /* Need A in rINST, ssssCCCC in ecx, vB in eax */ + movl rINST, %eax # rax <- 000000BA + sarl $$4, %eax # eax <- B + GET_VREG %eax, %rax # eax <- vB + movswl 2(rPC), %ecx # ecx <- ssssCCCC + andb $$0xf, rINSTbl # rINST <- A + testl %ecx, %ecx + jz common_errDivideByZero + cmpl $$-1, %ecx + je 2f + cdq # rax <- sign-extended of eax + idivl %ecx +1: + SET_VREG $result, rINSTq # vA <- result + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 +2: + .if $rem + xorl $result, $result + .else + negl $result + .endif + jmp 1b diff --git a/runtime/interpreter/mterp/x86_64/bindivLit8.S b/runtime/interpreter/mterp/x86_64/bindivLit8.S new file mode 100644 index 0000000000..ab535f3fb0 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/bindivLit8.S @@ -0,0 +1,25 @@ +%default {"result":"","rem":"0"} +/* + * 32-bit div/rem "lit8" binary operation. Handles special case of + * op0=minint & op1=-1 + */ + /* div/rem/lit8 vAA, vBB, #+CC */ + movzbq 2(rPC), %rax # eax <- BB + movsbl 3(rPC), %ecx # ecx <- ssssssCC + GET_VREG %eax, %rax # eax <- rBB + testl %ecx, %ecx + je common_errDivideByZero + cmpl $$-1, %ecx + je 2f + cdq # rax <- sign-extended of eax + idivl %ecx +1: + SET_VREG $result, rINSTq # vA <- result + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 +2: + .if $rem + xorl $result, $result + .else + negl $result + .endif + jmp 1b diff --git a/runtime/interpreter/mterp/x86_64/binop.S b/runtime/interpreter/mterp/x86_64/binop.S new file mode 100644 index 0000000000..962dd61eea --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/binop.S @@ -0,0 +1,17 @@ +%default {"result":"%eax"} +/* + * Generic 32-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = eax op (rFP,%ecx,4)". + * This could be an x86 instruction or a function call. (If the result + * comes back in a register other than eax, you can override "result".) + * + * For: add-int, sub-int, and-int, or-int, + * xor-int, shl-int, shr-int, ushr-int + */ + /* binop vAA, vBB, vCC */ + movzbq 2(rPC), %rax # rax <- BB + movzbq 3(rPC), %rcx # rcx <- CC + GET_VREG %eax, %rax # eax <- vBB + $instr # ex: addl (rFP,%rcx,4),%eax + SET_VREG $result, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/binop1.S b/runtime/interpreter/mterp/x86_64/binop1.S new file mode 100644 index 0000000000..bdd57325aa --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/binop1.S @@ -0,0 +1,19 @@ +%default {"wide":"0"} +/* + * Generic 32-bit binary operation in which both operands loaded to + * registers (op0 in eax, op1 in ecx). + */ + /* binop vAA, vBB, vCC */ + movzbq 2(rPC), %rax # eax <- BB + movzbq 3(rPC), %rcx # ecx <- CC + GET_VREG %ecx, %rcx # eax <- vCC + .if $wide + GET_WIDE_VREG %rax, %rax # rax <- vBB + $instr # ex: addl %ecx,%eax + SET_WIDE_VREG %rax, rINSTq + .else + GET_VREG %eax, %rax # eax <- vBB + $instr # ex: addl %ecx,%eax + SET_VREG %eax, rINSTq + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/binop2addr.S b/runtime/interpreter/mterp/x86_64/binop2addr.S new file mode 100644 index 0000000000..4448a815e9 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/binop2addr.S @@ -0,0 +1,19 @@ +%default {"result":"%eax"} +/* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = r0 op r1". + * This could be an instruction or a function call. + * + * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, + * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, + * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr, + * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr + */ + /* binop/2addr vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $$4, rINST # rINST <- B + andb $$0xf, %cl # ecx <- A + GET_VREG %eax, rINSTq # eax <- vB + $instr # for ex: addl %eax,(rFP,%ecx,4) + CLEAR_REF %rcx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/binopLit16.S b/runtime/interpreter/mterp/x86_64/binopLit16.S new file mode 100644 index 0000000000..de43b53d5c --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/binopLit16.S @@ -0,0 +1,19 @@ +%default {"result":"%eax"} +/* + * Generic 32-bit "lit16" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = eax op ecx". + * This could be an x86 instruction or a function call. (If the result + * comes back in a register other than eax, you can override "result".) + * + * For: add-int/lit16, rsub-int, + * and-int/lit16, or-int/lit16, xor-int/lit16 + */ + /* binop/lit16 vA, vB, #+CCCC */ + movl rINST, %eax # rax <- 000000BA + sarl $$4, %eax # eax <- B + GET_VREG %eax, %rax # eax <- vB + andb $$0xf, rINSTbl # rINST <- A + movswl 2(rPC), %ecx # ecx <- ssssCCCC + $instr # for example: addl %ecx, %eax + SET_VREG $result, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/binopLit8.S b/runtime/interpreter/mterp/x86_64/binopLit8.S new file mode 100644 index 0000000000..995002b7e4 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/binopLit8.S @@ -0,0 +1,18 @@ +%default {"result":"%eax"} +/* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = eax op ecx". + * This could be an x86 instruction or a function call. (If the result + * comes back in a register other than r0, you can override "result".) + * + * For: add-int/lit8, rsub-int/lit8 + * and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + /* binop/lit8 vAA, vBB, #+CC */ + movzbq 2(rPC), %rax # rax <- BB + movsbl 3(rPC), %ecx # rcx <- ssssssCC + GET_VREG %eax, %rax # eax <- rBB + $instr # ex: addl %ecx,%eax + SET_VREG $result, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/binopWide.S b/runtime/interpreter/mterp/x86_64/binopWide.S new file mode 100644 index 0000000000..f92f18e013 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/binopWide.S @@ -0,0 +1,10 @@ +/* + * Generic 64-bit binary operation. + */ + /* binop vAA, vBB, vCC */ + movzbq 2(rPC), %rax # eax <- BB + movzbq 3(rPC), %rcx # ecx <- CC + GET_WIDE_VREG %rax, %rax # rax <- v[BB] + $instr # ex: addq (rFP,%rcx,4),%rax + SET_WIDE_VREG %rax, rINSTq # v[AA] <- rax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/binopWide2addr.S b/runtime/interpreter/mterp/x86_64/binopWide2addr.S new file mode 100644 index 0000000000..d9e6cfbc9d --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/binopWide2addr.S @@ -0,0 +1,11 @@ +/* + * Generic 64-bit binary operation. + */ + /* binop/2addr vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $$4, rINST # rINST <- B + andb $$0xf, %cl # ecx <- A + GET_WIDE_VREG %rax, rINSTq # rax <- vB + $instr # for ex: addq %rax,(rFP,%rcx,4) + CLEAR_WIDE_REF %rcx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/cvtfp_int.S b/runtime/interpreter/mterp/x86_64/cvtfp_int.S new file mode 100644 index 0000000000..1472bd26bd --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/cvtfp_int.S @@ -0,0 +1,27 @@ +%default {"fp_suffix":"","i_suffix":"","max_const":"","result_reg":"","wide":""} +/* On fp to int conversions, Java requires that + * if the result > maxint, it should be clamped to maxint. If it is less + * than minint, it should be clamped to minint. If it is a nan, the result + * should be zero. Further, the rounding mode is to truncate. + */ + /* float/double to int/long vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $$4, rINST # rINST <- B + andb $$0xf, %cl # ecx <- A + movs${fp_suffix} VREG_ADDRESS(rINSTq), %xmm0 + mov${i_suffix} ${max_const}, ${result_reg} + cvtsi2s${fp_suffix}${i_suffix} ${result_reg}, %xmm1 + comis${fp_suffix} %xmm1, %xmm0 + jae 1f + jp 2f + cvtts${fp_suffix}2si${i_suffix} %xmm0, ${result_reg} + jmp 1f +2: + xor${i_suffix} ${result_reg}, ${result_reg} +1: + .if $wide + SET_WIDE_VREG ${result_reg}, %rcx + .else + SET_VREG ${result_reg}, %rcx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/entry.S b/runtime/interpreter/mterp/x86_64/entry.S new file mode 100644 index 0000000000..69b2371dea --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/entry.S @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * Interpreter entry point. + */ + + .text + .global SYMBOL(ExecuteMterpImpl) + FUNCTION_TYPE(ExecuteMterpImpl) + +/* + * On entry: + * 0 Thread* self + * 1 code_item + * 2 ShadowFrame + * 3 JValue* result_register + * + */ + +SYMBOL(ExecuteMterpImpl): + .cfi_startproc + .cfi_def_cfa rsp, 8 + + /* Spill callee save regs */ + PUSH %rbx + PUSH %rbp + PUSH %r12 + PUSH %r13 + PUSH %r14 + PUSH %r15 + + /* Allocate frame */ + subq $$FRAME_SIZE, %rsp + .cfi_adjust_cfa_offset FRAME_SIZE + + /* Remember the return register */ + movq IN_ARG3, SHADOWFRAME_RESULT_REGISTER_OFFSET(IN_ARG2) + + /* Remember the code_item */ + movq IN_ARG1, SHADOWFRAME_CODE_ITEM_OFFSET(IN_ARG2) + + /* set up "named" registers */ + movl SHADOWFRAME_NUMBER_OF_VREGS_OFFSET(IN_ARG2), %eax + leaq SHADOWFRAME_VREGS_OFFSET(IN_ARG2), rFP + leaq (rFP, %rax, 4), rREFS + movl SHADOWFRAME_DEX_PC_OFFSET(IN_ARG2), %eax + leaq CODEITEM_INSNS_OFFSET(IN_ARG1), rPC + leaq (rPC, %rax, 2), rPC + EXPORT_PC + + /* Starting ibase */ + movq IN_ARG0, rSELF + REFRESH_IBASE + + /* start executing the instruction at rPC */ + FETCH_INST + GOTO_NEXT + /* NOTE: no fallthrough */ diff --git a/runtime/interpreter/mterp/x86_64/fallback.S b/runtime/interpreter/mterp/x86_64/fallback.S new file mode 100644 index 0000000000..8d61166f63 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/fallback.S @@ -0,0 +1,3 @@ +/* Transfer stub to alternate interpreter */ + jmp MterpFallback + diff --git a/runtime/interpreter/mterp/x86_64/footer.S b/runtime/interpreter/mterp/x86_64/footer.S new file mode 100644 index 0000000000..cb60c0155e --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/footer.S @@ -0,0 +1,165 @@ +/* + * =========================================================================== + * Common subroutines and data + * =========================================================================== + */ + + .text + .align 2 + +/* + * We've detected a condition that will result in an exception, but the exception + * has not yet been thrown. Just bail out to the reference interpreter to deal with it. + * TUNING: for consistency, we may want to just go ahead and handle these here. + */ +#define MTERP_LOGGING 0 +common_errDivideByZero: + EXPORT_PC +#if MTERP_LOGGING + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpLogDivideByZeroException) +#endif + jmp MterpCommonFallback + +common_errArrayIndex: + EXPORT_PC +#if MTERP_LOGGING + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpLogArrayIndexException) +#endif + jmp MterpCommonFallback + +common_errNegativeArraySize: + EXPORT_PC +#if MTERP_LOGGING + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpLogNegativeArraySizeException) +#endif + jmp MterpCommonFallback + +common_errNoSuchMethod: + EXPORT_PC +#if MTERP_LOGGING + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpLogNoSuchMethodException) +#endif + jmp MterpCommonFallback + +common_errNullObject: + EXPORT_PC +#if MTERP_LOGGING + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpLogNullObjectException) +#endif + jmp MterpCommonFallback + +common_exceptionThrown: + EXPORT_PC +#if MTERP_LOGGING + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpLogExceptionThrownException) +#endif + jmp MterpCommonFallback + +MterpSuspendFallback: + EXPORT_PC +#if MTERP_LOGGING + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + movl THREAD_FLAGS_OFFSET(rSELF), OUT_32_ARG2 + call SYMBOL(MterpLogSuspendFallback) +#endif + jmp MterpCommonFallback + +/* + * If we're here, something is out of the ordinary. If there is a pending + * exception, handle it. Otherwise, roll back and retry with the reference + * interpreter. + */ +MterpPossibleException: + cmpq $$0, THREAD_EXCEPTION_OFFSET(rSELF) + jz MterpFallback + /* intentional fallthrough - handle pending exception. */ + +/* + * On return from a runtime helper routine, we've found a pending exception. + * Can we handle it here - or need to bail out to caller? + * + */ +MterpException: + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpHandleException) + testb %al, %al + jz MterpExceptionReturn + REFRESH_IBASE + movq OFF_FP_CODE_ITEM(rFP), %rax + mov OFF_FP_DEX_PC(rFP), %ecx + leaq CODEITEM_INSNS_OFFSET(%rax), rPC + leaq (rPC, %rcx, 2), rPC + movq rPC, OFF_FP_DEX_PC_PTR(rFP) + /* resume execution at catch block */ + FETCH_INST + GOTO_NEXT + /* NOTE: no fallthrough */ + +/* + * Check for suspend check request. Assumes rINST already loaded, rPC advanced and + * still needs to get the opcode and branch to it, and flags are in lr. + */ +MterpCheckSuspendAndContinue: + REFRESH_IBASE + testl $$(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(rSELF) + jz 1f + EXPORT_PC + movq rSELF, OUT_ARG0 + call SYMBOL(MterpSuspendCheck) +1: + GOTO_NEXT + +/* + * Bail out to reference interpreter. + */ +MterpFallback: + EXPORT_PC +#if MTERP_LOGGING + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpLogFallback) +#endif +MterpCommonFallback: + xorl %eax, %eax + jmp MterpDone + +/* + * On entry: + * uint32_t* rFP (should still be live, pointer to base of vregs) + */ +MterpExceptionReturn: + movl $$1, %eax + jmp MterpDone +MterpReturn: + movq OFF_FP_RESULT_REGISTER(rFP), %rdx + movq %rax, (%rdx) + movl $$1, %eax +MterpDone: + /* pop up frame */ + addq $$FRAME_SIZE, %rsp + .cfi_adjust_cfa_offset -FRAME_SIZE + + /* Restore callee save register */ + POP %r15 + POP %r14 + POP %r13 + POP %r12 + POP %rbp + POP %rbx + ret + .cfi_endproc + SIZE(ExecuteMterpImpl,ExecuteMterpImpl) diff --git a/runtime/interpreter/mterp/x86_64/fpcmp.S b/runtime/interpreter/mterp/x86_64/fpcmp.S new file mode 100644 index 0000000000..806bc2b1ef --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/fpcmp.S @@ -0,0 +1,35 @@ +%default {"suff":"d","nanval":"pos"} +/* + * Compare two floating-point values. Puts 0, 1, or -1 into the + * destination register based on the results of the comparison. + * + * int compare(x, y) { + * if (x == y) { + * return 0; + * } else if (x < y) { + * return -1; + * } else if (x > y) { + * return 1; + * } else { + * return nanval ? 1 : -1; + * } + * } + */ + /* op vAA, vBB, vCC */ + movzbq 3(rPC), %rcx # ecx<- CC + movzbq 2(rPC), %rax # eax<- BB + movs${suff} VREG_ADDRESS(%rax), %xmm0 + xor %eax, %eax + ucomis${suff} VREG_ADDRESS(%rcx), %xmm0 + jp .L${opcode}_nan_is_${nanval} + je .L${opcode}_finish + jb .L${opcode}_less +.L${opcode}_nan_is_pos: + addb $$1, %al + jmp .L${opcode}_finish +.L${opcode}_nan_is_neg: +.L${opcode}_less: + movl $$-1, %eax +.L${opcode}_finish: + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/fpcvt.S b/runtime/interpreter/mterp/x86_64/fpcvt.S new file mode 100644 index 0000000000..657869e0bd --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/fpcvt.S @@ -0,0 +1,17 @@ +%default {"source_suffix":"","dest_suffix":"","wide":""} +/* + * Generic 32-bit FP conversion operation. + */ + /* unop vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $$4, rINST # rINST <- B + andb $$0xf, %cl # ecx <- A + cvts${source_suffix}2s${dest_suffix} VREG_ADDRESS(rINSTq), %xmm0 + .if $wide + movsd %xmm0, VREG_ADDRESS(%rcx) + CLEAR_WIDE_REF %rcx + .else + movss %xmm0, VREG_ADDRESS(%rcx) + CLEAR_REF %rcx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/header.S b/runtime/interpreter/mterp/x86_64/header.S new file mode 100644 index 0000000000..dfc7b53de8 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/header.S @@ -0,0 +1,293 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + Art assembly interpreter notes: + + First validate assembly code by implementing ExecuteXXXImpl() style body (doesn't + handle invoke, allows higher-level code to create frame & shadow frame. + + Once that's working, support direct entry code & eliminate shadow frame (and + excess locals allocation. + + Some (hopefully) temporary ugliness. We'll treat rFP as pointing to the + base of the vreg array within the shadow frame. Access the other fields, + dex_pc_, method_ and number_of_vregs_ via negative offsets. For now, we'll continue + the shadow frame mechanism of double-storing object references - via rFP & + number_of_vregs_. + + */ + +/* +x86_64 ABI general notes: + +Caller save set: + rax, rdx, rcx, rsi, rdi, r8-r11, st(0)-st(7) +Callee save set: + rbx, rbp, r12-r15 +Return regs: + 32-bit in eax + 64-bit in rax + fp on xmm0 + +First 8 fp parameters came in xmm0-xmm7. +First 6 non-fp parameters came in rdi, rsi, rdx, rcx, r8, r9. +Other parameters passed on stack, pushed right-to-left. On entry to target, first +param is at 8(%esp). Traditional entry code is: + +Stack must be 16-byte aligned to support SSE in native code. + +If we're not doing variable stack allocation (alloca), the frame pointer can be +eliminated and all arg references adjusted to be esp relative. +*/ + +/* +Mterp and x86_64 notes: + +Some key interpreter variables will be assigned to registers. + + nick reg purpose + rSELF rbp pointer to ThreadSelf. + rPC r12 interpreted program counter, used for fetching instructions + rFP r13 interpreted frame pointer, used for accessing locals and args + rINSTw bx first 16-bit code of current instruction + rINSTbl bl opcode portion of instruction word + rINSTbh bh high byte of inst word, usually contains src/tgt reg names + rIBASE r14 base of instruction handler table + rREFS r15 base of object references in shadow frame. + +Notes: + o High order 16 bits of ebx must be zero on entry to handler + o rPC, rFP, rINSTw/rINSTbl valid on handler entry and exit + o eax and ecx are scratch, rINSTw/ebx sometimes scratch + +Macros are provided for common operations. Each macro MUST emit only +one instruction to make instruction-counting easier. They MUST NOT alter +unspecified registers or condition codes. +*/ + +/* + * This is a #include, not a %include, because we want the C pre-processor + * to expand the macros into assembler assignment statements. + */ +#include "asm_support.h" + +/* + * Handle mac compiler specific + */ +#if defined(__APPLE__) + #define MACRO_LITERAL(value) $$(value) + #define FUNCTION_TYPE(name) + #define SIZE(start,end) + // Mac OS' symbols have an _ prefix. + #define SYMBOL(name) _ ## name +#else + #define MACRO_LITERAL(value) $$value + #define FUNCTION_TYPE(name) .type name, @function + #define SIZE(start,end) .size start, .-end + #define SYMBOL(name) name +#endif + +.macro PUSH _reg + pushq \_reg + .cfi_adjust_cfa_offset 8 + .cfi_rel_offset \_reg, 0 +.endm + +.macro POP _reg + popq \_reg + .cfi_adjust_cfa_offset -8 + .cfi_restore \_reg +.endm + +/* Frame size must be 16-byte aligned. + * Remember about 8 bytes for return address + 6 * 8 for spills. + */ +#define FRAME_SIZE 8 + +/* Frame diagram while executing ExecuteMterpImpl, high to low addresses */ +#define IN_ARG3 %rcx +#define IN_ARG2 %rdx +#define IN_ARG1 %rsi +#define IN_ARG0 %rdi +/* Out Args */ +#define OUT_ARG3 %rcx +#define OUT_ARG2 %rdx +#define OUT_ARG1 %rsi +#define OUT_ARG0 %rdi +#define OUT_32_ARG3 %ecx +#define OUT_32_ARG2 %edx +#define OUT_32_ARG1 %esi +#define OUT_32_ARG0 %edi +#define OUT_FP_ARG1 %xmm1 +#define OUT_FP_ARG0 %xmm0 + +/* During bringup, we'll use the shadow frame model instead of rFP */ +/* single-purpose registers, given names for clarity */ +#define rSELF %rbp +#define rPC %r12 +#define rFP %r13 +#define rINST %ebx +#define rINSTq %rbx +#define rINSTw %bx +#define rINSTbh %bh +#define rINSTbl %bl +#define rIBASE %r14 +#define rREFS %r15 + +/* + * Instead of holding a pointer to the shadow frame, we keep rFP at the base of the vregs. So, + * to access other shadow frame fields, we need to use a backwards offset. Define those here. + */ +#define OFF_FP(a) (a - SHADOWFRAME_VREGS_OFFSET) +#define OFF_FP_NUMBER_OF_VREGS OFF_FP(SHADOWFRAME_NUMBER_OF_VREGS_OFFSET) +#define OFF_FP_DEX_PC OFF_FP(SHADOWFRAME_DEX_PC_OFFSET) +#define OFF_FP_LINK OFF_FP(SHADOWFRAME_LINK_OFFSET) +#define OFF_FP_METHOD OFF_FP(SHADOWFRAME_METHOD_OFFSET) +#define OFF_FP_RESULT_REGISTER OFF_FP(SHADOWFRAME_RESULT_REGISTER_OFFSET) +#define OFF_FP_DEX_PC_PTR OFF_FP(SHADOWFRAME_DEX_PC_PTR_OFFSET) +#define OFF_FP_CODE_ITEM OFF_FP(SHADOWFRAME_CODE_ITEM_OFFSET) +#define OFF_FP_SHADOWFRAME (-SHADOWFRAME_VREGS_OFFSET) + +/* + * + * The reference interpreter performs explicit suspect checks, which is somewhat wasteful. + * Dalvik's interpreter folded suspend checks into the jump table mechanism, and eventually + * mterp should do so as well. + */ +#define MTERP_SUSPEND 0 + +/* + * "export" the PC to dex_pc field in the shadow frame, f/b/o future exception objects. Must + * be done *before* something throws. + * + * It's okay to do this more than once. + * + * NOTE: the fast interpreter keeps track of dex pc as a direct pointer to the mapped + * dex byte codes. However, the rest of the runtime expects dex pc to be an instruction + * offset into the code_items_[] array. For effiency, we will "export" the + * current dex pc as a direct pointer using the EXPORT_PC macro, and rely on GetDexPC + * to convert to a dex pc when needed. + */ +.macro EXPORT_PC + movq rPC, OFF_FP_DEX_PC_PTR(rFP) +.endm + +/* + * Refresh handler table. + * IBase handles uses the caller save register so we must restore it after each call. + * Also it is used as a result of some 64-bit operations (like imul) and we should + * restore it in such cases also. + * + */ +.macro REFRESH_IBASE + movq THREAD_CURRENT_IBASE_OFFSET(rSELF), rIBASE +.endm + +/* + * Refresh rINST. + * At enter to handler rINST does not contain the opcode number. + * However some utilities require the full value, so this macro + * restores the opcode number. + */ +.macro REFRESH_INST _opnum + movb rINSTbl, rINSTbh + movb $$\_opnum, rINSTbl +.endm + +/* + * Fetch the next instruction from rPC into rINSTw. Does not advance rPC. + */ +.macro FETCH_INST + movzwq (rPC), rINSTq +.endm + +/* + * Remove opcode from rINST, compute the address of handler and jump to it. + */ +.macro GOTO_NEXT + movzx rINSTbl,%eax + movzbl rINSTbh,rINST + shll MACRO_LITERAL(${handler_size_bits}), %eax + addq rIBASE, %rax + jmp *%rax +.endm + +/* + * Advance rPC by instruction count. + */ +.macro ADVANCE_PC _count + leaq 2*\_count(rPC), rPC +.endm + +/* + * Advance rPC by instruction count, fetch instruction and jump to handler. + */ +.macro ADVANCE_PC_FETCH_AND_GOTO_NEXT _count + ADVANCE_PC \_count + FETCH_INST + GOTO_NEXT +.endm + +/* + * Get/set the 32-bit value from a Dalvik register. + */ +#define VREG_ADDRESS(_vreg) (rFP,_vreg,4) +#define VREG_REF_ADDRESS(_vreg) (rREFS,_vreg,4) + +.macro GET_VREG _reg _vreg + movl (rFP,\_vreg,4), \_reg +.endm + +/* Read wide value. */ +.macro GET_WIDE_VREG _reg _vreg + movq (rFP,\_vreg,4), \_reg +.endm + +.macro SET_VREG _reg _vreg + movl \_reg, (rFP,\_vreg,4) + movl MACRO_LITERAL(0), (rREFS,\_vreg,4) +.endm + +/* Write wide value. reg is clobbered. */ +.macro SET_WIDE_VREG _reg _vreg + movq \_reg, (rFP,\_vreg,4) + xorq \_reg, \_reg + movq \_reg, (rREFS,\_vreg,4) +.endm + +.macro SET_VREG_OBJECT _reg _vreg + movl \_reg, (rFP,\_vreg,4) + movl \_reg, (rREFS,\_vreg,4) +.endm + +.macro GET_VREG_HIGH _reg _vreg + movl 4(rFP,\_vreg,4), \_reg +.endm + +.macro SET_VREG_HIGH _reg _vreg + movl \_reg, 4(rFP,\_vreg,4) + movl MACRO_LITERAL(0), 4(rREFS,\_vreg,4) +.endm + +.macro CLEAR_REF _vreg + movl MACRO_LITERAL(0), (rREFS,\_vreg,4) +.endm + +.macro CLEAR_WIDE_REF _vreg + movl MACRO_LITERAL(0), (rREFS,\_vreg,4) + movl MACRO_LITERAL(0), 4(rREFS,\_vreg,4) +.endm diff --git a/runtime/interpreter/mterp/x86_64/invoke.S b/runtime/interpreter/mterp/x86_64/invoke.S new file mode 100644 index 0000000000..86eccdbf91 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/invoke.S @@ -0,0 +1,17 @@ +%default { "helper":"UndefinedInvokeHandler" } +/* + * Generic invoke handler wrapper. + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + .extern $helper + EXPORT_PC + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + movq rPC, OUT_ARG2 + REFRESH_INST ${opnum} + movl rINST, OUT_32_ARG3 + call SYMBOL($helper) + testb %al, %al + jz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 diff --git a/runtime/interpreter/mterp/x86_64/op_add_double.S b/runtime/interpreter/mterp/x86_64/op_add_double.S new file mode 100644 index 0000000000..cb462cb816 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_add_double.S @@ -0,0 +1 @@ +%include "x86_64/sseBinop.S" {"instr":"adds","suff":"d"} diff --git a/runtime/interpreter/mterp/x86_64/op_add_double_2addr.S b/runtime/interpreter/mterp/x86_64/op_add_double_2addr.S new file mode 100644 index 0000000000..063bde3fb3 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_add_double_2addr.S @@ -0,0 +1 @@ +%include "x86_64/sseBinop2Addr.S" {"instr":"adds","suff":"d"} diff --git a/runtime/interpreter/mterp/x86_64/op_add_float.S b/runtime/interpreter/mterp/x86_64/op_add_float.S new file mode 100644 index 0000000000..7753bf8870 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_add_float.S @@ -0,0 +1 @@ +%include "x86_64/sseBinop.S" {"instr":"adds","suff":"s"} diff --git a/runtime/interpreter/mterp/x86_64/op_add_float_2addr.S b/runtime/interpreter/mterp/x86_64/op_add_float_2addr.S new file mode 100644 index 0000000000..6c8005b182 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_add_float_2addr.S @@ -0,0 +1 @@ +%include "x86_64/sseBinop2Addr.S" {"instr":"adds","suff":"s"} diff --git a/runtime/interpreter/mterp/x86_64/op_add_int.S b/runtime/interpreter/mterp/x86_64/op_add_int.S new file mode 100644 index 0000000000..e316be7b9d --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_add_int.S @@ -0,0 +1 @@ +%include "x86_64/binop.S" {"instr":"addl (rFP,%rcx,4), %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_add_int_2addr.S b/runtime/interpreter/mterp/x86_64/op_add_int_2addr.S new file mode 100644 index 0000000000..2ff82935ae --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_add_int_2addr.S @@ -0,0 +1 @@ +%include "x86_64/binop2addr.S" {"instr":"addl %eax, (rFP,%rcx,4)"} diff --git a/runtime/interpreter/mterp/x86_64/op_add_int_lit16.S b/runtime/interpreter/mterp/x86_64/op_add_int_lit16.S new file mode 100644 index 0000000000..bfeb7caabc --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_add_int_lit16.S @@ -0,0 +1 @@ +%include "x86_64/binopLit16.S" {"instr":"addl %ecx, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_add_int_lit8.S b/runtime/interpreter/mterp/x86_64/op_add_int_lit8.S new file mode 100644 index 0000000000..8954844eae --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_add_int_lit8.S @@ -0,0 +1 @@ +%include "x86_64/binopLit8.S" {"instr":"addl %ecx, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_add_long.S b/runtime/interpreter/mterp/x86_64/op_add_long.S new file mode 100644 index 0000000000..89131ffe0e --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_add_long.S @@ -0,0 +1 @@ +%include "x86_64/binopWide.S" {"instr":"addq (rFP,%rcx,4), %rax"} diff --git a/runtime/interpreter/mterp/x86_64/op_add_long_2addr.S b/runtime/interpreter/mterp/x86_64/op_add_long_2addr.S new file mode 100644 index 0000000000..fed98bc3e6 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_add_long_2addr.S @@ -0,0 +1 @@ +%include "x86_64/binopWide2addr.S" {"instr":"addq %rax, (rFP,%rcx,4)"} diff --git a/runtime/interpreter/mterp/x86_64/op_aget.S b/runtime/interpreter/mterp/x86_64/op_aget.S new file mode 100644 index 0000000000..58d49481cf --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_aget.S @@ -0,0 +1,24 @@ +%default { "load":"movl", "shift":"4", "data_offset":"MIRROR_INT_ARRAY_DATA_OFFSET", "wide":"0" } +/* + * Array get, 32 bits or less. vAA <- vBB[vCC]. + * + * for: aget, aget-boolean, aget-byte, aget-char, aget-short, aget-wide + * + */ + /* op vAA, vBB, vCC */ + movzbq 2(rPC), %rax # eax <- BB + movzbq 3(rPC), %rcx # ecx <- CC + GET_VREG %eax, %rax # eax <- vBB (array object) + GET_VREG %ecx, %rcx # ecx <- vCC (requested index) + testl %eax, %eax # null array object? + je common_errNullObject # bail if so + cmpl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ecx + jae common_errArrayIndex # index >= length, bail. + .if $wide + movq $data_offset(%rax,%rcx,8), %rax + SET_WIDE_VREG %rax, rINSTq + .else + $load $data_offset(%rax,%rcx,$shift), %eax + SET_VREG %eax, rINSTq + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_aget_boolean.S b/runtime/interpreter/mterp/x86_64/op_aget_boolean.S new file mode 100644 index 0000000000..cf7bdb582b --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_aget_boolean.S @@ -0,0 +1 @@ +%include "x86_64/op_aget.S" { "load":"movzbl", "shift":"1", "data_offset":"MIRROR_BOOLEAN_ARRAY_DATA_OFFSET" } diff --git a/runtime/interpreter/mterp/x86_64/op_aget_byte.S b/runtime/interpreter/mterp/x86_64/op_aget_byte.S new file mode 100644 index 0000000000..1cbb569024 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_aget_byte.S @@ -0,0 +1 @@ +%include "x86_64/op_aget.S" { "load":"movsbl", "shift":"1", "data_offset":"MIRROR_BYTE_ARRAY_DATA_OFFSET" } diff --git a/runtime/interpreter/mterp/x86_64/op_aget_char.S b/runtime/interpreter/mterp/x86_64/op_aget_char.S new file mode 100644 index 0000000000..45c90851fd --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_aget_char.S @@ -0,0 +1 @@ +%include "x86_64/op_aget.S" { "load":"movzwl", "shift":"2", "data_offset":"MIRROR_CHAR_ARRAY_DATA_OFFSET" } diff --git a/runtime/interpreter/mterp/x86_64/op_aget_object.S b/runtime/interpreter/mterp/x86_64/op_aget_object.S new file mode 100644 index 0000000000..8baedeab5e --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_aget_object.S @@ -0,0 +1,16 @@ +/* + * Array object get. vAA <- vBB[vCC]. + * + * for: aget-object + */ + /* op vAA, vBB, vCC */ + movzbq 2(rPC), %rax # rax <- BB + movzbq 3(rPC), %rcx # rcx <- CC + GET_VREG OUT_32_ARG0, %rax # eax <- vBB (array object) + GET_VREG OUT_32_ARG1, %rcx # ecx <- vCC (requested index) + EXPORT_PC + call SYMBOL(artAGetObjectFromMterp) # (array, index) + cmpq $$0, THREAD_EXCEPTION_OFFSET(rSELF) + jnz MterpException + SET_VREG_OBJECT %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_aget_short.S b/runtime/interpreter/mterp/x86_64/op_aget_short.S new file mode 100644 index 0000000000..82c4a1ddf3 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_aget_short.S @@ -0,0 +1 @@ +%include "x86_64/op_aget.S" { "load":"movswl", "shift":"2", "data_offset":"MIRROR_SHORT_ARRAY_DATA_OFFSET" } diff --git a/runtime/interpreter/mterp/x86_64/op_aget_wide.S b/runtime/interpreter/mterp/x86_64/op_aget_wide.S new file mode 100644 index 0000000000..4f2771b9c4 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_aget_wide.S @@ -0,0 +1 @@ +%include "x86_64/op_aget.S" { "load":"movq", "shift":"8", "data_offset":"MIRROR_WIDE_ARRAY_DATA_OFFSET", "wide":"1" } diff --git a/runtime/interpreter/mterp/x86_64/op_and_int.S b/runtime/interpreter/mterp/x86_64/op_and_int.S new file mode 100644 index 0000000000..446988993d --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_and_int.S @@ -0,0 +1 @@ +%include "x86_64/binop.S" {"instr":"andl (rFP,%rcx,4), %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_and_int_2addr.S b/runtime/interpreter/mterp/x86_64/op_and_int_2addr.S new file mode 100644 index 0000000000..16315bba03 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_and_int_2addr.S @@ -0,0 +1 @@ +%include "x86_64/binop2addr.S" {"instr":"andl %eax, (rFP,%rcx,4)"} diff --git a/runtime/interpreter/mterp/x86_64/op_and_int_lit16.S b/runtime/interpreter/mterp/x86_64/op_and_int_lit16.S new file mode 100644 index 0000000000..63e851b449 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_and_int_lit16.S @@ -0,0 +1 @@ +%include "x86_64/binopLit16.S" {"instr":"andl %ecx, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_and_int_lit8.S b/runtime/interpreter/mterp/x86_64/op_and_int_lit8.S new file mode 100644 index 0000000000..da7a20fdff --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_and_int_lit8.S @@ -0,0 +1 @@ +%include "x86_64/binopLit8.S" {"instr":"andl %ecx, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_and_long.S b/runtime/interpreter/mterp/x86_64/op_and_long.S new file mode 100644 index 0000000000..ce1dd264dd --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_and_long.S @@ -0,0 +1 @@ +%include "x86_64/binopWide.S" {"instr":"andq (rFP,%rcx,4), %rax"} diff --git a/runtime/interpreter/mterp/x86_64/op_and_long_2addr.S b/runtime/interpreter/mterp/x86_64/op_and_long_2addr.S new file mode 100644 index 0000000000..d17ab8d58b --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_and_long_2addr.S @@ -0,0 +1 @@ +%include "x86_64/binopWide2addr.S" {"instr":"andq %rax, (rFP,%rcx,4)"} diff --git a/runtime/interpreter/mterp/x86_64/op_aput.S b/runtime/interpreter/mterp/x86_64/op_aput.S new file mode 100644 index 0000000000..11500ad201 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_aput.S @@ -0,0 +1,23 @@ +%default { "reg":"rINST", "store":"movl", "shift":"4", "data_offset":"MIRROR_INT_ARRAY_DATA_OFFSET", "wide":"0" } +/* + * Array put, 32 bits or less. vBB[vCC] <- vAA. + * + * for: aput, aput-boolean, aput-byte, aput-char, aput-short, aput-wide + * + */ + /* op vAA, vBB, vCC */ + movzbq 2(rPC), %rax # rax <- BB + movzbq 3(rPC), %rcx # rcx <- CC + GET_VREG %eax, %rax # eax <- vBB (array object) + GET_VREG %ecx, %rcx # ecx <- vCC (requested index) + testl %eax, %eax # null array object? + je common_errNullObject # bail if so + cmpl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ecx + jae common_errArrayIndex # index >= length, bail. + .if $wide + GET_WIDE_VREG rINSTq, rINSTq + .else + GET_VREG rINST, rINSTq + .endif + $store $reg, $data_offset(%rax,%rcx,$shift) + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_aput_boolean.S b/runtime/interpreter/mterp/x86_64/op_aput_boolean.S new file mode 100644 index 0000000000..7d77a86528 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_aput_boolean.S @@ -0,0 +1 @@ +%include "x86_64/op_aput.S" { "reg":"rINSTbl", "store":"movb", "shift":"1", "data_offset":"MIRROR_BOOLEAN_ARRAY_DATA_OFFSET" } diff --git a/runtime/interpreter/mterp/x86_64/op_aput_byte.S b/runtime/interpreter/mterp/x86_64/op_aput_byte.S new file mode 100644 index 0000000000..7a1723e0fe --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_aput_byte.S @@ -0,0 +1 @@ +%include "x86_64/op_aput.S" { "reg":"rINSTbl", "store":"movb", "shift":"1", "data_offset":"MIRROR_BYTE_ARRAY_DATA_OFFSET" } diff --git a/runtime/interpreter/mterp/x86_64/op_aput_char.S b/runtime/interpreter/mterp/x86_64/op_aput_char.S new file mode 100644 index 0000000000..f8f50a3b2e --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_aput_char.S @@ -0,0 +1 @@ +%include "x86_64/op_aput.S" { "reg":"rINSTw", "store":"movw", "shift":"2", "data_offset":"MIRROR_CHAR_ARRAY_DATA_OFFSET" } diff --git a/runtime/interpreter/mterp/x86_64/op_aput_object.S b/runtime/interpreter/mterp/x86_64/op_aput_object.S new file mode 100644 index 0000000000..b1bae0f457 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_aput_object.S @@ -0,0 +1,13 @@ +/* + * Store an object into an array. vBB[vCC] <- vAA. + */ + /* op vAA, vBB, vCC */ + EXPORT_PC + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG0 + movq rPC, OUT_ARG1 + REFRESH_INST ${opnum} + movq rINSTq, OUT_ARG2 + call SYMBOL(MterpAputObject) # (array, index) + testb %al, %al + jz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_aput_short.S b/runtime/interpreter/mterp/x86_64/op_aput_short.S new file mode 100644 index 0000000000..481fd6847b --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_aput_short.S @@ -0,0 +1 @@ +%include "x86_64/op_aput.S" { "reg":"rINSTw", "store":"movw", "shift":"2", "data_offset":"MIRROR_SHORT_ARRAY_DATA_OFFSET" } diff --git a/runtime/interpreter/mterp/x86_64/op_aput_wide.S b/runtime/interpreter/mterp/x86_64/op_aput_wide.S new file mode 100644 index 0000000000..5bbd39b0b6 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_aput_wide.S @@ -0,0 +1 @@ +%include "x86_64/op_aput.S" { "reg":"rINSTq", "store":"movq", "shift":"8", "data_offset":"MIRROR_WIDE_ARRAY_DATA_OFFSET", "wide":"1" } diff --git a/runtime/interpreter/mterp/x86_64/op_array_length.S b/runtime/interpreter/mterp/x86_64/op_array_length.S new file mode 100644 index 0000000000..e80d665160 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_array_length.S @@ -0,0 +1,12 @@ +/* + * Return the length of an array. + */ + movl rINST, %eax # eax <- BA + sarl $$4, rINST # rINST <- B + GET_VREG %ecx, rINSTq # ecx <- vB (object ref) + testl %ecx, %ecx # is null? + je common_errNullObject + andb $$0xf, %al # eax <- A + movl MIRROR_ARRAY_LENGTH_OFFSET(%rcx), rINST + SET_VREG rINST, %rax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/op_check_cast.S b/runtime/interpreter/mterp/x86_64/op_check_cast.S new file mode 100644 index 0000000000..f8fa7b2036 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_check_cast.S @@ -0,0 +1,13 @@ +/* + * Check to see if a cast from one class to another is allowed. + */ + /* check-cast vAA, class@BBBB */ + EXPORT_PC + movzwq 2(rPC), OUT_ARG0 # OUT_ARG0 <- BBBB + leaq VREG_ADDRESS(rINSTq), OUT_ARG1 + movq OFF_FP_METHOD(rFP), OUT_ARG2 + movq rSELF, OUT_ARG3 + call SYMBOL(MterpCheckCast) # (index, &obj, method, self) + testb %al, %al + jnz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_cmp_long.S b/runtime/interpreter/mterp/x86_64/op_cmp_long.S new file mode 100644 index 0000000000..23ca3e5e6d --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_cmp_long.S @@ -0,0 +1,17 @@ +/* + * Compare two 64-bit values. Puts 0, 1, or -1 into the destination + * register based on the results of the comparison. + */ + /* cmp-long vAA, vBB, vCC */ + movzbq 2(rPC), %rdx # edx <- BB + movzbq 3(rPC), %rcx # ecx <- CC + GET_WIDE_VREG %rdx, %rdx # rdx <- v[BB] + xorl %eax, %eax + xorl %edi, %edi + addb $$1, %al + movl $$-1, %esi + cmpq VREG_ADDRESS(%rcx), %rdx + cmovl %esi, %edi + cmovg %eax, %edi + SET_VREG %edi, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_cmpg_double.S b/runtime/interpreter/mterp/x86_64/op_cmpg_double.S new file mode 100644 index 0000000000..7c0aa1bdba --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_cmpg_double.S @@ -0,0 +1 @@ +%include "x86_64/fpcmp.S" {"suff":"d","nanval":"pos"} diff --git a/runtime/interpreter/mterp/x86_64/op_cmpg_float.S b/runtime/interpreter/mterp/x86_64/op_cmpg_float.S new file mode 100644 index 0000000000..14e8472672 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_cmpg_float.S @@ -0,0 +1 @@ +%include "x86_64/fpcmp.S" {"suff":"s","nanval":"pos"} diff --git a/runtime/interpreter/mterp/x86_64/op_cmpl_double.S b/runtime/interpreter/mterp/x86_64/op_cmpl_double.S new file mode 100644 index 0000000000..1d4c4243ae --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_cmpl_double.S @@ -0,0 +1 @@ +%include "x86_64/fpcmp.S" {"suff":"d","nanval":"neg"} diff --git a/runtime/interpreter/mterp/x86_64/op_cmpl_float.S b/runtime/interpreter/mterp/x86_64/op_cmpl_float.S new file mode 100644 index 0000000000..97a12a6a7d --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_cmpl_float.S @@ -0,0 +1 @@ +%include "x86_64/fpcmp.S" {"suff":"s","nanval":"neg"} diff --git a/runtime/interpreter/mterp/x86_64/op_const.S b/runtime/interpreter/mterp/x86_64/op_const.S new file mode 100644 index 0000000000..3cfafdb13b --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_const.S @@ -0,0 +1,4 @@ + /* const vAA, #+BBBBbbbb */ + movl 2(rPC), %eax # grab all 32 bits at once + SET_VREG %eax, rINSTq # vAA<- eax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 diff --git a/runtime/interpreter/mterp/x86_64/op_const_16.S b/runtime/interpreter/mterp/x86_64/op_const_16.S new file mode 100644 index 0000000000..1a139c683e --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_const_16.S @@ -0,0 +1,4 @@ + /* const/16 vAA, #+BBBB */ + movswl 2(rPC), %ecx # ecx <- ssssBBBB + SET_VREG %ecx, rINSTq # vAA <- ssssBBBB + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_const_4.S b/runtime/interpreter/mterp/x86_64/op_const_4.S new file mode 100644 index 0000000000..23c4816f82 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_const_4.S @@ -0,0 +1,7 @@ + /* const/4 vA, #+B */ + movsbl rINSTbl, %eax # eax <-ssssssBx + movl $$0xf, rINST + andl %eax, rINST # rINST <- A + sarl $$4, %eax + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/op_const_class.S b/runtime/interpreter/mterp/x86_64/op_const_class.S new file mode 100644 index 0000000000..494920a4a8 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_const_class.S @@ -0,0 +1,10 @@ + /* const/class vAA, Class@BBBB */ + EXPORT_PC + movzwq 2(rPC), OUT_ARG0 # eax <- OUT_ARG0 + movq rINSTq, OUT_ARG1 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG2 + movq rSELF, OUT_ARG3 + call SYMBOL(MterpConstClass) # (index, tgt_reg, shadow_frame, self) + testb %al, %al + jnz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_const_high16.S b/runtime/interpreter/mterp/x86_64/op_const_high16.S new file mode 100644 index 0000000000..64e633c7a0 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_const_high16.S @@ -0,0 +1,5 @@ + /* const/high16 vAA, #+BBBB0000 */ + movzwl 2(rPC), %eax # eax <- 0000BBBB + sall $$16, %eax # eax <- BBBB0000 + SET_VREG %eax, rINSTq # vAA <- eax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_const_string.S b/runtime/interpreter/mterp/x86_64/op_const_string.S new file mode 100644 index 0000000000..7c199ecad9 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_const_string.S @@ -0,0 +1,10 @@ + /* const/string vAA, String@BBBB */ + EXPORT_PC + movzwq 2(rPC), OUT_ARG0 # OUT_ARG0 <- BBBB + movq rINSTq, OUT_ARG1 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG2 + movq rSELF, OUT_ARG3 + call SYMBOL(MterpConstString) # (index, tgt_reg, shadow_frame, self) + testb %al, %al + jnz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_const_string_jumbo.S b/runtime/interpreter/mterp/x86_64/op_const_string_jumbo.S new file mode 100644 index 0000000000..ae03d20f4f --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_const_string_jumbo.S @@ -0,0 +1,10 @@ + /* const/string vAA, String@BBBBBBBB */ + EXPORT_PC + movl 2(rPC), OUT_32_ARG0 # OUT_32_ARG0 <- BBBB + movq rINSTq, OUT_ARG1 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG2 + movq rSELF, OUT_ARG3 + call SYMBOL(MterpConstString) # (index, tgt_reg, shadow_frame, self) + testb %al, %al + jnz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 diff --git a/runtime/interpreter/mterp/x86_64/op_const_wide.S b/runtime/interpreter/mterp/x86_64/op_const_wide.S new file mode 100644 index 0000000000..5615177175 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_const_wide.S @@ -0,0 +1,4 @@ + /* const-wide vAA, #+HHHHhhhhBBBBbbbb */ + movq 2(rPC), %rax # rax <- HHHHhhhhBBBBbbbb + SET_WIDE_VREG %rax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 5 diff --git a/runtime/interpreter/mterp/x86_64/op_const_wide_16.S b/runtime/interpreter/mterp/x86_64/op_const_wide_16.S new file mode 100644 index 0000000000..593b62466f --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_const_wide_16.S @@ -0,0 +1,4 @@ + /* const-wide/16 vAA, #+BBBB */ + movswq 2(rPC), %rax # rax <- ssssBBBB + SET_WIDE_VREG %rax, rINSTq # store + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_const_wide_32.S b/runtime/interpreter/mterp/x86_64/op_const_wide_32.S new file mode 100644 index 0000000000..5ef3636129 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_const_wide_32.S @@ -0,0 +1,4 @@ + /* const-wide/32 vAA, #+BBBBbbbb */ + movslq 2(rPC), %rax # eax <- ssssssssBBBBbbbb + SET_WIDE_VREG %rax, rINSTq # store + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 diff --git a/runtime/interpreter/mterp/x86_64/op_const_wide_high16.S b/runtime/interpreter/mterp/x86_64/op_const_wide_high16.S new file mode 100644 index 0000000000..b86b4e582b --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_const_wide_high16.S @@ -0,0 +1,5 @@ + /* const-wide/high16 vAA, #+BBBB000000000000 */ + movzwq 2(rPC), %rax # eax <- 0000BBBB + salq $$48, %rax # eax <- BBBB0000 + SET_WIDE_VREG %rax, rINSTq # v[AA+0] <- eax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_div_double.S b/runtime/interpreter/mterp/x86_64/op_div_double.S new file mode 100644 index 0000000000..45c700c066 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_div_double.S @@ -0,0 +1 @@ +%include "x86_64/sseBinop.S" {"instr":"divs","suff":"d"} diff --git a/runtime/interpreter/mterp/x86_64/op_div_double_2addr.S b/runtime/interpreter/mterp/x86_64/op_div_double_2addr.S new file mode 100644 index 0000000000..83f270e245 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_div_double_2addr.S @@ -0,0 +1 @@ +%include "x86_64/sseBinop2Addr.S" {"instr":"divs","suff":"d"} diff --git a/runtime/interpreter/mterp/x86_64/op_div_float.S b/runtime/interpreter/mterp/x86_64/op_div_float.S new file mode 100644 index 0000000000..aa90b24698 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_div_float.S @@ -0,0 +1 @@ +%include "x86_64/sseBinop.S" {"instr":"divs","suff":"s"} diff --git a/runtime/interpreter/mterp/x86_64/op_div_float_2addr.S b/runtime/interpreter/mterp/x86_64/op_div_float_2addr.S new file mode 100644 index 0000000000..f0f8f1a6c8 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_div_float_2addr.S @@ -0,0 +1 @@ +%include "x86_64/sseBinop2Addr.S" {"instr":"divs","suff":"s"} diff --git a/runtime/interpreter/mterp/x86_64/op_div_int.S b/runtime/interpreter/mterp/x86_64/op_div_int.S new file mode 100644 index 0000000000..bba5a176a0 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_div_int.S @@ -0,0 +1 @@ +%include "x86_64/bindiv.S" {"result":"%eax","second":"%ecx","wide":"0","suffix":"l"} diff --git a/runtime/interpreter/mterp/x86_64/op_div_int_2addr.S b/runtime/interpreter/mterp/x86_64/op_div_int_2addr.S new file mode 100644 index 0000000000..fa4255ddfa --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_div_int_2addr.S @@ -0,0 +1 @@ +%include "x86_64/bindiv2addr.S" {"result":"%eax","second":"%ecx","wide":"0","suffix":"l"} diff --git a/runtime/interpreter/mterp/x86_64/op_div_int_lit16.S b/runtime/interpreter/mterp/x86_64/op_div_int_lit16.S new file mode 100644 index 0000000000..3fa1e09fd6 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_div_int_lit16.S @@ -0,0 +1 @@ +%include "x86_64/bindivLit16.S" {"result":"%eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_div_int_lit8.S b/runtime/interpreter/mterp/x86_64/op_div_int_lit8.S new file mode 100644 index 0000000000..859883e5c7 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_div_int_lit8.S @@ -0,0 +1 @@ +%include "x86_64/bindivLit8.S" {"result":"%eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_div_long.S b/runtime/interpreter/mterp/x86_64/op_div_long.S new file mode 100644 index 0000000000..a061a88b13 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_div_long.S @@ -0,0 +1 @@ +%include "x86_64/bindiv.S" {"result":"%rax","second":"%rcx","wide":"1","suffix":"q","ext":"cqo"} diff --git a/runtime/interpreter/mterp/x86_64/op_div_long_2addr.S b/runtime/interpreter/mterp/x86_64/op_div_long_2addr.S new file mode 100644 index 0000000000..8886e68248 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_div_long_2addr.S @@ -0,0 +1 @@ +%include "x86_64/bindiv2addr.S" {"result":"%rax","second":"%rcx","wide":"1","suffix":"q","ext":"cqo"} diff --git a/runtime/interpreter/mterp/x86_64/op_double_to_float.S b/runtime/interpreter/mterp/x86_64/op_double_to_float.S new file mode 100644 index 0000000000..cea1482038 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_double_to_float.S @@ -0,0 +1 @@ +%include "x86_64/fpcvt.S" {"source_suffix":"d","dest_suffix":"s","wide":"0"} diff --git a/runtime/interpreter/mterp/x86_64/op_double_to_int.S b/runtime/interpreter/mterp/x86_64/op_double_to_int.S new file mode 100644 index 0000000000..a9965edcc3 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_double_to_int.S @@ -0,0 +1 @@ +%include "x86_64/cvtfp_int.S" {"fp_suffix":"d","i_suffix":"l","max_const":"$0x7fffffff","result_reg":"%eax","wide":"0"} diff --git a/runtime/interpreter/mterp/x86_64/op_double_to_long.S b/runtime/interpreter/mterp/x86_64/op_double_to_long.S new file mode 100644 index 0000000000..179e6a1605 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_double_to_long.S @@ -0,0 +1 @@ +%include "x86_64/cvtfp_int.S" {"fp_suffix":"d","i_suffix":"q","max_const":"$0x7fffffffffffffff","result_reg":"%rax","wide":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_fill_array_data.S b/runtime/interpreter/mterp/x86_64/op_fill_array_data.S new file mode 100644 index 0000000000..626bad47c9 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_fill_array_data.S @@ -0,0 +1,9 @@ + /* fill-array-data vAA, +BBBBBBBB */ + EXPORT_PC + movl 2(rPC), %ecx # ecx <- BBBBbbbb + leaq (rPC,%rcx,2), OUT_ARG1 # OUT_ARG1 <- PC + BBBBbbbb*2 + GET_VREG OUT_32_ARG0, rINSTq # OUT_ARG0 <- vAA (array object) + call SYMBOL(MterpFillArrayData) # (obj, payload) + testb %al, %al # 0 means an exception is thrown + jz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 diff --git a/runtime/interpreter/mterp/x86_64/op_filled_new_array.S b/runtime/interpreter/mterp/x86_64/op_filled_new_array.S new file mode 100644 index 0000000000..a7f7ddc2a0 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_filled_new_array.S @@ -0,0 +1,17 @@ +%default { "helper":"MterpFilledNewArray" } +/* + * Create a new array with elements filled from registers. + * + * for: filled-new-array, filled-new-array/range + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, type@BBBB */ + .extern $helper + EXPORT_PC + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG0 + movq rPC, OUT_ARG1 + movq rSELF, OUT_ARG2 + call SYMBOL($helper) + testb %al, %al # 0 means an exception is thrown + jz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 diff --git a/runtime/interpreter/mterp/x86_64/op_filled_new_array_range.S b/runtime/interpreter/mterp/x86_64/op_filled_new_array_range.S new file mode 100644 index 0000000000..4ca79a3fe1 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_filled_new_array_range.S @@ -0,0 +1 @@ +%include "x86_64/op_filled_new_array.S" { "helper":"MterpFilledNewArrayRange" } diff --git a/runtime/interpreter/mterp/x86_64/op_float_to_double.S b/runtime/interpreter/mterp/x86_64/op_float_to_double.S new file mode 100644 index 0000000000..7855205575 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_float_to_double.S @@ -0,0 +1 @@ +%include "x86_64/fpcvt.S" {"source_suffix":"s","dest_suffix":"d","wide":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_float_to_int.S b/runtime/interpreter/mterp/x86_64/op_float_to_int.S new file mode 100644 index 0000000000..cb90555405 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_float_to_int.S @@ -0,0 +1 @@ +%include "x86_64/cvtfp_int.S" {"fp_suffix":"s","i_suffix":"l","max_const":"$0x7fffffff","result_reg":"%eax","wide":"0"} diff --git a/runtime/interpreter/mterp/x86_64/op_float_to_long.S b/runtime/interpreter/mterp/x86_64/op_float_to_long.S new file mode 100644 index 0000000000..96bb4eee6f --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_float_to_long.S @@ -0,0 +1 @@ +%include "x86_64/cvtfp_int.S" {"fp_suffix":"s","i_suffix":"q","max_const":"$0x7fffffffffffffff","result_reg":"%rax","wide":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_goto.S b/runtime/interpreter/mterp/x86_64/op_goto.S new file mode 100644 index 0000000000..05a2dda1c0 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_goto.S @@ -0,0 +1,19 @@ +/* + * Unconditional branch, 8-bit offset. + * + * The branch distance is a signed code-unit offset, which we need to + * double to get a byte offset. + */ + /* goto +AA */ + movsbq rINSTbl, %rax # rax <- ssssssAA + addq %rax, %rax # rax <- AA * 2 + leaq (rPC, %rax), rPC + FETCH_INST + jg 1f # AA * 2 > 0 => no suspend check +#if MTERP_SUSPEND + REFRESH_IBASE +#else + jmp MterpCheckSuspendAndContinue +#endif +1: + GOTO_NEXT diff --git a/runtime/interpreter/mterp/x86_64/op_goto_16.S b/runtime/interpreter/mterp/x86_64/op_goto_16.S new file mode 100644 index 0000000000..029749c50a --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_goto_16.S @@ -0,0 +1,19 @@ +/* + * Unconditional branch, 16-bit offset. + * + * The branch distance is a signed code-unit offset, which we need to + * double to get a byte offset. + */ + /* goto/16 +AAAA */ + movswq 2(rPC), %rax # rax <- ssssAAAA + addq %rax, %rax # rax <- AA * 2 + leaq (rPC, %rax), rPC + FETCH_INST + jg 1f # AA * 2 > 0 => no suspend check +#if MTERP_SUSPEND + REFRESH_IBASE +#else + jmp MterpCheckSuspendAndContinue +#endif +1: + GOTO_NEXT diff --git a/runtime/interpreter/mterp/x86_64/op_goto_32.S b/runtime/interpreter/mterp/x86_64/op_goto_32.S new file mode 100644 index 0000000000..28233108e5 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_goto_32.S @@ -0,0 +1,22 @@ +/* + * Unconditional branch, 32-bit offset. + * + * The branch distance is a signed code-unit offset, which we need to + * double to get a byte offset. + * + * Because we need the SF bit set, we'll use an adds + * to convert from Dalvik offset to byte offset. + */ + /* goto/32 +AAAAAAAA */ + movslq 2(rPC), %rax # rax <- AAAAAAAA + addq %rax, %rax # rax <- AA * 2 + leaq (rPC, %rax), rPC + FETCH_INST + jg 1f # AA * 2 > 0 => no suspend check +#if MTERP_SUSPEND + REFRESH_IBASE +#else + jmp MterpCheckSuspendAndContinue +#endif +1: + GOTO_NEXT diff --git a/runtime/interpreter/mterp/x86_64/op_if_eq.S b/runtime/interpreter/mterp/x86_64/op_if_eq.S new file mode 100644 index 0000000000..d56ce72461 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_if_eq.S @@ -0,0 +1 @@ +%include "x86_64/bincmp.S" { "revcmp":"ne" } diff --git a/runtime/interpreter/mterp/x86_64/op_if_eqz.S b/runtime/interpreter/mterp/x86_64/op_if_eqz.S new file mode 100644 index 0000000000..a0fc4448a3 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_if_eqz.S @@ -0,0 +1 @@ +%include "x86_64/zcmp.S" { "revcmp":"ne" } diff --git a/runtime/interpreter/mterp/x86_64/op_if_ge.S b/runtime/interpreter/mterp/x86_64/op_if_ge.S new file mode 100644 index 0000000000..a7832efb68 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_if_ge.S @@ -0,0 +1 @@ +%include "x86_64/bincmp.S" { "revcmp":"l" } diff --git a/runtime/interpreter/mterp/x86_64/op_if_gez.S b/runtime/interpreter/mterp/x86_64/op_if_gez.S new file mode 100644 index 0000000000..f9af5db933 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_if_gez.S @@ -0,0 +1 @@ +%include "x86_64/zcmp.S" { "revcmp":"l" } diff --git a/runtime/interpreter/mterp/x86_64/op_if_gt.S b/runtime/interpreter/mterp/x86_64/op_if_gt.S new file mode 100644 index 0000000000..70f2b9e12f --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_if_gt.S @@ -0,0 +1 @@ +%include "x86_64/bincmp.S" { "revcmp":"le" } diff --git a/runtime/interpreter/mterp/x86_64/op_if_gtz.S b/runtime/interpreter/mterp/x86_64/op_if_gtz.S new file mode 100644 index 0000000000..2fb0d50937 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_if_gtz.S @@ -0,0 +1 @@ +%include "x86_64/zcmp.S" { "revcmp":"le" } diff --git a/runtime/interpreter/mterp/x86_64/op_if_le.S b/runtime/interpreter/mterp/x86_64/op_if_le.S new file mode 100644 index 0000000000..321962a040 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_if_le.S @@ -0,0 +1 @@ +%include "x86_64/bincmp.S" { "revcmp":"g" } diff --git a/runtime/interpreter/mterp/x86_64/op_if_lez.S b/runtime/interpreter/mterp/x86_64/op_if_lez.S new file mode 100644 index 0000000000..d3dc334f7b --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_if_lez.S @@ -0,0 +1 @@ +%include "x86_64/zcmp.S" { "revcmp":"g" } diff --git a/runtime/interpreter/mterp/x86_64/op_if_lt.S b/runtime/interpreter/mterp/x86_64/op_if_lt.S new file mode 100644 index 0000000000..f028005844 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_if_lt.S @@ -0,0 +1 @@ +%include "x86_64/bincmp.S" { "revcmp":"ge" } diff --git a/runtime/interpreter/mterp/x86_64/op_if_ltz.S b/runtime/interpreter/mterp/x86_64/op_if_ltz.S new file mode 100644 index 0000000000..383d73aa7a --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_if_ltz.S @@ -0,0 +1 @@ +%include "x86_64/zcmp.S" { "revcmp":"ge" } diff --git a/runtime/interpreter/mterp/x86_64/op_if_ne.S b/runtime/interpreter/mterp/x86_64/op_if_ne.S new file mode 100644 index 0000000000..ac6e063cd1 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_if_ne.S @@ -0,0 +1 @@ +%include "x86_64/bincmp.S" { "revcmp":"e" } diff --git a/runtime/interpreter/mterp/x86_64/op_if_nez.S b/runtime/interpreter/mterp/x86_64/op_if_nez.S new file mode 100644 index 0000000000..c96e4f3d16 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_if_nez.S @@ -0,0 +1 @@ +%include "x86_64/zcmp.S" { "revcmp":"e" } diff --git a/runtime/interpreter/mterp/x86_64/op_iget.S b/runtime/interpreter/mterp/x86_64/op_iget.S new file mode 100644 index 0000000000..a0d0fafba1 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iget.S @@ -0,0 +1,27 @@ +%default { "is_object":"0", "helper":"artGet32InstanceFromCode", "wide":"0"} +/* + * General instance field get. + * + * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short, iget-wide + */ + EXPORT_PC + movzbq rINSTbl, %rcx # rcx <- BA + movzwl 2(rPC), OUT_32_ARG0 # eax <- field ref CCCC + sarl $$4, %ecx # ecx <- B + GET_VREG OUT_32_ARG1, %rcx # the object pointer + movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer + movq rSELF, OUT_ARG3 + call SYMBOL($helper) + cmpq $$0, THREAD_EXCEPTION_OFFSET(rSELF) + jnz MterpException # bail out + andb $$0xf, rINSTbl # rINST <- A + .if $is_object + SET_VREG_OBJECT %eax, rINSTq # fp[A] <-value + .else + .if $wide + SET_WIDE_VREG %rax, rINSTq # fp[A] <-value + .else + SET_VREG %eax, rINSTq # fp[A] <-value + .endif + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_iget_boolean.S b/runtime/interpreter/mterp/x86_64/op_iget_boolean.S new file mode 100644 index 0000000000..6ac55231b8 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iget_boolean.S @@ -0,0 +1 @@ +%include "x86_64/op_iget.S" { "helper":"artGetBooleanInstanceFromCode" } diff --git a/runtime/interpreter/mterp/x86_64/op_iget_boolean_quick.S b/runtime/interpreter/mterp/x86_64/op_iget_boolean_quick.S new file mode 100644 index 0000000000..07139c75ee --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iget_boolean_quick.S @@ -0,0 +1 @@ +%include "x86_64/op_iget_quick.S" { "load":"movsbl" } diff --git a/runtime/interpreter/mterp/x86_64/op_iget_byte.S b/runtime/interpreter/mterp/x86_64/op_iget_byte.S new file mode 100644 index 0000000000..6a861b1b7a --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iget_byte.S @@ -0,0 +1 @@ +%include "x86_64/op_iget.S" { "helper":"artGetByteInstanceFromCode" } diff --git a/runtime/interpreter/mterp/x86_64/op_iget_byte_quick.S b/runtime/interpreter/mterp/x86_64/op_iget_byte_quick.S new file mode 100644 index 0000000000..07139c75ee --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iget_byte_quick.S @@ -0,0 +1 @@ +%include "x86_64/op_iget_quick.S" { "load":"movsbl" } diff --git a/runtime/interpreter/mterp/x86_64/op_iget_char.S b/runtime/interpreter/mterp/x86_64/op_iget_char.S new file mode 100644 index 0000000000..021a0f1b24 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iget_char.S @@ -0,0 +1 @@ +%include "x86_64/op_iget.S" { "helper":"artGetCharInstanceFromCode" } diff --git a/runtime/interpreter/mterp/x86_64/op_iget_char_quick.S b/runtime/interpreter/mterp/x86_64/op_iget_char_quick.S new file mode 100644 index 0000000000..8cb3be3b65 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iget_char_quick.S @@ -0,0 +1 @@ +%include "x86_64/op_iget_quick.S" { "load":"movzwl" } diff --git a/runtime/interpreter/mterp/x86_64/op_iget_object.S b/runtime/interpreter/mterp/x86_64/op_iget_object.S new file mode 100644 index 0000000000..d92bc9c345 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iget_object.S @@ -0,0 +1 @@ +%include "x86_64/op_iget.S" { "is_object":"1", "helper":"artGetObjInstanceFromCode" } diff --git a/runtime/interpreter/mterp/x86_64/op_iget_object_quick.S b/runtime/interpreter/mterp/x86_64/op_iget_object_quick.S new file mode 100644 index 0000000000..964d20ad74 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iget_object_quick.S @@ -0,0 +1,14 @@ + /* For: iget-object-quick */ + /* op vA, vB, offset@CCCC */ + .extern artIGetObjectFromMterp + movzbq rINSTbl, %rcx # rcx <- BA + sarl $$4, %ecx # ecx <- B + GET_VREG OUT_32_ARG0, %rcx # vB (object we're operating on) + movzwl 2(rPC), OUT_32_ARG1 # eax <- field byte offset + EXPORT_PC + callq SYMBOL(artIGetObjectFromMterp) # (obj, offset) + cmpq $$0, THREAD_EXCEPTION_OFFSET(rSELF) + jnz MterpException # bail out + andb $$0xf, rINSTbl # rINST <- A + SET_VREG_OBJECT %eax, rINSTq # fp[A] <- value + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_iget_quick.S b/runtime/interpreter/mterp/x86_64/op_iget_quick.S new file mode 100644 index 0000000000..bfb7530167 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iget_quick.S @@ -0,0 +1,18 @@ +%default { "load":"movl", "wide":"0"} + /* For: iget-quick, iget-boolean-quick, iget-byte-quick, iget-char-quick, iget-short-quick, iget-wide-quick */ + /* op vA, vB, offset@CCCC */ + movl rINST, %ecx # rcx <- BA + sarl $$4, %ecx # ecx <- B + GET_VREG %ecx, %rcx # vB (object we're operating on) + movzwq 2(rPC), %rax # eax <- field byte offset + testl %ecx, %ecx # is object null? + je common_errNullObject + andb $$0xf,rINSTbl # rINST <- A + .if $wide + movq (%rcx,%rax,1), %rax + SET_WIDE_VREG %rax, rINSTq # fp[A] <- value + .else + ${load} (%rcx,%rax,1), %eax + SET_VREG %eax, rINSTq # fp[A] <- value + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_iget_short.S b/runtime/interpreter/mterp/x86_64/op_iget_short.S new file mode 100644 index 0000000000..f158bea573 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iget_short.S @@ -0,0 +1 @@ +%include "x86_64/op_iget.S" { "helper":"artGetShortInstanceFromCode" } diff --git a/runtime/interpreter/mterp/x86_64/op_iget_short_quick.S b/runtime/interpreter/mterp/x86_64/op_iget_short_quick.S new file mode 100644 index 0000000000..56ca858e74 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iget_short_quick.S @@ -0,0 +1 @@ +%include "x86_64/op_iget_quick.S" { "load":"movswl" } diff --git a/runtime/interpreter/mterp/x86_64/op_iget_wide.S b/runtime/interpreter/mterp/x86_64/op_iget_wide.S new file mode 100644 index 0000000000..74bb9ffe1c --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iget_wide.S @@ -0,0 +1 @@ +%include "x86_64/op_iget.S" { "helper":"artGet64InstanceFromCode", "wide":"1" } diff --git a/runtime/interpreter/mterp/x86_64/op_iget_wide_quick.S b/runtime/interpreter/mterp/x86_64/op_iget_wide_quick.S new file mode 100644 index 0000000000..169d625529 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iget_wide_quick.S @@ -0,0 +1 @@ +%include "x86_64/op_iget_quick.S" { "load":"movswl", "wide":"1" } diff --git a/runtime/interpreter/mterp/x86_64/op_instance_of.S b/runtime/interpreter/mterp/x86_64/op_instance_of.S new file mode 100644 index 0000000000..6be37f9166 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_instance_of.S @@ -0,0 +1,21 @@ +/* + * Check to see if an object reference is an instance of a class. + * + * Most common situation is a non-null object, being compared against + * an already-resolved class. + */ + /* instance-of vA, vB, class@CCCC */ + EXPORT_PC + movzwl 2(rPC), OUT_32_ARG0 # OUT_32_ARG0 <- CCCC + movl rINST, %eax # eax <- BA + sarl $$4, %eax # eax <- B + leaq VREG_ADDRESS(%rax), OUT_ARG1 # Get object address + movq OFF_FP_METHOD(rFP), OUT_ARG2 + movq rSELF, OUT_ARG3 + call SYMBOL(MterpInstanceOf) # (index, &obj, method, self) + movsbl %al, %eax + cmpq $$0, THREAD_EXCEPTION_OFFSET(rSELF) + jnz MterpException + andb $$0xf, rINSTbl # rINSTbl <- A + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_int_to_byte.S b/runtime/interpreter/mterp/x86_64/op_int_to_byte.S new file mode 100644 index 0000000000..f4e578f868 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_int_to_byte.S @@ -0,0 +1 @@ +%include "x86_64/unop.S" {"instr":"movsbl %al, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_int_to_char.S b/runtime/interpreter/mterp/x86_64/op_int_to_char.S new file mode 100644 index 0000000000..c1bf17f271 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_int_to_char.S @@ -0,0 +1 @@ +%include "x86_64/unop.S" {"instr":"movzwl %ax,%eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_int_to_double.S b/runtime/interpreter/mterp/x86_64/op_int_to_double.S new file mode 100644 index 0000000000..27ebf42dbb --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_int_to_double.S @@ -0,0 +1 @@ +%include "x86_64/fpcvt.S" {"source_suffix":"i","dest_suffix":"dl","wide":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_int_to_float.S b/runtime/interpreter/mterp/x86_64/op_int_to_float.S new file mode 100644 index 0000000000..5a98d44337 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_int_to_float.S @@ -0,0 +1 @@ +%include "x86_64/fpcvt.S" {"source_suffix":"i","dest_suffix":"sl","wide":"0"} diff --git a/runtime/interpreter/mterp/x86_64/op_int_to_long.S b/runtime/interpreter/mterp/x86_64/op_int_to_long.S new file mode 100644 index 0000000000..9281137a54 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_int_to_long.S @@ -0,0 +1,8 @@ + /* int to long vA, vB */ + movzbq rINSTbl, %rax # rax <- +A + sarl $$4, %eax # eax <- B + andb $$0xf, rINSTbl # rINST <- A + movslq VREG_ADDRESS(%rax), %rax + SET_WIDE_VREG %rax, rINSTq # v[A] <- %rax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + diff --git a/runtime/interpreter/mterp/x86_64/op_int_to_short.S b/runtime/interpreter/mterp/x86_64/op_int_to_short.S new file mode 100644 index 0000000000..6ae6b50f34 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_int_to_short.S @@ -0,0 +1 @@ +%include "x86_64/unop.S" {"instr":"movswl %ax, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_invoke_direct.S b/runtime/interpreter/mterp/x86_64/op_invoke_direct.S new file mode 100644 index 0000000000..9628589b03 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_invoke_direct.S @@ -0,0 +1 @@ +%include "x86_64/invoke.S" { "helper":"MterpInvokeDirect" } diff --git a/runtime/interpreter/mterp/x86_64/op_invoke_direct_range.S b/runtime/interpreter/mterp/x86_64/op_invoke_direct_range.S new file mode 100644 index 0000000000..09ac8812fc --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_invoke_direct_range.S @@ -0,0 +1 @@ +%include "x86_64/invoke.S" { "helper":"MterpInvokeDirectRange" } diff --git a/runtime/interpreter/mterp/x86_64/op_invoke_interface.S b/runtime/interpreter/mterp/x86_64/op_invoke_interface.S new file mode 100644 index 0000000000..76d9cd426f --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_invoke_interface.S @@ -0,0 +1,8 @@ +%include "x86_64/invoke.S" { "helper":"MterpInvokeInterface" } +/* + * Handle an interface method call. + * + * for: invoke-interface, invoke-interface/range + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ diff --git a/runtime/interpreter/mterp/x86_64/op_invoke_interface_range.S b/runtime/interpreter/mterp/x86_64/op_invoke_interface_range.S new file mode 100644 index 0000000000..785b43c1a8 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_invoke_interface_range.S @@ -0,0 +1 @@ +%include "x86_64/invoke.S" { "helper":"MterpInvokeInterfaceRange" } diff --git a/runtime/interpreter/mterp/x86_64/op_invoke_static.S b/runtime/interpreter/mterp/x86_64/op_invoke_static.S new file mode 100644 index 0000000000..dd8027d58c --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_invoke_static.S @@ -0,0 +1,2 @@ +%include "x86_64/invoke.S" { "helper":"MterpInvokeStatic" } + diff --git a/runtime/interpreter/mterp/x86_64/op_invoke_static_range.S b/runtime/interpreter/mterp/x86_64/op_invoke_static_range.S new file mode 100644 index 0000000000..ee26074f92 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_invoke_static_range.S @@ -0,0 +1 @@ +%include "x86_64/invoke.S" { "helper":"MterpInvokeStaticRange" } diff --git a/runtime/interpreter/mterp/x86_64/op_invoke_super.S b/runtime/interpreter/mterp/x86_64/op_invoke_super.S new file mode 100644 index 0000000000..d07f8d555b --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_invoke_super.S @@ -0,0 +1,8 @@ +%include "x86_64/invoke.S" { "helper":"MterpInvokeSuper" } +/* + * Handle a "super" method call. + * + * for: invoke-super, invoke-super/range + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ diff --git a/runtime/interpreter/mterp/x86_64/op_invoke_super_range.S b/runtime/interpreter/mterp/x86_64/op_invoke_super_range.S new file mode 100644 index 0000000000..7245cfd405 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_invoke_super_range.S @@ -0,0 +1 @@ +%include "x86_64/invoke.S" { "helper":"MterpInvokeSuperRange" } diff --git a/runtime/interpreter/mterp/x86_64/op_invoke_virtual.S b/runtime/interpreter/mterp/x86_64/op_invoke_virtual.S new file mode 100644 index 0000000000..19c708bd2a --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_invoke_virtual.S @@ -0,0 +1,8 @@ +%include "x86_64/invoke.S" { "helper":"MterpInvokeVirtual" } +/* + * Handle a virtual method call. + * + * for: invoke-virtual, invoke-virtual/range + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ diff --git a/runtime/interpreter/mterp/x86_64/op_invoke_virtual_quick.S b/runtime/interpreter/mterp/x86_64/op_invoke_virtual_quick.S new file mode 100644 index 0000000000..313bd058b1 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_invoke_virtual_quick.S @@ -0,0 +1 @@ +%include "x86_64/invoke.S" { "helper":"MterpInvokeVirtualQuick" } diff --git a/runtime/interpreter/mterp/x86_64/op_invoke_virtual_range.S b/runtime/interpreter/mterp/x86_64/op_invoke_virtual_range.S new file mode 100644 index 0000000000..424ad321a3 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_invoke_virtual_range.S @@ -0,0 +1 @@ +%include "x86_64/invoke.S" { "helper":"MterpInvokeVirtualRange" } diff --git a/runtime/interpreter/mterp/x86_64/op_invoke_virtual_range_quick.S b/runtime/interpreter/mterp/x86_64/op_invoke_virtual_range_quick.S new file mode 100644 index 0000000000..556f718ffb --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_invoke_virtual_range_quick.S @@ -0,0 +1 @@ +%include "x86_64/invoke.S" { "helper":"MterpInvokeVirtualQuickRange" } diff --git a/runtime/interpreter/mterp/x86_64/op_iput.S b/runtime/interpreter/mterp/x86_64/op_iput.S new file mode 100644 index 0000000000..6b7cb1cc84 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iput.S @@ -0,0 +1,20 @@ +%default { "handler":"artSet32InstanceFromMterp"} +/* + * General 32-bit instance field put. + * + * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short + */ + /* op vA, vB, field@CCCC */ + .extern $handler + EXPORT_PC + movzwl 2(rPC), OUT_32_ARG0 # field ref <- 0000CCCC + movzbq rINSTbl, %rcx # rcx<- BA + sarl $$4, %ecx # ecx<- B + GET_VREG OUT_32_ARG1, %rcx # the object pointer + andb $$0xf, rINSTbl # rINST<- A + GET_VREG OUT_32_ARG2, rINSTq # fp[A] + movq OFF_FP_METHOD(rFP), OUT_ARG3 # referrer + call SYMBOL($handler) + testb %al, %al + jnz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_iput_boolean.S b/runtime/interpreter/mterp/x86_64/op_iput_boolean.S new file mode 100644 index 0000000000..cb4b1cde45 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iput_boolean.S @@ -0,0 +1 @@ +%include "x86_64/op_iput.S" { "handler":"artSet8InstanceFromMterp" } diff --git a/runtime/interpreter/mterp/x86_64/op_iput_boolean_quick.S b/runtime/interpreter/mterp/x86_64/op_iput_boolean_quick.S new file mode 100644 index 0000000000..6bd060e4f3 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iput_boolean_quick.S @@ -0,0 +1 @@ +%include "x86_64/op_iput_quick.S" { "reg":"rINSTbl", "store":"movb" } diff --git a/runtime/interpreter/mterp/x86_64/op_iput_byte.S b/runtime/interpreter/mterp/x86_64/op_iput_byte.S new file mode 100644 index 0000000000..cb4b1cde45 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iput_byte.S @@ -0,0 +1 @@ +%include "x86_64/op_iput.S" { "handler":"artSet8InstanceFromMterp" } diff --git a/runtime/interpreter/mterp/x86_64/op_iput_byte_quick.S b/runtime/interpreter/mterp/x86_64/op_iput_byte_quick.S new file mode 100644 index 0000000000..6bd060e4f3 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iput_byte_quick.S @@ -0,0 +1 @@ +%include "x86_64/op_iput_quick.S" { "reg":"rINSTbl", "store":"movb" } diff --git a/runtime/interpreter/mterp/x86_64/op_iput_char.S b/runtime/interpreter/mterp/x86_64/op_iput_char.S new file mode 100644 index 0000000000..b4e147cf5e --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iput_char.S @@ -0,0 +1 @@ +%include "x86_64/op_iput.S" { "handler":"artSet16InstanceFromMterp" } diff --git a/runtime/interpreter/mterp/x86_64/op_iput_char_quick.S b/runtime/interpreter/mterp/x86_64/op_iput_char_quick.S new file mode 100644 index 0000000000..3da96d53af --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iput_char_quick.S @@ -0,0 +1 @@ +%include "x86_64/op_iput_quick.S" { "reg":"rINSTw", "store":"movw" } diff --git a/runtime/interpreter/mterp/x86_64/op_iput_object.S b/runtime/interpreter/mterp/x86_64/op_iput_object.S new file mode 100644 index 0000000000..828712d8ba --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iput_object.S @@ -0,0 +1,10 @@ + EXPORT_PC + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG0 + movq rPC, OUT_ARG1 + REFRESH_INST ${opnum} + movl rINST, OUT_32_ARG2 + movq rSELF, OUT_ARG3 + call SYMBOL(MterpIputObject) + testb %al, %al + jz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_iput_object_quick.S b/runtime/interpreter/mterp/x86_64/op_iput_object_quick.S new file mode 100644 index 0000000000..b5b128ab7f --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iput_object_quick.S @@ -0,0 +1,9 @@ + EXPORT_PC + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG0 + movq rPC, OUT_ARG1 + REFRESH_INST ${opnum} + movl rINST, OUT_32_ARG2 + call SYMBOL(MterpIputObjectQuick) + testb %al, %al + jz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_iput_quick.S b/runtime/interpreter/mterp/x86_64/op_iput_quick.S new file mode 100644 index 0000000000..ecaf98e415 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iput_quick.S @@ -0,0 +1,13 @@ +%default { "reg":"rINST", "store":"movl" } + /* For: iput-quick, iput-object-quick */ + /* op vA, vB, offset@CCCC */ + movzbq rINSTbl, %rcx # rcx <- BA + sarl $$4, %ecx # ecx <- B + GET_VREG %ecx, %rcx # vB (object we're operating on) + testl %ecx, %ecx # is object null? + je common_errNullObject + andb $$0xf, rINSTbl # rINST <- A + GET_VREG rINST, rINSTq # rINST <- v[A] + movzwq 2(rPC), %rax # rax <- field byte offset + ${store} ${reg}, (%rcx,%rax,1) + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_iput_short.S b/runtime/interpreter/mterp/x86_64/op_iput_short.S new file mode 100644 index 0000000000..b4e147cf5e --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iput_short.S @@ -0,0 +1 @@ +%include "x86_64/op_iput.S" { "handler":"artSet16InstanceFromMterp" } diff --git a/runtime/interpreter/mterp/x86_64/op_iput_short_quick.S b/runtime/interpreter/mterp/x86_64/op_iput_short_quick.S new file mode 100644 index 0000000000..3da96d53af --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iput_short_quick.S @@ -0,0 +1 @@ +%include "x86_64/op_iput_quick.S" { "reg":"rINSTw", "store":"movw" } diff --git a/runtime/interpreter/mterp/x86_64/op_iput_wide.S b/runtime/interpreter/mterp/x86_64/op_iput_wide.S new file mode 100644 index 0000000000..e59717b846 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iput_wide.S @@ -0,0 +1,14 @@ + /* iput-wide vA, vB, field@CCCC */ + .extern artSet64InstanceFromMterp + EXPORT_PC + movzwq 2(rPC), OUT_ARG0 # field ref CCCC + movzbq rINSTbl, %rcx # rcx <- BA + sarl $$4, %ecx # ecx <- B + GET_VREG OUT_32_ARG1, %rcx # the object pointer + andb $$0xf, rINSTbl # rINST <- A + leaq VREG_ADDRESS(rINSTq), OUT_ARG2 # &fp[A] + movq OFF_FP_METHOD(rFP), OUT_ARG3 # referrer + call SYMBOL(artSet64InstanceFromMterp) + testb %al, %al + jnz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_iput_wide_quick.S b/runtime/interpreter/mterp/x86_64/op_iput_wide_quick.S new file mode 100644 index 0000000000..473189d007 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iput_wide_quick.S @@ -0,0 +1,12 @@ + /* iput-wide-quick vA, vB, offset@CCCC */ + movzbq rINSTbl, %rcx # rcx<- BA + sarl $$4, %ecx # ecx<- B + GET_VREG %ecx, %rcx # vB (object we're operating on) + testl %ecx, %ecx # is object null? + je common_errNullObject + movzwq 2(rPC), %rax # rax<- field byte offset + leaq (%rcx,%rax,1), %rcx # ecx<- Address of 64-bit target + andb $$0xf, rINSTbl # rINST<- A + GET_WIDE_VREG %rax, rINSTq # rax<- fp[A]/fp[A+1] + movq %rax, (%rcx) # obj.field<- r0/r1 + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_long_to_double.S b/runtime/interpreter/mterp/x86_64/op_long_to_double.S new file mode 100644 index 0000000000..7cdae32373 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_long_to_double.S @@ -0,0 +1 @@ +%include "x86_64/fpcvt.S" {"source_suffix":"i","dest_suffix":"dq","wide":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_long_to_float.S b/runtime/interpreter/mterp/x86_64/op_long_to_float.S new file mode 100644 index 0000000000..7553348633 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_long_to_float.S @@ -0,0 +1 @@ +%include "x86_64/fpcvt.S" {"source_suffix":"i","dest_suffix":"sq","wide":"0"} diff --git a/runtime/interpreter/mterp/x86_64/op_long_to_int.S b/runtime/interpreter/mterp/x86_64/op_long_to_int.S new file mode 100644 index 0000000000..7b50c8e0b3 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_long_to_int.S @@ -0,0 +1,2 @@ +/* we ignore the high word, making this equivalent to a 32-bit reg move */ +%include "x86_64/op_move.S" diff --git a/runtime/interpreter/mterp/x86_64/op_monitor_enter.S b/runtime/interpreter/mterp/x86_64/op_monitor_enter.S new file mode 100644 index 0000000000..411091f23e --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_monitor_enter.S @@ -0,0 +1,11 @@ +/* + * Synchronize on an object. + */ + /* monitor-enter vAA */ + EXPORT_PC + GET_VREG OUT_32_ARG0, rINSTq + movq rSELF, OUT_ARG1 + call SYMBOL(artLockObjectFromCode) # (object, self) + testq %rax, %rax + jnz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/op_monitor_exit.S b/runtime/interpreter/mterp/x86_64/op_monitor_exit.S new file mode 100644 index 0000000000..72d9a23a87 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_monitor_exit.S @@ -0,0 +1,15 @@ +/* + * Unlock an object. + * + * Exceptions that occur when unlocking a monitor need to appear as + * if they happened at the following instruction. See the Dalvik + * instruction spec. + */ + /* monitor-exit vAA */ + EXPORT_PC + GET_VREG OUT_32_ARG0, rINSTq + movq rSELF, OUT_ARG1 + call SYMBOL(artUnlockObjectFromCode) # (object, self) + testq %rax, %rax + jnz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/op_move.S b/runtime/interpreter/mterp/x86_64/op_move.S new file mode 100644 index 0000000000..ccaac2caa8 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_move.S @@ -0,0 +1,13 @@ +%default { "is_object":"0" } + /* for move, move-object, long-to-int */ + /* op vA, vB */ + movl rINST, %eax # eax <- BA + andb $$0xf, %al # eax <- A + shrl $$4, rINST # rINST <- B + GET_VREG %edx, rINSTq + .if $is_object + SET_VREG_OBJECT %edx, %rax # fp[A] <- fp[B] + .else + SET_VREG %edx, %rax # fp[A] <- fp[B] + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/op_move_16.S b/runtime/interpreter/mterp/x86_64/op_move_16.S new file mode 100644 index 0000000000..6a813eb5ce --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_move_16.S @@ -0,0 +1,12 @@ +%default { "is_object":"0" } + /* for: move/16, move-object/16 */ + /* op vAAAA, vBBBB */ + movzwq 4(rPC), %rcx # ecx <- BBBB + movzwq 2(rPC), %rax # eax <- AAAA + GET_VREG %edx, %rcx + .if $is_object + SET_VREG_OBJECT %edx, %rax # fp[A] <- fp[B] + .else + SET_VREG %edx, %rax # fp[A] <- fp[B] + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 diff --git a/runtime/interpreter/mterp/x86_64/op_move_exception.S b/runtime/interpreter/mterp/x86_64/op_move_exception.S new file mode 100644 index 0000000000..d0a14fdc8d --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_move_exception.S @@ -0,0 +1,5 @@ + /* move-exception vAA */ + movl THREAD_EXCEPTION_OFFSET(rSELF), %eax + SET_VREG_OBJECT %eax, rINSTq # fp[AA] <- exception object + movl $$0, THREAD_EXCEPTION_OFFSET(rSELF) + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/op_move_from16.S b/runtime/interpreter/mterp/x86_64/op_move_from16.S new file mode 100644 index 0000000000..150e9c2f2c --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_move_from16.S @@ -0,0 +1,11 @@ +%default { "is_object":"0" } + /* for: move/from16, move-object/from16 */ + /* op vAA, vBBBB */ + movzwq 2(rPC), %rax # eax <- BBBB + GET_VREG %edx, %rax # edx <- fp[BBBB] + .if $is_object + SET_VREG_OBJECT %edx, rINSTq # fp[A] <- fp[B] + .else + SET_VREG %edx, rINSTq # fp[A] <- fp[B] + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_move_object.S b/runtime/interpreter/mterp/x86_64/op_move_object.S new file mode 100644 index 0000000000..0d866496e8 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_move_object.S @@ -0,0 +1 @@ +%include "x86_64/op_move.S" {"is_object":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_move_object_16.S b/runtime/interpreter/mterp/x86_64/op_move_object_16.S new file mode 100644 index 0000000000..32541ff2bf --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_move_object_16.S @@ -0,0 +1 @@ +%include "x86_64/op_move_16.S" {"is_object":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_move_object_from16.S b/runtime/interpreter/mterp/x86_64/op_move_object_from16.S new file mode 100644 index 0000000000..983e4abae5 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_move_object_from16.S @@ -0,0 +1 @@ +%include "x86_64/op_move_from16.S" {"is_object":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_move_result.S b/runtime/interpreter/mterp/x86_64/op_move_result.S new file mode 100644 index 0000000000..8268344bce --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_move_result.S @@ -0,0 +1,11 @@ +%default { "is_object":"0" } + /* for: move-result, move-result-object */ + /* op vAA */ + movq OFF_FP_RESULT_REGISTER(rFP), %rax # get pointer to result JType. + movl (%rax), %eax # r0 <- result.i. + .if $is_object + SET_VREG_OBJECT %eax, rINSTq # fp[A] <- fp[B] + .else + SET_VREG %eax, rINSTq # fp[A] <- fp[B] + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/op_move_result_object.S b/runtime/interpreter/mterp/x86_64/op_move_result_object.S new file mode 100644 index 0000000000..c5aac17f41 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_move_result_object.S @@ -0,0 +1 @@ +%include "x86_64/op_move_result.S" {"is_object":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_move_result_wide.S b/runtime/interpreter/mterp/x86_64/op_move_result_wide.S new file mode 100644 index 0000000000..03de783627 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_move_result_wide.S @@ -0,0 +1,5 @@ + /* move-result-wide vAA */ + movq OFF_FP_RESULT_REGISTER(rFP), %rax # get pointer to result JType. + movq (%rax), %rdx # Get wide + SET_WIDE_VREG %rdx, rINSTq # v[AA] <- rdx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/op_move_wide.S b/runtime/interpreter/mterp/x86_64/op_move_wide.S new file mode 100644 index 0000000000..508f8cc152 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_move_wide.S @@ -0,0 +1,8 @@ + /* move-wide vA, vB */ + /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */ + movl rINST, %ecx # ecx <- BA + sarl $$4, rINST # rINST <- B + andb $$0xf, %cl # ecx <- A + GET_WIDE_VREG %rdx, rINSTq # rdx <- v[B] + SET_WIDE_VREG %rdx, %rcx # v[A] <- rdx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/op_move_wide_16.S b/runtime/interpreter/mterp/x86_64/op_move_wide_16.S new file mode 100644 index 0000000000..ce371a920e --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_move_wide_16.S @@ -0,0 +1,7 @@ + /* move-wide/16 vAAAA, vBBBB */ + /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */ + movzwq 4(rPC), %rcx # ecx<- BBBB + movzwq 2(rPC), %rax # eax<- AAAA + GET_WIDE_VREG %rdx, %rcx # rdx <- v[B] + SET_WIDE_VREG %rdx, %rax # v[A] <- rdx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 diff --git a/runtime/interpreter/mterp/x86_64/op_move_wide_from16.S b/runtime/interpreter/mterp/x86_64/op_move_wide_from16.S new file mode 100644 index 0000000000..0d6971a674 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_move_wide_from16.S @@ -0,0 +1,6 @@ + /* move-wide/from16 vAA, vBBBB */ + /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */ + movzwl 2(rPC), %ecx # ecx <- BBBB + GET_WIDE_VREG %rdx, %rcx # rdx <- v[B] + SET_WIDE_VREG %rdx, rINSTq # v[A] <- rdx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_mul_double.S b/runtime/interpreter/mterp/x86_64/op_mul_double.S new file mode 100644 index 0000000000..1f4bcb3d00 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_mul_double.S @@ -0,0 +1 @@ +%include "x86_64/sseBinop.S" {"instr":"muls","suff":"d"} diff --git a/runtime/interpreter/mterp/x86_64/op_mul_double_2addr.S b/runtime/interpreter/mterp/x86_64/op_mul_double_2addr.S new file mode 100644 index 0000000000..9850a28995 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_mul_double_2addr.S @@ -0,0 +1 @@ +%include "x86_64/sseBinop2Addr.S" {"instr":"muls","suff":"d"} diff --git a/runtime/interpreter/mterp/x86_64/op_mul_float.S b/runtime/interpreter/mterp/x86_64/op_mul_float.S new file mode 100644 index 0000000000..85960e9dec --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_mul_float.S @@ -0,0 +1 @@ +%include "x86_64/sseBinop.S" {"instr":"muls","suff":"s"} diff --git a/runtime/interpreter/mterp/x86_64/op_mul_float_2addr.S b/runtime/interpreter/mterp/x86_64/op_mul_float_2addr.S new file mode 100644 index 0000000000..6d36b6a178 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_mul_float_2addr.S @@ -0,0 +1 @@ +%include "x86_64/sseBinop2Addr.S" {"instr":"muls","suff":"s"} diff --git a/runtime/interpreter/mterp/x86_64/op_mul_int.S b/runtime/interpreter/mterp/x86_64/op_mul_int.S new file mode 100644 index 0000000000..5f3923a20e --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_mul_int.S @@ -0,0 +1 @@ +%include "x86_64/binop.S" {"instr":"imull (rFP,%rcx,4), %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_mul_int_2addr.S b/runtime/interpreter/mterp/x86_64/op_mul_int_2addr.S new file mode 100644 index 0000000000..0b5af8a927 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_mul_int_2addr.S @@ -0,0 +1,8 @@ + /* mul vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $$4, rINST # rINST <- B + andb $$0xf, %cl # ecx <- A + GET_VREG %eax, %rcx # eax <- vA + imull (rFP,rINSTq,4), %eax + SET_VREG %eax, %rcx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/op_mul_int_lit16.S b/runtime/interpreter/mterp/x86_64/op_mul_int_lit16.S new file mode 100644 index 0000000000..a4cfdbce3e --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_mul_int_lit16.S @@ -0,0 +1 @@ +%include "x86_64/binopLit16.S" {"instr":"imull %ecx, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_mul_int_lit8.S b/runtime/interpreter/mterp/x86_64/op_mul_int_lit8.S new file mode 100644 index 0000000000..89e9acb77d --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_mul_int_lit8.S @@ -0,0 +1 @@ +%include "x86_64/binopLit8.S" {"instr":"imull %ecx, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_mul_long.S b/runtime/interpreter/mterp/x86_64/op_mul_long.S new file mode 100644 index 0000000000..2b853705cf --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_mul_long.S @@ -0,0 +1 @@ +%include "x86_64/binopWide.S" {"instr":"imulq (rFP,%rcx,4), %rax"} diff --git a/runtime/interpreter/mterp/x86_64/op_mul_long_2addr.S b/runtime/interpreter/mterp/x86_64/op_mul_long_2addr.S new file mode 100644 index 0000000000..167128b4d1 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_mul_long_2addr.S @@ -0,0 +1,8 @@ + /* mul vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $$4, rINST # rINST <- B + andb $$0xf, %cl # ecx <- A + GET_WIDE_VREG %rax, %rcx # rax <- vA + imulq (rFP,rINSTq,4), %rax + SET_WIDE_VREG %rax, %rcx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/op_neg_double.S b/runtime/interpreter/mterp/x86_64/op_neg_double.S new file mode 100644 index 0000000000..2c14b091cb --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_neg_double.S @@ -0,0 +1 @@ +%include "x86_64/unop.S" {"preinstr":" movq $0x8000000000000000, %rsi", "instr":" xorq %rsi, %rax", "wide":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_neg_float.S b/runtime/interpreter/mterp/x86_64/op_neg_float.S new file mode 100644 index 0000000000..148b21ec9a --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_neg_float.S @@ -0,0 +1 @@ +%include "x86_64/unop.S" {"instr":" xorl $0x80000000, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_neg_int.S b/runtime/interpreter/mterp/x86_64/op_neg_int.S new file mode 100644 index 0000000000..f90a937a8b --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_neg_int.S @@ -0,0 +1 @@ +%include "x86_64/unop.S" {"instr":" negl %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_neg_long.S b/runtime/interpreter/mterp/x86_64/op_neg_long.S new file mode 100644 index 0000000000..18fc3ccece --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_neg_long.S @@ -0,0 +1 @@ +%include "x86_64/unop.S" {"instr":" negq %rax", "wide":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_new_array.S b/runtime/interpreter/mterp/x86_64/op_new_array.S new file mode 100644 index 0000000000..9831a0b888 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_new_array.S @@ -0,0 +1,18 @@ +/* + * Allocate an array of objects, specified with the array class + * and a count. + * + * The verifier guarantees that this is an array class, so we don't + * check for it here. + */ + /* new-array vA, vB, class@CCCC */ + EXPORT_PC + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG0 + movq rPC, OUT_ARG1 + REFRESH_INST ${opnum} + movq rINSTq, OUT_ARG2 + movq rSELF, OUT_ARG3 + call SYMBOL(MterpNewArray) + testb %al, %al # 0 means an exception is thrown + jz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_new_instance.S b/runtime/interpreter/mterp/x86_64/op_new_instance.S new file mode 100644 index 0000000000..fc8c8cd98c --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_new_instance.S @@ -0,0 +1,13 @@ +/* + * Create a new instance of a class. + */ + /* new-instance vAA, class@BBBB */ + EXPORT_PC + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG0 + movq rSELF, OUT_ARG1 + REFRESH_INST ${opnum} + movq rINSTq, OUT_ARG2 + call SYMBOL(MterpNewInstance) + testb %al, %al # 0 means an exception is thrown + jz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_nop.S b/runtime/interpreter/mterp/x86_64/op_nop.S new file mode 100644 index 0000000000..4cb68e392e --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_nop.S @@ -0,0 +1 @@ + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/op_not_int.S b/runtime/interpreter/mterp/x86_64/op_not_int.S new file mode 100644 index 0000000000..463d080de9 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_not_int.S @@ -0,0 +1 @@ +%include "x86_64/unop.S" {"instr":" notl %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_not_long.S b/runtime/interpreter/mterp/x86_64/op_not_long.S new file mode 100644 index 0000000000..c97bb9ea1a --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_not_long.S @@ -0,0 +1 @@ +%include "x86_64/unop.S" {"instr":" notq %rax", "wide":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_or_int.S b/runtime/interpreter/mterp/x86_64/op_or_int.S new file mode 100644 index 0000000000..730310f6af --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_or_int.S @@ -0,0 +1 @@ +%include "x86_64/binop.S" {"instr":"orl (rFP,%rcx,4), %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_or_int_2addr.S b/runtime/interpreter/mterp/x86_64/op_or_int_2addr.S new file mode 100644 index 0000000000..f722e4dd9c --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_or_int_2addr.S @@ -0,0 +1 @@ +%include "x86_64/binop2addr.S" {"instr":"orl %eax, (rFP,%rcx,4)"} diff --git a/runtime/interpreter/mterp/x86_64/op_or_int_lit16.S b/runtime/interpreter/mterp/x86_64/op_or_int_lit16.S new file mode 100644 index 0000000000..fee86c7c62 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_or_int_lit16.S @@ -0,0 +1 @@ +%include "x86_64/binopLit16.S" {"instr":"orl %ecx, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_or_int_lit8.S b/runtime/interpreter/mterp/x86_64/op_or_int_lit8.S new file mode 100644 index 0000000000..81104c7e56 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_or_int_lit8.S @@ -0,0 +1 @@ +%include "x86_64/binopLit8.S" {"instr":"orl %ecx, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_or_long.S b/runtime/interpreter/mterp/x86_64/op_or_long.S new file mode 100644 index 0000000000..6c70a2001e --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_or_long.S @@ -0,0 +1 @@ +%include "x86_64/binopWide.S" {"instr":"orq (rFP,%rcx,4), %rax"} diff --git a/runtime/interpreter/mterp/x86_64/op_or_long_2addr.S b/runtime/interpreter/mterp/x86_64/op_or_long_2addr.S new file mode 100644 index 0000000000..546da1de2d --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_or_long_2addr.S @@ -0,0 +1 @@ +%include "x86_64/binopWide2addr.S" {"instr":"orq %rax, (rFP,%rcx,4)"} diff --git a/runtime/interpreter/mterp/x86_64/op_packed_switch.S b/runtime/interpreter/mterp/x86_64/op_packed_switch.S new file mode 100644 index 0000000000..0400ca45cf --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_packed_switch.S @@ -0,0 +1,27 @@ +%default { "func":"MterpDoPackedSwitch" } +/* + * Handle a packed-switch or sparse-switch instruction. In both cases + * we decode it and hand it off to a helper function. + * + * We don't really expect backward branches in a switch statement, but + * they're perfectly legal, so we check for them here. + * + * for: packed-switch, sparse-switch + */ + /* op vAA, +BBBB */ + movslq 2(rPC), OUT_ARG0 # rcx <- BBBBbbbb + leaq (rPC,OUT_ARG0,2), OUT_ARG0 # rcx <- PC + BBBBbbbb*2 + GET_VREG OUT_32_ARG1, rINSTq # eax <- vAA + call SYMBOL($func) + addl %eax, %eax + movslq %eax, %rax + leaq (rPC, %rax), rPC + FETCH_INST + jg 1f +#if MTERP_SUSPEND + REFRESH_IBASE +#else + jmp MterpCheckSuspendAndContinue +#endif +1: + GOTO_NEXT diff --git a/runtime/interpreter/mterp/x86_64/op_rem_double.S b/runtime/interpreter/mterp/x86_64/op_rem_double.S new file mode 100644 index 0000000000..00aed787cb --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_rem_double.S @@ -0,0 +1,14 @@ + /* rem_double vAA, vBB, vCC */ + movzbq 3(rPC), %rcx # ecx <- BB + movzbq 2(rPC), %rax # eax <- CC + fldl VREG_ADDRESS(%rcx) # %st1 <- fp[vBB] + fldl VREG_ADDRESS(%rax) # %st0 <- fp[vCC] +1: + fprem + fstsw %ax + sahf + jp 1b + fstp %st(1) + fstpl VREG_ADDRESS(rINSTq) # fp[vAA] <- %st + CLEAR_WIDE_REF rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_rem_double_2addr.S b/runtime/interpreter/mterp/x86_64/op_rem_double_2addr.S new file mode 100644 index 0000000000..9768266e21 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_rem_double_2addr.S @@ -0,0 +1,15 @@ + /* rem_double/2addr vA, vB */ + movzbq rINSTbl, %rcx # ecx <- A+ + sarl $$4, rINST # rINST <- B + fldl VREG_ADDRESS(rINSTq) # vB to fp stack + andb $$0xf, %cl # ecx <- A + fldl VREG_ADDRESS(%rcx) # vA to fp stack +1: + fprem + fstsw %ax + sahf + jp 1b + fstp %st(1) + fstpl VREG_ADDRESS(%rcx) # %st to vA + CLEAR_WIDE_REF %rcx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/op_rem_float.S b/runtime/interpreter/mterp/x86_64/op_rem_float.S new file mode 100644 index 0000000000..5af28accec --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_rem_float.S @@ -0,0 +1,14 @@ + /* rem_float vAA, vBB, vCC */ + movzbq 3(rPC), %rcx # ecx <- BB + movzbq 2(rPC), %rax # eax <- CC + flds VREG_ADDRESS(%rcx) # vBB to fp stack + flds VREG_ADDRESS(%rax) # vCC to fp stack +1: + fprem + fstsw %ax + sahf + jp 1b + fstp %st(1) + fstps VREG_ADDRESS(rINSTq) # %st to vAA + CLEAR_REF rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_rem_float_2addr.S b/runtime/interpreter/mterp/x86_64/op_rem_float_2addr.S new file mode 100644 index 0000000000..e9282a8de9 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_rem_float_2addr.S @@ -0,0 +1,15 @@ + /* rem_float/2addr vA, vB */ + movzbq rINSTbl, %rcx # ecx <- A+ + sarl $$4, rINST # rINST <- B + flds VREG_ADDRESS(rINSTq) # vB to fp stack + andb $$0xf, %cl # ecx <- A + flds VREG_ADDRESS(%rcx) # vA to fp stack +1: + fprem + fstsw %ax + sahf + jp 1b + fstp %st(1) + fstps VREG_ADDRESS(%rcx) # %st to vA + CLEAR_REF %rcx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/op_rem_int.S b/runtime/interpreter/mterp/x86_64/op_rem_int.S new file mode 100644 index 0000000000..fd77d7cdfe --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_rem_int.S @@ -0,0 +1 @@ +%include "x86_64/bindiv.S" {"result":"%edx","second":"%ecx","wide":"0","suffix":"l","rem":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_rem_int_2addr.S b/runtime/interpreter/mterp/x86_64/op_rem_int_2addr.S new file mode 100644 index 0000000000..25ffbf713b --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_rem_int_2addr.S @@ -0,0 +1 @@ +%include "x86_64/bindiv2addr.S" {"result":"%edx","second":"%ecx","wide":"0","suffix":"l","rem":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_rem_int_lit16.S b/runtime/interpreter/mterp/x86_64/op_rem_int_lit16.S new file mode 100644 index 0000000000..21cc37087d --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_rem_int_lit16.S @@ -0,0 +1 @@ +%include "x86_64/bindivLit16.S" {"result":"%edx","rem":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_rem_int_lit8.S b/runtime/interpreter/mterp/x86_64/op_rem_int_lit8.S new file mode 100644 index 0000000000..2eb0150f63 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_rem_int_lit8.S @@ -0,0 +1 @@ +%include "x86_64/bindivLit8.S" {"result":"%edx","rem":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_rem_long.S b/runtime/interpreter/mterp/x86_64/op_rem_long.S new file mode 100644 index 0000000000..efa721520d --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_rem_long.S @@ -0,0 +1 @@ +%include "x86_64/bindiv.S" {"result":"%rdx","second":"%rcx","wide":"1","suffix":"q","ext":"cqo","rem":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_rem_long_2addr.S b/runtime/interpreter/mterp/x86_64/op_rem_long_2addr.S new file mode 100644 index 0000000000..ce0dd86539 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_rem_long_2addr.S @@ -0,0 +1 @@ +%include "x86_64/bindiv2addr.S" {"result":"%rdx","second":"%rcx","wide":"1","suffix":"q","rem":"1","ext":"cqo"} diff --git a/runtime/interpreter/mterp/x86_64/op_return.S b/runtime/interpreter/mterp/x86_64/op_return.S new file mode 100644 index 0000000000..14f4f8a446 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_return.S @@ -0,0 +1,15 @@ +/* + * Return a 32-bit value. + * + * for: return, return-object + */ + /* op vAA */ + .extern MterpThreadFenceForConstructor + call SYMBOL(MterpThreadFenceForConstructor) + testl $$(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(rSELF) + jz 1f + movq rSELF, OUT_ARG0 + call SYMBOL(MterpSuspendCheck) +1: + GET_VREG %eax, rINSTq # eax <- vAA + jmp MterpReturn diff --git a/runtime/interpreter/mterp/x86_64/op_return_object.S b/runtime/interpreter/mterp/x86_64/op_return_object.S new file mode 100644 index 0000000000..1ae69a501c --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_return_object.S @@ -0,0 +1 @@ +%include "x86_64/op_return.S" diff --git a/runtime/interpreter/mterp/x86_64/op_return_void.S b/runtime/interpreter/mterp/x86_64/op_return_void.S new file mode 100644 index 0000000000..46a5753c87 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_return_void.S @@ -0,0 +1,9 @@ + .extern MterpThreadFenceForConstructor + call SYMBOL(MterpThreadFenceForConstructor) + testl $$(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(rSELF) + jz 1f + movq rSELF, OUT_ARG0 + call SYMBOL(MterpSuspendCheck) +1: + xorq %rax, %rax + jmp MterpReturn diff --git a/runtime/interpreter/mterp/x86_64/op_return_void_no_barrier.S b/runtime/interpreter/mterp/x86_64/op_return_void_no_barrier.S new file mode 100644 index 0000000000..92e3506d1d --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_return_void_no_barrier.S @@ -0,0 +1,7 @@ + testl $$(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(rSELF) + jz 1f + movq rSELF, OUT_ARG0 + call SYMBOL(MterpSuspendCheck) +1: + xorq %rax, %rax + jmp MterpReturn diff --git a/runtime/interpreter/mterp/x86_64/op_return_wide.S b/runtime/interpreter/mterp/x86_64/op_return_wide.S new file mode 100644 index 0000000000..f2d6e04cab --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_return_wide.S @@ -0,0 +1,13 @@ +/* + * Return a 64-bit value. + */ + /* return-wide vAA */ + .extern MterpThreadFenceForConstructor + call SYMBOL(MterpThreadFenceForConstructor) + testl $$(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(rSELF) + jz 1f + movq rSELF, OUT_ARG0 + call SYMBOL(MterpSuspendCheck) +1: + GET_WIDE_VREG %rax, rINSTq # eax <- v[AA] + jmp MterpReturn diff --git a/runtime/interpreter/mterp/x86_64/op_rsub_int.S b/runtime/interpreter/mterp/x86_64/op_rsub_int.S new file mode 100644 index 0000000000..2dd20026df --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_rsub_int.S @@ -0,0 +1,2 @@ +/* this op is "rsub-int", but can be thought of as "rsub-int/lit16" */ +%include "x86_64/binopLit16.S" {"instr":"subl %eax, %ecx","result":"%ecx"} diff --git a/runtime/interpreter/mterp/x86_64/op_rsub_int_lit8.S b/runtime/interpreter/mterp/x86_64/op_rsub_int_lit8.S new file mode 100644 index 0000000000..64d0d8a704 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_rsub_int_lit8.S @@ -0,0 +1 @@ +%include "x86_64/binopLit8.S" {"instr":"subl %eax, %ecx" , "result":"%ecx"} diff --git a/runtime/interpreter/mterp/x86_64/op_sget.S b/runtime/interpreter/mterp/x86_64/op_sget.S new file mode 100644 index 0000000000..38d9a5e6c8 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_sget.S @@ -0,0 +1,25 @@ +%default { "is_object":"0", "helper":"artGet32StaticFromCode", "wide":"0" } +/* + * General SGET handler wrapper. + * + * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short, sget-wide + */ + /* op vAA, field@BBBB */ + .extern $helper + EXPORT_PC + movzwq 2(rPC), OUT_ARG0 # field ref CCCC + movq OFF_FP_METHOD(rFP), OUT_ARG1 # referrer + movq rSELF, OUT_ARG2 # self + call SYMBOL($helper) + cmpl $$0, THREAD_EXCEPTION_OFFSET(rSELF) + jnz MterpException + .if $is_object + SET_VREG_OBJECT %eax, rINSTq # fp[A] <- value + .else + .if $wide + SET_WIDE_VREG %rax, rINSTq # fp[A] <- value + .else + SET_VREG %eax, rINSTq # fp[A] <- value + .endif + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_sget_boolean.S b/runtime/interpreter/mterp/x86_64/op_sget_boolean.S new file mode 100644 index 0000000000..7d358daec2 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_sget_boolean.S @@ -0,0 +1 @@ +%include "x86_64/op_sget.S" {"helper":"artGetBooleanStaticFromCode"} diff --git a/runtime/interpreter/mterp/x86_64/op_sget_byte.S b/runtime/interpreter/mterp/x86_64/op_sget_byte.S new file mode 100644 index 0000000000..79d9ff448b --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_sget_byte.S @@ -0,0 +1 @@ +%include "x86_64/op_sget.S" {"helper":"artGetByteStaticFromCode"} diff --git a/runtime/interpreter/mterp/x86_64/op_sget_char.S b/runtime/interpreter/mterp/x86_64/op_sget_char.S new file mode 100644 index 0000000000..448861052f --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_sget_char.S @@ -0,0 +1 @@ +%include "x86_64/op_sget.S" {"helper":"artGetCharStaticFromCode"} diff --git a/runtime/interpreter/mterp/x86_64/op_sget_object.S b/runtime/interpreter/mterp/x86_64/op_sget_object.S new file mode 100644 index 0000000000..09b627e124 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_sget_object.S @@ -0,0 +1 @@ +%include "x86_64/op_sget.S" {"is_object":"1", "helper":"artGetObjStaticFromCode"} diff --git a/runtime/interpreter/mterp/x86_64/op_sget_short.S b/runtime/interpreter/mterp/x86_64/op_sget_short.S new file mode 100644 index 0000000000..47ac23803c --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_sget_short.S @@ -0,0 +1 @@ +%include "x86_64/op_sget.S" {"helper":"artGetShortStaticFromCode"} diff --git a/runtime/interpreter/mterp/x86_64/op_sget_wide.S b/runtime/interpreter/mterp/x86_64/op_sget_wide.S new file mode 100644 index 0000000000..aa223434cf --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_sget_wide.S @@ -0,0 +1 @@ +%include "x86_64/op_sget.S" {"helper":"artGet64StaticFromCode", "wide":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_shl_int.S b/runtime/interpreter/mterp/x86_64/op_shl_int.S new file mode 100644 index 0000000000..fa1edb7555 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_shl_int.S @@ -0,0 +1 @@ +%include "x86_64/binop1.S" {"instr":"sall %cl, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_shl_int_2addr.S b/runtime/interpreter/mterp/x86_64/op_shl_int_2addr.S new file mode 100644 index 0000000000..dd962792c9 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_shl_int_2addr.S @@ -0,0 +1 @@ +%include "x86_64/shop2addr.S" {"instr":"sall %cl, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_shl_int_lit8.S b/runtime/interpreter/mterp/x86_64/op_shl_int_lit8.S new file mode 100644 index 0000000000..39b23ae1fb --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_shl_int_lit8.S @@ -0,0 +1 @@ +%include "x86_64/binopLit8.S" {"instr":"sall %cl, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_shl_long.S b/runtime/interpreter/mterp/x86_64/op_shl_long.S new file mode 100644 index 0000000000..fdc7cb64de --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_shl_long.S @@ -0,0 +1 @@ +%include "x86_64/binop1.S" {"instr":"salq %cl, %rax","wide":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_shl_long_2addr.S b/runtime/interpreter/mterp/x86_64/op_shl_long_2addr.S new file mode 100644 index 0000000000..546633f7d5 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_shl_long_2addr.S @@ -0,0 +1 @@ +%include "x86_64/shop2addr.S" {"instr":"salq %cl, %rax","wide":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_shr_int.S b/runtime/interpreter/mterp/x86_64/op_shr_int.S new file mode 100644 index 0000000000..fc289f4638 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_shr_int.S @@ -0,0 +1 @@ +%include "x86_64/binop1.S" {"instr":"sarl %cl, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_shr_int_2addr.S b/runtime/interpreter/mterp/x86_64/op_shr_int_2addr.S new file mode 100644 index 0000000000..0e5bca7057 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_shr_int_2addr.S @@ -0,0 +1 @@ +%include "x86_64/shop2addr.S" {"instr":"sarl %cl, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_shr_int_lit8.S b/runtime/interpreter/mterp/x86_64/op_shr_int_lit8.S new file mode 100644 index 0000000000..3cc9307569 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_shr_int_lit8.S @@ -0,0 +1 @@ +%include "x86_64/binopLit8.S" {"instr":"sarl %cl, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_shr_long.S b/runtime/interpreter/mterp/x86_64/op_shr_long.S new file mode 100644 index 0000000000..25028d3560 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_shr_long.S @@ -0,0 +1 @@ +%include "x86_64/binop1.S" {"instr":"sarq %cl, %rax","wide":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_shr_long_2addr.S b/runtime/interpreter/mterp/x86_64/op_shr_long_2addr.S new file mode 100644 index 0000000000..373841322d --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_shr_long_2addr.S @@ -0,0 +1 @@ +%include "x86_64/shop2addr.S" {"instr":"sarq %cl, %rax","wide":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_sparse_switch.S b/runtime/interpreter/mterp/x86_64/op_sparse_switch.S new file mode 100644 index 0000000000..0eaa514813 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_sparse_switch.S @@ -0,0 +1 @@ +%include "x86_64/op_packed_switch.S" { "func":"MterpDoSparseSwitch" } diff --git a/runtime/interpreter/mterp/x86_64/op_sput.S b/runtime/interpreter/mterp/x86_64/op_sput.S new file mode 100644 index 0000000000..e92b03273b --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_sput.S @@ -0,0 +1,17 @@ +%default { "helper":"artSet32StaticFromCode"} +/* + * General SPUT handler wrapper. + * + * for: sput, sput-boolean, sput-byte, sput-char, sput-short + */ + /* op vAA, field@BBBB */ + .extern $helper + EXPORT_PC + movzwq 2(rPC), OUT_ARG0 # field ref BBBB + GET_VREG OUT_32_ARG1, rINSTq # fp[AA] + movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer + movq rSELF, OUT_ARG3 # self + call SYMBOL($helper) + testb %al, %al + jnz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_sput_boolean.S b/runtime/interpreter/mterp/x86_64/op_sput_boolean.S new file mode 100644 index 0000000000..8718915cb2 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_sput_boolean.S @@ -0,0 +1 @@ +%include "x86_64/op_sput.S" {"helper":"artSet8StaticFromCode"} diff --git a/runtime/interpreter/mterp/x86_64/op_sput_byte.S b/runtime/interpreter/mterp/x86_64/op_sput_byte.S new file mode 100644 index 0000000000..8718915cb2 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_sput_byte.S @@ -0,0 +1 @@ +%include "x86_64/op_sput.S" {"helper":"artSet8StaticFromCode"} diff --git a/runtime/interpreter/mterp/x86_64/op_sput_char.S b/runtime/interpreter/mterp/x86_64/op_sput_char.S new file mode 100644 index 0000000000..2fe9d14816 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_sput_char.S @@ -0,0 +1 @@ +%include "x86_64/op_sput.S" {"helper":"artSet16StaticFromCode"} diff --git a/runtime/interpreter/mterp/x86_64/op_sput_object.S b/runtime/interpreter/mterp/x86_64/op_sput_object.S new file mode 100644 index 0000000000..eb5a37673e --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_sput_object.S @@ -0,0 +1,10 @@ + EXPORT_PC + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG0 + movq rPC, OUT_ARG1 + REFRESH_INST ${opnum} + movq rINSTq, OUT_ARG2 + movq rSELF, OUT_ARG3 + call SYMBOL(MterpSputObject) + testb %al, %al + jz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_sput_short.S b/runtime/interpreter/mterp/x86_64/op_sput_short.S new file mode 100644 index 0000000000..2fe9d14816 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_sput_short.S @@ -0,0 +1 @@ +%include "x86_64/op_sput.S" {"helper":"artSet16StaticFromCode"} diff --git a/runtime/interpreter/mterp/x86_64/op_sput_wide.S b/runtime/interpreter/mterp/x86_64/op_sput_wide.S new file mode 100644 index 0000000000..c4bc269eb6 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_sput_wide.S @@ -0,0 +1,15 @@ +/* + * SPUT_WIDE handler wrapper. + * + */ + /* sput-wide vAA, field@BBBB */ + .extern artSet64IndirectStaticFromMterp + EXPORT_PC + movzwq 2(rPC), OUT_ARG0 # field ref BBBB + movq OFF_FP_METHOD(rFP), OUT_ARG1 # referrer + leaq VREG_ADDRESS(rINSTq), OUT_ARG2 # &fp[AA] + movq rSELF, OUT_ARG3 # self + call SYMBOL(artSet64IndirectStaticFromMterp) + testb %al, %al + jnz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_sub_double.S b/runtime/interpreter/mterp/x86_64/op_sub_double.S new file mode 100644 index 0000000000..952667e831 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_sub_double.S @@ -0,0 +1 @@ +%include "x86_64/sseBinop.S" {"instr":"subs","suff":"d"} diff --git a/runtime/interpreter/mterp/x86_64/op_sub_double_2addr.S b/runtime/interpreter/mterp/x86_64/op_sub_double_2addr.S new file mode 100644 index 0000000000..0bd5dbb8ff --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_sub_double_2addr.S @@ -0,0 +1 @@ +%include "x86_64/sseBinop2Addr.S" {"instr":"subs","suff":"d"} diff --git a/runtime/interpreter/mterp/x86_64/op_sub_float.S b/runtime/interpreter/mterp/x86_64/op_sub_float.S new file mode 100644 index 0000000000..ea0ae14f5b --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_sub_float.S @@ -0,0 +1 @@ +%include "x86_64/sseBinop.S" {"instr":"subs","suff":"s"} diff --git a/runtime/interpreter/mterp/x86_64/op_sub_float_2addr.S b/runtime/interpreter/mterp/x86_64/op_sub_float_2addr.S new file mode 100644 index 0000000000..9dd17805c8 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_sub_float_2addr.S @@ -0,0 +1 @@ +%include "x86_64/sseBinop2Addr.S" {"instr":"subs","suff":"s"} diff --git a/runtime/interpreter/mterp/x86_64/op_sub_int.S b/runtime/interpreter/mterp/x86_64/op_sub_int.S new file mode 100644 index 0000000000..560394f43f --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_sub_int.S @@ -0,0 +1 @@ +%include "x86_64/binop.S" {"instr":"subl (rFP,%rcx,4), %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_sub_int_2addr.S b/runtime/interpreter/mterp/x86_64/op_sub_int_2addr.S new file mode 100644 index 0000000000..6f50f78f41 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_sub_int_2addr.S @@ -0,0 +1 @@ +%include "x86_64/binop2addr.S" {"instr":"subl %eax, (rFP,%rcx,4)"} diff --git a/runtime/interpreter/mterp/x86_64/op_sub_long.S b/runtime/interpreter/mterp/x86_64/op_sub_long.S new file mode 100644 index 0000000000..7fa54e7a11 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_sub_long.S @@ -0,0 +1 @@ +%include "x86_64/binopWide.S" {"instr":"subq (rFP,%rcx,4), %rax"} diff --git a/runtime/interpreter/mterp/x86_64/op_sub_long_2addr.S b/runtime/interpreter/mterp/x86_64/op_sub_long_2addr.S new file mode 100644 index 0000000000..c18be10919 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_sub_long_2addr.S @@ -0,0 +1 @@ +%include "x86_64/binopWide2addr.S" {"instr":"subq %rax, (rFP,%rcx,4)"} diff --git a/runtime/interpreter/mterp/x86_64/op_throw.S b/runtime/interpreter/mterp/x86_64/op_throw.S new file mode 100644 index 0000000000..22ed990645 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_throw.S @@ -0,0 +1,10 @@ +/* + * Throw an exception object in the current thread. + */ + /* throw vAA */ + EXPORT_PC + GET_VREG %eax, rINSTq # eax<- vAA (exception object) + testb %al, %al + jz common_errNullObject + movq %rax, THREAD_EXCEPTION_OFFSET(rSELF) + jmp MterpException diff --git a/runtime/interpreter/mterp/x86_64/op_unused_3e.S b/runtime/interpreter/mterp/x86_64/op_unused_3e.S new file mode 100644 index 0000000000..280615f08b --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_unused_3e.S @@ -0,0 +1 @@ +%include "x86_64/unused.S" diff --git a/runtime/interpreter/mterp/x86_64/op_unused_3f.S b/runtime/interpreter/mterp/x86_64/op_unused_3f.S new file mode 100644 index 0000000000..280615f08b --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_unused_3f.S @@ -0,0 +1 @@ +%include "x86_64/unused.S" diff --git a/runtime/interpreter/mterp/x86_64/op_unused_40.S b/runtime/interpreter/mterp/x86_64/op_unused_40.S new file mode 100644 index 0000000000..280615f08b --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_unused_40.S @@ -0,0 +1 @@ +%include "x86_64/unused.S" diff --git a/runtime/interpreter/mterp/x86_64/op_unused_41.S b/runtime/interpreter/mterp/x86_64/op_unused_41.S new file mode 100644 index 0000000000..280615f08b --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_unused_41.S @@ -0,0 +1 @@ +%include "x86_64/unused.S" diff --git a/runtime/interpreter/mterp/x86_64/op_unused_42.S b/runtime/interpreter/mterp/x86_64/op_unused_42.S new file mode 100644 index 0000000000..280615f08b --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_unused_42.S @@ -0,0 +1 @@ +%include "x86_64/unused.S" diff --git a/runtime/interpreter/mterp/x86_64/op_unused_43.S b/runtime/interpreter/mterp/x86_64/op_unused_43.S new file mode 100644 index 0000000000..280615f08b --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_unused_43.S @@ -0,0 +1 @@ +%include "x86_64/unused.S" diff --git a/runtime/interpreter/mterp/x86_64/op_unused_79.S b/runtime/interpreter/mterp/x86_64/op_unused_79.S new file mode 100644 index 0000000000..280615f08b --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_unused_79.S @@ -0,0 +1 @@ +%include "x86_64/unused.S" diff --git a/runtime/interpreter/mterp/x86_64/op_unused_7a.S b/runtime/interpreter/mterp/x86_64/op_unused_7a.S new file mode 100644 index 0000000000..280615f08b --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_unused_7a.S @@ -0,0 +1 @@ +%include "x86_64/unused.S" diff --git a/runtime/interpreter/mterp/x86_64/op_unused_f4.S b/runtime/interpreter/mterp/x86_64/op_unused_f4.S new file mode 100644 index 0000000000..280615f08b --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_unused_f4.S @@ -0,0 +1 @@ +%include "x86_64/unused.S" diff --git a/runtime/interpreter/mterp/x86_64/op_unused_fa.S b/runtime/interpreter/mterp/x86_64/op_unused_fa.S new file mode 100644 index 0000000000..280615f08b --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_unused_fa.S @@ -0,0 +1 @@ +%include "x86_64/unused.S" diff --git a/runtime/interpreter/mterp/x86_64/op_unused_fb.S b/runtime/interpreter/mterp/x86_64/op_unused_fb.S new file mode 100644 index 0000000000..280615f08b --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_unused_fb.S @@ -0,0 +1 @@ +%include "x86_64/unused.S" diff --git a/runtime/interpreter/mterp/x86_64/op_unused_fc.S b/runtime/interpreter/mterp/x86_64/op_unused_fc.S new file mode 100644 index 0000000000..280615f08b --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_unused_fc.S @@ -0,0 +1 @@ +%include "x86_64/unused.S" diff --git a/runtime/interpreter/mterp/x86_64/op_unused_fd.S b/runtime/interpreter/mterp/x86_64/op_unused_fd.S new file mode 100644 index 0000000000..280615f08b --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_unused_fd.S @@ -0,0 +1 @@ +%include "x86_64/unused.S" diff --git a/runtime/interpreter/mterp/x86_64/op_unused_fe.S b/runtime/interpreter/mterp/x86_64/op_unused_fe.S new file mode 100644 index 0000000000..280615f08b --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_unused_fe.S @@ -0,0 +1 @@ +%include "x86_64/unused.S" diff --git a/runtime/interpreter/mterp/x86_64/op_unused_ff.S b/runtime/interpreter/mterp/x86_64/op_unused_ff.S new file mode 100644 index 0000000000..280615f08b --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_unused_ff.S @@ -0,0 +1 @@ +%include "x86_64/unused.S" diff --git a/runtime/interpreter/mterp/x86_64/op_ushr_int.S b/runtime/interpreter/mterp/x86_64/op_ushr_int.S new file mode 100644 index 0000000000..dd91086371 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_ushr_int.S @@ -0,0 +1 @@ +%include "x86_64/binop1.S" {"instr":"shrl %cl, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_ushr_int_2addr.S b/runtime/interpreter/mterp/x86_64/op_ushr_int_2addr.S new file mode 100644 index 0000000000..d38aedd234 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_ushr_int_2addr.S @@ -0,0 +1 @@ +%include "x86_64/shop2addr.S" {"instr":"shrl %cl, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_ushr_int_lit8.S b/runtime/interpreter/mterp/x86_64/op_ushr_int_lit8.S new file mode 100644 index 0000000000..f7ff8abc86 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_ushr_int_lit8.S @@ -0,0 +1 @@ +%include "x86_64/binopLit8.S" {"instr":"shrl %cl, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_ushr_long.S b/runtime/interpreter/mterp/x86_64/op_ushr_long.S new file mode 100644 index 0000000000..7c6daca05d --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_ushr_long.S @@ -0,0 +1 @@ +%include "x86_64/binop1.S" {"instr":"shrq %cl, %rax","wide":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_ushr_long_2addr.S b/runtime/interpreter/mterp/x86_64/op_ushr_long_2addr.S new file mode 100644 index 0000000000..cd6a22c6fa --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_ushr_long_2addr.S @@ -0,0 +1 @@ +%include "x86_64/shop2addr.S" {"instr":"shrq %cl, %rax","wide":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_xor_int.S b/runtime/interpreter/mterp/x86_64/op_xor_int.S new file mode 100644 index 0000000000..b295d74de0 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_xor_int.S @@ -0,0 +1 @@ +%include "x86_64/binop.S" {"instr":"xorl (rFP,%rcx,4), %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_xor_int_2addr.S b/runtime/interpreter/mterp/x86_64/op_xor_int_2addr.S new file mode 100644 index 0000000000..879bfc05dc --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_xor_int_2addr.S @@ -0,0 +1 @@ +%include "x86_64/binop2addr.S" {"instr":"xorl %eax, (rFP,%rcx,4)"} diff --git a/runtime/interpreter/mterp/x86_64/op_xor_int_lit16.S b/runtime/interpreter/mterp/x86_64/op_xor_int_lit16.S new file mode 100644 index 0000000000..5d375a1cf6 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_xor_int_lit16.S @@ -0,0 +1 @@ +%include "x86_64/binopLit16.S" {"instr":"xorl %ecx, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_xor_int_lit8.S b/runtime/interpreter/mterp/x86_64/op_xor_int_lit8.S new file mode 100644 index 0000000000..54cce9c18e --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_xor_int_lit8.S @@ -0,0 +1 @@ +%include "x86_64/binopLit8.S" {"instr":"xorl %ecx, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_xor_long.S b/runtime/interpreter/mterp/x86_64/op_xor_long.S new file mode 100644 index 0000000000..52b44e29c1 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_xor_long.S @@ -0,0 +1 @@ +%include "x86_64/binopWide.S" {"instr":"xorq (rFP,%rcx,4), %rax"} diff --git a/runtime/interpreter/mterp/x86_64/op_xor_long_2addr.S b/runtime/interpreter/mterp/x86_64/op_xor_long_2addr.S new file mode 100644 index 0000000000..d75c4ba6ce --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_xor_long_2addr.S @@ -0,0 +1 @@ +%include "x86_64/binopWide2addr.S" {"instr":"xorq %rax, (rFP,%rcx,4)"} diff --git a/runtime/interpreter/mterp/x86_64/shop2addr.S b/runtime/interpreter/mterp/x86_64/shop2addr.S new file mode 100644 index 0000000000..6b06d002fc --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/shop2addr.S @@ -0,0 +1,19 @@ +%default {"wide":"0"} +/* + * Generic 32-bit "shift/2addr" operation. + */ + /* shift/2addr vA, vB */ + movl rINST, %ecx # ecx <- BA + sarl $$4, %ecx # ecx <- B + GET_VREG %ecx, %rcx # ecx <- vBB + andb $$0xf, rINSTbl # rINST <- A + .if $wide + GET_WIDE_VREG %rax, rINSTq # rax <- vAA + $instr # ex: sarl %cl, %eax + SET_WIDE_VREG %rax, rINSTq + .else + GET_VREG %eax, rINSTq # eax <- vAA + $instr # ex: sarl %cl, %eax + SET_VREG %eax, rINSTq + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/sseBinop.S b/runtime/interpreter/mterp/x86_64/sseBinop.S new file mode 100644 index 0000000000..09d3364de7 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/sseBinop.S @@ -0,0 +1,9 @@ +%default {"instr":"","suff":""} + movzbq 2(rPC), %rcx # ecx <- BB + movzbq 3(rPC), %rax # eax <- CC + movs${suff} VREG_ADDRESS(%rcx), %xmm0 # %xmm0 <- 1st src + ${instr}${suff} VREG_ADDRESS(%rax), %xmm0 + movs${suff} %xmm0, VREG_ADDRESS(rINSTq) # vAA <- %xmm0 + pxor %xmm0, %xmm0 + movs${suff} %xmm0, VREG_REF_ADDRESS(rINSTq) # clear ref + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/sseBinop2Addr.S b/runtime/interpreter/mterp/x86_64/sseBinop2Addr.S new file mode 100644 index 0000000000..084166b95d --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/sseBinop2Addr.S @@ -0,0 +1,10 @@ +%default {"instr":"","suff":""} + movl rINST, %ecx # ecx <- A+ + andl $$0xf, %ecx # ecx <- A + movs${suff} VREG_ADDRESS(%rcx), %xmm0 # %xmm0 <- 1st src + sarl $$4, rINST # rINST<- B + ${instr}${suff} VREG_ADDRESS(rINSTq), %xmm0 + movs${suff} %xmm0, VREG_ADDRESS(%rcx) # vAA<- %xmm0 + pxor %xmm0, %xmm0 + movs${suff} %xmm0, VREG_REF_ADDRESS(rINSTq) # clear ref + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/unop.S b/runtime/interpreter/mterp/x86_64/unop.S new file mode 100644 index 0000000000..1777123f36 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/unop.S @@ -0,0 +1,22 @@ +%default {"preinstr":"", "instr":"", "wide":"0"} +/* + * Generic 32/64-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "result = op eax". + */ + /* unop vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $$4,rINST # rINST <- B + .if ${wide} + GET_WIDE_VREG %rax, rINSTq # rax <- vB + .else + GET_VREG %eax, rINSTq # eax <- vB + .endif + andb $$0xf,%cl # ecx <- A +$preinstr +$instr + .if ${wide} + SET_WIDE_VREG %rax, %rcx + .else + SET_VREG %eax, %rcx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/unused.S b/runtime/interpreter/mterp/x86_64/unused.S new file mode 100644 index 0000000000..c95ef947d3 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/unused.S @@ -0,0 +1,4 @@ +/* + * Bail to reference interpreter to throw. + */ + jmp MterpFallback diff --git a/runtime/interpreter/mterp/x86_64/zcmp.S b/runtime/interpreter/mterp/x86_64/zcmp.S new file mode 100644 index 0000000000..e503ec1f38 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/zcmp.S @@ -0,0 +1,24 @@ +/* + * Generic one-operand compare-and-branch operation. Provide a "revcmp" + * fragment that specifies the *reverse* comparison to perform, e.g. + * for "if-le" you would use "gt". + * + * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez + */ + /* if-cmp vAA, +BBBB */ + cmpl $$0, VREG_ADDRESS(rINSTq) # compare (vA, 0) + movl $$2, %eax # assume branch not taken + j${revcmp} 1f + movswq 2(rPC),%rax # fetch signed displacement +1: + addq %rax, %rax # eax <- AA * 2 + leaq (rPC, %rax), rPC + FETCH_INST + jg 2f # AA * 2 > 0 => no suspend check +#if MTERP_SUSPEND + REFRESH_IBASE +#else + jmp MterpCheckSuspendAndContinue +#endif +2: + GOTO_NEXT -- GitLab From 897338d7eaf5059f481d581e3c795068ae64d8b3 Mon Sep 17 00:00:00 2001 From: Serguei Katkov Date: Tue, 1 Mar 2016 15:53:22 +0600 Subject: [PATCH 075/204] x86 Fast Interpreter: Fix CFI With this change gdb can fall through x86 Fast Interpreter frame. Change-Id: Ia5efe0059ecbdb09491d37ab47367e222d29f1d3 Signed-off-by: Serguei Katkov --- runtime/interpreter/mterp/out/mterp_x86.S | 62 +++++++++++++---------- runtime/interpreter/mterp/x86/entry.S | 14 ++--- runtime/interpreter/mterp/x86/footer.S | 13 +++-- runtime/interpreter/mterp/x86/header.S | 35 ++++++++----- 4 files changed, 70 insertions(+), 54 deletions(-) diff --git a/runtime/interpreter/mterp/out/mterp_x86.S b/runtime/interpreter/mterp/out/mterp_x86.S index 567550f41f..ebac5fca48 100644 --- a/runtime/interpreter/mterp/out/mterp_x86.S +++ b/runtime/interpreter/mterp/out/mterp_x86.S @@ -112,25 +112,32 @@ unspecified registers or condition codes. #define SYMBOL(name) name #endif +.macro PUSH _reg + pushl \_reg + .cfi_adjust_cfa_offset 4 + .cfi_rel_offset \_reg, 0 +.endm + +.macro POP _reg + popl \_reg + .cfi_adjust_cfa_offset -4 + .cfi_restore \_reg +.endm + /* Frame size must be 16-byte aligned. - * Remember about 4 bytes for return address + * Remember about 4 bytes for return address + 4 * 4 for spills */ -#define FRAME_SIZE 44 +#define FRAME_SIZE 28 /* Frame diagram while executing ExecuteMterpImpl, high to low addresses */ -#define IN_ARG3 (FRAME_SIZE + 16) -#define IN_ARG2 (FRAME_SIZE + 12) -#define IN_ARG1 (FRAME_SIZE + 8) -#define IN_ARG0 (FRAME_SIZE + 4) -#define CALLER_RP (FRAME_SIZE + 0) +#define IN_ARG3 (FRAME_SIZE + 16 + 16) +#define IN_ARG2 (FRAME_SIZE + 16 + 12) +#define IN_ARG1 (FRAME_SIZE + 16 + 8) +#define IN_ARG0 (FRAME_SIZE + 16 + 4) /* Spill offsets relative to %esp */ -#define EBP_SPILL (FRAME_SIZE - 4) -#define EDI_SPILL (FRAME_SIZE - 8) -#define ESI_SPILL (FRAME_SIZE - 12) -#define EBX_SPILL (FRAME_SIZE - 16) -#define LOCAL0 (FRAME_SIZE - 20) -#define LOCAL1 (FRAME_SIZE - 24) -#define LOCAL2 (FRAME_SIZE - 28) +#define LOCAL0 (FRAME_SIZE - 4) +#define LOCAL1 (FRAME_SIZE - 8) +#define LOCAL2 (FRAME_SIZE - 12) /* Out Arg offsets, relative to %esp */ #define OUT_ARG3 ( 12) #define OUT_ARG2 ( 8) @@ -360,16 +367,18 @@ unspecified registers or condition codes. SYMBOL(ExecuteMterpImpl): .cfi_startproc + .cfi_def_cfa esp, 4 + + /* Spill callee save regs */ + PUSH %ebp + PUSH %edi + PUSH %esi + PUSH %ebx + /* Allocate frame */ subl $FRAME_SIZE, %esp .cfi_adjust_cfa_offset FRAME_SIZE - /* Spill callee save regs */ - movl %ebp, EBP_SPILL(%esp) - movl %edi, EDI_SPILL(%esp) - movl %esi, ESI_SPILL(%esp) - movl %ebx, EBX_SPILL(%esp) - /* Load ShadowFrame pointer */ movl IN_ARG2(%esp), %edx @@ -12985,17 +12994,16 @@ MterpReturn: movl %ecx, 4(%edx) mov $1, %eax MterpDone: - /* Restore callee save register */ - movl EBP_SPILL(%esp), %ebp - movl EDI_SPILL(%esp), %edi - movl ESI_SPILL(%esp), %esi - movl EBX_SPILL(%esp), %ebx - /* pop up frame */ addl $FRAME_SIZE, %esp .cfi_adjust_cfa_offset -FRAME_SIZE - ret + /* Restore callee save register */ + POP %ebx + POP %esi + POP %edi + POP %ebp + ret .cfi_endproc SIZE(ExecuteMterpImpl,ExecuteMterpImpl) diff --git a/runtime/interpreter/mterp/x86/entry.S b/runtime/interpreter/mterp/x86/entry.S index b83f7e1d83..785efdc5cc 100644 --- a/runtime/interpreter/mterp/x86/entry.S +++ b/runtime/interpreter/mterp/x86/entry.S @@ -32,16 +32,18 @@ SYMBOL(ExecuteMterpImpl): .cfi_startproc + .cfi_def_cfa esp, 4 + + /* Spill callee save regs */ + PUSH %ebp + PUSH %edi + PUSH %esi + PUSH %ebx + /* Allocate frame */ subl $$FRAME_SIZE, %esp .cfi_adjust_cfa_offset FRAME_SIZE - /* Spill callee save regs */ - movl %ebp, EBP_SPILL(%esp) - movl %edi, EDI_SPILL(%esp) - movl %esi, ESI_SPILL(%esp) - movl %ebx, EBX_SPILL(%esp) - /* Load ShadowFrame pointer */ movl IN_ARG2(%esp), %edx diff --git a/runtime/interpreter/mterp/x86/footer.S b/runtime/interpreter/mterp/x86/footer.S index 64d72d7709..3965ecde62 100644 --- a/runtime/interpreter/mterp/x86/footer.S +++ b/runtime/interpreter/mterp/x86/footer.S @@ -189,16 +189,15 @@ MterpReturn: movl %ecx, 4(%edx) mov $$1, %eax MterpDone: - /* Restore callee save register */ - movl EBP_SPILL(%esp), %ebp - movl EDI_SPILL(%esp), %edi - movl ESI_SPILL(%esp), %esi - movl EBX_SPILL(%esp), %ebx - /* pop up frame */ addl $$FRAME_SIZE, %esp .cfi_adjust_cfa_offset -FRAME_SIZE - ret + /* Restore callee save register */ + POP %ebx + POP %esi + POP %edi + POP %ebp + ret .cfi_endproc SIZE(ExecuteMterpImpl,ExecuteMterpImpl) diff --git a/runtime/interpreter/mterp/x86/header.S b/runtime/interpreter/mterp/x86/header.S index 6bddaf9344..5729b90ea6 100644 --- a/runtime/interpreter/mterp/x86/header.S +++ b/runtime/interpreter/mterp/x86/header.S @@ -105,25 +105,32 @@ unspecified registers or condition codes. #define SYMBOL(name) name #endif +.macro PUSH _reg + pushl \_reg + .cfi_adjust_cfa_offset 4 + .cfi_rel_offset \_reg, 0 +.endm + +.macro POP _reg + popl \_reg + .cfi_adjust_cfa_offset -4 + .cfi_restore \_reg +.endm + /* Frame size must be 16-byte aligned. - * Remember about 4 bytes for return address + * Remember about 4 bytes for return address + 4 * 4 for spills */ -#define FRAME_SIZE 44 +#define FRAME_SIZE 28 /* Frame diagram while executing ExecuteMterpImpl, high to low addresses */ -#define IN_ARG3 (FRAME_SIZE + 16) -#define IN_ARG2 (FRAME_SIZE + 12) -#define IN_ARG1 (FRAME_SIZE + 8) -#define IN_ARG0 (FRAME_SIZE + 4) -#define CALLER_RP (FRAME_SIZE + 0) +#define IN_ARG3 (FRAME_SIZE + 16 + 16) +#define IN_ARG2 (FRAME_SIZE + 16 + 12) +#define IN_ARG1 (FRAME_SIZE + 16 + 8) +#define IN_ARG0 (FRAME_SIZE + 16 + 4) /* Spill offsets relative to %esp */ -#define EBP_SPILL (FRAME_SIZE - 4) -#define EDI_SPILL (FRAME_SIZE - 8) -#define ESI_SPILL (FRAME_SIZE - 12) -#define EBX_SPILL (FRAME_SIZE - 16) -#define LOCAL0 (FRAME_SIZE - 20) -#define LOCAL1 (FRAME_SIZE - 24) -#define LOCAL2 (FRAME_SIZE - 28) +#define LOCAL0 (FRAME_SIZE - 4) +#define LOCAL1 (FRAME_SIZE - 8) +#define LOCAL2 (FRAME_SIZE - 12) /* Out Arg offsets, relative to %esp */ #define OUT_ARG3 ( 12) #define OUT_ARG2 ( 8) -- GitLab From 2a524bd724d746aa807bf891cc379ad6abd81b64 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Tue, 1 Mar 2016 12:18:47 +0000 Subject: [PATCH 076/204] Dump more information to diagnose problem. bug:27424509 Change-Id: Id502cf408a7f117a2a891534f5c999d60f9370b1 --- runtime/art_method.cc | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/runtime/art_method.cc b/runtime/art_method.cc index cd38e16cf7..a60f31e52e 100644 --- a/runtime/art_method.cc +++ b/runtime/art_method.cc @@ -411,7 +411,12 @@ const OatQuickMethodHeader* ArtMethod::GetOatQuickMethodHeader(uintptr_t pc) { DCHECK(method_header->Contains(pc)); return method_header; } else { - DCHECK(!code_cache->ContainsPc(reinterpret_cast(pc))) << std::hex << pc; + DCHECK(!code_cache->ContainsPc(reinterpret_cast(pc))) + << PrettyMethod(this) + << ", pc=" << std::hex << pc + << ", entry_point=" << std::hex << reinterpret_cast(existing_entry_point) + << ", copy=" << std::boolalpha << IsCopied() + << ", proxy=" << std::boolalpha << IsProxyMethod(); } } -- GitLab From 25e0456b6ea13eba290b63ea88b6b7120ed89413 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Tue, 1 Mar 2016 13:17:58 +0000 Subject: [PATCH 077/204] Give the JIT its own arena pool to avoid lock contentions. Sharing it with the verifier and the class loader is not ideal, especially at startup time. bug:27398183 bug:23128949 Change-Id: I1b91663a13f6c5b33ad3b4be780d93eb7fe445b4 --- compiler/jit/jit_compiler.cc | 4 ++-- compiler/optimizing/optimizing_compiler.cc | 2 +- runtime/base/arena_allocator.cc | 15 +++++++++------ runtime/base/arena_allocator.h | 7 +++++-- runtime/runtime.cc | 8 ++++++-- runtime/runtime.h | 4 ++++ 6 files changed, 27 insertions(+), 13 deletions(-) diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc index 23601c39e4..79a6d38fc6 100644 --- a/compiler/jit/jit_compiler.cc +++ b/compiler/jit/jit_compiler.cc @@ -230,10 +230,10 @@ bool JitCompiler::CompileMethod(Thread* self, ArtMethod* method, bool osr) { } // Trim maps to reduce memory usage. - // TODO: measure how much this increases compile time. + // TODO: move this to an idle phase. { TimingLogger::ScopedTiming t2("TrimMaps", &logger); - runtime->GetArenaPool()->TrimMaps(); + runtime->GetJitArenaPool()->TrimMaps(); } total_time_ += NanoTime() - start_time; diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index 5a9f2583fd..13d6d620f8 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -861,7 +861,7 @@ bool OptimizingCompiler::JitCompile(Thread* self, const uint32_t access_flags = method->GetAccessFlags(); const InvokeType invoke_type = method->GetInvokeType(); - ArenaAllocator arena(Runtime::Current()->GetArenaPool()); + ArenaAllocator arena(Runtime::Current()->GetJitArenaPool()); CodeVectorAllocator code_allocator(&arena); std::unique_ptr codegen; { diff --git a/runtime/base/arena_allocator.cc b/runtime/base/arena_allocator.cc index a4b38ea963..44af3f75b9 100644 --- a/runtime/base/arena_allocator.cc +++ b/runtime/base/arena_allocator.cc @@ -183,10 +183,10 @@ MallocArena::~MallocArena() { free(reinterpret_cast(memory_)); } -MemMapArena::MemMapArena(size_t size, bool low_4gb) { +MemMapArena::MemMapArena(size_t size, bool low_4gb, const char* name) { std::string error_msg; map_.reset(MemMap::MapAnonymous( - "LinearAlloc", nullptr, size, PROT_READ | PROT_WRITE, low_4gb, false, &error_msg)); + name, nullptr, size, PROT_READ | PROT_WRITE, low_4gb, false, &error_msg)); CHECK(map_.get() != nullptr) << error_msg; memory_ = map_->Begin(); size_ = map_->Size(); @@ -210,9 +210,12 @@ void Arena::Reset() { } } -ArenaPool::ArenaPool(bool use_malloc, bool low_4gb) - : use_malloc_(use_malloc), lock_("Arena pool lock", kArenaPoolLock), free_arenas_(nullptr), - low_4gb_(low_4gb) { +ArenaPool::ArenaPool(bool use_malloc, bool low_4gb, const char* name) + : use_malloc_(use_malloc), + lock_("Arena pool lock", kArenaPoolLock), + free_arenas_(nullptr), + low_4gb_(low_4gb), + name_(name) { if (low_4gb) { CHECK(!use_malloc) << "low4gb must use map implementation"; } @@ -250,7 +253,7 @@ Arena* ArenaPool::AllocArena(size_t size) { } if (ret == nullptr) { ret = use_malloc_ ? static_cast(new MallocArena(size)) : - new MemMapArena(size, low_4gb_); + new MemMapArena(size, low_4gb_, name_); } ret->Reset(); return ret; diff --git a/runtime/base/arena_allocator.h b/runtime/base/arena_allocator.h index 8a96571e99..728f897229 100644 --- a/runtime/base/arena_allocator.h +++ b/runtime/base/arena_allocator.h @@ -261,7 +261,7 @@ class MallocArena FINAL : public Arena { class MemMapArena FINAL : public Arena { public: - MemMapArena(size_t size, bool low_4gb); + MemMapArena(size_t size, bool low_4gb, const char* name); virtual ~MemMapArena(); void Release() OVERRIDE; @@ -271,7 +271,9 @@ class MemMapArena FINAL : public Arena { class ArenaPool { public: - explicit ArenaPool(bool use_malloc = true, bool low_4gb = false); + ArenaPool(bool use_malloc = true, + bool low_4gb = false, + const char* name = "LinearAlloc"); ~ArenaPool(); Arena* AllocArena(size_t size) REQUIRES(!lock_); void FreeArenaChain(Arena* first) REQUIRES(!lock_); @@ -287,6 +289,7 @@ class ArenaPool { mutable Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; Arena* free_arenas_ GUARDED_BY(lock_); const bool low_4gb_; + const char* name_; DISALLOW_COPY_AND_ASSIGN(ArenaPool); }; diff --git a/runtime/runtime.cc b/runtime/runtime.cc index eb5455a4cd..47ef2143ff 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -316,6 +316,7 @@ Runtime::~Runtime() { linear_alloc_.reset(); low_4gb_arena_pool_.reset(); arena_pool_.reset(); + jit_arena_pool_.reset(); MemMap::Shutdown(); ATRACE_END(); @@ -1019,10 +1020,13 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { // Use MemMap arena pool for jit, malloc otherwise. Malloc arenas are faster to allocate but // can't be trimmed as easily. const bool use_malloc = IsAotCompiler(); - arena_pool_.reset(new ArenaPool(use_malloc, false)); + arena_pool_.reset(new ArenaPool(use_malloc, /* low_4gb */ false)); + jit_arena_pool_.reset( + new ArenaPool(/* use_malloc */ false, /* low_4gb */ false, "CompilerMetadata")); + if (IsAotCompiler() && Is64BitInstructionSet(kRuntimeISA)) { // 4gb, no malloc. Explanation in header. - low_4gb_arena_pool_.reset(new ArenaPool(false, true)); + low_4gb_arena_pool_.reset(new ArenaPool(/* use_malloc */ false, /* low_4gb */ true)); } linear_alloc_.reset(CreateLinearAlloc()); diff --git a/runtime/runtime.h b/runtime/runtime.h index 8aac4ce9b4..83e77d2372 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -561,6 +561,9 @@ class Runtime { ArenaPool* GetArenaPool() { return arena_pool_.get(); } + ArenaPool* GetJitArenaPool() { + return jit_arena_pool_.get(); + } const ArenaPool* GetArenaPool() const { return arena_pool_.get(); } @@ -669,6 +672,7 @@ class Runtime { gc::Heap* heap_; + std::unique_ptr jit_arena_pool_; std::unique_ptr arena_pool_; // Special low 4gb pool for compiler linear alloc. We need ArtFields to be in low 4gb if we are // compiling using a 32 bit image on a 64 bit compiler in case we resolve things in the image -- GitLab From 7273a5d045d3ceb3ff011ad65765356b69b155e8 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Mon, 29 Feb 2016 15:35:39 +0000 Subject: [PATCH 078/204] Use the interpreter as a heartbeat for the JIT. When doing a partial code cache collection, update all entrypoints to interpreter, so that the next full collection will remove code that wasn't executed during that window. bug:27398183 bug:23128949 bug:26846185 Change-Id: I4423f5c4810dac183dc8973078bf218818745e80 --- runtime/interpreter/interpreter.cc | 4 +- runtime/jit/jit.cc | 4 + runtime/jit/jit.h | 4 + runtime/jit/jit_code_cache.cc | 199 ++++++++++++++++------------- runtime/jit/jit_code_cache.h | 13 +- runtime/jit/jit_instrumentation.cc | 9 +- runtime/jit/profiling_info.h | 15 ++- 7 files changed, 150 insertions(+), 98 deletions(-) diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc index a595d33f04..9808e22927 100644 --- a/runtime/interpreter/interpreter.cc +++ b/runtime/interpreter/interpreter.cc @@ -295,9 +295,7 @@ static inline JValue Execute(Thread* self, const DexFile::CodeItem* code_item, } jit::Jit* jit = Runtime::Current()->GetJit(); - if (UNLIKELY(jit != nullptr && - jit->JitAtFirstUse() && - jit->GetCodeCache()->ContainsMethod(method))) { + if (jit != nullptr && jit->CanInvokeCompiledCode(method)) { JValue result; // Pop the shadow frame before calling into compiled code. diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc index 3e66ce20eb..91b006a3ea 100644 --- a/runtime/jit/jit.cc +++ b/runtime/jit/jit.cc @@ -213,6 +213,10 @@ bool Jit::JitAtFirstUse() { return false; } +bool Jit::CanInvokeCompiledCode(ArtMethod* method) { + return code_cache_->ContainsPc(method->GetEntryPointFromQuickCompiledCode()); +} + Jit::~Jit() { DCHECK(!save_profiling_info_ || !ProfileSaver::IsStarted()); if (dump_info_on_shutdown_) { diff --git a/runtime/jit/jit.h b/runtime/jit/jit.h index 109ca3dbd1..3f54192d9f 100644 --- a/runtime/jit/jit.h +++ b/runtime/jit/jit.h @@ -84,8 +84,12 @@ class Jit { // into the specified class linker to the jit debug interface, void DumpTypeInfoForLoadedTypes(ClassLinker* linker); + // Return whether we should try to JIT compiled code as soon as an ArtMethod is invoked. bool JitAtFirstUse(); + // Return whether we can invoke JIT code for `method`. + bool CanInvokeCompiledCode(ArtMethod* method); + // If an OSR compiled version is available for `method`, // and `dex_pc + dex_pc_offset` is an entry point of that compiled // version, this method will jump to the compiled code, let it run, diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index 8858b486f9..4500dbdf67 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -123,7 +123,7 @@ JitCodeCache::JitCodeCache(MemMap* code_map, current_capacity_(initial_code_capacity + initial_data_capacity), code_end_(initial_code_capacity), data_end_(initial_data_capacity), - has_done_full_collection_(false), + last_collection_increased_code_cache_(false), last_update_time_ns_(0), garbage_collect_code_(garbage_collect_code), used_memory_for_data_(0), @@ -546,34 +546,20 @@ void JitCodeCache::MarkCompiledCodeOnThreadStacks(Thread* self) { } } -void JitCodeCache::RemoveUnusedCode(Thread* self) { - // Clear the osr map, chances are most of the code in it is now dead. - { - MutexLock mu(self, lock_); - osr_code_map_.clear(); - } - - // Run a checkpoint on all threads to mark the JIT compiled code they are running. - MarkCompiledCodeOnThreadStacks(self); - - // Iterate over all compiled code and remove entries that are not marked and not - // the entrypoint of their corresponding ArtMethod. - { - MutexLock mu(self, lock_); - ScopedCodeCacheWrite scc(code_map_.get()); - for (auto it = method_code_map_.begin(); it != method_code_map_.end();) { - const void* code_ptr = it->first; - ArtMethod* method = it->second; - uintptr_t allocation = FromCodeToAllocation(code_ptr); - const OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromCodePointer(code_ptr); - if ((method->GetEntryPointFromQuickCompiledCode() != method_header->GetEntryPoint()) && - !GetLiveBitmap()->Test(allocation)) { - FreeCode(code_ptr, method); - it = method_code_map_.erase(it); - } else { - ++it; - } - } +bool JitCodeCache::ShouldDoFullCollection() { + if (current_capacity_ == max_capacity_) { + // Always do a full collection when the code cache is full. + return true; + } else if (current_capacity_ < kReservedCapacity) { + // Always do partial collection when the code cache size is below the reserved + // capacity. + return false; + } else if (last_collection_increased_code_cache_) { + // This time do a full collection. + return true; + } else { + // This time do a partial collection. + return false; } } @@ -599,21 +585,10 @@ void JitCodeCache::GarbageCollectCache(Thread* self) { } } - // Check if we want to do a full collection. - bool do_full_collection = true; + bool do_full_collection = false; { MutexLock mu(self, lock_); - if (current_capacity_ == max_capacity_) { - // Always do a full collection when the code cache is full. - do_full_collection = true; - } else if (current_capacity_ < kReservedCapacity) { - // Do a partial collection until we hit the reserved capacity limit. - do_full_collection = false; - } else if (has_done_full_collection_) { - // Do a partial collection if we have done a full collection in the last - // collection round. - do_full_collection = false; - } + do_full_collection = ShouldDoFullCollection(); } if (!kIsDebugBuild || VLOG_IS_ON(jit)) { @@ -624,45 +599,94 @@ void JitCodeCache::GarbageCollectCache(Thread* self) { << ", data=" << PrettySize(DataCacheSize()); } - if (do_full_collection) { - DoFullCollection(self); - } else { - RemoveUnusedCode(self); + DoCollection(self, /* collect_profiling_info */ do_full_collection); + + if (!kIsDebugBuild || VLOG_IS_ON(jit)) { + LOG(INFO) << "After code cache collection, code=" + << PrettySize(CodeCacheSize()) + << ", data=" << PrettySize(DataCacheSize()); } { MutexLock mu(self, lock_); - if (!do_full_collection) { - has_done_full_collection_ = false; - IncreaseCodeCacheCapacity(); + + // Increase the code cache only when we do partial collections. + // TODO: base this strategy on how full the code cache is? + if (do_full_collection) { + last_collection_increased_code_cache_ = false; } else { - has_done_full_collection_ = true; + last_collection_increased_code_cache_ = true; + IncreaseCodeCacheCapacity(); + } + + bool next_collection_will_be_full = ShouldDoFullCollection(); + + // Start polling the liveness of compiled code to prepare for the next full collection. + if (next_collection_will_be_full) { + // Save the entry point of methods we have compiled, and update the entry + // point of those methods to the interpreter. If the method is invoked, the + // interpreter will update its entry point to the compiled code and call it. + for (ProfilingInfo* info : profiling_infos_) { + const void* entry_point = info->GetMethod()->GetEntryPointFromQuickCompiledCode(); + if (ContainsPc(entry_point)) { + info->SetSavedEntryPoint(entry_point); + info->GetMethod()->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge()); + } + } + + if (kIsDebugBuild) { + // Check that methods we have compiled do have a ProfilingInfo object. We would + // have memory leaks of compiled code otherwise. + for (const auto& it : method_code_map_) { + DCHECK(it.second->GetProfilingInfo(sizeof(void*)) != nullptr); + } + } } live_bitmap_.reset(nullptr); NotifyCollectionDone(self); } +} - if (!kIsDebugBuild || VLOG_IS_ON(jit)) { - LOG(INFO) << "After code cache collection, code=" - << PrettySize(CodeCacheSize()) - << ", data=" << PrettySize(DataCacheSize()); +void JitCodeCache::RemoveUnusedAndUnmarkedCode(Thread* self) { + MutexLock mu(self, lock_); + ScopedCodeCacheWrite scc(code_map_.get()); + // Iterate over all compiled code and remove entries that are not marked and not + // the entrypoint of their corresponding ArtMethod. + for (auto it = method_code_map_.begin(); it != method_code_map_.end();) { + const void* code_ptr = it->first; + ArtMethod* method = it->second; + uintptr_t allocation = FromCodeToAllocation(code_ptr); + const OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromCodePointer(code_ptr); + const void* entrypoint = method->GetEntryPointFromQuickCompiledCode(); + if ((entrypoint == method_header->GetEntryPoint()) || GetLiveBitmap()->Test(allocation)) { + ++it; + } else { + if (entrypoint == GetQuickToInterpreterBridge()) { + method->ClearCounter(); + } + FreeCode(code_ptr, method); + it = method_code_map_.erase(it); + } } } -void JitCodeCache::DoFullCollection(Thread* self) { - instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); +void JitCodeCache::DoCollection(Thread* self, bool collect_profiling_info) { { MutexLock mu(self, lock_); - // Walk over all compiled methods and set the entry points of these - // methods to interpreter. - for (auto& it : method_code_map_) { - instrumentation->UpdateMethodsCode(it.second, GetQuickToInterpreterBridge()); - } - - // Clear the profiling info of methods that are not being compiled. - for (ProfilingInfo* info : profiling_infos_) { - if (!info->IsMethodBeingCompiled()) { - info->GetMethod()->SetProfilingInfo(nullptr); + if (collect_profiling_info) { + // Clear the profiling info of methods that do not have compiled code as entrypoint. + // Also remove the saved entry point from the ProfilingInfo objects. + for (ProfilingInfo* info : profiling_infos_) { + const void* ptr = info->GetMethod()->GetEntryPointFromQuickCompiledCode(); + if (!ContainsPc(ptr) && !info->IsMethodBeingCompiled()) { + info->GetMethod()->SetProfilingInfo(nullptr); + } + info->SetSavedEntryPoint(nullptr); + } + } else if (kIsDebugBuild) { + // Sanity check that the profiling infos do not have a dangling entry point. + for (ProfilingInfo* info : profiling_infos_) { + DCHECK(info->GetSavedEntryPoint() == nullptr); } } @@ -674,32 +698,22 @@ void JitCodeCache::DoFullCollection(Thread* self) { // Run a checkpoint on all threads to mark the JIT compiled code they are running. MarkCompiledCodeOnThreadStacks(self); - { - MutexLock mu(self, lock_); - // Free unused compiled code, and restore the entry point of used compiled code. - { - ScopedCodeCacheWrite scc(code_map_.get()); - for (auto it = method_code_map_.begin(); it != method_code_map_.end();) { - const void* code_ptr = it->first; - ArtMethod* method = it->second; - uintptr_t allocation = FromCodeToAllocation(code_ptr); - const OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromCodePointer(code_ptr); - if (GetLiveBitmap()->Test(allocation)) { - instrumentation->UpdateMethodsCode(method, method_header->GetEntryPoint()); - ++it; - } else { - method->ClearCounter(); - DCHECK_NE(method->GetEntryPointFromQuickCompiledCode(), method_header->GetEntryPoint()); - FreeCode(code_ptr, method); - it = method_code_map_.erase(it); - } - } - } + // Remove compiled code that is not the entrypoint of their method and not in the call + // stack. + RemoveUnusedAndUnmarkedCode(self); - // Free all profiling infos of methods that were not being compiled. + if (collect_profiling_info) { + MutexLock mu(self, lock_); + // Free all profiling infos of methods not compiled nor being compiled. auto profiling_kept_end = std::remove_if(profiling_infos_.begin(), profiling_infos_.end(), [this] (ProfilingInfo* info) NO_THREAD_SAFETY_ANALYSIS { - if (info->GetMethod()->GetProfilingInfo(sizeof(void*)) == nullptr) { + const void* ptr = info->GetMethod()->GetEntryPointFromQuickCompiledCode(); + if (ContainsPc(ptr) && info->GetMethod()->GetProfilingInfo(sizeof(void*)) == nullptr) { + // Make sure compiled methods have a ProfilingInfo object. It is needed for + // code cache collection. + info->GetMethod()->SetProfilingInfo(info); + } else if (info->GetMethod()->GetProfilingInfo(sizeof(void*)) != info) { + // No need for this ProfilingInfo object anymore. FreeData(reinterpret_cast(info)); return true; } @@ -849,6 +863,13 @@ size_t JitCodeCache::GetMemorySizeOfCodePointer(const void* ptr) { void JitCodeCache::InvalidateCompiledCodeFor(ArtMethod* method, const OatQuickMethodHeader* header) { + ProfilingInfo* profiling_info = method->GetProfilingInfo(sizeof(void*)); + if ((profiling_info != nullptr) && + (profiling_info->GetSavedEntryPoint() == header->GetEntryPoint())) { + // Prevent future uses of the compiled code. + profiling_info->SetSavedEntryPoint(nullptr); + } + if (method->GetEntryPointFromQuickCompiledCode() == header->GetEntryPoint()) { // The entrypoint is the one to invalidate, so we just update // it to the interpreter entry point and clear the counter to get the method diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h index 4574edfb46..2cf3c4d0f4 100644 --- a/runtime/jit/jit_code_cache.h +++ b/runtime/jit/jit_code_cache.h @@ -124,6 +124,11 @@ class JitCodeCache { return live_bitmap_.get(); } + // Return whether we should do a full collection given the current state of the cache. + bool ShouldDoFullCollection() + REQUIRES(lock_) + SHARED_REQUIRES(Locks::mutator_lock_); + // Perform a collection on the code cache. void GarbageCollectCache(Thread* self) REQUIRES(!lock_) @@ -235,11 +240,11 @@ class JitCodeCache { // Set the footprint limit of the code cache. void SetFootprintLimit(size_t new_footprint) REQUIRES(lock_); - void DoFullCollection(Thread* self) + void DoCollection(Thread* self, bool collect_profiling_info) REQUIRES(!lock_) SHARED_REQUIRES(Locks::mutator_lock_); - void RemoveUnusedCode(Thread* self) + void RemoveUnusedAndUnmarkedCode(Thread* self) REQUIRES(!lock_) SHARED_REQUIRES(Locks::mutator_lock_); @@ -282,8 +287,8 @@ class JitCodeCache { // The current footprint in bytes of the data portion of the code cache. size_t data_end_ GUARDED_BY(lock_); - // Whether a full collection has already been done on the current capacity. - bool has_done_full_collection_ GUARDED_BY(lock_); + // Whether the last collection round increased the code cache. + bool last_collection_increased_code_cache_ GUARDED_BY(lock_); // Last time the the code_cache was updated. // It is atomic to avoid locking when reading it. diff --git a/runtime/jit/jit_instrumentation.cc b/runtime/jit/jit_instrumentation.cc index a4e40ad3fd..86761e402f 100644 --- a/runtime/jit/jit_instrumentation.cc +++ b/runtime/jit/jit_instrumentation.cc @@ -183,7 +183,14 @@ void JitInstrumentationListener::MethodEntered(Thread* thread, return; } - instrumentation_cache_->AddSamples(thread, method, 1); + ProfilingInfo* profiling_info = method->GetProfilingInfo(sizeof(void*)); + if ((profiling_info != nullptr) && (profiling_info->GetSavedEntryPoint() != nullptr)) { + // Update the entrypoint if the ProfilingInfo has one. The interpreter will call it + // instead of interpreting the method. + method->SetEntryPointFromQuickCompiledCode(profiling_info->GetSavedEntryPoint()); + } else { + instrumentation_cache_->AddSamples(thread, method, 1); + } } void JitInstrumentationListener::Branch(Thread* thread, diff --git a/runtime/jit/profiling_info.h b/runtime/jit/profiling_info.h index ab7237376b..d54f3dfc11 100644 --- a/runtime/jit/profiling_info.h +++ b/runtime/jit/profiling_info.h @@ -126,11 +126,20 @@ class ProfilingInfo { is_method_being_compiled_ = value; } + void SetSavedEntryPoint(const void* entry_point) { + saved_entry_point_ = entry_point; + } + + const void* GetSavedEntryPoint() const { + return saved_entry_point_; + } + private: ProfilingInfo(ArtMethod* method, const std::vector& entries) : number_of_inline_caches_(entries.size()), method_(method), - is_method_being_compiled_(false) { + is_method_being_compiled_(false), + saved_entry_point_(nullptr) { memset(&cache_, 0, number_of_inline_caches_ * sizeof(InlineCache)); for (size_t i = 0; i < number_of_inline_caches_; ++i) { cache_[i].dex_pc_ = entries[i]; @@ -148,6 +157,10 @@ class ProfilingInfo { // TODO: Make the JIT code cache lock global. bool is_method_being_compiled_; + // Entry point of the corresponding ArtMethod, while the JIT code cache + // is poking for the liveness of compiled code. + const void* saved_entry_point_; + // Dynamically allocated array of size `number_of_inline_caches_`. InlineCache cache_[0]; -- GitLab From 1e7da9bd04bdd2664a4196f1d7e285c010f8881f Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Tue, 1 Mar 2016 14:11:40 +0000 Subject: [PATCH 079/204] Do a TryLock when allocating a ProfilingInfo from the interpreter. This removes some thread contentions just for allocating ProfilingInfo. bug:23128949 Change-Id: I9ff7d44c4b0ee272425cf4c6248d3065f67958f3 --- runtime/jit/jit_code_cache.cc | 29 ++++++++++++++++++++++------- runtime/jit/jit_code_cache.h | 2 +- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index 8858b486f9..a6c8c5299b 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -751,23 +751,38 @@ OatQuickMethodHeader* JitCodeCache::LookupOsrMethodHeader(ArtMethod* method) { ProfilingInfo* JitCodeCache::AddProfilingInfo(Thread* self, ArtMethod* method, const std::vector& entries, - bool retry_allocation) { - ProfilingInfo* info = AddProfilingInfoInternal(self, method, entries); + bool retry_allocation) + // No thread safety analysis as we are using TryLock/Unlock explicitly. + NO_THREAD_SAFETY_ANALYSIS { + ProfilingInfo* info = nullptr; + if (!retry_allocation) { + // If we are allocating for the interpreter, just try to lock, to avoid + // lock contention with the JIT. + if (lock_.ExclusiveTryLock(self)) { + info = AddProfilingInfoInternal(self, method, entries); + lock_.ExclusiveUnlock(self); + } + } else { + { + MutexLock mu(self, lock_); + info = AddProfilingInfoInternal(self, method, entries); + } - if (info == nullptr && retry_allocation) { - GarbageCollectCache(self); - info = AddProfilingInfoInternal(self, method, entries); + if (info == nullptr) { + GarbageCollectCache(self); + MutexLock mu(self, lock_); + info = AddProfilingInfoInternal(self, method, entries); + } } return info; } -ProfilingInfo* JitCodeCache::AddProfilingInfoInternal(Thread* self, +ProfilingInfo* JitCodeCache::AddProfilingInfoInternal(Thread* self ATTRIBUTE_UNUSED, ArtMethod* method, const std::vector& entries) { size_t profile_info_size = RoundUp( sizeof(ProfilingInfo) + sizeof(InlineCache) * entries.size(), sizeof(void*)); - MutexLock mu(self, lock_); // Check whether some other thread has concurrently created it. ProfilingInfo* info = method->GetProfilingInfo(sizeof(void*)); diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h index 4574edfb46..0117776264 100644 --- a/runtime/jit/jit_code_cache.h +++ b/runtime/jit/jit_code_cache.h @@ -208,7 +208,7 @@ class JitCodeCache { ProfilingInfo* AddProfilingInfoInternal(Thread* self, ArtMethod* method, const std::vector& entries) - REQUIRES(!lock_) + REQUIRES(lock_) SHARED_REQUIRES(Locks::mutator_lock_); // If a collection is in progress, wait for it to finish. Return -- GitLab From c94ff128d770f5126d7685972b95a789c0b55cb0 Mon Sep 17 00:00:00 2001 From: Sebastien Hertz Date: Mon, 29 Feb 2016 16:54:16 +0100 Subject: [PATCH 080/204] More run-test with Jack Updates the following tests * 004-ReferenceMap: updates expectations * 004-StackWalk: updates expectations * 089-many-methods: updates expectations * 138-duplicate-classes-check2: add Jack support * 454-get-vreg: updates expectations Bug: 19467889 Change-Id: I086e170b1951c9fdac5bd4e0234ac504644db5a1 --- test/004-ReferenceMap/build | 26 --- .../004-ReferenceMap/stack_walk_refmap_jni.cc | 207 +++++++----------- test/004-StackWalk/build | 26 --- test/004-StackWalk/stack_walk_jni.cc | 16 +- test/089-many-methods/build | 6 +- test/089-many-methods/expected.txt | 8 +- test/138-duplicate-classes-check2/build | 12 +- test/454-get-vreg/build | 26 --- 8 files changed, 102 insertions(+), 225 deletions(-) delete mode 100644 test/004-ReferenceMap/build delete mode 100644 test/004-StackWalk/build delete mode 100644 test/454-get-vreg/build diff --git a/test/004-ReferenceMap/build b/test/004-ReferenceMap/build deleted file mode 100644 index 08987b556c..0000000000 --- a/test/004-ReferenceMap/build +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/bash -# -# Copyright (C) 2015 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Stop if something fails. -set -e - -# The test relies on DEX file produced by javac+dx so keep building with them for now -# (see b/19467889) -mkdir classes -${JAVAC} -d classes `find src -name '*.java'` -${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex \ - --dump-width=1000 ${DX_FLAGS} classes -zip $TEST_NAME.jar classes.dex diff --git a/test/004-ReferenceMap/stack_walk_refmap_jni.cc b/test/004-ReferenceMap/stack_walk_refmap_jni.cc index 2dbd7e8126..2d26fa1ac9 100644 --- a/test/004-ReferenceMap/stack_walk_refmap_jni.cc +++ b/test/004-ReferenceMap/stack_walk_refmap_jni.cc @@ -65,19 +65,19 @@ struct ReferenceMap2Visitor : public CheckReferenceMapVisitor { // We eliminate the non-live registers at a return, so only v3 is live. // Note that it is OK for a compiler to not have a dex map at this dex PC because // a return is not necessarily a safepoint. - CHECK_REGS_CONTAIN_REFS(0x13U, false, 3); // v3: y + CHECK_REGS_CONTAIN_REFS(0x14U, false, 2); // v2: y // Note that v0: ex can be eliminated because it's a dead merge of two different exceptions. CHECK_REGS_CONTAIN_REFS(0x18U, true, 8, 2, 1); // v8: this, v2: y, v1: x (dead v0: ex) if (!GetCurrentOatQuickMethodHeader()->IsOptimized()) { - // v8: this, v5: x[1], v2: y, v1: x (dead v0: ex) - CHECK_REGS_CONTAIN_REFS(0x1aU, true, 8, 5, 2, 1); - // v8: this, v5: x[1], v2: y, v1: x (dead v0: ex) - CHECK_REGS_CONTAIN_REFS(0x1dU, true, 8, 5, 2, 1); - // v5 is removed from the root set because there is a "merge" operation. - // See 0015: if-nez v2, 001f. - CHECK_REGS_CONTAIN_REFS(0x1fU, true, 8, 2, 1); // v8: this, v2: y, v1: x (dead v0: ex) + // v8: this, v4: x[1], v2: y, v1: x (dead v0: ex) + CHECK_REGS_CONTAIN_REFS(0x1aU, true, 8, 4, 2, 1); + // v8: this, v4: x[1], v2: y, v1: x (dead v0: ex) + CHECK_REGS_CONTAIN_REFS(0x1eU, true, 8, 4, 2, 1); + // v4 is removed from the root set because there is a "merge" operation. + // See 0016: if-nez v2, 0020. + CHECK_REGS_CONTAIN_REFS(0x20U, true, 8, 2, 1); // v8: this, v2: y, v1: x (dead v0: ex) } - CHECK_REGS_CONTAIN_REFS(0x21U, true, 8, 2, 1); // v8: this, v2: y, v1: x (dead v0: ex) + CHECK_REGS_CONTAIN_REFS(0x22U, true, 8, 2, 1); // v8: this, v2: y, v1: x (dead v0: ex) if (!GetCurrentOatQuickMethodHeader()->IsOptimized()) { CHECK_REGS_CONTAIN_REFS(0x27U, true, 8, 4, 2, 1); // v8: this, v4: ex, v2: y, v1: x @@ -94,124 +94,79 @@ struct ReferenceMap2Visitor : public CheckReferenceMapVisitor { } }; -// Dex instructions for the function 'f' in ReferenceMap.java -// Virtual methods - -// #0 : (in LReferenceMap;) -// name : 'f' -// type : '()Ljava/lang/Object;' -// access : 0x0000 () -// code - -// registers : 9 -// ins : 1 -// outs : 2 -// insns size : 51 16-bit code units -// |[0001e8] ReferenceMap.f:()Ljava/lang/Object; -// |0000: const/4 v4, #int 2 // #2 -// |0001: const/4 v7, #int 0 // #0 -// |0002: const/4 v6, #int 1 // #1 +// DEX code // -// 0:[Unknown],1:[Unknown],2:[Unknown],3:[Unknown],4:[32-bit Constant: 2],5:[Unknown],6:[32-bit Constant: 1],7:[Zero],8:[Reference: ReferenceMap], -// |0003: new-array v1, v4, [Ljava/lang/Object; // type@0007 -// |0005: const/4 v2, #int 0 // #0 - -// 0:[Unknown],1:[Reference: java.lang.Object[]],2:[Zero],3:[Unknown],4:[32-bit Constant: 2],5:[Unknown],6:[32-bit Constant: 1],7:[Zero],8:[Reference: ReferenceMap], -// |0006: new-instance v3, Ljava/lang/Object; // type@0003 - -// [Unknown],1:[Reference: java.lang.Object[]],2:[Zero],3:[Uninitialized Reference: java.lang.Object],4:[32-bit Constant: 2],5:[Unknown],6:[32-bit Constant: 1],7:[Zero],8:[Reference: ReferenceMap], -// |0008: +invoke-object-init/range {}, Ljava/lang/Object;.:()V // method@0005 -// |000b: const/4 v4, #int 2 // #2 - -// 0:[Unknown],1:[Reference: java.lang.Object[]],2:[Zero],3:[Reference: java.lang.Object],4:[32-bit Constant: 2],5:[Unknown],6:[32-bit Constant: 1],7:[Zero],8:[Reference: ReferenceMap], -// |000c: aput-object v3, v1, v4 - -// 0:[Unknown],1:[Reference: java.lang.Object[]],2:[Zero],3:[Reference: java.lang.Object],4:[32-bit Constant: 2],5:[Unknown],6:[32-bit Constant: 1],7:[Zero],8:[Reference: ReferenceMap], -// |000e: aput-object v3, v1, v6 - -// 0:[Unknown],1:[Reference: java.lang.Object[]],2:[Zero],3:[Reference: java.lang.Object],4:[32-bit Constant: 2],5:[Unknown],6:[32-bit Constant: 1],7:[Zero],8:[Reference: ReferenceMap], -// |0010: +invoke-virtual-quick {v8, v7}, [000c] // vtable #000c - -// 0:[Conflict],1:[Conflict],2:[Conflict],3:[Reference: java.lang.Object],4:[Conflict],5:[Conflict],6:[Conflict],7:[Conflict],8:[Conflict], -// |0013: return-object v3 -// |0014: move-exception v0 - -// 0:[Reference: java.lang.Exception],1:[Reference: java.lang.Object[]],2:[Reference: java.lang.Object],3:[Conflict],4:[32-bit Constant: 2],5:[Unknown],6:[32-bit Constant: 1],7:[Zero],8:[Reference: ReferenceMap], -// |0015: if-nez v2, 001f // +000a -// |0017: const/4 v4, #int 1 // #1 - -// 0:[Reference: java.lang.Exception],1:[Reference: java.lang.Object[]],2:[Reference: java.lang.Object],3:[Conflict],4:[32-bit Constant: 1],5:[Unknown],6:[32-bit Constant: 1],7:[Zero],8:[Reference: ReferenceMap], -// |0018: new-instance v5, Ljava/lang/Object; // type@0003 - -// 0:[Reference: java.lang.Exception],1:[Reference: java.lang.Object[]],2:[Reference: java.lang.Object],3:[Conflict],4:[32-bit Constant: 1],5:[Uninitialized Reference: java.lang.Object],6:[32-bit Constant: 1],7:[Zero],8:[Reference: ReferenceMap], -// |001a: +invoke-object-init/range {}, Ljava/lang/Object;.:()V // method@0005 - -// 0:[Reference: java.lang.Exception],1:[Reference: java.lang.Object[]],2:[Reference: java.lang.Object],3:[Conflict],4:[32-bit Constant: 1],5:[Reference: java.lang.Object],6:[32-bit Constant: 1],7:[Zero],8:[Reference: ReferenceMap], -// |001d: aput-object v5, v1, v4 - -// 0:[Reference: java.lang.Exception],1:[Reference: java.lang.Object[]],2:[Reference: java.lang.Object],3:[Conflict],4:[32-bit Constant: 2],5:[Conflict],6:[32-bit Constant: 1],7:[Zero],8:[Reference: ReferenceMap], -// |001f: aput-object v2, v1, v6 - -// 0:[Reference: java.lang.Exception],1:[Reference: java.lang.Object[]],2:[Reference: java.lang.Object],3:[Conflict],4:[32-bit Constant: 2],5:[Conflict],6:[32-bit Constant: 1],7:[Zero],8:[Reference: ReferenceMap], -// |0021: +invoke-virtual-quick {v8, v7}, [000c] // vtable #000c -// |0024: move-object v3, v2 - -// 0:[Reference: java.lang.Exception],1:[Reference: java.lang.Object[]],2:[Reference: java.lang.Object],3:[Reference: java.lang.Object],4:[32-bit Constant: 2],5:[Conflict],6:[32-bit Constant: 1],7:[Zero],8:[Reference: ReferenceMap], -// |0025: goto 0013 // -0012 -// |0026: move-exception v4 - -// 0:[Conflict],1:[Reference: java.lang.Object[]],2:[Reference: java.lang.Object],3:[Conflict],4:[Reference: java.lang.Throwable],5:[Conflict],6:[32-bit Constant: 1],7:[Zero],8:[Reference: ReferenceMap], -// |0027: aput-object v2, v1, v6 - -// 0:[Conflict],1:[Reference: java.lang.Object[]],2:[Reference: java.lang.Object],3:[Conflict],4:[Reference: java.lang.Throwable],5:[Conflict],6:[32-bit Constant: 1],7:[Zero],8:[Reference: ReferenceMap], -// |0029: +invoke-virtual-quick {v8, v7}, [000c] // vtable #000c - -// 0:[Conflict],1:[Reference: java.lang.Object[]],2:[Reference: java.lang.Object],3:[Conflict],4:[Reference: java.lang.Throwable],5:[Conflict],6:[32-bit Constant: 1],7:[Zero],8:[Reference: ReferenceMap], -// |002c: throw v4 -// |002d: move-exception v4 -// |002e: move-object v2, v3 - -// 0:[Unknown],1:[Reference: java.lang.Object[]],2:[Reference: java.lang.Object],3:[Reference: java.lang.Object],4:[Reference: java.lang.Throwable],5:[Unknown],6:[32-bit Constant: 1],7:[Zero],8:[Reference: ReferenceMap], -// |002f: goto 0027 // -0008 -// |0030: move-exception v0 -// |0031: move-object v2, v3 - -// 0:[Reference: java.lang.Exception],1:[Reference: java.lang.Object[]],2:[Reference: java.lang.Object],3:[Reference: java.lang.Object],4:[32-bit Constant: 2],5:[Unknown],6:[32-bit Constant: 1],7:[Zero],8:[Reference: ReferenceMap], -// |0032: goto 0015 // -001d -// catches : 3 -// 0x0006 - 0x000b -// Ljava/lang/Exception; -> 0x0014 -// -> 0x0026 -// 0x000c - 0x000e -// Ljava/lang/Exception; -> 0x0030 -// -> 0x002d -// 0x0018 - 0x001f -// -> 0x0026 -// positions : -// 0x0003 line=8 -// 0x0005 line=9 -// 0x0006 line=11 -// 0x000b line=12 -// 0x000e line=18 -// 0x0010 line=19 -// 0x0013 line=21 -// 0x0014 line=13 -// 0x0015 line=14 -// 0x0017 line=15 -// 0x001f line=18 -// 0x0021 line=19 -// 0x0025 line=20 -// 0x0026 line=18 -// 0x0029 line=19 -// 0x002d line=18 -// 0x0030 line=13 -// locals : -// 0x0006 - 0x000b reg=2 y Ljava/lang/Object; -// 0x000b - 0x0013 reg=3 y Ljava/lang/Object; -// 0x0014 - 0x0015 reg=2 y Ljava/lang/Object; -// 0x0015 - 0x0026 reg=0 ex Ljava/lang/Exception; -// 0x002d - 0x0032 reg=3 y Ljava/lang/Object; -// 0x0005 - 0x0033 reg=1 x [Ljava/lang/Object; -// 0x0032 - 0x0033 reg=2 y Ljava/lang/Object; -// 0x0000 - 0x0033 reg=8 this LReferenceMap; +// 0000: const/4 v4, #int 2 // #2 +// 0001: const/4 v7, #int 0 // #0 +// 0002: const/4 v6, #int 1 // #1 +// 0003: new-array v1, v4, [Ljava/lang/Object; // type@0007 +// 0005: const/4 v2, #int 0 // #0 +// 0006: new-instance v3, Ljava/lang/Object; // type@0003 +// 0008: invoke-direct {v3}, Ljava/lang/Object;.:()V // method@0004 +// 000b: const/4 v4, #int 2 // #2 +// 000c: aput-object v3, v1, v4 +// 000e: aput-object v3, v1, v6 +// 0010: invoke-virtual {v8, v7}, LMain;.refmap:(I)I // method@0003 +// 0013: move-object v2, v3 +// 0014: return-object v2 +// 0015: move-exception v0 +// 0016: if-nez v2, 0020 // +000a +// 0018: new-instance v4, Ljava/lang/Object; // type@0003 +// 001a: invoke-direct {v4}, Ljava/lang/Object;.:()V // method@0004 +// 001d: const/4 v5, #int 1 // #1 +// 001e: aput-object v4, v1, v5 +// 0020: aput-object v2, v1, v6 +// 0022: invoke-virtual {v8, v7}, LMain;.refmap:(I)I // method@0003 +// 0025: goto 0014 // -0011 +// 0026: move-exception v4 +// 0027: aput-object v2, v1, v6 +// 0029: invoke-virtual {v8, v7}, LMain;.refmap:(I)I // method@0003 +// 002c: throw v4 +// 002d: move-exception v4 +// 002e: move-object v2, v3 +// 002f: goto 0027 // -0008 +// 0030: move-exception v0 +// 0031: move-object v2, v3 +// 0032: goto 0016 // -001c +// catches : 3 +// 0x0006 - 0x000b +// Ljava/lang/Exception; -> 0x0015 +// -> 0x0026 +// 0x000c - 0x000e +// Ljava/lang/Exception; -> 0x0030 +// -> 0x002d +// 0x0018 - 0x0020 +// -> 0x0026 +// positions : +// 0x0003 line=22 +// 0x0005 line=23 +// 0x0006 line=25 +// 0x000b line=26 +// 0x000e line=32 +// 0x0010 line=33 +// 0x0014 line=35 +// 0x0015 line=27 +// 0x0016 line=28 +// 0x0018 line=29 +// 0x0020 line=32 +// 0x0022 line=33 +// 0x0026 line=31 +// 0x0027 line=32 +// 0x0029 line=33 +// 0x002c line=31 +// 0x0030 line=27 +// locals : +// 0x0006 - 0x000b reg=2 y Ljava/lang/Object; +// 0x000b - 0x0014 reg=3 y Ljava/lang/Object; +// 0x0015 - 0x0016 reg=2 y Ljava/lang/Object; +// 0x0016 - 0x0026 reg=0 ex Ljava/lang/Exception; +// 0x002d - 0x002f reg=3 y Ljava/lang/Object; +// 0x002f - 0x0030 reg=2 y Ljava/lang/Object; +// 0x0030 - 0x0032 reg=3 y Ljava/lang/Object; +// 0x0031 - 0x0033 reg=0 ex Ljava/lang/Exception; +// 0x0005 - 0x0033 reg=1 x [Ljava/lang/Object; +// 0x0032 - 0x0033 reg=2 y Ljava/lang/Object; +// 0x0000 - 0x0033 reg=8 this LMain; extern "C" JNIEXPORT jint JNICALL Java_Main_refmap(JNIEnv*, jobject, jint count) { // Visitor diff --git a/test/004-StackWalk/build b/test/004-StackWalk/build deleted file mode 100644 index 08987b556c..0000000000 --- a/test/004-StackWalk/build +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/bash -# -# Copyright (C) 2015 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Stop if something fails. -set -e - -# The test relies on DEX file produced by javac+dx so keep building with them for now -# (see b/19467889) -mkdir classes -${JAVAC} -d classes `find src -name '*.java'` -${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex \ - --dump-width=1000 ${DX_FLAGS} classes -zip $TEST_NAME.jar classes.dex diff --git a/test/004-StackWalk/stack_walk_jni.cc b/test/004-StackWalk/stack_walk_jni.cc index 3a5854ba96..51bb68f24a 100644 --- a/test/004-StackWalk/stack_walk_jni.cc +++ b/test/004-StackWalk/stack_walk_jni.cc @@ -42,31 +42,31 @@ class TestReferenceMapVisitor : public CheckReferenceMapVisitor { // Given the method name and the number of times the method has been called, // we know the Dex registers with live reference values. Assert that what we // find is what is expected. - if (m_name == "f") { + if (m_name == "$noinline$f") { if (gJava_StackWalk_refmap_calls == 1) { CHECK_EQ(1U, GetDexPc()); - CHECK_REGS(4); + CHECK_REGS(1); // v1: this } else { CHECK_EQ(gJava_StackWalk_refmap_calls, 2); CHECK_EQ(5U, GetDexPc()); - CHECK_REGS(4); + CHECK_REGS(1); // v1: this } } else if (m_name == "g") { if (gJava_StackWalk_refmap_calls == 1) { - CHECK_EQ(0xcU, GetDexPc()); - CHECK_REGS(0, 2); // Note that v1 is not in the minimal root set + CHECK_EQ(0xdU, GetDexPc()); + CHECK_REGS(0, 2); // v2: this (Note that v1 is not in the minimal root set) } else { CHECK_EQ(gJava_StackWalk_refmap_calls, 2); - CHECK_EQ(0xcU, GetDexPc()); + CHECK_EQ(0xdU, GetDexPc()); CHECK_REGS(0, 2); } } else if (m_name == "shlemiel") { if (gJava_StackWalk_refmap_calls == 1) { - CHECK_EQ(0x380U, GetDexPc()); + CHECK_EQ(0x393U, GetDexPc()); CHECK_REGS(2, 4, 5, 7, 8, 9, 10, 11, 13, 14, 15, 16, 17, 18, 19, 21, 25); } else { CHECK_EQ(gJava_StackWalk_refmap_calls, 2); - CHECK_EQ(0x380U, GetDexPc()); + CHECK_EQ(0x393U, GetDexPc()); CHECK_REGS(2, 4, 5, 7, 8, 9, 10, 11, 13, 14, 15, 16, 17, 18, 19, 21, 25); } } diff --git a/test/089-many-methods/build b/test/089-many-methods/build index ff77c60f64..58144e1dc8 100644 --- a/test/089-many-methods/build +++ b/test/089-many-methods/build @@ -43,8 +43,4 @@ function writeFileMethod(name) { printf("}\n") > fileName; }' -# The test relies on the error message produced by dx, not jack, so keep building with dx for now -# (b/19467889). -mkdir classes -${JAVAC} -d classes `find src -name '*.java'` -${DX} -JXmx1024m --dex --no-optimize classes +./default-build diff --git a/test/089-many-methods/expected.txt b/test/089-many-methods/expected.txt index b74e0eef8c..bfee8b3874 100644 --- a/test/089-many-methods/expected.txt +++ b/test/089-many-methods/expected.txt @@ -1,6 +1,2 @@ - -trouble writing output: Too many field references: 131000; max is 65536. -You may try using --multi-dex option. -References by package: -131000 default -build exit status: 2 +ERROR: Dex writing phase: classes.dex has too many IDs. Try using multi-dex +build exit status: 4 diff --git a/test/138-duplicate-classes-check2/build b/test/138-duplicate-classes-check2/build index abcbbb8437..d346251edb 100755 --- a/test/138-duplicate-classes-check2/build +++ b/test/138-duplicate-classes-check2/build @@ -24,9 +24,17 @@ mkdir classes-ex ${JAVAC} -d classes-ex `find src-ex -name '*.java'` rm classes-ex/A.class -if [ ${NEED_DEX} = "true" ]; then +if [ ${USE_JACK} = "true" ]; then + jar cf classes.jill.jar -C classes . + ${JACK} --import classes.jill.jar --output-dex . + zip ${TEST_NAME}.jar classes.dex + + jar cf classes-ex.jill.jar -C classes-ex . + ${JACK} --import classes-ex.jill.jar --output-dex . + zip ${TEST_NAME}-ex.jar classes.dex +elif [ ${NEED_DEX} = "true" ]; then ${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex --dump-width=1000 classes - zip $TEST_NAME.jar classes.dex + zip ${TEST_NAME}.jar classes.dex ${DX} -JXmx256m --debug --dex --dump-to=classes-ex.lst --output=classes.dex --dump-width=1000 classes-ex zip ${TEST_NAME}-ex.jar classes.dex fi diff --git a/test/454-get-vreg/build b/test/454-get-vreg/build deleted file mode 100644 index 08987b556c..0000000000 --- a/test/454-get-vreg/build +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/bash -# -# Copyright (C) 2015 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Stop if something fails. -set -e - -# The test relies on DEX file produced by javac+dx so keep building with them for now -# (see b/19467889) -mkdir classes -${JAVAC} -d classes `find src -name '*.java'` -${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex \ - --dump-width=1000 ${DX_FLAGS} classes -zip $TEST_NAME.jar classes.dex -- GitLab From e9924b7d4fcafe7261233d2c16ebb73d7bb5e8e8 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Tue, 1 Mar 2016 18:43:46 +0000 Subject: [PATCH 081/204] Fix bogus interaction between code cache and instrumentation. Change-Id: Icc314089ec1a05248b8994476d478a543e629e4d --- runtime/jit/jit_code_cache.cc | 5 ++++- runtime/jit/jit_instrumentation.cc | 10 +++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index 4500dbdf67..9aaef658a5 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -622,7 +622,10 @@ void JitCodeCache::GarbageCollectCache(Thread* self) { bool next_collection_will_be_full = ShouldDoFullCollection(); // Start polling the liveness of compiled code to prepare for the next full collection. - if (next_collection_will_be_full) { + // We avoid doing this if exit stubs are installed to not mess with the instrumentation. + // TODO(ngeoffray): Clean up instrumentation and code cache interactions. + if (!Runtime::Current()->GetInstrumentation()->AreExitStubsInstalled() && + next_collection_will_be_full) { // Save the entry point of methods we have compiled, and update the entry // point of those methods to the interpreter. If the method is invoked, the // interpreter will update its entry point to the compiled code and call it. diff --git a/runtime/jit/jit_instrumentation.cc b/runtime/jit/jit_instrumentation.cc index 86761e402f..f2ced18c9b 100644 --- a/runtime/jit/jit_instrumentation.cc +++ b/runtime/jit/jit_instrumentation.cc @@ -184,9 +184,13 @@ void JitInstrumentationListener::MethodEntered(Thread* thread, } ProfilingInfo* profiling_info = method->GetProfilingInfo(sizeof(void*)); - if ((profiling_info != nullptr) && (profiling_info->GetSavedEntryPoint() != nullptr)) { - // Update the entrypoint if the ProfilingInfo has one. The interpreter will call it - // instead of interpreting the method. + // Update the entrypoint if the ProfilingInfo has one. The interpreter will call it + // instead of interpreting the method. + // We avoid doing this if exit stubs are installed to not mess with the instrumentation. + // TODO(ngeoffray): Clean up instrumentation and code cache interactions. + if ((profiling_info != nullptr) && + (profiling_info->GetSavedEntryPoint() != nullptr) && + !Runtime::Current()->GetInstrumentation()->AreExitStubsInstalled()) { method->SetEntryPointFromQuickCompiledCode(profiling_info->GetSavedEntryPoint()); } else { instrumentation_cache_->AddSamples(thread, method, 1); -- GitLab From 591ad29eb1aa7c5ac7faadb426f5eb2b4a4ccd02 Mon Sep 17 00:00:00 2001 From: Aart Bik Date: Tue, 1 Mar 2016 10:39:25 -0800 Subject: [PATCH 082/204] Standby list for dyn bce in potentially infinite loops. Rationale: The old code relied on "luck" to revisit basic blocks after dynamic bce and incorporate all bounds checks in potentially infinite loops that were "made" finite. Now that revisiting has been removed completely, keeping a standby list ensures all candidates are considered. Change-Id: Ida3cf63be1307be6c2b0258d3e64b163f12be235 --- .../optimizing/bounds_check_elimination.cc | 20 +++++++++++++++++-- test/449-checker-bce/src/Main.java | 3 +-- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/compiler/optimizing/bounds_check_elimination.cc b/compiler/optimizing/bounds_check_elimination.cc index 288322e1c7..f2929bcc18 100644 --- a/compiler/optimizing/bounds_check_elimination.cc +++ b/compiler/optimizing/bounds_check_elimination.cc @@ -533,6 +533,8 @@ class BCEVisitor : public HGraphVisitor { first_index_bounds_check_map_( std::less(), graph->GetArena()->Adapter(kArenaAllocBoundsCheckElimination)), + dynamic_bce_standby_( + graph->GetArena()->Adapter(kArenaAllocBoundsCheckElimination)), early_exit_loop_( std::less(), graph->GetArena()->Adapter(kArenaAllocBoundsCheckElimination)), @@ -553,6 +555,13 @@ class BCEVisitor : public HGraphVisitor { } void Finish() { + // Retry dynamic bce candidates on standby that are still in the graph. + for (HBoundsCheck* bounds_check : dynamic_bce_standby_) { + if (bounds_check->IsInBlock()) { + TryDynamicBCE(bounds_check); + } + } + // Preserve SSA structure which may have been broken by adding one or more // new taken-test structures (see TransformLoopForDeoptimizationIfNeeded()). InsertPhiNodes(); @@ -561,6 +570,7 @@ class BCEVisitor : public HGraphVisitor { early_exit_loop_.clear(); taken_test_loop_.clear(); finite_loop_.clear(); + dynamic_bce_standby_.clear(); } private: @@ -1301,7 +1311,7 @@ class BCEVisitor : public HGraphVisitor { if (DynamicBCESeemsProfitable(loop, instruction->GetBlock()) && induction_range_.CanGenerateCode( instruction, index, &needs_finite_test, &needs_taken_test) && - CanHandleInfiniteLoop(loop, index, needs_finite_test) && + CanHandleInfiniteLoop(loop, instruction, index, needs_finite_test) && CanHandleLength(loop, length, needs_taken_test)) { // do this test last (may code gen) HInstruction* lower = nullptr; HInstruction* upper = nullptr; @@ -1433,7 +1443,7 @@ class BCEVisitor : public HGraphVisitor { * ensure the loop is finite. */ bool CanHandleInfiniteLoop( - HLoopInformation* loop, HInstruction* index, bool needs_infinite_test) { + HLoopInformation* loop, HBoundsCheck* check, HInstruction* index, bool needs_infinite_test) { if (needs_infinite_test) { // If we already forced the loop to be finite, allow directly. const uint32_t loop_id = loop->GetHeader()->GetBlockId(); @@ -1455,6 +1465,9 @@ class BCEVisitor : public HGraphVisitor { } } } + // If bounds check made it this far, it is worthwhile to check later if + // the loop was forced finite by another candidate. + dynamic_bce_standby_.push_back(check); return false; } return true; @@ -1676,6 +1689,9 @@ class BCEVisitor : public HGraphVisitor { // in a block that checks an index against that HArrayLength. ArenaSafeMap first_index_bounds_check_map_; + // Stand by list for dynamic bce. + ArenaVector dynamic_bce_standby_; + // Early-exit loop bookkeeping. ArenaSafeMap early_exit_loop_; diff --git a/test/449-checker-bce/src/Main.java b/test/449-checker-bce/src/Main.java index 39467fc626..66e1d92cc2 100644 --- a/test/449-checker-bce/src/Main.java +++ b/test/449-checker-bce/src/Main.java @@ -1260,8 +1260,7 @@ public class Main { } else { assertIsManaged(); } - // TODO: order matters, make it not so. - array[i] = (array[i] + array[i-2] + array[i-1] + array[i+1] + array[i+2]) / 5; + array[i] = (array[i-2] + array[i-1] + array[i] + array[i+1] + array[i+2]) / 5; } } -- GitLab From 034eb1a6ae870d53f9b3774e416ebbbd9859fdda Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Tue, 1 Mar 2016 22:16:35 +0000 Subject: [PATCH 083/204] Revert "Fix bogus interaction between code cache and instrumentation." hits a DCHECK when testing libcore: art F 11973 12675 art/runtime/jit/jit_code_cache.cc:644] Check failed: it.second->GetProfilingInfo(sizeof(void*)) != nullptr This reverts commit e9924b7d4fcafe7261233d2c16ebb73d7bb5e8e8. Change-Id: I37d05991b9506a53a7c76738a7e4a6def1010958 --- runtime/jit/jit_code_cache.cc | 5 +---- runtime/jit/jit_instrumentation.cc | 10 +++------- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index 9aaef658a5..4500dbdf67 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -622,10 +622,7 @@ void JitCodeCache::GarbageCollectCache(Thread* self) { bool next_collection_will_be_full = ShouldDoFullCollection(); // Start polling the liveness of compiled code to prepare for the next full collection. - // We avoid doing this if exit stubs are installed to not mess with the instrumentation. - // TODO(ngeoffray): Clean up instrumentation and code cache interactions. - if (!Runtime::Current()->GetInstrumentation()->AreExitStubsInstalled() && - next_collection_will_be_full) { + if (next_collection_will_be_full) { // Save the entry point of methods we have compiled, and update the entry // point of those methods to the interpreter. If the method is invoked, the // interpreter will update its entry point to the compiled code and call it. diff --git a/runtime/jit/jit_instrumentation.cc b/runtime/jit/jit_instrumentation.cc index f2ced18c9b..86761e402f 100644 --- a/runtime/jit/jit_instrumentation.cc +++ b/runtime/jit/jit_instrumentation.cc @@ -184,13 +184,9 @@ void JitInstrumentationListener::MethodEntered(Thread* thread, } ProfilingInfo* profiling_info = method->GetProfilingInfo(sizeof(void*)); - // Update the entrypoint if the ProfilingInfo has one. The interpreter will call it - // instead of interpreting the method. - // We avoid doing this if exit stubs are installed to not mess with the instrumentation. - // TODO(ngeoffray): Clean up instrumentation and code cache interactions. - if ((profiling_info != nullptr) && - (profiling_info->GetSavedEntryPoint() != nullptr) && - !Runtime::Current()->GetInstrumentation()->AreExitStubsInstalled()) { + if ((profiling_info != nullptr) && (profiling_info->GetSavedEntryPoint() != nullptr)) { + // Update the entrypoint if the ProfilingInfo has one. The interpreter will call it + // instead of interpreting the method. method->SetEntryPointFromQuickCompiledCode(profiling_info->GetSavedEntryPoint()); } else { instrumentation_cache_->AddSamples(thread, method, 1); -- GitLab From a96917a6983a5abbe973255a3846fda549fb1657 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Tue, 1 Mar 2016 22:18:02 +0000 Subject: [PATCH 084/204] Revert "Use the interpreter as a heartbeat for the JIT." Hits a DCHECK testing libcore. art F 11973 12675 art/runtime/jit/jit_code_cache.cc:644] Check failed: it.second->GetProfilingInfo(sizeof(void*)) != nullptr Bug: 27398183 Bug: 23128949 Bug: 26846185 This reverts commit 7273a5d045d3ceb3ff011ad65765356b69b155e8. Change-Id: I6614a82e775ea71aa16f041313f67546db41eac8 --- runtime/interpreter/interpreter.cc | 4 +- runtime/jit/jit.cc | 4 - runtime/jit/jit.h | 4 - runtime/jit/jit_code_cache.cc | 199 +++++++++++++---------------- runtime/jit/jit_code_cache.h | 13 +- runtime/jit/jit_instrumentation.cc | 9 +- runtime/jit/profiling_info.h | 15 +-- 7 files changed, 98 insertions(+), 150 deletions(-) diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc index 9808e22927..a595d33f04 100644 --- a/runtime/interpreter/interpreter.cc +++ b/runtime/interpreter/interpreter.cc @@ -295,7 +295,9 @@ static inline JValue Execute(Thread* self, const DexFile::CodeItem* code_item, } jit::Jit* jit = Runtime::Current()->GetJit(); - if (jit != nullptr && jit->CanInvokeCompiledCode(method)) { + if (UNLIKELY(jit != nullptr && + jit->JitAtFirstUse() && + jit->GetCodeCache()->ContainsMethod(method))) { JValue result; // Pop the shadow frame before calling into compiled code. diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc index 91b006a3ea..3e66ce20eb 100644 --- a/runtime/jit/jit.cc +++ b/runtime/jit/jit.cc @@ -213,10 +213,6 @@ bool Jit::JitAtFirstUse() { return false; } -bool Jit::CanInvokeCompiledCode(ArtMethod* method) { - return code_cache_->ContainsPc(method->GetEntryPointFromQuickCompiledCode()); -} - Jit::~Jit() { DCHECK(!save_profiling_info_ || !ProfileSaver::IsStarted()); if (dump_info_on_shutdown_) { diff --git a/runtime/jit/jit.h b/runtime/jit/jit.h index 3f54192d9f..109ca3dbd1 100644 --- a/runtime/jit/jit.h +++ b/runtime/jit/jit.h @@ -84,12 +84,8 @@ class Jit { // into the specified class linker to the jit debug interface, void DumpTypeInfoForLoadedTypes(ClassLinker* linker); - // Return whether we should try to JIT compiled code as soon as an ArtMethod is invoked. bool JitAtFirstUse(); - // Return whether we can invoke JIT code for `method`. - bool CanInvokeCompiledCode(ArtMethod* method); - // If an OSR compiled version is available for `method`, // and `dex_pc + dex_pc_offset` is an entry point of that compiled // version, this method will jump to the compiled code, let it run, diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index 4500dbdf67..8858b486f9 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -123,7 +123,7 @@ JitCodeCache::JitCodeCache(MemMap* code_map, current_capacity_(initial_code_capacity + initial_data_capacity), code_end_(initial_code_capacity), data_end_(initial_data_capacity), - last_collection_increased_code_cache_(false), + has_done_full_collection_(false), last_update_time_ns_(0), garbage_collect_code_(garbage_collect_code), used_memory_for_data_(0), @@ -546,20 +546,34 @@ void JitCodeCache::MarkCompiledCodeOnThreadStacks(Thread* self) { } } -bool JitCodeCache::ShouldDoFullCollection() { - if (current_capacity_ == max_capacity_) { - // Always do a full collection when the code cache is full. - return true; - } else if (current_capacity_ < kReservedCapacity) { - // Always do partial collection when the code cache size is below the reserved - // capacity. - return false; - } else if (last_collection_increased_code_cache_) { - // This time do a full collection. - return true; - } else { - // This time do a partial collection. - return false; +void JitCodeCache::RemoveUnusedCode(Thread* self) { + // Clear the osr map, chances are most of the code in it is now dead. + { + MutexLock mu(self, lock_); + osr_code_map_.clear(); + } + + // Run a checkpoint on all threads to mark the JIT compiled code they are running. + MarkCompiledCodeOnThreadStacks(self); + + // Iterate over all compiled code and remove entries that are not marked and not + // the entrypoint of their corresponding ArtMethod. + { + MutexLock mu(self, lock_); + ScopedCodeCacheWrite scc(code_map_.get()); + for (auto it = method_code_map_.begin(); it != method_code_map_.end();) { + const void* code_ptr = it->first; + ArtMethod* method = it->second; + uintptr_t allocation = FromCodeToAllocation(code_ptr); + const OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromCodePointer(code_ptr); + if ((method->GetEntryPointFromQuickCompiledCode() != method_header->GetEntryPoint()) && + !GetLiveBitmap()->Test(allocation)) { + FreeCode(code_ptr, method); + it = method_code_map_.erase(it); + } else { + ++it; + } + } } } @@ -585,10 +599,21 @@ void JitCodeCache::GarbageCollectCache(Thread* self) { } } - bool do_full_collection = false; + // Check if we want to do a full collection. + bool do_full_collection = true; { MutexLock mu(self, lock_); - do_full_collection = ShouldDoFullCollection(); + if (current_capacity_ == max_capacity_) { + // Always do a full collection when the code cache is full. + do_full_collection = true; + } else if (current_capacity_ < kReservedCapacity) { + // Do a partial collection until we hit the reserved capacity limit. + do_full_collection = false; + } else if (has_done_full_collection_) { + // Do a partial collection if we have done a full collection in the last + // collection round. + do_full_collection = false; + } } if (!kIsDebugBuild || VLOG_IS_ON(jit)) { @@ -599,94 +624,45 @@ void JitCodeCache::GarbageCollectCache(Thread* self) { << ", data=" << PrettySize(DataCacheSize()); } - DoCollection(self, /* collect_profiling_info */ do_full_collection); - - if (!kIsDebugBuild || VLOG_IS_ON(jit)) { - LOG(INFO) << "After code cache collection, code=" - << PrettySize(CodeCacheSize()) - << ", data=" << PrettySize(DataCacheSize()); + if (do_full_collection) { + DoFullCollection(self); + } else { + RemoveUnusedCode(self); } { MutexLock mu(self, lock_); - - // Increase the code cache only when we do partial collections. - // TODO: base this strategy on how full the code cache is? - if (do_full_collection) { - last_collection_increased_code_cache_ = false; - } else { - last_collection_increased_code_cache_ = true; + if (!do_full_collection) { + has_done_full_collection_ = false; IncreaseCodeCacheCapacity(); - } - - bool next_collection_will_be_full = ShouldDoFullCollection(); - - // Start polling the liveness of compiled code to prepare for the next full collection. - if (next_collection_will_be_full) { - // Save the entry point of methods we have compiled, and update the entry - // point of those methods to the interpreter. If the method is invoked, the - // interpreter will update its entry point to the compiled code and call it. - for (ProfilingInfo* info : profiling_infos_) { - const void* entry_point = info->GetMethod()->GetEntryPointFromQuickCompiledCode(); - if (ContainsPc(entry_point)) { - info->SetSavedEntryPoint(entry_point); - info->GetMethod()->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge()); - } - } - - if (kIsDebugBuild) { - // Check that methods we have compiled do have a ProfilingInfo object. We would - // have memory leaks of compiled code otherwise. - for (const auto& it : method_code_map_) { - DCHECK(it.second->GetProfilingInfo(sizeof(void*)) != nullptr); - } - } + } else { + has_done_full_collection_ = true; } live_bitmap_.reset(nullptr); NotifyCollectionDone(self); } -} -void JitCodeCache::RemoveUnusedAndUnmarkedCode(Thread* self) { - MutexLock mu(self, lock_); - ScopedCodeCacheWrite scc(code_map_.get()); - // Iterate over all compiled code and remove entries that are not marked and not - // the entrypoint of their corresponding ArtMethod. - for (auto it = method_code_map_.begin(); it != method_code_map_.end();) { - const void* code_ptr = it->first; - ArtMethod* method = it->second; - uintptr_t allocation = FromCodeToAllocation(code_ptr); - const OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromCodePointer(code_ptr); - const void* entrypoint = method->GetEntryPointFromQuickCompiledCode(); - if ((entrypoint == method_header->GetEntryPoint()) || GetLiveBitmap()->Test(allocation)) { - ++it; - } else { - if (entrypoint == GetQuickToInterpreterBridge()) { - method->ClearCounter(); - } - FreeCode(code_ptr, method); - it = method_code_map_.erase(it); - } + if (!kIsDebugBuild || VLOG_IS_ON(jit)) { + LOG(INFO) << "After code cache collection, code=" + << PrettySize(CodeCacheSize()) + << ", data=" << PrettySize(DataCacheSize()); } } -void JitCodeCache::DoCollection(Thread* self, bool collect_profiling_info) { +void JitCodeCache::DoFullCollection(Thread* self) { + instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); { MutexLock mu(self, lock_); - if (collect_profiling_info) { - // Clear the profiling info of methods that do not have compiled code as entrypoint. - // Also remove the saved entry point from the ProfilingInfo objects. - for (ProfilingInfo* info : profiling_infos_) { - const void* ptr = info->GetMethod()->GetEntryPointFromQuickCompiledCode(); - if (!ContainsPc(ptr) && !info->IsMethodBeingCompiled()) { - info->GetMethod()->SetProfilingInfo(nullptr); - } - info->SetSavedEntryPoint(nullptr); - } - } else if (kIsDebugBuild) { - // Sanity check that the profiling infos do not have a dangling entry point. - for (ProfilingInfo* info : profiling_infos_) { - DCHECK(info->GetSavedEntryPoint() == nullptr); + // Walk over all compiled methods and set the entry points of these + // methods to interpreter. + for (auto& it : method_code_map_) { + instrumentation->UpdateMethodsCode(it.second, GetQuickToInterpreterBridge()); + } + + // Clear the profiling info of methods that are not being compiled. + for (ProfilingInfo* info : profiling_infos_) { + if (!info->IsMethodBeingCompiled()) { + info->GetMethod()->SetProfilingInfo(nullptr); } } @@ -698,22 +674,32 @@ void JitCodeCache::DoCollection(Thread* self, bool collect_profiling_info) { // Run a checkpoint on all threads to mark the JIT compiled code they are running. MarkCompiledCodeOnThreadStacks(self); - // Remove compiled code that is not the entrypoint of their method and not in the call - // stack. - RemoveUnusedAndUnmarkedCode(self); - - if (collect_profiling_info) { + { MutexLock mu(self, lock_); - // Free all profiling infos of methods not compiled nor being compiled. + // Free unused compiled code, and restore the entry point of used compiled code. + { + ScopedCodeCacheWrite scc(code_map_.get()); + for (auto it = method_code_map_.begin(); it != method_code_map_.end();) { + const void* code_ptr = it->first; + ArtMethod* method = it->second; + uintptr_t allocation = FromCodeToAllocation(code_ptr); + const OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromCodePointer(code_ptr); + if (GetLiveBitmap()->Test(allocation)) { + instrumentation->UpdateMethodsCode(method, method_header->GetEntryPoint()); + ++it; + } else { + method->ClearCounter(); + DCHECK_NE(method->GetEntryPointFromQuickCompiledCode(), method_header->GetEntryPoint()); + FreeCode(code_ptr, method); + it = method_code_map_.erase(it); + } + } + } + + // Free all profiling infos of methods that were not being compiled. auto profiling_kept_end = std::remove_if(profiling_infos_.begin(), profiling_infos_.end(), [this] (ProfilingInfo* info) NO_THREAD_SAFETY_ANALYSIS { - const void* ptr = info->GetMethod()->GetEntryPointFromQuickCompiledCode(); - if (ContainsPc(ptr) && info->GetMethod()->GetProfilingInfo(sizeof(void*)) == nullptr) { - // Make sure compiled methods have a ProfilingInfo object. It is needed for - // code cache collection. - info->GetMethod()->SetProfilingInfo(info); - } else if (info->GetMethod()->GetProfilingInfo(sizeof(void*)) != info) { - // No need for this ProfilingInfo object anymore. + if (info->GetMethod()->GetProfilingInfo(sizeof(void*)) == nullptr) { FreeData(reinterpret_cast(info)); return true; } @@ -863,13 +849,6 @@ size_t JitCodeCache::GetMemorySizeOfCodePointer(const void* ptr) { void JitCodeCache::InvalidateCompiledCodeFor(ArtMethod* method, const OatQuickMethodHeader* header) { - ProfilingInfo* profiling_info = method->GetProfilingInfo(sizeof(void*)); - if ((profiling_info != nullptr) && - (profiling_info->GetSavedEntryPoint() == header->GetEntryPoint())) { - // Prevent future uses of the compiled code. - profiling_info->SetSavedEntryPoint(nullptr); - } - if (method->GetEntryPointFromQuickCompiledCode() == header->GetEntryPoint()) { // The entrypoint is the one to invalidate, so we just update // it to the interpreter entry point and clear the counter to get the method diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h index 2cf3c4d0f4..4574edfb46 100644 --- a/runtime/jit/jit_code_cache.h +++ b/runtime/jit/jit_code_cache.h @@ -124,11 +124,6 @@ class JitCodeCache { return live_bitmap_.get(); } - // Return whether we should do a full collection given the current state of the cache. - bool ShouldDoFullCollection() - REQUIRES(lock_) - SHARED_REQUIRES(Locks::mutator_lock_); - // Perform a collection on the code cache. void GarbageCollectCache(Thread* self) REQUIRES(!lock_) @@ -240,11 +235,11 @@ class JitCodeCache { // Set the footprint limit of the code cache. void SetFootprintLimit(size_t new_footprint) REQUIRES(lock_); - void DoCollection(Thread* self, bool collect_profiling_info) + void DoFullCollection(Thread* self) REQUIRES(!lock_) SHARED_REQUIRES(Locks::mutator_lock_); - void RemoveUnusedAndUnmarkedCode(Thread* self) + void RemoveUnusedCode(Thread* self) REQUIRES(!lock_) SHARED_REQUIRES(Locks::mutator_lock_); @@ -287,8 +282,8 @@ class JitCodeCache { // The current footprint in bytes of the data portion of the code cache. size_t data_end_ GUARDED_BY(lock_); - // Whether the last collection round increased the code cache. - bool last_collection_increased_code_cache_ GUARDED_BY(lock_); + // Whether a full collection has already been done on the current capacity. + bool has_done_full_collection_ GUARDED_BY(lock_); // Last time the the code_cache was updated. // It is atomic to avoid locking when reading it. diff --git a/runtime/jit/jit_instrumentation.cc b/runtime/jit/jit_instrumentation.cc index 86761e402f..a4e40ad3fd 100644 --- a/runtime/jit/jit_instrumentation.cc +++ b/runtime/jit/jit_instrumentation.cc @@ -183,14 +183,7 @@ void JitInstrumentationListener::MethodEntered(Thread* thread, return; } - ProfilingInfo* profiling_info = method->GetProfilingInfo(sizeof(void*)); - if ((profiling_info != nullptr) && (profiling_info->GetSavedEntryPoint() != nullptr)) { - // Update the entrypoint if the ProfilingInfo has one. The interpreter will call it - // instead of interpreting the method. - method->SetEntryPointFromQuickCompiledCode(profiling_info->GetSavedEntryPoint()); - } else { - instrumentation_cache_->AddSamples(thread, method, 1); - } + instrumentation_cache_->AddSamples(thread, method, 1); } void JitInstrumentationListener::Branch(Thread* thread, diff --git a/runtime/jit/profiling_info.h b/runtime/jit/profiling_info.h index d54f3dfc11..ab7237376b 100644 --- a/runtime/jit/profiling_info.h +++ b/runtime/jit/profiling_info.h @@ -126,20 +126,11 @@ class ProfilingInfo { is_method_being_compiled_ = value; } - void SetSavedEntryPoint(const void* entry_point) { - saved_entry_point_ = entry_point; - } - - const void* GetSavedEntryPoint() const { - return saved_entry_point_; - } - private: ProfilingInfo(ArtMethod* method, const std::vector& entries) : number_of_inline_caches_(entries.size()), method_(method), - is_method_being_compiled_(false), - saved_entry_point_(nullptr) { + is_method_being_compiled_(false) { memset(&cache_, 0, number_of_inline_caches_ * sizeof(InlineCache)); for (size_t i = 0; i < number_of_inline_caches_; ++i) { cache_[i].dex_pc_ = entries[i]; @@ -157,10 +148,6 @@ class ProfilingInfo { // TODO: Make the JIT code cache lock global. bool is_method_being_compiled_; - // Entry point of the corresponding ArtMethod, while the JIT code cache - // is poking for the liveness of compiled code. - const void* saved_entry_point_; - // Dynamically allocated array of size `number_of_inline_caches_`. InlineCache cache_[0]; -- GitLab From f7286721a51c772b2abb42fef5bc0d2548df2372 Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Tue, 1 Mar 2016 14:38:37 -0800 Subject: [PATCH 085/204] ART: Allow unwinding unattached threads Partial revert of commit ed8b723c5f3989d2593ec21c65c96d6d8bf25579. Make it (constexpr) configurable whether we allow unwinding native stacks of unattached threads. Change-Id: Ibb1fd0956fb796583f78ec9c3a74521f6fbe96d9 --- runtime/thread_list.cc | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc index 49d54fda00..d45e4bdcfe 100644 --- a/runtime/thread_list.cc +++ b/runtime/thread_list.cc @@ -59,6 +59,10 @@ static constexpr useconds_t kThreadSuspendInitialSleepUs = 0; static constexpr useconds_t kThreadSuspendMaxYieldUs = 3000; static constexpr useconds_t kThreadSuspendMaxSleepUs = 5000; +// Whether we should try to dump the native stack of unattached threads. See commit ed8b723 for +// some history. +static constexpr bool kDumpUnattachedThreadNativeStack = true; + ThreadList::ThreadList() : suspend_all_count_(0), debug_suspend_all_count_(0), @@ -149,9 +153,7 @@ static void DumpUnattachedThread(std::ostream& os, pid_t tid) NO_THREAD_SAFETY_A // refactor DumpState to avoid skipping analysis. Thread::DumpState(os, nullptr, tid); DumpKernelStack(os, tid, " kernel: ", false); - // TODO: Reenable this when the native code in system_server can handle it. - // Currently "adb shell kill -3 `pid system_server`" will cause it to exit. - if (false) { + if (kDumpUnattachedThreadNativeStack) { DumpNativeStack(os, tid, nullptr, " native: "); } os << "\n"; -- GitLab From 9e927f57415372591bab2a5c7af2520204902ca4 Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Mon, 29 Feb 2016 20:49:38 -0800 Subject: [PATCH 086/204] Lower JIT thread priority Modify the JIT thread to run at a lower priority in order to allow UI-critical threads to get enough CPU time. Bug: 27417985 (cherry picked from commit e701f088f8fe3a2c1f16e2895402f26283f4bcc7) Change-Id: I5b962a7970ae81dac9e01a8011128c538cd78e40 --- runtime/jit/jit_instrumentation.cc | 4 ++++ runtime/thread_pool.cc | 24 ++++++++++++++++++++++++ runtime/thread_pool.h | 6 ++++++ 3 files changed, 34 insertions(+) diff --git a/runtime/jit/jit_instrumentation.cc b/runtime/jit/jit_instrumentation.cc index a4e40ad3fd..46c362ac62 100644 --- a/runtime/jit/jit_instrumentation.cc +++ b/runtime/jit/jit_instrumentation.cc @@ -25,6 +25,9 @@ namespace art { namespace jit { +// At what priority to schedule jit threads. 9 is the lowest foreground priority on device. +static constexpr int kJitPoolThreadPthreadPriority = 9; + class JitCompileTask FINAL : public Task { public: enum TaskKind { @@ -92,6 +95,7 @@ void JitInstrumentationCache::CreateThreadPool() { // There is a DCHECK in the 'AddSamples' method to ensure the tread pool // is not null when we instrument. thread_pool_.reset(new ThreadPool("Jit thread pool", 1)); + thread_pool_->SetPthreadPriority(kJitPoolThreadPthreadPriority); thread_pool_->StartWorkers(Thread::Current()); { // Add Jit interpreter instrumentation, tells the interpreter when diff --git a/runtime/thread_pool.cc b/runtime/thread_pool.cc index 5a4dfb8cfd..2fba805fa2 100644 --- a/runtime/thread_pool.cc +++ b/runtime/thread_pool.cc @@ -16,6 +16,11 @@ #include "thread_pool.h" +#include + +#include +#include + #include "base/bit_utils.h" #include "base/casts.h" #include "base/logging.h" @@ -53,6 +58,19 @@ ThreadPoolWorker::~ThreadPoolWorker() { CHECK_PTHREAD_CALL(pthread_join, (pthread_, nullptr), "thread pool worker shutdown"); } +void ThreadPoolWorker::SetPthreadPriority(int priority) { + CHECK_GE(priority, PRIO_MIN); + CHECK_LE(priority, PRIO_MAX); +#if defined(__ANDROID__) + int result = setpriority(PRIO_PROCESS, pthread_gettid_np(pthread_), priority); + if (result != 0) { + PLOG(ERROR) << "Failed to setpriority to :" << priority; + } +#else + UNUSED(priority); +#endif +} + void ThreadPoolWorker::Run() { Thread* self = Thread::Current(); Task* task = nullptr; @@ -214,4 +232,10 @@ size_t ThreadPool::GetTaskCount(Thread* self) { return tasks_.size(); } +void ThreadPool::SetPthreadPriority(int priority) { + for (ThreadPoolWorker* worker : threads_) { + worker->SetPthreadPriority(priority); + } +} + } // namespace art diff --git a/runtime/thread_pool.h b/runtime/thread_pool.h index 6cd4ad3cdc..b6c6f02db8 100644 --- a/runtime/thread_pool.h +++ b/runtime/thread_pool.h @@ -59,6 +59,9 @@ class ThreadPoolWorker { virtual ~ThreadPoolWorker(); + // Set the "nice" priorty for this worker. + void SetPthreadPriority(int priority); + protected: ThreadPoolWorker(ThreadPool* thread_pool, const std::string& name, size_t stack_size); static void* Callback(void* arg) REQUIRES(!Locks::mutator_lock_); @@ -111,6 +114,9 @@ class ThreadPool { // thread count of the thread pool. void SetMaxActiveWorkers(size_t threads) REQUIRES(!task_queue_lock_); + // Set the "nice" priorty for threads in the pool. + void SetPthreadPriority(int priority); + protected: // get a task to run, blocks if there are no tasks left virtual Task* GetTask(Thread* self) REQUIRES(!task_queue_lock_); -- GitLab From 3f52306b259caed1c654c4b3fd5b594d5ec8d46c Mon Sep 17 00:00:00 2001 From: David Brazdil Date: Mon, 29 Feb 2016 16:53:33 +0000 Subject: [PATCH 087/204] ART: Fix overlapping instruction IDs in inliner Inliner creates the inner graph so that it generates instruction IDs higher than the outer graph. This was broken because the inliner would create instructions in the outer graph before the inner graph is inlined. The bug cannot be triggered because the offending instruction would share the same ID as the first inner HLocal, which is removed before the inner graph is inlined. The added DCHECKs reveal the hidden problem and make it safe for HLocals to be removed in the future. Change-Id: I486eb0f3987e20c50cbec0fb06332229e07fbae9 --- compiler/optimizing/inliner.cc | 13 +++++++- compiler/optimizing/nodes.cc | 61 ++++++++++++++++------------------ compiler/optimizing/nodes.h | 1 + 3 files changed, 42 insertions(+), 33 deletions(-) diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index 3f67e481f9..3e3719e6ea 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -1010,6 +1010,8 @@ bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction, // at runtime, we change this call as if it was a virtual call. invoke_type = kVirtual; } + + const int32_t caller_instruction_counter = graph_->GetCurrentInstructionId(); HGraph* callee_graph = new (graph_->GetArena()) HGraph( graph_->GetArena(), callee_dex_file, @@ -1019,7 +1021,7 @@ bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction, invoke_type, graph_->IsDebuggable(), /* osr */ false, - graph_->GetCurrentInstructionId()); + caller_instruction_counter); callee_graph->SetArtMethod(resolved_method); OptimizingCompilerStats inline_stats; @@ -1219,7 +1221,16 @@ bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction, } number_of_inlined_instructions_ += number_of_instructions; + DCHECK_EQ(caller_instruction_counter, graph_->GetCurrentInstructionId()) + << "No instructions can be added to the outer graph while inner graph is being built"; + + const int32_t callee_instruction_counter = callee_graph->GetCurrentInstructionId(); + graph_->SetCurrentInstructionId(callee_instruction_counter); *return_replacement = callee_graph->InlineInto(graph_, invoke_instruction); + + DCHECK_EQ(callee_instruction_counter, callee_graph->GetCurrentInstructionId()) + << "No instructions can be added to the inner graph during inlining into the outer graph"; + return true; } diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index 87b9c022df..0e0b83e4b4 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -1976,34 +1976,6 @@ HInstruction* HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) { at->MergeWithInlined(first); exit_block_->ReplaceWith(to); - // Update all predecessors of the exit block (now the `to` block) - // to not `HReturn` but `HGoto` instead. - bool returns_void = to->GetPredecessors()[0]->GetLastInstruction()->IsReturnVoid(); - if (to->GetPredecessors().size() == 1) { - HBasicBlock* predecessor = to->GetPredecessors()[0]; - HInstruction* last = predecessor->GetLastInstruction(); - if (!returns_void) { - return_value = last->InputAt(0); - } - predecessor->AddInstruction(new (allocator) HGoto(last->GetDexPc())); - predecessor->RemoveInstruction(last); - } else { - if (!returns_void) { - // There will be multiple returns. - return_value = new (allocator) HPhi( - allocator, kNoRegNumber, 0, HPhi::ToPhiType(invoke->GetType()), to->GetDexPc()); - to->AddPhi(return_value->AsPhi()); - } - for (HBasicBlock* predecessor : to->GetPredecessors()) { - HInstruction* last = predecessor->GetLastInstruction(); - if (!returns_void) { - return_value->AsPhi()->AddInput(last->InputAt(0)); - } - predecessor->AddInstruction(new (allocator) HGoto(last->GetDexPc())); - predecessor->RemoveInstruction(last); - } - } - // Update the meta information surrounding blocks: // (1) the graph they are now in, // (2) the reverse post order of that graph, @@ -2048,11 +2020,36 @@ HInstruction* HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) { // Only `to` can become a back edge, as the inlined blocks // are predecessors of `to`. UpdateLoopAndTryInformationOfNewBlock(to, at, /* replace_if_back_edge */ true); - } - // Update the next instruction id of the outer graph, so that instructions - // added later get bigger ids than those in the inner graph. - outer_graph->SetCurrentInstructionId(GetNextInstructionId()); + // Update all predecessors of the exit block (now the `to` block) + // to not `HReturn` but `HGoto` instead. + bool returns_void = to->GetPredecessors()[0]->GetLastInstruction()->IsReturnVoid(); + if (to->GetPredecessors().size() == 1) { + HBasicBlock* predecessor = to->GetPredecessors()[0]; + HInstruction* last = predecessor->GetLastInstruction(); + if (!returns_void) { + return_value = last->InputAt(0); + } + predecessor->AddInstruction(new (allocator) HGoto(last->GetDexPc())); + predecessor->RemoveInstruction(last); + } else { + if (!returns_void) { + // There will be multiple returns. + return_value = new (allocator) HPhi( + allocator, kNoRegNumber, 0, HPhi::ToPhiType(invoke->GetType()), to->GetDexPc()); + to->AddPhi(return_value->AsPhi()); + } + for (HBasicBlock* predecessor : to->GetPredecessors()) { + HInstruction* last = predecessor->GetLastInstruction(); + if (!returns_void) { + DCHECK(last->IsReturn()); + return_value->AsPhi()->AddInput(last->InputAt(0)); + } + predecessor->AddInstruction(new (allocator) HGoto(last->GetDexPc())); + predecessor->RemoveInstruction(last); + } + } + } // Walk over the entry block and: // - Move constants from the entry block to the outer_graph's entry block, diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index e3dbe16547..b684cc697f 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -387,6 +387,7 @@ class HGraph : public ArenaObject { } void SetCurrentInstructionId(int32_t id) { + DCHECK_GE(id, current_instruction_id_); current_instruction_id_ = id; } -- GitLab From eebc3af4453f5c1fb5fd80c710cfd49566080d28 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Mon, 29 Feb 2016 18:13:38 -0800 Subject: [PATCH 088/204] Fix allocation tracking race Check if changed from uninstrumented to instrumented during GC for alloc. If we changed, retry the allocation with kInstrumented = true. Added stress test. Bug: 27337759 Change-Id: Iaad7977693c9ed927f779a66b29cd58341a837da --- runtime/gc/heap-inl.h | 25 ++++--- runtime/gc/heap.cc | 37 +++++++--- runtime/gc/heap.h | 1 + runtime/instrumentation.h | 10 ++- test/145-alloc-tracking-stress/expected.txt | 1 + test/145-alloc-tracking-stress/info.txt | 1 + test/145-alloc-tracking-stress/src/Main.java | 74 ++++++++++++++++++++ 7 files changed, 130 insertions(+), 19 deletions(-) create mode 100644 test/145-alloc-tracking-stress/expected.txt create mode 100644 test/145-alloc-tracking-stress/info.txt create mode 100644 test/145-alloc-tracking-stress/src/Main.java diff --git a/runtime/gc/heap-inl.h b/runtime/gc/heap-inl.h index f437830e11..d7023d8ee0 100644 --- a/runtime/gc/heap-inl.h +++ b/runtime/gc/heap-inl.h @@ -109,16 +109,25 @@ inline mirror::Object* Heap::AllocObjectWithAllocator(Thread* self, obj = TryToAllocate(self, allocator, byte_count, &bytes_allocated, &usable_size, &bytes_tl_bulk_allocated); if (UNLIKELY(obj == nullptr)) { - bool is_current_allocator = allocator == GetCurrentAllocator(); - obj = AllocateInternalWithGc(self, allocator, byte_count, &bytes_allocated, &usable_size, + // AllocateInternalWithGc can cause thread suspension, if someone instruments the entrypoints + // or changes the allocator in a suspend point here, we need to retry the allocation. + obj = AllocateInternalWithGc(self, + allocator, + kInstrumented, + byte_count, + &bytes_allocated, + &usable_size, &bytes_tl_bulk_allocated, &klass); if (obj == nullptr) { - bool after_is_current_allocator = allocator == GetCurrentAllocator(); - // If there is a pending exception, fail the allocation right away since the next one - // could cause OOM and abort the runtime. - if (!self->IsExceptionPending() && is_current_allocator && !after_is_current_allocator) { - // If the allocator changed, we need to restart the allocation. - return AllocObject(self, klass, byte_count, pre_fence_visitor); + // The only way that we can get a null return if there is no pending exception is if the + // allocator or instrumentation changed. + if (!self->IsExceptionPending()) { + // AllocObject will pick up the new allocator type, and instrumented as true is the safe + // default. + return AllocObject(self, + klass, + byte_count, + pre_fence_visitor); } return nullptr; } diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index 4bee46285d..1b4cbeccda 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -1650,8 +1650,15 @@ space::RosAllocSpace* Heap::GetRosAllocSpace(gc::allocator::RosAlloc* rosalloc) return nullptr; } +static inline bool EntrypointsInstrumented() SHARED_REQUIRES(Locks::mutator_lock_) { + instrumentation::Instrumentation* const instrumentation = + Runtime::Current()->GetInstrumentation(); + return instrumentation != nullptr && instrumentation->AllocEntrypointsInstrumented(); +} + mirror::Object* Heap::AllocateInternalWithGc(Thread* self, AllocatorType allocator, + bool instrumented, size_t alloc_size, size_t* bytes_allocated, size_t* usable_size, @@ -1667,12 +1674,13 @@ mirror::Object* Heap::AllocateInternalWithGc(Thread* self, // The allocation failed. If the GC is running, block until it completes, and then retry the // allocation. collector::GcType last_gc = WaitForGcToComplete(kGcCauseForAlloc, self); + // If we were the default allocator but the allocator changed while we were suspended, + // abort the allocation. + if ((was_default_allocator && allocator != GetCurrentAllocator()) || + (!instrumented && EntrypointsInstrumented())) { + return nullptr; + } if (last_gc != collector::kGcTypeNone) { - // If we were the default allocator but the allocator changed while we were suspended, - // abort the allocation. - if (was_default_allocator && allocator != GetCurrentAllocator()) { - return nullptr; - } // A GC was in progress and we blocked, retry allocation now that memory has been freed. mirror::Object* ptr = TryToAllocate(self, allocator, alloc_size, bytes_allocated, usable_size, bytes_tl_bulk_allocated); @@ -1684,7 +1692,8 @@ mirror::Object* Heap::AllocateInternalWithGc(Thread* self, collector::GcType tried_type = next_gc_type_; const bool gc_ran = CollectGarbageInternal(tried_type, kGcCauseForAlloc, false) != collector::kGcTypeNone; - if (was_default_allocator && allocator != GetCurrentAllocator()) { + if ((was_default_allocator && allocator != GetCurrentAllocator()) || + (!instrumented && EntrypointsInstrumented())) { return nullptr; } if (gc_ran) { @@ -1703,7 +1712,8 @@ mirror::Object* Heap::AllocateInternalWithGc(Thread* self, // Attempt to run the collector, if we succeed, re-try the allocation. const bool plan_gc_ran = CollectGarbageInternal(gc_type, kGcCauseForAlloc, false) != collector::kGcTypeNone; - if (was_default_allocator && allocator != GetCurrentAllocator()) { + if ((was_default_allocator && allocator != GetCurrentAllocator()) || + (!instrumented && EntrypointsInstrumented())) { return nullptr; } if (plan_gc_ran) { @@ -1732,7 +1742,8 @@ mirror::Object* Heap::AllocateInternalWithGc(Thread* self, // We don't need a WaitForGcToComplete here either. DCHECK(!gc_plan_.empty()); CollectGarbageInternal(gc_plan_.back(), kGcCauseForAlloc, true); - if (was_default_allocator && allocator != GetCurrentAllocator()) { + if ((was_default_allocator && allocator != GetCurrentAllocator()) || + (!instrumented && EntrypointsInstrumented())) { return nullptr; } ptr = TryToAllocate(self, allocator, alloc_size, bytes_allocated, usable_size, @@ -1748,6 +1759,11 @@ mirror::Object* Heap::AllocateInternalWithGc(Thread* self, min_interval_homogeneous_space_compaction_by_oom_) { last_time_homogeneous_space_compaction_by_oom_ = current_time; HomogeneousSpaceCompactResult result = PerformHomogeneousSpaceCompact(); + // Thread suspension could have occurred. + if ((was_default_allocator && allocator != GetCurrentAllocator()) || + (!instrumented && EntrypointsInstrumented())) { + return nullptr; + } switch (result) { case HomogeneousSpaceCompactResult::kSuccess: // If the allocation succeeded, we delayed an oom. @@ -1788,6 +1804,11 @@ mirror::Object* Heap::AllocateInternalWithGc(Thread* self, // If we aren't out of memory then the OOM was probably from the non moving space being // full. Attempt to disable compaction and turn the main space into a non moving space. DisableMovingGc(); + // Thread suspension could have occurred. + if ((was_default_allocator && allocator != GetCurrentAllocator()) || + (!instrumented && EntrypointsInstrumented())) { + return nullptr; + } // If we are still a moving GC then something must have caused the transition to fail. if (IsMovingGc(collector_type_)) { MutexLock mu(self, *gc_complete_lock_); diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index 6edb548155..46dce04325 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -865,6 +865,7 @@ class Heap { // an initial allocation attempt failed. mirror::Object* AllocateInternalWithGc(Thread* self, AllocatorType allocator, + bool instrumented, size_t num_bytes, size_t* bytes_allocated, size_t* usable_size, diff --git a/runtime/instrumentation.h b/runtime/instrumentation.h index 2e4be6b689..b3cdb410f3 100644 --- a/runtime/instrumentation.h +++ b/runtime/instrumentation.h @@ -419,6 +419,12 @@ class Instrumentation { size_t inlined_frames_before_frame) SHARED_REQUIRES(Locks::mutator_lock_); + // Does not hold lock, used to check if someone changed from not instrumented to instrumented + // during a GC suspend point. + bool AllocEntrypointsInstrumented() const SHARED_REQUIRES(Locks::mutator_lock_) { + return quick_alloc_entry_points_instrumentation_counter_ > 0; + } + private: InstrumentationLevel GetCurrentInstrumentationLevel() const; @@ -572,9 +578,7 @@ class Instrumentation { InterpreterHandlerTable interpreter_handler_table_ GUARDED_BY(Locks::mutator_lock_); // Greater than 0 if quick alloc entry points instrumented. - size_t quick_alloc_entry_points_instrumentation_counter_ - GUARDED_BY(Locks::instrument_entrypoints_lock_); - + size_t quick_alloc_entry_points_instrumentation_counter_; friend class InstrumentationTest; // For GetCurrentInstrumentationLevel and ConfigureStubs. DISALLOW_COPY_AND_ASSIGN(Instrumentation); diff --git a/test/145-alloc-tracking-stress/expected.txt b/test/145-alloc-tracking-stress/expected.txt new file mode 100644 index 0000000000..134d8d0b47 --- /dev/null +++ b/test/145-alloc-tracking-stress/expected.txt @@ -0,0 +1 @@ +Finishing diff --git a/test/145-alloc-tracking-stress/info.txt b/test/145-alloc-tracking-stress/info.txt new file mode 100644 index 0000000000..443062db96 --- /dev/null +++ b/test/145-alloc-tracking-stress/info.txt @@ -0,0 +1 @@ +Regression test for b/18661622 diff --git a/test/145-alloc-tracking-stress/src/Main.java b/test/145-alloc-tracking-stress/src/Main.java new file mode 100644 index 0000000000..752fdd9135 --- /dev/null +++ b/test/145-alloc-tracking-stress/src/Main.java @@ -0,0 +1,74 @@ +/* + + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.lang.reflect.Method; +import java.util.Map; + +public class Main implements Runnable { + static final int numberOfThreads = 4; + static final int totalOperations = 1000; + static Method enableAllocTrackingMethod; + static Object holder; + static volatile boolean trackingThreadDone = false; + int threadIndex; + + Main(int index) { + threadIndex = index; + } + + public static void main(String[] args) throws Exception { + Class klass = Class.forName("org.apache.harmony.dalvik.ddmc.DdmVmInternal"); + if (klass == null) { + throw new AssertionError("Couldn't find DdmVmInternal class"); + } + enableAllocTrackingMethod = klass.getDeclaredMethod("enableRecentAllocations", + Boolean.TYPE); + if (enableAllocTrackingMethod == null) { + throw new AssertionError("Couldn't find enableRecentAllocations method"); + } + + final Thread[] threads = new Thread[numberOfThreads]; + for (int t = 0; t < threads.length; t++) { + threads[t] = new Thread(new Main(t)); + threads[t].start(); + } + for (Thread t : threads) { + t.join(); + } + System.out.println("Finishing"); + } + + public void run() { + if (threadIndex == 0) { + for (int i = 0; i < totalOperations; ++i) { + try { + enableAllocTrackingMethod.invoke(null, true); + holder = new Object(); + enableAllocTrackingMethod.invoke(null, false); + } catch (Exception e) { + System.out.println(e); + return; + } + } + trackingThreadDone = true; + } else { + while (!trackingThreadDone) { + holder = new Object(); + } + } + } +} -- GitLab From 9198976d8dec82b96568cab7a23ab65d1abeca7e Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Tue, 1 Mar 2016 17:40:55 -0800 Subject: [PATCH 089/204] Revert "Disable flaky 130-hprof test." Bug: 27337759 This reverts commit 2f0d3bc12f9ade80f4d3c21b7c4aad2cfe10f19e. --- test/Android.run-test.mk | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index d6a05ea3dd..167ad859d2 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -222,10 +222,8 @@ ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES), # Disable 097-duplicate-method while investigation (broken by latest Jack release, b/27358065) -# Disable 130-hprof, it has races (b/27337759) TEST_ART_BROKEN_ALL_TARGET_TESTS := \ - 097-duplicate-method \ - 130-hprof + 097-duplicate-method ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \ $(COMPILER_TYPES), $(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \ -- GitLab From fa8598dc6ef0358c20109faf58101425bbd80941 Mon Sep 17 00:00:00 2001 From: Dan Willemsen Date: Mon, 29 Feb 2016 21:09:08 -0800 Subject: [PATCH 090/204] Remove references to $(ACP) We're changing the implementation of these macros to not use acp. Instead of having to keep this in sync, move to using a standard prebuilt module. The build system has been updated to explicitly add the executable bit to prebuilt modules in the EXECUTABLES class. Change-Id: I29cf4d48619e022b7da65ef2df05a92bed08a456 --- tools/Android.mk | 15 ++++----------- tools/ahat/Android.mk | 10 ++-------- tools/dexfuzz/Android.mk | 8 ++------ 3 files changed, 8 insertions(+), 25 deletions(-) diff --git a/tools/Android.mk b/tools/Android.mk index 9a96f7a6e7..bc2fd8c53c 100644 --- a/tools/Android.mk +++ b/tools/Android.mk @@ -19,21 +19,14 @@ LOCAL_PATH := $(call my-dir) # Copy the art shell script to the host's bin directory include $(CLEAR_VARS) LOCAL_IS_HOST_MODULE := true -LOCAL_MODULE_TAGS := optional LOCAL_MODULE_CLASS := EXECUTABLES LOCAL_MODULE := art -include $(BUILD_SYSTEM)/base_rules.mk -$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/art $(ACP) - @echo "Copy: $(PRIVATE_MODULE) ($@)" - $(copy-file-to-new-target) - $(hide) chmod 755 $@ +LOCAL_SRC_FILES := art +include $(BUILD_PREBUILT) # Copy the art shell script to the target's bin directory include $(CLEAR_VARS) LOCAL_MODULE_CLASS := EXECUTABLES LOCAL_MODULE := art -include $(BUILD_SYSTEM)/base_rules.mk -$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/art $(ACP) - @echo "Copy: $(PRIVATE_MODULE) ($@)" - $(copy-file-to-new-target) - $(hide) chmod 755 $@ +LOCAL_SRC_FILES := art +include $(BUILD_PREBUILT) diff --git a/tools/ahat/Android.mk b/tools/ahat/Android.mk index 6869b04a0b..cfbafde52b 100644 --- a/tools/ahat/Android.mk +++ b/tools/ahat/Android.mk @@ -35,16 +35,10 @@ include $(BUILD_HOST_JAVA_LIBRARY) # --- ahat script ---------------- include $(CLEAR_VARS) LOCAL_IS_HOST_MODULE := true -LOCAL_MODULE_TAGS := optional LOCAL_MODULE_CLASS := EXECUTABLES LOCAL_MODULE := ahat -include $(BUILD_SYSTEM)/base_rules.mk -$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/ahat $(ACP) - @echo "Copy: $(PRIVATE_MODULE) ($@)" - $(copy-file-to-new-target) - $(hide) chmod 755 $@ - -ahat: $(LOCAL_BUILT_MODULE) +LOCAL_SRC_FILES := ahat +include $(BUILD_PREBUILT) # --- ahat-tests.jar -------------- include $(CLEAR_VARS) diff --git a/tools/dexfuzz/Android.mk b/tools/dexfuzz/Android.mk index 1580bc37fb..473f6de3e5 100644 --- a/tools/dexfuzz/Android.mk +++ b/tools/dexfuzz/Android.mk @@ -27,14 +27,10 @@ include $(BUILD_HOST_JAVA_LIBRARY) # --- dexfuzz script ---------------- include $(CLEAR_VARS) LOCAL_IS_HOST_MODULE := true -LOCAL_MODULE_TAGS := optional LOCAL_MODULE_CLASS := EXECUTABLES LOCAL_MODULE := dexfuzz -include $(BUILD_SYSTEM)/base_rules.mk -$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/dexfuzz $(ACP) - @echo "Copy: $(PRIVATE_MODULE) ($@)" - $(copy-file-to-new-target) - $(hide) chmod 755 $@ +LOCAL_SRC_FILES := dexfuzz +include $(BUILD_PREBUILT) # --- dexfuzz script with core image dependencies ---------------- fuzzer: $(LOCAL_BUILT_MODULE) $(HOST_CORE_IMG_OUTS) -- GitLab From c8705a7801338b85cf9a8f8908b9e92a3283b114 Mon Sep 17 00:00:00 2001 From: Serguei Katkov Date: Fri, 26 Feb 2016 13:00:40 +0600 Subject: [PATCH 091/204] ART: Enable JitProfiling for x86_64 Mterp Adds branch profiling and enables for x86_64. Support interpreter switching in x86_64 mterp. Change-Id: I0cb9fcf3e2a01e411d84efc78449e86c10e6bcac Signed-off-by: Serguei Katkov --- runtime/interpreter/mterp/mterp.cc | 3 +- runtime/interpreter/mterp/out/mterp_x86_64.S | 403 +++++++++--------- runtime/interpreter/mterp/x86_64/bincmp.S | 17 +- runtime/interpreter/mterp/x86_64/footer.S | 20 +- runtime/interpreter/mterp/x86_64/header.S | 20 +- runtime/interpreter/mterp/x86_64/invoke.S | 7 +- runtime/interpreter/mterp/x86_64/op_goto.S | 15 +- runtime/interpreter/mterp/x86_64/op_goto_16.S | 15 +- runtime/interpreter/mterp/x86_64/op_goto_32.S | 15 +- .../mterp/x86_64/op_packed_switch.S | 15 +- runtime/interpreter/mterp/x86_64/zcmp.S | 17 +- 11 files changed, 275 insertions(+), 272 deletions(-) diff --git a/runtime/interpreter/mterp/mterp.cc b/runtime/interpreter/mterp/mterp.cc index e1bde1ba01..ca727f47be 100644 --- a/runtime/interpreter/mterp/mterp.cc +++ b/runtime/interpreter/mterp/mterp.cc @@ -150,7 +150,8 @@ extern "C" bool MterpShouldSwitchInterpreters() bool unhandled_instrumentation; // TODO: enable for other targets after more extensive testing. if ((kRuntimeISA == kArm64) || (kRuntimeISA == kArm) || - (kRuntimeISA == kX86) || (kRuntimeISA == kMips)) { + (kRuntimeISA == kX86_64) || (kRuntimeISA == kX86) || + (kRuntimeISA == kMips)) { unhandled_instrumentation = instrumentation->NonJitProfilingActive(); } else { unhandled_instrumentation = instrumentation->IsActive(); diff --git a/runtime/interpreter/mterp/out/mterp_x86_64.S b/runtime/interpreter/mterp/out/mterp_x86_64.S index 53fa50fc23..a1360e0934 100644 --- a/runtime/interpreter/mterp/out/mterp_x86_64.S +++ b/runtime/interpreter/mterp/out/mterp_x86_64.S @@ -169,13 +169,23 @@ unspecified registers or condition codes. #define OFF_FP_CODE_ITEM OFF_FP(SHADOWFRAME_CODE_ITEM_OFFSET) #define OFF_FP_SHADOWFRAME (-SHADOWFRAME_VREGS_OFFSET) +#define MTERP_PROFILE_BRANCHES 1 +#define MTERP_LOGGING 0 + /* - * - * The reference interpreter performs explicit suspect checks, which is somewhat wasteful. - * Dalvik's interpreter folded suspend checks into the jump table mechanism, and eventually - * mterp should do so as well. + * Profile branch. rINST should contain the offset. %eax is scratch. */ -#define MTERP_SUSPEND 0 +.macro MTERP_PROFILE_BRANCH +#ifdef MTERP_PROFILE_BRANCHES + EXPORT_PC + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + movl rINST, OUT_32_ARG2 + call SYMBOL(MterpProfileBranch) + testb %al, %al + jnz MterpOnStackReplacement +#endif +.endm /* * "export" the PC to dex_pc field in the shadow frame, f/b/o future exception objects. Must @@ -992,17 +1002,12 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop * double to get a byte offset. */ /* goto +AA */ - movsbq rINSTbl, %rax # rax <- ssssssAA - addq %rax, %rax # rax <- AA * 2 - leaq (rPC, %rax), rPC + movsbq rINSTbl, rINSTq # rINSTq <- ssssssAA + MTERP_PROFILE_BRANCH + addq rINSTq, rINSTq # rINSTq <- AA * 2 + leaq (rPC, rINSTq), rPC FETCH_INST - jg 1f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -1: + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check GOTO_NEXT /* ------------------------------ */ @@ -1016,17 +1021,12 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop * double to get a byte offset. */ /* goto/16 +AAAA */ - movswq 2(rPC), %rax # rax <- ssssAAAA - addq %rax, %rax # rax <- AA * 2 - leaq (rPC, %rax), rPC + movswq 2(rPC), rINSTq # rINSTq <- ssssAAAA + MTERP_PROFILE_BRANCH + addq rINSTq, rINSTq # rINSTq <- AA * 2 + leaq (rPC, rINSTq), rPC FETCH_INST - jg 1f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -1: + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check GOTO_NEXT /* ------------------------------ */ @@ -1043,17 +1043,12 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop * to convert from Dalvik offset to byte offset. */ /* goto/32 +AAAAAAAA */ - movslq 2(rPC), %rax # rax <- AAAAAAAA - addq %rax, %rax # rax <- AA * 2 - leaq (rPC, %rax), rPC + movslq 2(rPC), rINSTq # rINSTq <- AAAAAAAA + MTERP_PROFILE_BRANCH + addq rINSTq, rINSTq # rINSTq <- AA * 2 + leaq (rPC, rINSTq), rPC FETCH_INST - jg 1f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -1: + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check GOTO_NEXT /* ------------------------------ */ @@ -1074,17 +1069,12 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop leaq (rPC,OUT_ARG0,2), OUT_ARG0 # rcx <- PC + BBBBbbbb*2 GET_VREG OUT_32_ARG1, rINSTq # eax <- vAA call SYMBOL(MterpDoPackedSwitch) - addl %eax, %eax - movslq %eax, %rax - leaq (rPC, %rax), rPC + movslq %eax, rINSTq + MTERP_PROFILE_BRANCH + addq rINSTq, rINSTq + leaq (rPC, rINSTq), rPC FETCH_INST - jg 1f -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -1: + jle MterpCheckSuspendAndContinue GOTO_NEXT /* ------------------------------ */ @@ -1106,17 +1096,12 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop leaq (rPC,OUT_ARG0,2), OUT_ARG0 # rcx <- PC + BBBBbbbb*2 GET_VREG OUT_32_ARG1, rINSTq # eax <- vAA call SYMBOL(MterpDoSparseSwitch) - addl %eax, %eax - movslq %eax, %rax - leaq (rPC, %rax), rPC + movslq %eax, rINSTq + MTERP_PROFILE_BRANCH + addq rINSTq, rINSTq + leaq (rPC, rINSTq), rPC FETCH_INST - jg 1f -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -1: + jle MterpCheckSuspendAndContinue GOTO_NEXT @@ -1324,20 +1309,15 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop andb $0xf, %cl # rcx <- A GET_VREG %eax, %rcx # eax <- vA cmpl VREG_ADDRESS(rINSTq), %eax # compare (vA, vB) - movl $2, %eax # assume not taken + movl $2, rINST # assume not taken jne 1f - movswq 2(rPC),%rax # Get signed branch offset + movswq 2(rPC), rINSTq # Get signed branch offset 1: - addq %rax, %rax # rax <- AA * 2 - leaq (rPC, %rax), rPC + MTERP_PROFILE_BRANCH + addq rINSTq, rINSTq # rax <- AA * 2 + leaq (rPC, rINSTq), rPC FETCH_INST - jg 2f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -2: + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check GOTO_NEXT @@ -1359,20 +1339,15 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop andb $0xf, %cl # rcx <- A GET_VREG %eax, %rcx # eax <- vA cmpl VREG_ADDRESS(rINSTq), %eax # compare (vA, vB) - movl $2, %eax # assume not taken + movl $2, rINST # assume not taken je 1f - movswq 2(rPC),%rax # Get signed branch offset + movswq 2(rPC), rINSTq # Get signed branch offset 1: - addq %rax, %rax # rax <- AA * 2 - leaq (rPC, %rax), rPC + MTERP_PROFILE_BRANCH + addq rINSTq, rINSTq # rax <- AA * 2 + leaq (rPC, rINSTq), rPC FETCH_INST - jg 2f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -2: + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check GOTO_NEXT @@ -1394,20 +1369,15 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop andb $0xf, %cl # rcx <- A GET_VREG %eax, %rcx # eax <- vA cmpl VREG_ADDRESS(rINSTq), %eax # compare (vA, vB) - movl $2, %eax # assume not taken + movl $2, rINST # assume not taken jge 1f - movswq 2(rPC),%rax # Get signed branch offset + movswq 2(rPC), rINSTq # Get signed branch offset 1: - addq %rax, %rax # rax <- AA * 2 - leaq (rPC, %rax), rPC + MTERP_PROFILE_BRANCH + addq rINSTq, rINSTq # rax <- AA * 2 + leaq (rPC, rINSTq), rPC FETCH_INST - jg 2f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -2: + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check GOTO_NEXT @@ -1429,20 +1399,15 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop andb $0xf, %cl # rcx <- A GET_VREG %eax, %rcx # eax <- vA cmpl VREG_ADDRESS(rINSTq), %eax # compare (vA, vB) - movl $2, %eax # assume not taken + movl $2, rINST # assume not taken jl 1f - movswq 2(rPC),%rax # Get signed branch offset + movswq 2(rPC), rINSTq # Get signed branch offset 1: - addq %rax, %rax # rax <- AA * 2 - leaq (rPC, %rax), rPC + MTERP_PROFILE_BRANCH + addq rINSTq, rINSTq # rax <- AA * 2 + leaq (rPC, rINSTq), rPC FETCH_INST - jg 2f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -2: + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check GOTO_NEXT @@ -1464,20 +1429,15 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop andb $0xf, %cl # rcx <- A GET_VREG %eax, %rcx # eax <- vA cmpl VREG_ADDRESS(rINSTq), %eax # compare (vA, vB) - movl $2, %eax # assume not taken + movl $2, rINST # assume not taken jle 1f - movswq 2(rPC),%rax # Get signed branch offset + movswq 2(rPC), rINSTq # Get signed branch offset 1: - addq %rax, %rax # rax <- AA * 2 - leaq (rPC, %rax), rPC + MTERP_PROFILE_BRANCH + addq rINSTq, rINSTq # rax <- AA * 2 + leaq (rPC, rINSTq), rPC FETCH_INST - jg 2f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -2: + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check GOTO_NEXT @@ -1499,20 +1459,15 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop andb $0xf, %cl # rcx <- A GET_VREG %eax, %rcx # eax <- vA cmpl VREG_ADDRESS(rINSTq), %eax # compare (vA, vB) - movl $2, %eax # assume not taken + movl $2, rINST # assume not taken jg 1f - movswq 2(rPC),%rax # Get signed branch offset + movswq 2(rPC), rINSTq # Get signed branch offset 1: - addq %rax, %rax # rax <- AA * 2 - leaq (rPC, %rax), rPC + MTERP_PROFILE_BRANCH + addq rINSTq, rINSTq # rax <- AA * 2 + leaq (rPC, rINSTq), rPC FETCH_INST - jg 2f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -2: + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check GOTO_NEXT @@ -1530,20 +1485,15 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop */ /* if-cmp vAA, +BBBB */ cmpl $0, VREG_ADDRESS(rINSTq) # compare (vA, 0) - movl $2, %eax # assume branch not taken + movl $2, rINST # assume branch not taken jne 1f - movswq 2(rPC),%rax # fetch signed displacement + movswq 2(rPC), rINSTq # fetch signed displacement 1: - addq %rax, %rax # eax <- AA * 2 - leaq (rPC, %rax), rPC + MTERP_PROFILE_BRANCH + addq rINSTq, rINSTq # rINSTq <- AA * 2 + leaq (rPC, rINSTq), rPC FETCH_INST - jg 2f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -2: + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check GOTO_NEXT @@ -1561,20 +1511,15 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop */ /* if-cmp vAA, +BBBB */ cmpl $0, VREG_ADDRESS(rINSTq) # compare (vA, 0) - movl $2, %eax # assume branch not taken + movl $2, rINST # assume branch not taken je 1f - movswq 2(rPC),%rax # fetch signed displacement + movswq 2(rPC), rINSTq # fetch signed displacement 1: - addq %rax, %rax # eax <- AA * 2 - leaq (rPC, %rax), rPC + MTERP_PROFILE_BRANCH + addq rINSTq, rINSTq # rINSTq <- AA * 2 + leaq (rPC, rINSTq), rPC FETCH_INST - jg 2f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -2: + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check GOTO_NEXT @@ -1592,20 +1537,15 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop */ /* if-cmp vAA, +BBBB */ cmpl $0, VREG_ADDRESS(rINSTq) # compare (vA, 0) - movl $2, %eax # assume branch not taken + movl $2, rINST # assume branch not taken jge 1f - movswq 2(rPC),%rax # fetch signed displacement + movswq 2(rPC), rINSTq # fetch signed displacement 1: - addq %rax, %rax # eax <- AA * 2 - leaq (rPC, %rax), rPC + MTERP_PROFILE_BRANCH + addq rINSTq, rINSTq # rINSTq <- AA * 2 + leaq (rPC, rINSTq), rPC FETCH_INST - jg 2f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -2: + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check GOTO_NEXT @@ -1623,20 +1563,15 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop */ /* if-cmp vAA, +BBBB */ cmpl $0, VREG_ADDRESS(rINSTq) # compare (vA, 0) - movl $2, %eax # assume branch not taken + movl $2, rINST # assume branch not taken jl 1f - movswq 2(rPC),%rax # fetch signed displacement + movswq 2(rPC), rINSTq # fetch signed displacement 1: - addq %rax, %rax # eax <- AA * 2 - leaq (rPC, %rax), rPC + MTERP_PROFILE_BRANCH + addq rINSTq, rINSTq # rINSTq <- AA * 2 + leaq (rPC, rINSTq), rPC FETCH_INST - jg 2f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -2: + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check GOTO_NEXT @@ -1654,20 +1589,15 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop */ /* if-cmp vAA, +BBBB */ cmpl $0, VREG_ADDRESS(rINSTq) # compare (vA, 0) - movl $2, %eax # assume branch not taken + movl $2, rINST # assume branch not taken jle 1f - movswq 2(rPC),%rax # fetch signed displacement + movswq 2(rPC), rINSTq # fetch signed displacement 1: - addq %rax, %rax # eax <- AA * 2 - leaq (rPC, %rax), rPC + MTERP_PROFILE_BRANCH + addq rINSTq, rINSTq # rINSTq <- AA * 2 + leaq (rPC, rINSTq), rPC FETCH_INST - jg 2f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -2: + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check GOTO_NEXT @@ -1685,20 +1615,15 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop */ /* if-cmp vAA, +BBBB */ cmpl $0, VREG_ADDRESS(rINSTq) # compare (vA, 0) - movl $2, %eax # assume branch not taken + movl $2, rINST # assume branch not taken jg 1f - movswq 2(rPC),%rax # fetch signed displacement + movswq 2(rPC), rINSTq # fetch signed displacement 1: - addq %rax, %rax # eax <- AA * 2 - leaq (rPC, %rax), rPC + MTERP_PROFILE_BRANCH + addq rINSTq, rINSTq # rINSTq <- AA * 2 + leaq (rPC, rINSTq), rPC FETCH_INST - jg 2f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -2: + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check GOTO_NEXT @@ -2931,7 +2856,12 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop call SYMBOL(MterpInvokeVirtual) testb %al, %al jz MterpException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + ADVANCE_PC 3 + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback + FETCH_INST + GOTO_NEXT /* * Handle a virtual method call. @@ -2961,7 +2891,12 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop call SYMBOL(MterpInvokeSuper) testb %al, %al jz MterpException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + ADVANCE_PC 3 + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback + FETCH_INST + GOTO_NEXT /* * Handle a "super" method call. @@ -2991,7 +2926,12 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop call SYMBOL(MterpInvokeDirect) testb %al, %al jz MterpException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + ADVANCE_PC 3 + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback + FETCH_INST + GOTO_NEXT /* ------------------------------ */ @@ -3014,7 +2954,12 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop call SYMBOL(MterpInvokeStatic) testb %al, %al jz MterpException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + ADVANCE_PC 3 + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback + FETCH_INST + GOTO_NEXT @@ -3038,7 +2983,12 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop call SYMBOL(MterpInvokeInterface) testb %al, %al jz MterpException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + ADVANCE_PC 3 + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback + FETCH_INST + GOTO_NEXT /* * Handle an interface method call. @@ -3080,7 +3030,12 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop call SYMBOL(MterpInvokeVirtualRange) testb %al, %al jz MterpException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + ADVANCE_PC 3 + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback + FETCH_INST + GOTO_NEXT /* ------------------------------ */ @@ -3103,7 +3058,12 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop call SYMBOL(MterpInvokeSuperRange) testb %al, %al jz MterpException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + ADVANCE_PC 3 + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback + FETCH_INST + GOTO_NEXT /* ------------------------------ */ @@ -3126,7 +3086,12 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop call SYMBOL(MterpInvokeDirectRange) testb %al, %al jz MterpException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + ADVANCE_PC 3 + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback + FETCH_INST + GOTO_NEXT /* ------------------------------ */ @@ -3149,7 +3114,12 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop call SYMBOL(MterpInvokeStaticRange) testb %al, %al jz MterpException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + ADVANCE_PC 3 + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback + FETCH_INST + GOTO_NEXT /* ------------------------------ */ @@ -3172,7 +3142,12 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop call SYMBOL(MterpInvokeInterfaceRange) testb %al, %al jz MterpException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + ADVANCE_PC 3 + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback + FETCH_INST + GOTO_NEXT /* ------------------------------ */ @@ -5811,7 +5786,12 @@ movswl %ax, %eax call SYMBOL(MterpInvokeVirtualQuick) testb %al, %al jz MterpException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + ADVANCE_PC 3 + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback + FETCH_INST + GOTO_NEXT /* ------------------------------ */ @@ -5834,7 +5814,12 @@ movswl %ax, %eax call SYMBOL(MterpInvokeVirtualQuickRange) testb %al, %al jz MterpException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + ADVANCE_PC 3 + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback + FETCH_INST + GOTO_NEXT /* ------------------------------ */ @@ -11805,7 +11790,6 @@ SYMBOL(artMterpAsmAltInstructionEnd): * has not yet been thrown. Just bail out to the reference interpreter to deal with it. * TUNING: for consistency, we may want to just go ahead and handle these here. */ -#define MTERP_LOGGING 0 common_errDivideByZero: EXPORT_PC #if MTERP_LOGGING @@ -11891,13 +11875,17 @@ MterpException: call SYMBOL(MterpHandleException) testb %al, %al jz MterpExceptionReturn - REFRESH_IBASE movq OFF_FP_CODE_ITEM(rFP), %rax mov OFF_FP_DEX_PC(rFP), %ecx leaq CODEITEM_INSNS_OFFSET(%rax), rPC leaq (rPC, %rcx, 2), rPC movq rPC, OFF_FP_DEX_PC_PTR(rFP) + /* Do we need to switch interpreters? */ + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback /* resume execution at catch block */ + REFRESH_IBASE FETCH_INST GOTO_NEXT /* NOTE: no fallthrough */ @@ -11916,6 +11904,19 @@ MterpCheckSuspendAndContinue: 1: GOTO_NEXT +/* + * On-stack replacement has happened, and now we've returned from the compiled method. + */ +MterpOnStackReplacement: +#if MTERP_LOGGING + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + movl rINST, OUT_32_ARG2 + call SYMBOL(MterpLogOSR) +#endif + movl $1, %eax + jmp MterpDone + /* * Bail out to reference interpreter. */ diff --git a/runtime/interpreter/mterp/x86_64/bincmp.S b/runtime/interpreter/mterp/x86_64/bincmp.S index 5e4225fd86..a16050b371 100644 --- a/runtime/interpreter/mterp/x86_64/bincmp.S +++ b/runtime/interpreter/mterp/x86_64/bincmp.S @@ -11,18 +11,13 @@ andb $$0xf, %cl # rcx <- A GET_VREG %eax, %rcx # eax <- vA cmpl VREG_ADDRESS(rINSTq), %eax # compare (vA, vB) - movl $$2, %eax # assume not taken + movl $$2, rINST # assume not taken j${revcmp} 1f - movswq 2(rPC),%rax # Get signed branch offset + movswq 2(rPC), rINSTq # Get signed branch offset 1: - addq %rax, %rax # rax <- AA * 2 - leaq (rPC, %rax), rPC + MTERP_PROFILE_BRANCH + addq rINSTq, rINSTq # rax <- AA * 2 + leaq (rPC, rINSTq), rPC FETCH_INST - jg 2f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -2: + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check GOTO_NEXT diff --git a/runtime/interpreter/mterp/x86_64/footer.S b/runtime/interpreter/mterp/x86_64/footer.S index cb60c0155e..573256b781 100644 --- a/runtime/interpreter/mterp/x86_64/footer.S +++ b/runtime/interpreter/mterp/x86_64/footer.S @@ -12,7 +12,6 @@ * has not yet been thrown. Just bail out to the reference interpreter to deal with it. * TUNING: for consistency, we may want to just go ahead and handle these here. */ -#define MTERP_LOGGING 0 common_errDivideByZero: EXPORT_PC #if MTERP_LOGGING @@ -98,13 +97,17 @@ MterpException: call SYMBOL(MterpHandleException) testb %al, %al jz MterpExceptionReturn - REFRESH_IBASE movq OFF_FP_CODE_ITEM(rFP), %rax mov OFF_FP_DEX_PC(rFP), %ecx leaq CODEITEM_INSNS_OFFSET(%rax), rPC leaq (rPC, %rcx, 2), rPC movq rPC, OFF_FP_DEX_PC_PTR(rFP) + /* Do we need to switch interpreters? */ + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback /* resume execution at catch block */ + REFRESH_IBASE FETCH_INST GOTO_NEXT /* NOTE: no fallthrough */ @@ -123,6 +126,19 @@ MterpCheckSuspendAndContinue: 1: GOTO_NEXT +/* + * On-stack replacement has happened, and now we've returned from the compiled method. + */ +MterpOnStackReplacement: +#if MTERP_LOGGING + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + movl rINST, OUT_32_ARG2 + call SYMBOL(MterpLogOSR) +#endif + movl $$1, %eax + jmp MterpDone + /* * Bail out to reference interpreter. */ diff --git a/runtime/interpreter/mterp/x86_64/header.S b/runtime/interpreter/mterp/x86_64/header.S index dfc7b53de8..eb84ea1eb5 100644 --- a/runtime/interpreter/mterp/x86_64/header.S +++ b/runtime/interpreter/mterp/x86_64/header.S @@ -162,13 +162,23 @@ unspecified registers or condition codes. #define OFF_FP_CODE_ITEM OFF_FP(SHADOWFRAME_CODE_ITEM_OFFSET) #define OFF_FP_SHADOWFRAME (-SHADOWFRAME_VREGS_OFFSET) +#define MTERP_PROFILE_BRANCHES 1 +#define MTERP_LOGGING 0 + /* - * - * The reference interpreter performs explicit suspect checks, which is somewhat wasteful. - * Dalvik's interpreter folded suspend checks into the jump table mechanism, and eventually - * mterp should do so as well. + * Profile branch. rINST should contain the offset. %eax is scratch. */ -#define MTERP_SUSPEND 0 +.macro MTERP_PROFILE_BRANCH +#ifdef MTERP_PROFILE_BRANCHES + EXPORT_PC + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + movl rINST, OUT_32_ARG2 + call SYMBOL(MterpProfileBranch) + testb %al, %al + jnz MterpOnStackReplacement +#endif +.endm /* * "export" the PC to dex_pc field in the shadow frame, f/b/o future exception objects. Must diff --git a/runtime/interpreter/mterp/x86_64/invoke.S b/runtime/interpreter/mterp/x86_64/invoke.S index 86eccdbf91..f7e6155c16 100644 --- a/runtime/interpreter/mterp/x86_64/invoke.S +++ b/runtime/interpreter/mterp/x86_64/invoke.S @@ -14,4 +14,9 @@ call SYMBOL($helper) testb %al, %al jz MterpException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + ADVANCE_PC 3 + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback + FETCH_INST + GOTO_NEXT diff --git a/runtime/interpreter/mterp/x86_64/op_goto.S b/runtime/interpreter/mterp/x86_64/op_goto.S index 05a2dda1c0..c4fc97644f 100644 --- a/runtime/interpreter/mterp/x86_64/op_goto.S +++ b/runtime/interpreter/mterp/x86_64/op_goto.S @@ -5,15 +5,10 @@ * double to get a byte offset. */ /* goto +AA */ - movsbq rINSTbl, %rax # rax <- ssssssAA - addq %rax, %rax # rax <- AA * 2 - leaq (rPC, %rax), rPC + movsbq rINSTbl, rINSTq # rINSTq <- ssssssAA + MTERP_PROFILE_BRANCH + addq rINSTq, rINSTq # rINSTq <- AA * 2 + leaq (rPC, rINSTq), rPC FETCH_INST - jg 1f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -1: + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check GOTO_NEXT diff --git a/runtime/interpreter/mterp/x86_64/op_goto_16.S b/runtime/interpreter/mterp/x86_64/op_goto_16.S index 029749c50a..8cb9a5c50f 100644 --- a/runtime/interpreter/mterp/x86_64/op_goto_16.S +++ b/runtime/interpreter/mterp/x86_64/op_goto_16.S @@ -5,15 +5,10 @@ * double to get a byte offset. */ /* goto/16 +AAAA */ - movswq 2(rPC), %rax # rax <- ssssAAAA - addq %rax, %rax # rax <- AA * 2 - leaq (rPC, %rax), rPC + movswq 2(rPC), rINSTq # rINSTq <- ssssAAAA + MTERP_PROFILE_BRANCH + addq rINSTq, rINSTq # rINSTq <- AA * 2 + leaq (rPC, rINSTq), rPC FETCH_INST - jg 1f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -1: + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check GOTO_NEXT diff --git a/runtime/interpreter/mterp/x86_64/op_goto_32.S b/runtime/interpreter/mterp/x86_64/op_goto_32.S index 28233108e5..4ecdacd3e6 100644 --- a/runtime/interpreter/mterp/x86_64/op_goto_32.S +++ b/runtime/interpreter/mterp/x86_64/op_goto_32.S @@ -8,15 +8,10 @@ * to convert from Dalvik offset to byte offset. */ /* goto/32 +AAAAAAAA */ - movslq 2(rPC), %rax # rax <- AAAAAAAA - addq %rax, %rax # rax <- AA * 2 - leaq (rPC, %rax), rPC + movslq 2(rPC), rINSTq # rINSTq <- AAAAAAAA + MTERP_PROFILE_BRANCH + addq rINSTq, rINSTq # rINSTq <- AA * 2 + leaq (rPC, rINSTq), rPC FETCH_INST - jg 1f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -1: + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check GOTO_NEXT diff --git a/runtime/interpreter/mterp/x86_64/op_packed_switch.S b/runtime/interpreter/mterp/x86_64/op_packed_switch.S index 0400ca45cf..cb0acb7a72 100644 --- a/runtime/interpreter/mterp/x86_64/op_packed_switch.S +++ b/runtime/interpreter/mterp/x86_64/op_packed_switch.S @@ -13,15 +13,10 @@ leaq (rPC,OUT_ARG0,2), OUT_ARG0 # rcx <- PC + BBBBbbbb*2 GET_VREG OUT_32_ARG1, rINSTq # eax <- vAA call SYMBOL($func) - addl %eax, %eax - movslq %eax, %rax - leaq (rPC, %rax), rPC + movslq %eax, rINSTq + MTERP_PROFILE_BRANCH + addq rINSTq, rINSTq + leaq (rPC, rINSTq), rPC FETCH_INST - jg 1f -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -1: + jle MterpCheckSuspendAndContinue GOTO_NEXT diff --git a/runtime/interpreter/mterp/x86_64/zcmp.S b/runtime/interpreter/mterp/x86_64/zcmp.S index e503ec1f38..0051407cad 100644 --- a/runtime/interpreter/mterp/x86_64/zcmp.S +++ b/runtime/interpreter/mterp/x86_64/zcmp.S @@ -7,18 +7,13 @@ */ /* if-cmp vAA, +BBBB */ cmpl $$0, VREG_ADDRESS(rINSTq) # compare (vA, 0) - movl $$2, %eax # assume branch not taken + movl $$2, rINST # assume branch not taken j${revcmp} 1f - movswq 2(rPC),%rax # fetch signed displacement + movswq 2(rPC), rINSTq # fetch signed displacement 1: - addq %rax, %rax # eax <- AA * 2 - leaq (rPC, %rax), rPC + MTERP_PROFILE_BRANCH + addq rINSTq, rINSTq # rINSTq <- AA * 2 + leaq (rPC, rINSTq), rPC FETCH_INST - jg 2f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -2: + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check GOTO_NEXT -- GitLab From 18047e4cd06387958d315d4de6d9c1753a08ee9e Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Wed, 2 Mar 2016 08:30:44 +0000 Subject: [PATCH 092/204] Disable test after libunwind change. Change that broke it: https://android-review.googlesource.com/205606 bug:27391690 Change-Id: I73c545619a6a1bb03e49120466ac91686363230f --- test/Android.run-test.mk | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index 167ad859d2..7406aa33ed 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -222,8 +222,10 @@ ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES), # Disable 097-duplicate-method while investigation (broken by latest Jack release, b/27358065) +# Disable 137-cfi (b/27391690). TEST_ART_BROKEN_ALL_TARGET_TESTS := \ - 097-duplicate-method + 097-duplicate-method \ + 137-cfi ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \ $(COMPILER_TYPES), $(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \ -- GitLab From 0c659394d1ee74d28157c42656b31ea48cb54f8b Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Wed, 2 Mar 2016 10:27:01 +0000 Subject: [PATCH 093/204] Revert "ART: Allow unwinding unattached threads" This reverts commit f7286721a51c772b2abb42fef5bc0d2548df2372. Change-Id: I6c96ece063041e94d40a96bdd054f89cd7875ade --- runtime/thread_list.cc | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc index d45e4bdcfe..49d54fda00 100644 --- a/runtime/thread_list.cc +++ b/runtime/thread_list.cc @@ -59,10 +59,6 @@ static constexpr useconds_t kThreadSuspendInitialSleepUs = 0; static constexpr useconds_t kThreadSuspendMaxYieldUs = 3000; static constexpr useconds_t kThreadSuspendMaxSleepUs = 5000; -// Whether we should try to dump the native stack of unattached threads. See commit ed8b723 for -// some history. -static constexpr bool kDumpUnattachedThreadNativeStack = true; - ThreadList::ThreadList() : suspend_all_count_(0), debug_suspend_all_count_(0), @@ -153,7 +149,9 @@ static void DumpUnattachedThread(std::ostream& os, pid_t tid) NO_THREAD_SAFETY_A // refactor DumpState to avoid skipping analysis. Thread::DumpState(os, nullptr, tid); DumpKernelStack(os, tid, " kernel: ", false); - if (kDumpUnattachedThreadNativeStack) { + // TODO: Reenable this when the native code in system_server can handle it. + // Currently "adb shell kill -3 `pid system_server`" will cause it to exit. + if (false) { DumpNativeStack(os, tid, nullptr, " native: "); } os << "\n"; -- GitLab From f02c3cf66c2c24533f6da43970e7b766b2ca9938 Mon Sep 17 00:00:00 2001 From: David Brazdil Date: Mon, 29 Feb 2016 09:14:51 +0000 Subject: [PATCH 094/204] ART: Switch Checker run-tests to Jack Bug: 25635944 Change-Id: Ic6774028025b51f54589e9625003e8f69cf39dea --- .../src/Main.java | 479 +++++++++--------- test/450-checker-types/smali/SmaliTests.smali | 120 +++++ test/450-checker-types/src/Main.java | 52 -- .../smali/SmaliTests.smali | 193 +++++++ .../src/Main.java | 256 ++-------- .../src/Main.java | 18 +- .../src/Main.java | 6 +- test/555-checker-regression-x86const/build | 10 +- .../src/Main.java | 31 +- test/Android.run-test.mk | 5 +- test/run-test | 6 +- 11 files changed, 640 insertions(+), 536 deletions(-) create mode 100644 test/450-checker-types/smali/SmaliTests.smali create mode 100644 test/458-checker-instruction-simplification/smali/SmaliTests.smali diff --git a/test/442-checker-constant-folding/src/Main.java b/test/442-checker-constant-folding/src/Main.java index 93fe397273..b7712a701f 100644 --- a/test/442-checker-constant-folding/src/Main.java +++ b/test/442-checker-constant-folding/src/Main.java @@ -51,6 +51,21 @@ public class Main { } } + private static int $inline$int(int x) { + return x; + } + + private static long $inline$long(long x) { + return x; + } + + private static float $inline$float(float x) { + return x; + } + + private static double $inline$double(double x) { + return x; + } // Wrappers around methods located in file TestCmp.smali. @@ -194,121 +209,119 @@ public class Main { return y; } - /** * Exercise constant folding on addition. */ - /// CHECK-START: int Main.IntAddition1() constant_folding (before) + /// CHECK-START: int Main.IntAddition1() constant_folding_after_inlining (before) /// CHECK-DAG: <> IntConstant 1 /// CHECK-DAG: <> IntConstant 2 /// CHECK-DAG: <> Add [<>,<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: int Main.IntAddition1() constant_folding (after) + /// CHECK-START: int Main.IntAddition1() constant_folding_after_inlining (after) /// CHECK-DAG: <> IntConstant 3 /// CHECK-DAG: Return [<>] - /// CHECK-START: int Main.IntAddition1() constant_folding (after) + /// CHECK-START: int Main.IntAddition1() constant_folding_after_inlining (after) /// CHECK-NOT: Add public static int IntAddition1() { int a, b, c; - a = 1; - b = 2; + a = $inline$int(1); + b = $inline$int(2); c = a + b; return c; } - /// CHECK-START: int Main.IntAddition2() constant_folding (before) + /// CHECK-START: int Main.IntAddition2() constant_folding_after_inlining (before) /// CHECK-DAG: <> IntConstant 1 /// CHECK-DAG: <> IntConstant 2 /// CHECK-DAG: <> IntConstant 5 /// CHECK-DAG: <> IntConstant 6 - /// CHECK-DAG: <> IntConstant 11 /// CHECK-DAG: <> Add [<>,<>] - /// CHECK-DAG: Add [<>,<>] - /// CHECK-DAG: <> Add [<>,<>] + /// CHECK-DAG: <> Add [<>,<>] + /// CHECK-DAG: <> Add [<>,<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: int Main.IntAddition2() constant_folding (after) + /// CHECK-START: int Main.IntAddition2() constant_folding_after_inlining (after) /// CHECK-DAG: <> IntConstant 14 /// CHECK-DAG: Return [<>] - /// CHECK-START: int Main.IntAddition2() constant_folding (after) + /// CHECK-START: int Main.IntAddition2() constant_folding_after_inlining (after) /// CHECK-NOT: Add public static int IntAddition2() { int a, b, c; - a = 1; - b = 2; + a = $inline$int(1); + b = $inline$int(2); a += b; - b = 5; - c = 6; + b = $inline$int(5); + c = $inline$int(6); b += c; c = a + b; return c; } - /// CHECK-START: long Main.LongAddition() constant_folding (before) + /// CHECK-START: long Main.LongAddition() constant_folding_after_inlining (before) /// CHECK-DAG: <> LongConstant 1 /// CHECK-DAG: <> LongConstant 2 /// CHECK-DAG: <> Add [<>,<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: long Main.LongAddition() constant_folding (after) + /// CHECK-START: long Main.LongAddition() constant_folding_after_inlining (after) /// CHECK-DAG: <> LongConstant 3 /// CHECK-DAG: Return [<>] - /// CHECK-START: long Main.LongAddition() constant_folding (after) + /// CHECK-START: long Main.LongAddition() constant_folding_after_inlining (after) /// CHECK-NOT: Add public static long LongAddition() { long a, b, c; - a = 1L; - b = 2L; + a = $inline$long(1L); + b = $inline$long(2L); c = a + b; return c; } - /// CHECK-START: float Main.FloatAddition() constant_folding (before) + /// CHECK-START: float Main.FloatAddition() constant_folding_after_inlining (before) /// CHECK-DAG: <> FloatConstant 1 /// CHECK-DAG: <> FloatConstant 2 /// CHECK-DAG: <> Add [<>,<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: float Main.FloatAddition() constant_folding (after) + /// CHECK-START: float Main.FloatAddition() constant_folding_after_inlining (after) /// CHECK-DAG: <> FloatConstant 3 /// CHECK-DAG: Return [<>] - /// CHECK-START: float Main.FloatAddition() constant_folding (after) + /// CHECK-START: float Main.FloatAddition() constant_folding_after_inlining (after) /// CHECK-NOT: Add public static float FloatAddition() { float a, b, c; - a = 1F; - b = 2F; + a = $inline$float(1F); + b = $inline$float(2F); c = a + b; return c; } - /// CHECK-START: double Main.DoubleAddition() constant_folding (before) + /// CHECK-START: double Main.DoubleAddition() constant_folding_after_inlining (before) /// CHECK-DAG: <> DoubleConstant 1 /// CHECK-DAG: <> DoubleConstant 2 /// CHECK-DAG: <> Add [<>,<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: double Main.DoubleAddition() constant_folding (after) + /// CHECK-START: double Main.DoubleAddition() constant_folding_after_inlining (after) /// CHECK-DAG: <> DoubleConstant 3 /// CHECK-DAG: Return [<>] - /// CHECK-START: double Main.DoubleAddition() constant_folding (after) + /// CHECK-START: double Main.DoubleAddition() constant_folding_after_inlining (after) /// CHECK-NOT: Add public static double DoubleAddition() { double a, b, c; - a = 1D; - b = 2D; + a = $inline$double(1D); + b = $inline$double(2D); c = a + b; return c; } @@ -318,86 +331,86 @@ public class Main { * Exercise constant folding on subtraction. */ - /// CHECK-START: int Main.IntSubtraction() constant_folding (before) + /// CHECK-START: int Main.IntSubtraction() constant_folding_after_inlining (before) /// CHECK-DAG: <> IntConstant 6 /// CHECK-DAG: <> IntConstant 2 /// CHECK-DAG: <> Sub [<>,<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: int Main.IntSubtraction() constant_folding (after) + /// CHECK-START: int Main.IntSubtraction() constant_folding_after_inlining (after) /// CHECK-DAG: <> IntConstant 4 /// CHECK-DAG: Return [<>] - /// CHECK-START: int Main.IntSubtraction() constant_folding (after) + /// CHECK-START: int Main.IntSubtraction() constant_folding_after_inlining (after) /// CHECK-NOT: Sub public static int IntSubtraction() { int a, b, c; - a = 6; - b = 2; + a = $inline$int(6); + b = $inline$int(2); c = a - b; return c; } - /// CHECK-START: long Main.LongSubtraction() constant_folding (before) + /// CHECK-START: long Main.LongSubtraction() constant_folding_after_inlining (before) /// CHECK-DAG: <> LongConstant 6 /// CHECK-DAG: <> LongConstant 2 /// CHECK-DAG: <> Sub [<>,<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: long Main.LongSubtraction() constant_folding (after) + /// CHECK-START: long Main.LongSubtraction() constant_folding_after_inlining (after) /// CHECK-DAG: <> LongConstant 4 /// CHECK-DAG: Return [<>] - /// CHECK-START: long Main.LongSubtraction() constant_folding (after) + /// CHECK-START: long Main.LongSubtraction() constant_folding_after_inlining (after) /// CHECK-NOT: Sub public static long LongSubtraction() { long a, b, c; - a = 6L; - b = 2L; + a = $inline$long(6L); + b = $inline$long(2L); c = a - b; return c; } - /// CHECK-START: float Main.FloatSubtraction() constant_folding (before) + /// CHECK-START: float Main.FloatSubtraction() constant_folding_after_inlining (before) /// CHECK-DAG: <> FloatConstant 6 /// CHECK-DAG: <> FloatConstant 2 /// CHECK-DAG: <> Sub [<>,<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: float Main.FloatSubtraction() constant_folding (after) + /// CHECK-START: float Main.FloatSubtraction() constant_folding_after_inlining (after) /// CHECK-DAG: <> FloatConstant 4 /// CHECK-DAG: Return [<>] - /// CHECK-START: float Main.FloatSubtraction() constant_folding (after) + /// CHECK-START: float Main.FloatSubtraction() constant_folding_after_inlining (after) /// CHECK-NOT: Sub public static float FloatSubtraction() { float a, b, c; - a = 6F; - b = 2F; + a = $inline$float(6F); + b = $inline$float(2F); c = a - b; return c; } - /// CHECK-START: double Main.DoubleSubtraction() constant_folding (before) + /// CHECK-START: double Main.DoubleSubtraction() constant_folding_after_inlining (before) /// CHECK-DAG: <> DoubleConstant 6 /// CHECK-DAG: <> DoubleConstant 2 /// CHECK-DAG: <> Sub [<>,<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: double Main.DoubleSubtraction() constant_folding (after) + /// CHECK-START: double Main.DoubleSubtraction() constant_folding_after_inlining (after) /// CHECK-DAG: <> DoubleConstant 4 /// CHECK-DAG: Return [<>] - /// CHECK-START: double Main.DoubleSubtraction() constant_folding (after) + /// CHECK-START: double Main.DoubleSubtraction() constant_folding_after_inlining (after) /// CHECK-NOT: Sub public static double DoubleSubtraction() { double a, b, c; - a = 6D; - b = 2D; + a = $inline$double(6D); + b = $inline$double(2D); c = a - b; return c; } @@ -407,86 +420,86 @@ public class Main { * Exercise constant folding on multiplication. */ - /// CHECK-START: int Main.IntMultiplication() constant_folding (before) + /// CHECK-START: int Main.IntMultiplication() constant_folding_after_inlining (before) /// CHECK-DAG: <> IntConstant 7 /// CHECK-DAG: <> IntConstant 3 /// CHECK-DAG: <> Mul [<>,<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: int Main.IntMultiplication() constant_folding (after) + /// CHECK-START: int Main.IntMultiplication() constant_folding_after_inlining (after) /// CHECK-DAG: <> IntConstant 21 /// CHECK-DAG: Return [<>] - /// CHECK-START: int Main.IntMultiplication() constant_folding (after) + /// CHECK-START: int Main.IntMultiplication() constant_folding_after_inlining (after) /// CHECK-NOT: Mul public static int IntMultiplication() { int a, b, c; - a = 7; - b = 3; + a = $inline$int(7); + b = $inline$int(3); c = a * b; return c; } - /// CHECK-START: long Main.LongMultiplication() constant_folding (before) + /// CHECK-START: long Main.LongMultiplication() constant_folding_after_inlining (before) /// CHECK-DAG: <> LongConstant 7 /// CHECK-DAG: <> LongConstant 3 /// CHECK-DAG: <> Mul [<>,<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: long Main.LongMultiplication() constant_folding (after) + /// CHECK-START: long Main.LongMultiplication() constant_folding_after_inlining (after) /// CHECK-DAG: <> LongConstant 21 /// CHECK-DAG: Return [<>] - /// CHECK-START: long Main.LongMultiplication() constant_folding (after) + /// CHECK-START: long Main.LongMultiplication() constant_folding_after_inlining (after) /// CHECK-NOT: Mul public static long LongMultiplication() { long a, b, c; - a = 7L; - b = 3L; + a = $inline$long(7L); + b = $inline$long(3L); c = a * b; return c; } - /// CHECK-START: float Main.FloatMultiplication() constant_folding (before) + /// CHECK-START: float Main.FloatMultiplication() constant_folding_after_inlining (before) /// CHECK-DAG: <> FloatConstant 7 /// CHECK-DAG: <> FloatConstant 3 /// CHECK-DAG: <> Mul [<>,<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: float Main.FloatMultiplication() constant_folding (after) + /// CHECK-START: float Main.FloatMultiplication() constant_folding_after_inlining (after) /// CHECK-DAG: <> FloatConstant 21 /// CHECK-DAG: Return [<>] - /// CHECK-START: float Main.FloatMultiplication() constant_folding (after) + /// CHECK-START: float Main.FloatMultiplication() constant_folding_after_inlining (after) /// CHECK-NOT: Mul public static float FloatMultiplication() { float a, b, c; - a = 7F; - b = 3F; + a = $inline$float(7F); + b = $inline$float(3F); c = a * b; return c; } - /// CHECK-START: double Main.DoubleMultiplication() constant_folding (before) + /// CHECK-START: double Main.DoubleMultiplication() constant_folding_after_inlining (before) /// CHECK-DAG: <> DoubleConstant 7 /// CHECK-DAG: <> DoubleConstant 3 /// CHECK-DAG: <> Mul [<>,<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: double Main.DoubleMultiplication() constant_folding (after) + /// CHECK-START: double Main.DoubleMultiplication() constant_folding_after_inlining (after) /// CHECK-DAG: <> DoubleConstant 21 /// CHECK-DAG: Return [<>] - /// CHECK-START: double Main.DoubleMultiplication() constant_folding (after) + /// CHECK-START: double Main.DoubleMultiplication() constant_folding_after_inlining (after) /// CHECK-NOT: Mul public static double DoubleMultiplication() { double a, b, c; - a = 7D; - b = 3D; + a = $inline$double(7D); + b = $inline$double(3D); c = a * b; return c; } @@ -496,90 +509,90 @@ public class Main { * Exercise constant folding on division. */ - /// CHECK-START: int Main.IntDivision() constant_folding (before) + /// CHECK-START: int Main.IntDivision() constant_folding_after_inlining (before) /// CHECK-DAG: <> IntConstant 8 /// CHECK-DAG: <> IntConstant 3 /// CHECK-DAG: <> DivZeroCheck [<>] /// CHECK-DAG: <> Div [<>,<>] /// CHECK-DAG: Return [<
>] - /// CHECK-START: int Main.IntDivision() constant_folding (after) + /// CHECK-START: int Main.IntDivision() constant_folding_after_inlining (after) /// CHECK-DAG: <> IntConstant 2 /// CHECK-DAG: Return [<>] - /// CHECK-START: int Main.IntDivision() constant_folding (after) + /// CHECK-START: int Main.IntDivision() constant_folding_after_inlining (after) /// CHECK-NOT: DivZeroCheck /// CHECK-NOT: Div public static int IntDivision() { int a, b, c; - a = 8; - b = 3; + a = $inline$int(8); + b = $inline$int(3); c = a / b; return c; } - /// CHECK-START: long Main.LongDivision() constant_folding (before) + /// CHECK-START: long Main.LongDivision() constant_folding_after_inlining (before) /// CHECK-DAG: <> LongConstant 8 /// CHECK-DAG: <> LongConstant 3 /// CHECK-DAG: <> DivZeroCheck [<>] /// CHECK-DAG: <> Div [<>,<>] /// CHECK-DAG: Return [<
>] - /// CHECK-START: long Main.LongDivision() constant_folding (after) + /// CHECK-START: long Main.LongDivision() constant_folding_after_inlining (after) /// CHECK-DAG: <> LongConstant 2 /// CHECK-DAG: Return [<>] - /// CHECK-START: long Main.LongDivision() constant_folding (after) + /// CHECK-START: long Main.LongDivision() constant_folding_after_inlining (after) /// CHECK-NOT: DivZeroCheck /// CHECK-NOT: Div public static long LongDivision() { long a, b, c; - a = 8L; - b = 3L; + a = $inline$long(8L); + b = $inline$long(3L); c = a / b; return c; } - /// CHECK-START: float Main.FloatDivision() constant_folding (before) + /// CHECK-START: float Main.FloatDivision() constant_folding_after_inlining (before) /// CHECK-DAG: <> FloatConstant 8 /// CHECK-DAG: <> FloatConstant 2.5 /// CHECK-DAG: <> Div [<>,<>] /// CHECK-DAG: Return [<
>] - /// CHECK-START: float Main.FloatDivision() constant_folding (after) + /// CHECK-START: float Main.FloatDivision() constant_folding_after_inlining (after) /// CHECK-DAG: <> FloatConstant 3.2 /// CHECK-DAG: Return [<>] - /// CHECK-START: float Main.FloatDivision() constant_folding (after) + /// CHECK-START: float Main.FloatDivision() constant_folding_after_inlining (after) /// CHECK-NOT: Div public static float FloatDivision() { float a, b, c; - a = 8F; - b = 2.5F; + a = $inline$float(8F); + b = $inline$float(2.5F); c = a / b; return c; } - /// CHECK-START: double Main.DoubleDivision() constant_folding (before) + /// CHECK-START: double Main.DoubleDivision() constant_folding_after_inlining (before) /// CHECK-DAG: <> DoubleConstant 8 /// CHECK-DAG: <> DoubleConstant 2.5 /// CHECK-DAG: <> Div [<>,<>] /// CHECK-DAG: Return [<
>] - /// CHECK-START: double Main.DoubleDivision() constant_folding (after) + /// CHECK-START: double Main.DoubleDivision() constant_folding_after_inlining (after) /// CHECK-DAG: <> DoubleConstant 3.2 /// CHECK-DAG: Return [<>] - /// CHECK-START: double Main.DoubleDivision() constant_folding (after) + /// CHECK-START: double Main.DoubleDivision() constant_folding_after_inlining (after) /// CHECK-NOT: Div public static double DoubleDivision() { double a, b, c; - a = 8D; - b = 2.5D; + a = $inline$double(8D); + b = $inline$double(2.5D); c = a / b; return c; } @@ -589,90 +602,90 @@ public class Main { * Exercise constant folding on remainder. */ - /// CHECK-START: int Main.IntRemainder() constant_folding (before) + /// CHECK-START: int Main.IntRemainder() constant_folding_after_inlining (before) /// CHECK-DAG: <> IntConstant 8 /// CHECK-DAG: <> IntConstant 3 /// CHECK-DAG: <> DivZeroCheck [<>] /// CHECK-DAG: <> Rem [<>,<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: int Main.IntRemainder() constant_folding (after) + /// CHECK-START: int Main.IntRemainder() constant_folding_after_inlining (after) /// CHECK-DAG: <> IntConstant 2 /// CHECK-DAG: Return [<>] - /// CHECK-START: int Main.IntRemainder() constant_folding (after) + /// CHECK-START: int Main.IntRemainder() constant_folding_after_inlining (after) /// CHECK-NOT: DivZeroCheck /// CHECK-NOT: Rem public static int IntRemainder() { int a, b, c; - a = 8; - b = 3; + a = $inline$int(8); + b = $inline$int(3); c = a % b; return c; } - /// CHECK-START: long Main.LongRemainder() constant_folding (before) + /// CHECK-START: long Main.LongRemainder() constant_folding_after_inlining (before) /// CHECK-DAG: <> LongConstant 8 /// CHECK-DAG: <> LongConstant 3 /// CHECK-DAG: <> DivZeroCheck [<>] /// CHECK-DAG: <> Rem [<>,<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: long Main.LongRemainder() constant_folding (after) + /// CHECK-START: long Main.LongRemainder() constant_folding_after_inlining (after) /// CHECK-DAG: <> LongConstant 2 /// CHECK-DAG: Return [<>] - /// CHECK-START: long Main.LongRemainder() constant_folding (after) + /// CHECK-START: long Main.LongRemainder() constant_folding_after_inlining (after) /// CHECK-NOT: DivZeroCheck /// CHECK-NOT: Rem public static long LongRemainder() { long a, b, c; - a = 8L; - b = 3L; + a = $inline$long(8L); + b = $inline$long(3L); c = a % b; return c; } - /// CHECK-START: float Main.FloatRemainder() constant_folding (before) + /// CHECK-START: float Main.FloatRemainder() constant_folding_after_inlining (before) /// CHECK-DAG: <> FloatConstant 8 /// CHECK-DAG: <> FloatConstant 2.5 /// CHECK-DAG: <> Rem [<>,<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: float Main.FloatRemainder() constant_folding (after) + /// CHECK-START: float Main.FloatRemainder() constant_folding_after_inlining (after) /// CHECK-DAG: <> FloatConstant 0.5 /// CHECK-DAG: Return [<>] - /// CHECK-START: float Main.FloatRemainder() constant_folding (after) + /// CHECK-START: float Main.FloatRemainder() constant_folding_after_inlining (after) /// CHECK-NOT: Rem public static float FloatRemainder() { float a, b, c; - a = 8F; - b = 2.5F; + a = $inline$float(8F); + b = $inline$float(2.5F); c = a % b; return c; } - /// CHECK-START: double Main.DoubleRemainder() constant_folding (before) + /// CHECK-START: double Main.DoubleRemainder() constant_folding_after_inlining (before) /// CHECK-DAG: <> DoubleConstant 8 /// CHECK-DAG: <> DoubleConstant 2.5 /// CHECK-DAG: <> Rem [<>,<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: double Main.DoubleRemainder() constant_folding (after) + /// CHECK-START: double Main.DoubleRemainder() constant_folding_after_inlining (after) /// CHECK-DAG: <> DoubleConstant 0.5 /// CHECK-DAG: Return [<>] - /// CHECK-START: double Main.DoubleRemainder() constant_folding (after) + /// CHECK-START: double Main.DoubleRemainder() constant_folding_after_inlining (after) /// CHECK-NOT: Rem public static double DoubleRemainder() { double a, b, c; - a = 8D; - b = 2.5D; + a = $inline$double(8D); + b = $inline$double(2.5D); c = a % b; return c; } @@ -682,42 +695,42 @@ public class Main { * Exercise constant folding on left shift. */ - /// CHECK-START: int Main.ShlIntLong() constant_folding (before) + /// CHECK-START: int Main.ShlIntLong() constant_folding_after_inlining (before) /// CHECK-DAG: <> IntConstant 1 /// CHECK-DAG: <> LongConstant 2 /// CHECK-DAG: <> TypeConversion [<>] /// CHECK-DAG: <> Shl [<>,<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: int Main.ShlIntLong() constant_folding (after) + /// CHECK-START: int Main.ShlIntLong() constant_folding_after_inlining (after) /// CHECK-DAG: <> IntConstant 4 /// CHECK-DAG: Return [<>] - /// CHECK-START: int Main.ShlIntLong() constant_folding (after) + /// CHECK-START: int Main.ShlIntLong() constant_folding_after_inlining (after) /// CHECK-NOT: Shl public static int ShlIntLong() { - int lhs = 1; - long rhs = 2; + int lhs = $inline$int(1); + long rhs = $inline$long(2L); return lhs << rhs; } - /// CHECK-START: long Main.ShlLongInt() constant_folding (before) + /// CHECK-START: long Main.ShlLongInt() constant_folding_after_inlining (before) /// CHECK-DAG: <> LongConstant 3 /// CHECK-DAG: <> IntConstant 2 /// CHECK-DAG: <> Shl [<>,<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: long Main.ShlLongInt() constant_folding (after) + /// CHECK-START: long Main.ShlLongInt() constant_folding_after_inlining (after) /// CHECK-DAG: <> LongConstant 12 /// CHECK-DAG: Return [<>] - /// CHECK-START: long Main.ShlLongInt() constant_folding (after) + /// CHECK-START: long Main.ShlLongInt() constant_folding_after_inlining (after) /// CHECK-NOT: Shl public static long ShlLongInt() { - long lhs = 3; - int rhs = 2; + long lhs = $inline$long(3L); + int rhs = $inline$int(2); return lhs << rhs; } @@ -726,42 +739,42 @@ public class Main { * Exercise constant folding on right shift. */ - /// CHECK-START: int Main.ShrIntLong() constant_folding (before) + /// CHECK-START: int Main.ShrIntLong() constant_folding_after_inlining (before) /// CHECK-DAG: <> IntConstant 7 /// CHECK-DAG: <> LongConstant 2 /// CHECK-DAG: <> TypeConversion [<>] /// CHECK-DAG: <> Shr [<>,<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: int Main.ShrIntLong() constant_folding (after) + /// CHECK-START: int Main.ShrIntLong() constant_folding_after_inlining (after) /// CHECK-DAG: <> IntConstant 1 /// CHECK-DAG: Return [<>] - /// CHECK-START: int Main.ShrIntLong() constant_folding (after) + /// CHECK-START: int Main.ShrIntLong() constant_folding_after_inlining (after) /// CHECK-NOT: Shr public static int ShrIntLong() { - int lhs = 7; - long rhs = 2; + int lhs = $inline$int(7); + long rhs = $inline$long(2L); return lhs >> rhs; } - /// CHECK-START: long Main.ShrLongInt() constant_folding (before) + /// CHECK-START: long Main.ShrLongInt() constant_folding_after_inlining (before) /// CHECK-DAG: <> LongConstant 9 /// CHECK-DAG: <> IntConstant 2 /// CHECK-DAG: <> Shr [<>,<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: long Main.ShrLongInt() constant_folding (after) + /// CHECK-START: long Main.ShrLongInt() constant_folding_after_inlining (after) /// CHECK-DAG: <> LongConstant 2 /// CHECK-DAG: Return [<>] - /// CHECK-START: long Main.ShrLongInt() constant_folding (after) + /// CHECK-START: long Main.ShrLongInt() constant_folding_after_inlining (after) /// CHECK-NOT: Shr public static long ShrLongInt() { - long lhs = 9; - int rhs = 2; + long lhs = $inline$long(9); + int rhs = $inline$int(2); return lhs >> rhs; } @@ -770,42 +783,42 @@ public class Main { * Exercise constant folding on unsigned right shift. */ - /// CHECK-START: int Main.UShrIntLong() constant_folding (before) + /// CHECK-START: int Main.UShrIntLong() constant_folding_after_inlining (before) /// CHECK-DAG: <> IntConstant -7 /// CHECK-DAG: <> LongConstant 2 /// CHECK-DAG: <> TypeConversion [<>] /// CHECK-DAG: <> UShr [<>,<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: int Main.UShrIntLong() constant_folding (after) + /// CHECK-START: int Main.UShrIntLong() constant_folding_after_inlining (after) /// CHECK-DAG: <> IntConstant 1073741822 /// CHECK-DAG: Return [<>] - /// CHECK-START: int Main.UShrIntLong() constant_folding (after) + /// CHECK-START: int Main.UShrIntLong() constant_folding_after_inlining (after) /// CHECK-NOT: UShr public static int UShrIntLong() { - int lhs = -7; - long rhs = 2; + int lhs = $inline$int(-7); + long rhs = $inline$long(2L); return lhs >>> rhs; } - /// CHECK-START: long Main.UShrLongInt() constant_folding (before) + /// CHECK-START: long Main.UShrLongInt() constant_folding_after_inlining (before) /// CHECK-DAG: <> LongConstant -9 /// CHECK-DAG: <> IntConstant 2 /// CHECK-DAG: <> UShr [<>,<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: long Main.UShrLongInt() constant_folding (after) + /// CHECK-START: long Main.UShrLongInt() constant_folding_after_inlining (after) /// CHECK-DAG: <> LongConstant 4611686018427387901 /// CHECK-DAG: Return [<>] - /// CHECK-START: long Main.UShrLongInt() constant_folding (after) + /// CHECK-START: long Main.UShrLongInt() constant_folding_after_inlining (after) /// CHECK-NOT: UShr public static long UShrLongInt() { - long lhs = -9; - int rhs = 2; + long lhs = $inline$long(-9); + int rhs = $inline$int(2); return lhs >>> rhs; } @@ -814,43 +827,43 @@ public class Main { * Exercise constant folding on logical and. */ - /// CHECK-START: long Main.AndIntLong() constant_folding (before) + /// CHECK-START: long Main.AndIntLong() constant_folding_after_inlining (before) /// CHECK-DAG: <> IntConstant 10 /// CHECK-DAG: <> LongConstant 3 /// CHECK-DAG: <> TypeConversion [<>] /// CHECK-DAG: <> And [<>,<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: long Main.AndIntLong() constant_folding (after) + /// CHECK-START: long Main.AndIntLong() constant_folding_after_inlining (after) /// CHECK-DAG: <> LongConstant 2 /// CHECK-DAG: Return [<>] - /// CHECK-START: long Main.AndIntLong() constant_folding (after) + /// CHECK-START: long Main.AndIntLong() constant_folding_after_inlining (after) /// CHECK-NOT: And public static long AndIntLong() { - int lhs = 10; - long rhs = 3; + int lhs = $inline$int(10); + long rhs = $inline$long(3L); return lhs & rhs; } - /// CHECK-START: long Main.AndLongInt() constant_folding (before) + /// CHECK-START: long Main.AndLongInt() constant_folding_after_inlining (before) /// CHECK-DAG: <> LongConstant 10 /// CHECK-DAG: <> IntConstant 3 /// CHECK-DAG: <> TypeConversion [<>] /// CHECK-DAG: <> And [<>,<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: long Main.AndLongInt() constant_folding (after) + /// CHECK-START: long Main.AndLongInt() constant_folding_after_inlining (after) /// CHECK-DAG: <> LongConstant 2 /// CHECK-DAG: Return [<>] - /// CHECK-START: long Main.AndLongInt() constant_folding (after) + /// CHECK-START: long Main.AndLongInt() constant_folding_after_inlining (after) /// CHECK-NOT: And public static long AndLongInt() { - long lhs = 10; - int rhs = 3; + long lhs = $inline$long(10L); + int rhs = $inline$int(3); return lhs & rhs; } @@ -859,43 +872,43 @@ public class Main { * Exercise constant folding on logical or. */ - /// CHECK-START: long Main.OrIntLong() constant_folding (before) + /// CHECK-START: long Main.OrIntLong() constant_folding_after_inlining (before) /// CHECK-DAG: <> IntConstant 10 /// CHECK-DAG: <> LongConstant 3 /// CHECK-DAG: <> TypeConversion [<>] /// CHECK-DAG: <> Or [<>,<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: long Main.OrIntLong() constant_folding (after) + /// CHECK-START: long Main.OrIntLong() constant_folding_after_inlining (after) /// CHECK-DAG: <> LongConstant 11 /// CHECK-DAG: Return [<>] - /// CHECK-START: long Main.OrIntLong() constant_folding (after) + /// CHECK-START: long Main.OrIntLong() constant_folding_after_inlining (after) /// CHECK-NOT: Or public static long OrIntLong() { - int lhs = 10; - long rhs = 3; + int lhs = $inline$int(10); + long rhs = $inline$long(3L); return lhs | rhs; } - /// CHECK-START: long Main.OrLongInt() constant_folding (before) + /// CHECK-START: long Main.OrLongInt() constant_folding_after_inlining (before) /// CHECK-DAG: <> LongConstant 10 /// CHECK-DAG: <> IntConstant 3 /// CHECK-DAG: <> TypeConversion [<>] /// CHECK-DAG: <> Or [<>,<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: long Main.OrLongInt() constant_folding (after) + /// CHECK-START: long Main.OrLongInt() constant_folding_after_inlining (after) /// CHECK-DAG: <> LongConstant 11 /// CHECK-DAG: Return [<>] - /// CHECK-START: long Main.OrLongInt() constant_folding (after) + /// CHECK-START: long Main.OrLongInt() constant_folding_after_inlining (after) /// CHECK-NOT: Or public static long OrLongInt() { - long lhs = 10; - int rhs = 3; + long lhs = $inline$long(10L); + int rhs = $inline$int(3); return lhs | rhs; } @@ -904,43 +917,43 @@ public class Main { * Exercise constant folding on logical exclusive or. */ - /// CHECK-START: long Main.XorIntLong() constant_folding (before) + /// CHECK-START: long Main.XorIntLong() constant_folding_after_inlining (before) /// CHECK-DAG: <> IntConstant 10 /// CHECK-DAG: <> LongConstant 3 /// CHECK-DAG: <> TypeConversion [<>] /// CHECK-DAG: <> Xor [<>,<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: long Main.XorIntLong() constant_folding (after) + /// CHECK-START: long Main.XorIntLong() constant_folding_after_inlining (after) /// CHECK-DAG: <> LongConstant 9 /// CHECK-DAG: Return [<>] - /// CHECK-START: long Main.XorIntLong() constant_folding (after) + /// CHECK-START: long Main.XorIntLong() constant_folding_after_inlining (after) /// CHECK-NOT: Xor public static long XorIntLong() { - int lhs = 10; - long rhs = 3; + int lhs = $inline$int(10); + long rhs = $inline$long(3L); return lhs ^ rhs; } - /// CHECK-START: long Main.XorLongInt() constant_folding (before) + /// CHECK-START: long Main.XorLongInt() constant_folding_after_inlining (before) /// CHECK-DAG: <> LongConstant 10 /// CHECK-DAG: <> IntConstant 3 /// CHECK-DAG: <> TypeConversion [<>] /// CHECK-DAG: <> Xor [<>,<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: long Main.XorLongInt() constant_folding (after) + /// CHECK-START: long Main.XorLongInt() constant_folding_after_inlining (after) /// CHECK-DAG: <> LongConstant 9 /// CHECK-DAG: Return [<>] - /// CHECK-START: long Main.XorLongInt() constant_folding (after) + /// CHECK-START: long Main.XorLongInt() constant_folding_after_inlining (after) /// CHECK-NOT: Xor public static long XorLongInt() { - long lhs = 10; - int rhs = 3; + long lhs = $inline$long(10L); + int rhs = $inline$int(3); return lhs ^ rhs; } @@ -949,23 +962,23 @@ public class Main { * Exercise constant folding on constant (static) condition. */ - /// CHECK-START: int Main.StaticCondition() constant_folding (before) + /// CHECK-START: int Main.StaticCondition() constant_folding_after_inlining (before) /// CHECK-DAG: <> IntConstant 7 /// CHECK-DAG: <> IntConstant 2 /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] - /// CHECK-DAG: If [<>] + /// CHECK-DAG: Select [{{i\d+}},{{i\d+}},<>] - /// CHECK-START: int Main.StaticCondition() constant_folding (after) + /// CHECK-START: int Main.StaticCondition() constant_folding_after_inlining (after) /// CHECK-DAG: <> IntConstant 1 - /// CHECK-DAG: If [<>] + /// CHECK-DAG: Select [{{i\d+}},{{i\d+}},<>] - /// CHECK-START: int Main.StaticCondition() constant_folding (after) + /// CHECK-START: int Main.StaticCondition() constant_folding_after_inlining (after) /// CHECK-NOT: GreaterThanOrEqual public static int StaticCondition() { int a, b, c; - a = 7; - b = 2; + a = $inline$int(7); + b = $inline$int(2); if (a < b) c = a + b; else @@ -1010,28 +1023,30 @@ public class Main { * (forward) post-order traversal of the the dominator tree. */ - /// CHECK-START: int Main.JumpsAndConditionals(boolean) constant_folding (before) + /// CHECK-START: int Main.JumpsAndConditionals(boolean) constant_folding_after_inlining (before) + /// CHECK-DAG: <> ParameterValue /// CHECK-DAG: <> IntConstant 2 /// CHECK-DAG: <> IntConstant 5 /// CHECK-DAG: <> Add [<>,<>] /// CHECK-DAG: <> Sub [<>,<>] - /// CHECK-DAG: <> Phi [<>,<>] + /// CHECK-DAG: <> Select [<>,<>,<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: int Main.JumpsAndConditionals(boolean) constant_folding (after) + /// CHECK-START: int Main.JumpsAndConditionals(boolean) constant_folding_after_inlining (after) + /// CHECK-DAG: <> ParameterValue /// CHECK-DAG: <> IntConstant 3 /// CHECK-DAG: <> IntConstant 7 - /// CHECK-DAG: <> Phi [<>,<>] + /// CHECK-DAG: <> Select [<>,<>,<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: int Main.JumpsAndConditionals(boolean) constant_folding (after) + /// CHECK-START: int Main.JumpsAndConditionals(boolean) constant_folding_after_inlining (after) /// CHECK-NOT: Add /// CHECK-NOT: Sub public static int JumpsAndConditionals(boolean cond) { int a, b, c; - a = 5; - b = 2; + a = $inline$int(5); + b = $inline$int(2); if (cond) c = a + b; else @@ -1310,204 +1325,204 @@ public class Main { * Exercise constant folding on type conversions. */ - /// CHECK-START: int Main.ReturnInt33() constant_folding (before) + /// CHECK-START: int Main.ReturnInt33() constant_folding_after_inlining (before) /// CHECK-DAG: <> LongConstant 33 /// CHECK-DAG: <> TypeConversion [<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: int Main.ReturnInt33() constant_folding (after) + /// CHECK-START: int Main.ReturnInt33() constant_folding_after_inlining (after) /// CHECK-DAG: <> IntConstant 33 /// CHECK-DAG: Return [<>] - /// CHECK-START: int Main.ReturnInt33() constant_folding (after) + /// CHECK-START: int Main.ReturnInt33() constant_folding_after_inlining (after) /// CHECK-NOT: TypeConversion public static int ReturnInt33() { - long imm = 33L; + long imm = $inline$long(33L); return (int) imm; } - /// CHECK-START: int Main.ReturnIntMax() constant_folding (before) + /// CHECK-START: int Main.ReturnIntMax() constant_folding_after_inlining (before) /// CHECK-DAG: <> FloatConstant 1e+34 /// CHECK-DAG: <> TypeConversion [<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: int Main.ReturnIntMax() constant_folding (after) + /// CHECK-START: int Main.ReturnIntMax() constant_folding_after_inlining (after) /// CHECK-DAG: <> IntConstant 2147483647 /// CHECK-DAG: Return [<>] - /// CHECK-START: int Main.ReturnIntMax() constant_folding (after) + /// CHECK-START: int Main.ReturnIntMax() constant_folding_after_inlining (after) /// CHECK-NOT: TypeConversion public static int ReturnIntMax() { - float imm = 1.0e34f; + float imm = $inline$float(1.0e34f); return (int) imm; } - /// CHECK-START: int Main.ReturnInt0() constant_folding (before) + /// CHECK-START: int Main.ReturnInt0() constant_folding_after_inlining (before) /// CHECK-DAG: <> DoubleConstant nan /// CHECK-DAG: <> TypeConversion [<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: int Main.ReturnInt0() constant_folding (after) + /// CHECK-START: int Main.ReturnInt0() constant_folding_after_inlining (after) /// CHECK-DAG: <> IntConstant 0 /// CHECK-DAG: Return [<>] - /// CHECK-START: int Main.ReturnInt0() constant_folding (after) + /// CHECK-START: int Main.ReturnInt0() constant_folding_after_inlining (after) /// CHECK-NOT: TypeConversion public static int ReturnInt0() { - double imm = Double.NaN; + double imm = $inline$double(Double.NaN); return (int) imm; } - /// CHECK-START: long Main.ReturnLong33() constant_folding (before) + /// CHECK-START: long Main.ReturnLong33() constant_folding_after_inlining (before) /// CHECK-DAG: <> IntConstant 33 /// CHECK-DAG: <> TypeConversion [<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: long Main.ReturnLong33() constant_folding (after) + /// CHECK-START: long Main.ReturnLong33() constant_folding_after_inlining (after) /// CHECK-DAG: <> LongConstant 33 /// CHECK-DAG: Return [<>] - /// CHECK-START: long Main.ReturnLong33() constant_folding (after) + /// CHECK-START: long Main.ReturnLong33() constant_folding_after_inlining (after) /// CHECK-NOT: TypeConversion public static long ReturnLong33() { - int imm = 33; + int imm = $inline$int(33); return (long) imm; } - /// CHECK-START: long Main.ReturnLong34() constant_folding (before) + /// CHECK-START: long Main.ReturnLong34() constant_folding_after_inlining (before) /// CHECK-DAG: <> FloatConstant 34 /// CHECK-DAG: <> TypeConversion [<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: long Main.ReturnLong34() constant_folding (after) + /// CHECK-START: long Main.ReturnLong34() constant_folding_after_inlining (after) /// CHECK-DAG: <> LongConstant 34 /// CHECK-DAG: Return [<>] - /// CHECK-START: long Main.ReturnLong34() constant_folding (after) + /// CHECK-START: long Main.ReturnLong34() constant_folding_after_inlining (after) /// CHECK-NOT: TypeConversion public static long ReturnLong34() { - float imm = 34.0f; + float imm = $inline$float(34.0f); return (long) imm; } - /// CHECK-START: long Main.ReturnLong0() constant_folding (before) + /// CHECK-START: long Main.ReturnLong0() constant_folding_after_inlining (before) /// CHECK-DAG: <> DoubleConstant nan /// CHECK-DAG: <> TypeConversion [<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: long Main.ReturnLong0() constant_folding (after) + /// CHECK-START: long Main.ReturnLong0() constant_folding_after_inlining (after) /// CHECK-DAG: <> LongConstant 0 /// CHECK-DAG: Return [<>] - /// CHECK-START: long Main.ReturnLong0() constant_folding (after) + /// CHECK-START: long Main.ReturnLong0() constant_folding_after_inlining (after) /// CHECK-NOT: TypeConversion public static long ReturnLong0() { - double imm = -Double.NaN; + double imm = $inline$double(-Double.NaN); return (long) imm; } - /// CHECK-START: float Main.ReturnFloat33() constant_folding (before) + /// CHECK-START: float Main.ReturnFloat33() constant_folding_after_inlining (before) /// CHECK-DAG: <> IntConstant 33 /// CHECK-DAG: <> TypeConversion [<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: float Main.ReturnFloat33() constant_folding (after) + /// CHECK-START: float Main.ReturnFloat33() constant_folding_after_inlining (after) /// CHECK-DAG: <> FloatConstant 33 /// CHECK-DAG: Return [<>] - /// CHECK-START: float Main.ReturnFloat33() constant_folding (after) + /// CHECK-START: float Main.ReturnFloat33() constant_folding_after_inlining (after) /// CHECK-NOT: TypeConversion public static float ReturnFloat33() { - int imm = 33; + int imm = $inline$int(33); return (float) imm; } - /// CHECK-START: float Main.ReturnFloat34() constant_folding (before) + /// CHECK-START: float Main.ReturnFloat34() constant_folding_after_inlining (before) /// CHECK-DAG: <> LongConstant 34 /// CHECK-DAG: <> TypeConversion [<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: float Main.ReturnFloat34() constant_folding (after) + /// CHECK-START: float Main.ReturnFloat34() constant_folding_after_inlining (after) /// CHECK-DAG: <> FloatConstant 34 /// CHECK-DAG: Return [<>] - /// CHECK-START: float Main.ReturnFloat34() constant_folding (after) + /// CHECK-START: float Main.ReturnFloat34() constant_folding_after_inlining (after) /// CHECK-NOT: TypeConversion public static float ReturnFloat34() { - long imm = 34L; + long imm = $inline$long(34L); return (float) imm; } - /// CHECK-START: float Main.ReturnFloat99P25() constant_folding (before) + /// CHECK-START: float Main.ReturnFloat99P25() constant_folding_after_inlining (before) /// CHECK-DAG: <> DoubleConstant 99.25 /// CHECK-DAG: <> TypeConversion [<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: float Main.ReturnFloat99P25() constant_folding (after) + /// CHECK-START: float Main.ReturnFloat99P25() constant_folding_after_inlining (after) /// CHECK-DAG: <> FloatConstant 99.25 /// CHECK-DAG: Return [<>] - /// CHECK-START: float Main.ReturnFloat99P25() constant_folding (after) + /// CHECK-START: float Main.ReturnFloat99P25() constant_folding_after_inlining (after) /// CHECK-NOT: TypeConversion public static float ReturnFloat99P25() { - double imm = 99.25; + double imm = $inline$double(99.25); return (float) imm; } - /// CHECK-START: double Main.ReturnDouble33() constant_folding (before) + /// CHECK-START: double Main.ReturnDouble33() constant_folding_after_inlining (before) /// CHECK-DAG: <> IntConstant 33 /// CHECK-DAG: <> TypeConversion [<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: double Main.ReturnDouble33() constant_folding (after) + /// CHECK-START: double Main.ReturnDouble33() constant_folding_after_inlining (after) /// CHECK-DAG: <> DoubleConstant 33 /// CHECK-DAG: Return [<>] public static double ReturnDouble33() { - int imm = 33; + int imm = $inline$int(33); return (double) imm; } - /// CHECK-START: double Main.ReturnDouble34() constant_folding (before) + /// CHECK-START: double Main.ReturnDouble34() constant_folding_after_inlining (before) /// CHECK-DAG: <> LongConstant 34 /// CHECK-DAG: <> TypeConversion [<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: double Main.ReturnDouble34() constant_folding (after) + /// CHECK-START: double Main.ReturnDouble34() constant_folding_after_inlining (after) /// CHECK-DAG: <> DoubleConstant 34 /// CHECK-DAG: Return [<>] - /// CHECK-START: double Main.ReturnDouble34() constant_folding (after) + /// CHECK-START: double Main.ReturnDouble34() constant_folding_after_inlining (after) /// CHECK-NOT: TypeConversion public static double ReturnDouble34() { - long imm = 34L; + long imm = $inline$long(34L); return (double) imm; } - /// CHECK-START: double Main.ReturnDouble99P25() constant_folding (before) + /// CHECK-START: double Main.ReturnDouble99P25() constant_folding_after_inlining (before) /// CHECK-DAG: <> FloatConstant 99.25 /// CHECK-DAG: <> TypeConversion [<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: double Main.ReturnDouble99P25() constant_folding (after) + /// CHECK-START: double Main.ReturnDouble99P25() constant_folding_after_inlining (after) /// CHECK-DAG: <> DoubleConstant 99.25 /// CHECK-DAG: Return [<>] - /// CHECK-START: double Main.ReturnDouble99P25() constant_folding (after) + /// CHECK-START: double Main.ReturnDouble99P25() constant_folding_after_inlining (after) /// CHECK-NOT: TypeConversion public static double ReturnDouble99P25() { - float imm = 99.25f; + float imm = $inline$float(99.25f); return (double) imm; } diff --git a/test/450-checker-types/smali/SmaliTests.smali b/test/450-checker-types/smali/SmaliTests.smali new file mode 100644 index 0000000000..6a3122e41b --- /dev/null +++ b/test/450-checker-types/smali/SmaliTests.smali @@ -0,0 +1,120 @@ +# Copyright (C) 2016 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +.class public LSmaliTests; +.super Ljava/lang/Object; + +## CHECK-START: void SmaliTests.testInstanceOf_EQ0_NotInlined(java.lang.Object) builder (after) +## CHECK-DAG: <> IntConstant 0 +## CHECK-DAG: <> InstanceOf +## CHECK-DAG: Equal [<>,<>] + +## CHECK-START: void SmaliTests.testInstanceOf_EQ0_NotInlined(java.lang.Object) instruction_simplifier (before) +## CHECK: CheckCast + +## CHECK-START: void SmaliTests.testInstanceOf_EQ0_NotInlined(java.lang.Object) instruction_simplifier (after) +## CHECK-NOT: CheckCast + +.method public static testInstanceOf_EQ0_NotInlined(Ljava/lang/Object;)V + .registers 3 + + const v0, 0x0 + instance-of v1, p0, LSubclassC; + if-eq v1, v0, :return + + check-cast p0, LSubclassC; + invoke-virtual {p0}, LSubclassC;->$noinline$g()V + + :return + return-void + +.end method + +## CHECK-START: void SmaliTests.testInstanceOf_EQ1_NotInlined(java.lang.Object) builder (after) +## CHECK-DAG: <> IntConstant 1 +## CHECK-DAG: <> InstanceOf +## CHECK-DAG: Equal [<>,<>] + +## CHECK-START: void SmaliTests.testInstanceOf_EQ1_NotInlined(java.lang.Object) instruction_simplifier (before) +## CHECK: CheckCast + +## CHECK-START: void SmaliTests.testInstanceOf_EQ1_NotInlined(java.lang.Object) instruction_simplifier (after) +## CHECK-NOT: CheckCast + +.method public static testInstanceOf_EQ1_NotInlined(Ljava/lang/Object;)V + .registers 3 + + const v0, 0x1 + instance-of v1, p0, LSubclassC; + if-eq v1, v0, :invoke + return-void + + :invoke + check-cast p0, LSubclassC; + invoke-virtual {p0}, LSubclassC;->$noinline$g()V + return-void + +.end method + +## CHECK-START: void SmaliTests.testInstanceOf_NE0_NotInlined(java.lang.Object) builder (after) +## CHECK-DAG: <> IntConstant 0 +## CHECK-DAG: <> InstanceOf +## CHECK-DAG: NotEqual [<>,<>] + +## CHECK-START: void SmaliTests.testInstanceOf_NE0_NotInlined(java.lang.Object) instruction_simplifier (before) +## CHECK: CheckCast + +## CHECK-START: void SmaliTests.testInstanceOf_NE0_NotInlined(java.lang.Object) instruction_simplifier (after) +## CHECK-NOT: CheckCast + +.method public static testInstanceOf_NE0_NotInlined(Ljava/lang/Object;)V + .registers 3 + + const v0, 0x0 + instance-of v1, p0, LSubclassC; + if-ne v1, v0, :invoke + return-void + + :invoke + check-cast p0, LSubclassC; + invoke-virtual {p0}, LSubclassC;->$noinline$g()V + return-void + +.end method + +## CHECK-START: void SmaliTests.testInstanceOf_NE1_NotInlined(java.lang.Object) builder (after) +## CHECK-DAG: <> IntConstant 1 +## CHECK-DAG: <> InstanceOf +## CHECK-DAG: NotEqual [<>,<>] + +## CHECK-START: void SmaliTests.testInstanceOf_NE1_NotInlined(java.lang.Object) instruction_simplifier (before) +## CHECK: CheckCast + +## CHECK-START: void SmaliTests.testInstanceOf_NE1_NotInlined(java.lang.Object) instruction_simplifier (after) +## CHECK-NOT: CheckCast + +.method public static testInstanceOf_NE1_NotInlined(Ljava/lang/Object;)V + .registers 3 + + const v0, 0x1 + instance-of v1, p0, LSubclassC; + if-ne v1, v0, :return + + check-cast p0, LSubclassC; + invoke-virtual {p0}, LSubclassC;->$noinline$g()V + + :return + return-void + +.end method diff --git a/test/450-checker-types/src/Main.java b/test/450-checker-types/src/Main.java index 027a9d9487..08b6cec35f 100644 --- a/test/450-checker-types/src/Main.java +++ b/test/450-checker-types/src/Main.java @@ -205,58 +205,6 @@ public class Main { public static boolean $inline$InstanceofSubclassB(Object o) { return o instanceof SubclassB; } public static boolean $inline$InstanceofSubclassC(Object o) { return o instanceof SubclassC; } - /// CHECK-START: void Main.testInstanceOf_NotInlined(java.lang.Object) builder (after) - /// CHECK-DAG: <> IntConstant 0 - /// CHECK-DAG: <> IntConstant 1 - /// CHECK-DAG: <> InstanceOf - /// CHECK-DAG: NotEqual [<>,<>] - /// CHECK-DAG: <> InstanceOf - /// CHECK-DAG: Equal [<>,<>] - - /// CHECK-START: void Main.testInstanceOf_NotInlined(java.lang.Object) instruction_simplifier (before) - /// CHECK: CheckCast - /// CHECK: CheckCast - /// CHECK-NOT: CheckCast - - /// CHECK-START: void Main.testInstanceOf_NotInlined(java.lang.Object) instruction_simplifier (after) - /// CHECK-NOT: CheckCast - public void testInstanceOf_NotInlined(Object o) { - if ((o instanceof SubclassC) == true) { - ((SubclassC)o).$noinline$g(); - } - if ((o instanceof SubclassB) != false) { - ((SubclassB)o).$noinline$g(); - } - } - - /// CHECK-START: void Main.testNotInstanceOf_NotInlined(java.lang.Object) builder (after) - /// CHECK-DAG: <> IntConstant 0 - /// CHECK-DAG: <> IntConstant 1 - /// CHECK-DAG: <> InstanceOf - /// CHECK-DAG: Equal [<>,<>] - /// CHECK-DAG: <> InstanceOf - /// CHECK-DAG: NotEqual [<>,<>] - - /// CHECK-START: void Main.testNotInstanceOf_NotInlined(java.lang.Object) instruction_simplifier (before) - /// CHECK: CheckCast - /// CHECK: CheckCast - /// CHECK-NOT: CheckCast - - /// CHECK-START: void Main.testNotInstanceOf_NotInlined(java.lang.Object) instruction_simplifier (after) - /// CHECK-NOT: CheckCast - public void testNotInstanceOf_NotInlined(Object o) { - if ((o instanceof SubclassC) != true) { - // Empty branch to flip the condition. - } else { - ((SubclassC)o).$noinline$g(); - } - if ((o instanceof SubclassB) == false) { - // Empty branch to flip the condition. - } else { - ((SubclassB)o).$noinline$g(); - } - } - /// CHECK-START: void Main.testInstanceOf_Inlined(java.lang.Object) inliner (after) /// CHECK-DAG: <> InstanceOf /// CHECK-DAG: If [<>] diff --git a/test/458-checker-instruction-simplification/smali/SmaliTests.smali b/test/458-checker-instruction-simplification/smali/SmaliTests.smali new file mode 100644 index 0000000000..ede599b213 --- /dev/null +++ b/test/458-checker-instruction-simplification/smali/SmaliTests.smali @@ -0,0 +1,193 @@ +# Copyright (C) 2016 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +.class public LSmaliTests; +.super Ljava/lang/Object; + +## CHECK-START: int SmaliTests.EqualTrueRhs(boolean) instruction_simplifier (before) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> IntConstant 1 +## CHECK-DAG: <> Equal [<>,<>] +## CHECK-DAG: If [<>] + +## CHECK-START: int SmaliTests.EqualTrueRhs(boolean) instruction_simplifier (after) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: If [<>] + +.method public static EqualTrueRhs(Z)I + .registers 3 + + const v0, 0x1 + const v1, 0x5 + if-eq p0, v0, :return + const v1, 0x3 + :return + return v1 + +.end method + +## CHECK-START: int SmaliTests.EqualTrueLhs(boolean) instruction_simplifier (before) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> IntConstant 1 +## CHECK-DAG: <> Equal [<>,<>] +## CHECK-DAG: If [<>] + +## CHECK-START: int SmaliTests.EqualTrueLhs(boolean) instruction_simplifier (after) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: If [<>] + +.method public static EqualTrueLhs(Z)I + .registers 3 + + const v0, 0x1 + const v1, 0x5 + if-eq v0, p0, :return + const v1, 0x3 + :return + return v1 + +.end method + +## CHECK-START: int SmaliTests.EqualFalseRhs(boolean) instruction_simplifier (before) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> IntConstant 0 +## CHECK-DAG: <> Equal [<>,<>] +## CHECK-DAG: If [<>] + +## CHECK-START: int SmaliTests.EqualFalseRhs(boolean) instruction_simplifier (after) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: If [<>] + +.method public static EqualFalseRhs(Z)I + .registers 3 + + const v0, 0x0 + const v1, 0x3 + if-eq p0, v0, :return + const v1, 0x5 + :return + return v1 + +.end method + +## CHECK-START: int SmaliTests.EqualFalseLhs(boolean) instruction_simplifier (before) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> IntConstant 0 +## CHECK-DAG: <> Equal [<>,<>] +## CHECK-DAG: If [<>] + +## CHECK-START: int SmaliTests.EqualFalseLhs(boolean) instruction_simplifier (after) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: If [<>] + +.method public static EqualFalseLhs(Z)I + .registers 3 + + const v0, 0x0 + const v1, 0x3 + if-eq v0, p0, :return + const v1, 0x5 + :return + return v1 + +.end method + +## CHECK-START: int SmaliTests.NotEqualTrueRhs(boolean) instruction_simplifier (before) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> IntConstant 1 +## CHECK-DAG: <> NotEqual [<>,<>] +## CHECK-DAG: If [<>] + +## CHECK-START: int SmaliTests.NotEqualTrueRhs(boolean) instruction_simplifier (after) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: If [<>] + +.method public static NotEqualTrueRhs(Z)I + .registers 3 + + const v0, 0x1 + const v1, 0x3 + if-ne p0, v0, :return + const v1, 0x5 + :return + return v1 + +.end method + +## CHECK-START: int SmaliTests.NotEqualTrueLhs(boolean) instruction_simplifier (before) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> IntConstant 1 +## CHECK-DAG: <> NotEqual [<>,<>] +## CHECK-DAG: If [<>] + +## CHECK-START: int SmaliTests.NotEqualTrueLhs(boolean) instruction_simplifier (after) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: If [<>] + +.method public static NotEqualTrueLhs(Z)I + .registers 3 + + const v0, 0x1 + const v1, 0x3 + if-ne v0, p0, :return + const v1, 0x5 + :return + return v1 + +.end method + +## CHECK-START: int SmaliTests.NotEqualFalseRhs(boolean) instruction_simplifier (before) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> IntConstant 0 +## CHECK-DAG: <> NotEqual [<>,<>] +## CHECK-DAG: If [<>] + +## CHECK-START: int SmaliTests.NotEqualFalseRhs(boolean) instruction_simplifier (after) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: If [<>] + +.method public static NotEqualFalseRhs(Z)I + .registers 3 + + const v0, 0x0 + const v1, 0x5 + if-ne p0, v0, :return + const v1, 0x3 + :return + return v1 + +.end method + +## CHECK-START: int SmaliTests.NotEqualFalseLhs(boolean) instruction_simplifier (before) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> IntConstant 0 +## CHECK-DAG: <> NotEqual [<>,<>] +## CHECK-DAG: If [<>] + +## CHECK-START: int SmaliTests.NotEqualFalseLhs(boolean) instruction_simplifier (after) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: If [<>] + +.method public static NotEqualFalseLhs(Z)I + .registers 3 + + const v0, 0x0 + const v1, 0x5 + if-ne v0, p0, :return + const v1, 0x3 + :return + return v1 + +.end method + diff --git a/test/458-checker-instruction-simplification/src/Main.java b/test/458-checker-instruction-simplification/src/Main.java index 8d6bb653f4..8640148795 100644 --- a/test/458-checker-instruction-simplification/src/Main.java +++ b/test/458-checker-instruction-simplification/src/Main.java @@ -14,6 +14,8 @@ * limitations under the License. */ +import java.lang.reflect.Method; + public class Main { public static void assertBooleanEquals(boolean expected, boolean result) { @@ -826,17 +828,16 @@ public class Main { /// CHECK-START: long Main.NotNot1(long) instruction_simplifier (before) /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> LongConstant -1 - /// CHECK-DAG: <> Xor [<>,<>] - /// CHECK-DAG: <> Xor [<>,<>] - /// CHECK-DAG: Return [<>] + /// CHECK-DAG: <> Not [<>] + /// CHECK-DAG: <> Not [<>] + /// CHECK-DAG: Return [<>] /// CHECK-START: long Main.NotNot1(long) instruction_simplifier (after) /// CHECK-DAG: <> ParameterValue /// CHECK-DAG: Return [<>] /// CHECK-START: long Main.NotNot1(long) instruction_simplifier (after) - /// CHECK-NOT: Xor + /// CHECK-NOT: Not public static long NotNot1(long arg) { return ~~arg; @@ -844,10 +845,9 @@ public class Main { /// CHECK-START: int Main.NotNot2(int) instruction_simplifier (before) /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> IntConstant -1 - /// CHECK-DAG: <> Xor [<>,<>] - /// CHECK-DAG: <> Xor [<>,<>] - /// CHECK-DAG: <> Add [<>,<>] + /// CHECK-DAG: <> Not [<>] + /// CHECK-DAG: <> Not [<>] + /// CHECK-DAG: <> Add [<>,<>] /// CHECK-DAG: Return [<>] /// CHECK-START: int Main.NotNot2(int) instruction_simplifier (after) @@ -857,7 +857,8 @@ public class Main { /// CHECK-DAG: Return [<>] /// CHECK-START: int Main.NotNot2(int) instruction_simplifier (after) - /// CHECK-NOT: Xor + /// CHECK: Not + /// CHECK-NOT: Not public static int NotNot2(int arg) { int temp = ~arg; @@ -965,174 +966,6 @@ public class Main { return res; } - /// CHECK-START: int Main.EqualTrueRhs(boolean) instruction_simplifier (before) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> IntConstant 1 - /// CHECK-DAG: <> Equal [<>,<>] - /// CHECK-DAG: If [<>] - - /// CHECK-START: int Main.EqualTrueRhs(boolean) instruction_simplifier (after) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: If [<>] - - /// CHECK-START: int Main.EqualTrueRhs(boolean) instruction_simplifier_before_codegen (after) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> IntConstant 3 - /// CHECK-DAG: <> IntConstant 5 - /// CHECK-DAG: <> Select [<>,<>,<>] - /// CHECK-DAG: Return [<>] - - public static int EqualTrueLhs(boolean arg) { - return (true != arg) ? 3 : 5; - } - - /// CHECK-START: int Main.EqualFalseRhs(boolean) instruction_simplifier (before) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> IntConstant 0 - /// CHECK-DAG: <> Equal [<>,<>] - /// CHECK-DAG: If [<>] - - /// CHECK-START: int Main.EqualFalseRhs(boolean) instruction_simplifier (after) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: If [<>] - - /// CHECK-START: int Main.EqualFalseRhs(boolean) instruction_simplifier_before_codegen (after) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> IntConstant 3 - /// CHECK-DAG: <> IntConstant 5 - /// CHECK-DAG: <> Select [<>,<>,<>] - /// CHECK-DAG: Return [<>] - - public static int EqualFalseLhs(boolean arg) { - return (false != arg) ? 3 : 5; - } - - /// CHECK-START: int Main.NotEqualTrueRhs(boolean) instruction_simplifier (before) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> IntConstant 1 - /// CHECK-DAG: <> NotEqual [<>,<>] - /// CHECK-DAG: If [<>] - - /// CHECK-START: int Main.NotEqualTrueRhs(boolean) instruction_simplifier (after) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: If [<>] - - /// CHECK-START: int Main.NotEqualTrueRhs(boolean) instruction_simplifier_before_codegen (after) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> IntConstant 3 - /// CHECK-DAG: <> IntConstant 5 - /// CHECK-DAG: <> Select [<>,<>,<>] - /// CHECK-DAG: Return [<>] - - public static int NotEqualTrueLhs(boolean arg) { - return (true == arg) ? 3 : 5; - } - - /// CHECK-START: int Main.NotEqualFalseRhs(boolean) instruction_simplifier (before) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> IntConstant 0 - /// CHECK-DAG: <> NotEqual [<>,<>] - /// CHECK-DAG: If [<>] - - /// CHECK-START: int Main.NotEqualFalseRhs(boolean) instruction_simplifier (after) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: If [<>] - - /// CHECK-START: int Main.NotEqualFalseRhs(boolean) instruction_simplifier_before_codegen (after) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> IntConstant 3 - /// CHECK-DAG: <> IntConstant 5 - /// CHECK-DAG: <> Select [<>,<>,<>] - /// CHECK-DAG: Return [<>] - - public static int NotEqualFalseLhs(boolean arg) { - return (false == arg) ? 3 : 5; - } - /// CHECK-START: boolean Main.EqualBoolVsIntConst(boolean) instruction_simplifier_after_bce (before) /// CHECK-DAG: <> ParameterValue /// CHECK-DAG: <> IntConstant 0 @@ -1307,17 +1140,16 @@ public class Main { return arg * 31; } - /// CHECK-START: int Main.booleanFieldNotEqualOne() instruction_simplifier (before) + /// CHECK-START: int Main.booleanFieldNotEqualOne() instruction_simplifier_after_bce (before) /// CHECK-DAG: <> IntConstant 1 + /// CHECK-DAG: <> IntConstant 13 + /// CHECK-DAG: <> IntConstant 54 /// CHECK-DAG: <> StaticFieldGet /// CHECK-DAG: <> NotEqual [<>,<>] - /// CHECK-DAG: If [<>] - - /// CHECK-START: int Main.booleanFieldNotEqualOne() instruction_simplifier (after) - /// CHECK-DAG: <> StaticFieldGet - /// CHECK-DAG: If [<>] + /// CHECK-DAG: <> Select [<>,<>,<>] + /// CHECK-DAG: Return [<>] public static int booleanFieldNotEqualOne() { - return (booleanField == true) ? 13 : 54; + return (booleanField == $inline$true()) ? 13 : 54; } - /// CHECK-START: int Main.booleanFieldEqualZero() instruction_simplifier (before) + /// CHECK-START: int Main.booleanFieldEqualZero() instruction_simplifier_after_bce (before) /// CHECK-DAG: <> IntConstant 0 + /// CHECK-DAG: <> IntConstant 13 + /// CHECK-DAG: <> IntConstant 54 /// CHECK-DAG: <> StaticFieldGet - /// CHECK-DAG: <> Equal [<>,<>] - /// CHECK-DAG: If [<>] - - /// CHECK-START: int Main.booleanFieldEqualZero() instruction_simplifier (after) - /// CHECK-DAG: <> StaticFieldGet - /// CHECK-DAG: If [<>] + /// CHECK-DAG: <> Equal [<>,<>] + /// CHECK-DAG: <> Select [<>,<>,<>] + /// CHECK-DAG: Return [<>] public static int booleanFieldEqualZero() { - return (booleanField != false) ? 13 : 54; + return (booleanField != $inline$false()) ? 13 : 54; } /// CHECK-START: int Main.intConditionNotEqualOne(int) instruction_simplifier_after_bce (before) @@ -1374,7 +1205,7 @@ public class Main { // LessThanOrEqual instructions. public static int intConditionNotEqualOne(int i) { - return ((i > 42) == true) ? 13 : 54; + return ((i > 42) == $inline$true()) ? 13 : 54; } /// CHECK-START: int Main.intConditionEqualZero(int) instruction_simplifier_after_bce (before) @@ -1402,7 +1233,7 @@ public class Main { // LessThanOrEqual instructions. public static int intConditionEqualZero(int i) { - return ((i > 42) != false) ? 13 : 54; + return ((i > 42) != $inline$false()) ? 13 : 54; } // Test that conditions on float/double are not flipped. @@ -1770,6 +1601,16 @@ public class Main { return (short) (value & 0x17fff); } + public static int runSmaliTest(String name, boolean input) { + try { + Class c = Class.forName("SmaliTests"); + Method m = c.getMethod(name, new Class[] { boolean.class }); + return (Integer) m.invoke(null, input); + } catch (Exception ex) { + throw new Error(ex); + } + } + public static void main(String[] args) { int arg = 123456; @@ -1804,14 +1645,6 @@ public class Main { assertIntEquals(SubNeg1(arg, arg + 1), -(arg + arg + 1)); assertIntEquals(SubNeg2(arg, arg + 1), -(arg + arg + 1)); assertLongEquals(SubNeg3(arg, arg + 1), -(2 * arg + 1)); - assertIntEquals(EqualTrueRhs(true), 5); - assertIntEquals(EqualTrueLhs(true), 5); - assertIntEquals(EqualFalseRhs(true), 3); - assertIntEquals(EqualFalseLhs(true), 3); - assertIntEquals(NotEqualTrueRhs(true), 3); - assertIntEquals(NotEqualTrueLhs(true), 3); - assertIntEquals(NotEqualFalseRhs(true), 5); - assertIntEquals(NotEqualFalseLhs(true), 5); assertBooleanEquals(EqualBoolVsIntConst(true), true); assertBooleanEquals(EqualBoolVsIntConst(true), true); assertBooleanEquals(NotEqualBoolVsIntConst(false), false); @@ -1906,7 +1739,20 @@ public class Main { assertIntEquals(intAnd0x17fffToShort(0x88888888), 0x0888); assertIntEquals(intAnd0x17fffToShort(Integer.MIN_VALUE), 0); assertIntEquals(intAnd0x17fffToShort(Integer.MAX_VALUE), Short.MAX_VALUE); + + for (String condition : new String[] { "Equal", "NotEqual" }) { + for (String constant : new String[] { "True", "False" }) { + for (String side : new String[] { "Rhs", "Lhs" }) { + String name = condition + constant + side; + assertIntEquals(runSmaliTest(name, true), 5); + assertIntEquals(runSmaliTest(name, false), 3); + } + } + } } + private static boolean $inline$true() { return true; } + private static boolean $inline$false() { return false; } + public static boolean booleanField; } diff --git a/test/463-checker-boolean-simplifier/src/Main.java b/test/463-checker-boolean-simplifier/src/Main.java index 682f12641f..f0fe1b172f 100644 --- a/test/463-checker-boolean-simplifier/src/Main.java +++ b/test/463-checker-boolean-simplifier/src/Main.java @@ -42,7 +42,7 @@ public class Main { /// CHECK-DAG: <> IntConstant 0 /// CHECK-DAG: <> IntConstant 1 /// CHECK-DAG: If [<>] - /// CHECK-DAG: <> Phi [<>,<>] + /// CHECK-DAG: <> Phi [<>,<>] /// CHECK-DAG: Return [<>] /// CHECK-START: boolean Main.BooleanNot(boolean) select_generator (before) @@ -185,11 +185,7 @@ public class Main { /// CHECK-NOT: BooleanNot public static int NegatedCondition(boolean x) { - if (x != false) { - return 42; - } else { - return 43; - } + return (x != false) ? 42 : 43; } /// CHECK-START: int Main.SimpleTrueBlock(boolean, int) select_generator (after) @@ -253,13 +249,7 @@ public class Main { /// CHECK-DAG: Return [<>] public static int ThreeBlocks(boolean x, boolean y) { - if (x) { - return 1; - } else if (y) { - return 2; - } else { - return 3; - } + return x ? 1 : (y ? 2 : 3); } /// CHECK-START: int Main.MultiplePhis() select_generator (before) @@ -292,8 +282,10 @@ public class Main { while (y++ < 10) { if (y > 1) { x = 13; + continue; } else { x = 42; + continue; } } return x; diff --git a/test/537-checker-inline-and-unverified/src/Main.java b/test/537-checker-inline-and-unverified/src/Main.java index bdc14b027c..b9d5fc98cc 100644 --- a/test/537-checker-inline-and-unverified/src/Main.java +++ b/test/537-checker-inline-and-unverified/src/Main.java @@ -45,12 +45,14 @@ public class Main { } public static boolean $opt$noinline$testNoInline() { + boolean result = true; try { - return null instanceof InaccessibleClass; + result = (null instanceof InaccessibleClass); + throw new Error("Unreachable"); } catch (IllegalAccessError e) { // expected } - return false; + return result; } public static boolean $opt$inline$testInline() { diff --git a/test/555-checker-regression-x86const/build b/test/555-checker-regression-x86const/build index 09dcc363dd..92ddfc9a58 100644 --- a/test/555-checker-regression-x86const/build +++ b/test/555-checker-regression-x86const/build @@ -27,14 +27,12 @@ mkdir classes-ex mv classes/UnresolvedClass.class classes-ex if [ ${USE_JACK} = "true" ]; then - # Create .jack files from classes generated with javac. - ${JILL} classes --output classes.jack - ${JILL} classes-ex --output classes-ex.jack + jar cf classes.jill.jar -C classes . + jar cf classes-ex.jill.jar -C classes-ex . - # Create DEX files from .jack files. - ${JACK} --import classes.jack --output-dex . + ${JACK} --import classes.jill.jar --output-dex . zip $TEST_NAME.jar classes.dex - ${JACK} --import classes-ex.jack --output-dex . + ${JACK} --import classes-ex.jill.jar --output-dex . zip ${TEST_NAME}-ex.jar classes.dex else if [ ${NEED_DEX} = "true" ]; then diff --git a/test/565-checker-doublenegbitwise/src/Main.java b/test/565-checker-doublenegbitwise/src/Main.java index 2d70e111aa..e426b75bf0 100644 --- a/test/565-checker-doublenegbitwise/src/Main.java +++ b/test/565-checker-doublenegbitwise/src/Main.java @@ -35,14 +35,11 @@ public class Main { * Test transformation of Not/Not/And into Or/Not. */ - // Note: before the instruction_simplifier pass, Xor's are used instead of - // Not's (the simplification happens during the same pass). /// CHECK-START: int Main.$opt$noinline$andToOr(int, int) instruction_simplifier (before) /// CHECK: <> ParameterValue /// CHECK: <> ParameterValue - /// CHECK: <> IntConstant -1 - /// CHECK: <> Xor [<>,<>] - /// CHECK: <> Xor [<>,<>] + /// CHECK: <> Not [<>] + /// CHECK: <> Not [<>] /// CHECK: <> And [<>,<>] /// CHECK: Return [<>] @@ -106,14 +103,11 @@ public class Main { * Test transformation of Not/Not/Or into And/Not. */ - // See note above. - // The second Xor has its arguments reversed for no obvious reason. /// CHECK-START: long Main.$opt$noinline$orToAnd(long, long) instruction_simplifier (before) /// CHECK: <> ParameterValue /// CHECK: <> ParameterValue - /// CHECK: <> LongConstant -1 - /// CHECK: <> Xor [<>,<>] - /// CHECK: <> Xor [<>,<>] + /// CHECK: <> Not [<>] + /// CHECK: <> Not [<>] /// CHECK: <> Or [<>,<>] /// CHECK: Return [<>] @@ -183,12 +177,11 @@ public class Main { /// CHECK-START: int Main.$opt$noinline$regressInputsAway(int, int) instruction_simplifier (before) /// CHECK: <> ParameterValue /// CHECK: <> ParameterValue - /// CHECK-DAG: <> IntConstant 1 - /// CHECK-DAG: <> IntConstant -1 + /// CHECK: <> IntConstant 1 /// CHECK: <> Add [<>,<>] - /// CHECK: <> Xor [<>,<>] + /// CHECK: <> Not [<>] /// CHECK: <> Add [<>,<>] - /// CHECK: <> Xor [<>,<>] + /// CHECK: <> Not [<>] /// CHECK: <> Or [<>,<>] /// CHECK: Return [<>] @@ -226,9 +219,8 @@ public class Main { /// CHECK-START: int Main.$opt$noinline$notXorToXor(int, int) instruction_simplifier (before) /// CHECK: <> ParameterValue /// CHECK: <> ParameterValue - /// CHECK: <> IntConstant -1 - /// CHECK: <> Xor [<>,<>] - /// CHECK: <> Xor [<>,<>] + /// CHECK: <> Not [<>] + /// CHECK: <> Not [<>] /// CHECK: <> Xor [<>,<>] /// CHECK: Return [<>] @@ -285,11 +277,10 @@ public class Main { /// CHECK-START: int Main.$opt$noinline$notMultipleUses(int, int) instruction_simplifier (before) /// CHECK: <> ParameterValue /// CHECK: <> ParameterValue - /// CHECK: <> IntConstant -1 /// CHECK: <> IntConstant 1 - /// CHECK: <> Xor [<>,<>] + /// CHECK: <> Not [<>] /// CHECK: <> And [<>,<>] - /// CHECK: <> Xor [<>,<>] + /// CHECK: <> Not [<>] /// CHECK: <> And [<>,<>] /// CHECK: <> Add [<>,<>] /// CHECK: Return [<>] diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index 7406aa33ed..19b535858f 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -223,9 +223,12 @@ ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES), # Disable 097-duplicate-method while investigation (broken by latest Jack release, b/27358065) # Disable 137-cfi (b/27391690). +# Disable 536-checker-needs-access-check and 537-checker-inline-and-unverified (b/27425061) TEST_ART_BROKEN_ALL_TARGET_TESTS := \ 097-duplicate-method \ - 137-cfi + 137-cfi \ + 536-checker-needs-access-check \ + 537-checker-inline-and-unverified \ ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \ $(COMPILER_TYPES), $(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \ diff --git a/test/run-test b/test/run-test index f1875d71a5..d0f93b9231 100755 --- a/test/run-test +++ b/test/run-test @@ -677,11 +677,7 @@ function arch_supports_read_barrier() { # Tests named '-checker-*' will also have their CFGs verified with # Checker when compiled with Optimizing on host. if [[ "$TEST_NAME" =~ ^[0-9]+-checker- ]]; then - # Jack does not necessarily generate the same DEX output than dx. Because these tests depend - # on a particular DEX output, keep building them with dx for now (b/19467889). - USE_JACK="false" - - if [ "$runtime" = "art" -a "$image_suffix" = "-optimizing" ]; then + if [ "$runtime" = "art" -a "$image_suffix" = "-optimizing" -a "$USE_JACK" = "true" ]; then # Optimizing has read barrier support for certain architectures # only. On other architectures, compiling is disabled when read # barriers are enabled, meaning that we do not produce a CFG file -- GitLab From c90bc92bc577020ff4d3caced4cee1cdf41fa5de Mon Sep 17 00:00:00 2001 From: Calin Juravle Date: Wed, 24 Feb 2016 10:13:09 +0000 Subject: [PATCH 095/204] Record foreign dex files loaded by the app in the profile A foreign dex file is a file which is not owned by the app (it's not part of its code paths or its private data directory). When such a dex file is loaded by the app, the runtime will record a marker in a dedicated profile folder (foreing_dex_profile_path). The marker is just a file named after the canonical location of the dex file where '/' is replaced by '@'. The markers will be used by the system server system server to decide if the apk should be fully or profile guide compiled. Bug: 27334750 Bug: 26080105 (cherry picked from commit 86a9ebe4197e963249ffbbaa1830da97ed642fa5) Change-Id: I8be1fd4d854fa1e23c3c1054c9c083ad7b27317b --- runtime/jit/jit.cc | 6 +- runtime/jit/jit.h | 12 +- runtime/jit/profile_saver.cc | 112 ++++++++++- runtime/jit/profile_saver.h | 18 +- runtime/native/dalvik_system_VMRuntime.cc | 24 ++- runtime/oat_file_manager.cc | 4 + runtime/runtime.cc | 18 +- runtime/runtime.h | 5 +- test/577-profile-foreign-dex/expected.txt | 0 test/577-profile-foreign-dex/info.txt | 1 + test/577-profile-foreign-dex/run | 20 ++ .../src-ex/OtherDex.java | 17 ++ test/577-profile-foreign-dex/src/Main.java | 175 ++++++++++++++++++ 13 files changed, 397 insertions(+), 15 deletions(-) create mode 100644 test/577-profile-foreign-dex/expected.txt create mode 100644 test/577-profile-foreign-dex/info.txt create mode 100644 test/577-profile-foreign-dex/run create mode 100644 test/577-profile-foreign-dex/src-ex/OtherDex.java create mode 100644 test/577-profile-foreign-dex/src/Main.java diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc index 3e66ce20eb..4623a4a3a2 100644 --- a/runtime/jit/jit.cc +++ b/runtime/jit/jit.cc @@ -194,9 +194,11 @@ void Jit::DeleteThreadPool() { } void Jit::StartProfileSaver(const std::string& filename, - const std::vector& code_paths) { + const std::vector& code_paths, + const std::string& foreign_dex_profile_path, + const std::string& app_dir) { if (save_profiling_info_) { - ProfileSaver::Start(filename, code_cache_.get(), code_paths); + ProfileSaver::Start(filename, code_cache_.get(), code_paths, foreign_dex_profile_path, app_dir); } } diff --git a/runtime/jit/jit.h b/runtime/jit/jit.h index 109ca3dbd1..570f683598 100644 --- a/runtime/jit/jit.h +++ b/runtime/jit/jit.h @@ -70,7 +70,17 @@ class Jit { return instrumentation_cache_.get(); } - void StartProfileSaver(const std::string& filename, const std::vector& code_paths); + // Starts the profile saver if the config options allow profile recording. + // The profile will be stored in the specified `filename` and will contain + // information collected from the given `code_paths` (a set of dex locations). + // The `foreign_dex_profile_path` is the path where the saver will put the + // profile markers for loaded dex files which are not owned by the application. + // The `app_dir` is the application directory and is used to decide which + // dex files belong to the application. + void StartProfileSaver(const std::string& filename, + const std::vector& code_paths, + const std::string& foreign_dex_profile_path, + const std::string& app_dir); void StopProfileSaver(); void DumpForSigQuit(std::ostream& os) { diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc index ab26f6ffa9..5abfa6c6a0 100644 --- a/runtime/jit/profile_saver.cc +++ b/runtime/jit/profile_saver.cc @@ -16,6 +16,10 @@ #include "profile_saver.h" +#include +#include +#include + #include "art_method-inl.h" #include "scoped_thread_state_change.h" #include "oat_file_manager.h" @@ -42,14 +46,31 @@ pthread_t ProfileSaver::profiler_pthread_ = 0U; ProfileSaver::ProfileSaver(const std::string& output_filename, jit::JitCodeCache* jit_code_cache, - const std::vector& code_paths) + const std::vector& code_paths, + const std::string& foreign_dex_profile_path, + const std::string& app_data_dir) : jit_code_cache_(jit_code_cache), + foreign_dex_profile_path_(foreign_dex_profile_path), code_cache_last_update_time_ns_(0), shutting_down_(false), first_profile_(true), wait_lock_("ProfileSaver wait lock"), period_condition_("ProfileSaver period condition", wait_lock_) { AddTrackedLocations(output_filename, code_paths); + app_data_dir_ = ""; + if (!app_data_dir.empty()) { + // The application directory is used to determine which dex files are owned by app. + // Since it could be a symlink (e.g. /data/data instead of /data/user/0), and we + // don't have control over how the dex files are actually loaded (symlink or canonical path), + // store it's canonical form to be sure we use the same base when comparing. + UniqueCPtr app_data_dir_real_path(realpath(app_data_dir.c_str(), nullptr)); + if (app_data_dir_real_path != nullptr) { + app_data_dir_.assign(app_data_dir_real_path.get()); + } else { + LOG(WARNING) << "Failed to get the real path for app dir: " << app_data_dir_ + << ". The app dir will not be used to determine which dex files belong to the app"; + } + } } void ProfileSaver::Run() { @@ -162,7 +183,9 @@ void* ProfileSaver::RunProfileSaverThread(void* arg) { void ProfileSaver::Start(const std::string& output_filename, jit::JitCodeCache* jit_code_cache, - const std::vector& code_paths) { + const std::vector& code_paths, + const std::string& foreign_dex_profile_path, + const std::string& app_data_dir) { DCHECK(Runtime::Current()->UseJit()); DCHECK(!output_filename.empty()); DCHECK(jit_code_cache != nullptr); @@ -181,7 +204,11 @@ void ProfileSaver::Start(const std::string& output_filename, VLOG(profiler) << "Starting profile saver using output file: " << output_filename << ". Tracking: " << Join(code_paths, ':'); - instance_ = new ProfileSaver(output_filename, jit_code_cache, code_paths); + instance_ = new ProfileSaver(output_filename, + jit_code_cache, + code_paths, + foreign_dex_profile_path, + app_data_dir); // Create a new thread which does the saving. CHECK_PTHREAD_CALL( @@ -248,4 +275,83 @@ void ProfileSaver::AddTrackedLocations(const std::string& output_filename, } } +void ProfileSaver::NotifyDexUse(const std::string& dex_location) { + std::set app_code_paths; + std::string foreign_dex_profile_path; + std::string app_data_dir; + { + MutexLock mu(Thread::Current(), *Locks::profiler_lock_); + DCHECK(instance_ != nullptr); + // Make a copy so that we don't hold the lock while doing I/O. + for (const auto& it : instance_->tracked_dex_base_locations_) { + app_code_paths.insert(it.second.begin(), it.second.end()); + } + foreign_dex_profile_path = instance_->foreign_dex_profile_path_; + app_data_dir = instance_->app_data_dir_; + } + + MaybeRecordDexUseInternal(dex_location, + app_code_paths, + foreign_dex_profile_path, + app_data_dir); +} + +void ProfileSaver::MaybeRecordDexUseInternal( + const std::string& dex_location, + const std::set& app_code_paths, + const std::string& foreign_dex_profile_path, + const std::string& app_data_dir) { + if (foreign_dex_profile_path.empty()) { + LOG(WARNING) << "Asked to record foreign dex use without a valid profile path "; + return; + } + + UniqueCPtr dex_location_real_path(realpath(dex_location.c_str(), nullptr)); + std::string dex_location_real_path_str(dex_location_real_path.get()); + + if (dex_location_real_path_str.compare(0, app_data_dir.length(), app_data_dir) == 0) { + // The dex location is under the application folder. Nothing to record. + return; + } + + if (app_code_paths.find(dex_location) != app_code_paths.end()) { + // The dex location belongs to the application code paths. Nothing to record. + return; + } + // Do another round of checks with the real paths. + // Note that we could cache all the real locations in the saver (since it's an expensive + // operation). However we expect that app_code_paths is small (usually 1 element), and + // NotifyDexUse is called just a few times in the app lifetime. So we make the compromise + // to save some bytes of memory usage. + for (const auto& app_code_location : app_code_paths) { + UniqueCPtr real_app_code_location(realpath(app_code_location.c_str(), nullptr)); + std::string real_app_code_location_str(real_app_code_location.get()); + if (real_app_code_location_str == dex_location_real_path_str) { + // The dex location belongs to the application code paths. Nothing to record. + return; + } + } + + // For foreign dex files we record a flag on disk. PackageManager will (potentially) take this + // into account when deciding how to optimize the loaded dex file. + // The expected flag name is the canonical path of the apk where '/' is substituted to '@'. + // (it needs to be kept in sync with + // frameworks/base/services/core/java/com/android/server/pm/PackageDexOptimizer.java) + std::replace(dex_location_real_path_str.begin(), dex_location_real_path_str.end(), '/', '@'); + std::string flag_path = foreign_dex_profile_path + "/" + dex_location_real_path_str; + // No need to give any sort of access to flag_path. The system has enough permissions + // to test for its existence. + int fd = TEMP_FAILURE_RETRY(open(flag_path.c_str(), O_CREAT | O_EXCL, 0)); + if (fd != -1) { + if (close(fd) != 0) { + PLOG(WARNING) << "Could not close file after flagging foreign dex use " << flag_path; + } + } else { + if (errno != EEXIST) { + // Another app could have already created the file. + PLOG(WARNING) << "Could not create foreign dex use mark " << flag_path; + } + } +} + } // namespace art diff --git a/runtime/jit/profile_saver.h b/runtime/jit/profile_saver.h index 21017c1acd..e7eab95f3d 100644 --- a/runtime/jit/profile_saver.h +++ b/runtime/jit/profile_saver.h @@ -30,7 +30,9 @@ class ProfileSaver { // If the saver is already running it adds (output_filename, code_paths) to its tracked locations. static void Start(const std::string& output_filename, jit::JitCodeCache* jit_code_cache, - const std::vector& code_paths) + const std::vector& code_paths, + const std::string& foreign_dex_profile_path, + const std::string& app_data_dir) REQUIRES(!Locks::profiler_lock_, !wait_lock_); // Stops the profile saver thread. @@ -42,10 +44,14 @@ class ProfileSaver { // Returns true if the profile saver is started. static bool IsStarted() REQUIRES(!Locks::profiler_lock_); + static void NotifyDexUse(const std::string& dex_location); + private: ProfileSaver(const std::string& output_filename, jit::JitCodeCache* jit_code_cache, - const std::vector& code_paths); + const std::vector& code_paths, + const std::string& foreign_dex_profile_path, + const std::string& app_data_dir); // NO_THREAD_SAFETY_ANALYSIS for static function calling into member function with excludes lock. static void* RunProfileSaverThread(void* arg) @@ -64,6 +70,12 @@ class ProfileSaver { const std::vector& code_paths) REQUIRES(Locks::profiler_lock_); + static void MaybeRecordDexUseInternal( + const std::string& dex_location, + const std::set& tracked_locations, + const std::string& foreign_dex_profile_path, + const std::string& app_data_dir); + // The only instance of the saver. static ProfileSaver* instance_ GUARDED_BY(Locks::profiler_lock_); // Profile saver thread. @@ -72,6 +84,8 @@ class ProfileSaver { jit::JitCodeCache* jit_code_cache_; SafeMap> tracked_dex_base_locations_ GUARDED_BY(Locks::profiler_lock_); + std::string foreign_dex_profile_path_; + std::string app_data_dir_; uint64_t code_cache_last_update_time_ns_; bool shutting_down_ GUARDED_BY(Locks::profiler_lock_); bool first_profile_ = true; diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc index da4a891ff5..f6b2f21515 100644 --- a/runtime/native/dalvik_system_VMRuntime.cc +++ b/runtime/native/dalvik_system_VMRuntime.cc @@ -566,8 +566,9 @@ static void VMRuntime_preloadDexCaches(JNIEnv* env, jobject) { static void VMRuntime_registerAppInfo(JNIEnv* env, jclass clazz ATTRIBUTE_UNUSED, jstring profile_file, - jstring app_dir ATTRIBUTE_UNUSED, // TODO: remove argument - jobjectArray code_paths) { + jstring app_dir, + jobjectArray code_paths, + jstring foreign_dex_profile_path) { std::vector code_paths_vec; int code_paths_length = env->GetArrayLength(code_paths); for (int i = 0; i < code_paths_length; i++) { @@ -581,7 +582,22 @@ static void VMRuntime_registerAppInfo(JNIEnv* env, std::string profile_file_str(raw_profile_file); env->ReleaseStringUTFChars(profile_file, raw_profile_file); - Runtime::Current()->RegisterAppInfo(code_paths_vec, profile_file_str); + std::string foreign_dex_profile_path_str = ""; + if (foreign_dex_profile_path != nullptr) { + const char* raw_foreign_dex_profile_path = + env->GetStringUTFChars(foreign_dex_profile_path, nullptr); + foreign_dex_profile_path_str.assign(raw_foreign_dex_profile_path); + env->ReleaseStringUTFChars(foreign_dex_profile_path, raw_foreign_dex_profile_path); + } + + const char* raw_app_dir = env->GetStringUTFChars(app_dir, nullptr); + std::string app_dir_str(raw_app_dir); + env->ReleaseStringUTFChars(app_dir, raw_app_dir); + + Runtime::Current()->RegisterAppInfo(code_paths_vec, + profile_file_str, + foreign_dex_profile_path_str, + app_dir_str); } static jboolean VMRuntime_isBootClassPathOnDisk(JNIEnv* env, jclass, jstring java_instruction_set) { @@ -638,7 +654,7 @@ static JNINativeMethod gMethods[] = { NATIVE_METHOD(VMRuntime, isCheckJniEnabled, "!()Z"), NATIVE_METHOD(VMRuntime, preloadDexCaches, "()V"), NATIVE_METHOD(VMRuntime, registerAppInfo, - "(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;)V"), + "(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)V"), NATIVE_METHOD(VMRuntime, isBootClassPathOnDisk, "(Ljava/lang/String;)Z"), NATIVE_METHOD(VMRuntime, getCurrentInstructionSet, "()Ljava/lang/String;"), }; diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc index ea26d58767..3e6d0b5681 100644 --- a/runtime/oat_file_manager.cc +++ b/runtime/oat_file_manager.cc @@ -442,6 +442,10 @@ std::vector> OatFileManager::OpenDexFilesFromOat( + std::string(dex_location)); } } + + // TODO(calin): Consider optimizing this knowing that is useless to record the + // use of fully compiled apks. + Runtime::Current()->NotifyDexLoaded(dex_location); return dex_files; } diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 47ef2143ff..c2d2dca024 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -120,6 +120,7 @@ #include "os.h" #include "parsed_options.h" #include "profiler.h" +#include "jit/profile_saver.h" #include "quick/quick_method_frame_info.h" #include "reflection.h" #include "runtime_options.h" @@ -1717,7 +1718,9 @@ void Runtime::SetCalleeSaveMethod(ArtMethod* method, CalleeSaveType type) { } void Runtime::RegisterAppInfo(const std::vector& code_paths, - const std::string& profile_output_filename) { + const std::string& profile_output_filename, + const std::string& foreign_dex_profile_path, + const std::string& app_dir) { if (jit_.get() == nullptr) { // We are not JITing. Nothing to do. return; @@ -1740,7 +1743,18 @@ void Runtime::RegisterAppInfo(const std::vector& code_paths, } profile_output_filename_ = profile_output_filename; - jit_->StartProfileSaver(profile_output_filename, code_paths); + jit_->StartProfileSaver(profile_output_filename, + code_paths, + foreign_dex_profile_path, + app_dir); +} + +void Runtime::NotifyDexLoaded(const std::string& dex_location) { + VLOG(profiler) << "Notify dex loaded: " << dex_location; + // We know that if the ProfileSaver is started then we can record profile information. + if (ProfileSaver::IsStarted()) { + ProfileSaver::NotifyDexUse(dex_location); + } } // Transaction support. diff --git a/runtime/runtime.h b/runtime/runtime.h index 83e77d2372..8e99f800e0 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -467,7 +467,10 @@ class Runtime { } void RegisterAppInfo(const std::vector& code_paths, - const std::string& profile_output_filename); + const std::string& profile_output_filename, + const std::string& foreign_dex_profile_path, + const std::string& app_dir); + void NotifyDexLoaded(const std::string& dex_location); // Transaction support. bool IsActiveTransaction() const { diff --git a/test/577-profile-foreign-dex/expected.txt b/test/577-profile-foreign-dex/expected.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/577-profile-foreign-dex/info.txt b/test/577-profile-foreign-dex/info.txt new file mode 100644 index 0000000000..090db3fdc6 --- /dev/null +++ b/test/577-profile-foreign-dex/info.txt @@ -0,0 +1 @@ +Check that we record the use of foreign dex files when profiles are enabled. diff --git a/test/577-profile-foreign-dex/run b/test/577-profile-foreign-dex/run new file mode 100644 index 0000000000..ad57d14c60 --- /dev/null +++ b/test/577-profile-foreign-dex/run @@ -0,0 +1,20 @@ +#!/bin/bash +# +# Copyright 2016 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +exec ${RUN} \ + --runtime-option -Xjitsaveprofilinginfo \ + --runtime-option -Xusejit:true \ + "${@}" diff --git a/test/577-profile-foreign-dex/src-ex/OtherDex.java b/test/577-profile-foreign-dex/src-ex/OtherDex.java new file mode 100644 index 0000000000..cba73b3094 --- /dev/null +++ b/test/577-profile-foreign-dex/src-ex/OtherDex.java @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +public class OtherDex { +} diff --git a/test/577-profile-foreign-dex/src/Main.java b/test/577-profile-foreign-dex/src/Main.java new file mode 100644 index 0000000000..0cd85b58e8 --- /dev/null +++ b/test/577-profile-foreign-dex/src/Main.java @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.lang.reflect.Method; +import java.lang.reflect.Constructor; +import java.util.HashMap; + +public class Main { + + private static final String PROFILE_NAME = "primary.prof"; + private static final String APP_DIR_PREFIX = "app_dir_"; + private static final String FOREIGN_DEX_PROFILE_DIR = "foreign-dex"; + private static final String TEMP_FILE_NAME_PREFIX = "dummy"; + private static final String TEMP_FILE_NAME_SUFFIX = "-file"; + + public static void main(String[] args) throws Exception { + File tmpFile = null; + File appDir = null; + File profileFile = null; + File foreignDexProfileDir = null; + + try { + // Create the necessary files layout. + tmpFile = createTempFile(); + appDir = new File(tmpFile.getParent(), APP_DIR_PREFIX + tmpFile.getName()); + appDir.mkdir(); + foreignDexProfileDir = new File(tmpFile.getParent(), FOREIGN_DEX_PROFILE_DIR); + foreignDexProfileDir.mkdir(); + profileFile = createTempFile(); + + String codePath = System.getenv("DEX_LOCATION") + "/577-profile-foreign-dex.jar"; + + // Register the app with the runtime + VMRuntime.registerAppInfo(profileFile.getPath(), appDir.getPath(), + new String[] { codePath }, foreignDexProfileDir.getPath()); + + testMarkerForForeignDex(foreignDexProfileDir); + testMarkerForCodePath(foreignDexProfileDir); + testMarkerForApplicationDexFile(foreignDexProfileDir, appDir); + } finally { + if (tmpFile != null) { + tmpFile.delete(); + } + if (profileFile != null) { + profileFile.delete(); + } + if (foreignDexProfileDir != null) { + foreignDexProfileDir.delete(); + } + if (appDir != null) { + appDir.delete(); + } + } + } + + // Verify we actually create a marker on disk for foreign dex files. + private static void testMarkerForForeignDex(File foreignDexProfileDir) throws Exception { + String foreignDex = System.getenv("DEX_LOCATION") + "/577-profile-foreign-dex-ex.jar"; + loadDexFile(foreignDex); + checkMarker(foreignDexProfileDir, foreignDex, /* exists */ true); + } + + // Verify we do not create a marker on disk for dex files path of the code path. + private static void testMarkerForCodePath(File foreignDexProfileDir) throws Exception { + String codePath = System.getenv("DEX_LOCATION") + "/577-profile-foreign-dex.jar"; + loadDexFile(codePath); + checkMarker(foreignDexProfileDir, codePath, /* exists */ false); + } + + private static void testMarkerForApplicationDexFile(File foreignDexProfileDir, File appDir) + throws Exception { + // Copy the -ex jar to the application directory and load it from there. + // This will record duplicate class conflicts but we don't care for this use case. + File foreignDex = new File(System.getenv("DEX_LOCATION") + "/577-profile-foreign-dex-ex.jar"); + File appDex = new File(appDir, "appDex.jar"); + try { + copyFile(foreignDex, appDex); + + loadDexFile(appDex.getAbsolutePath()); + checkMarker(foreignDexProfileDir, appDex.getAbsolutePath(), /* exists */ false); + } finally { + if (appDex != null) { + appDex.delete(); + } + } + } + + private static void checkMarker(File foreignDexProfileDir, String dexFile, boolean exists) { + File marker = new File(foreignDexProfileDir, dexFile.replace('/', '@')); + boolean result_ok = exists ? marker.exists() : !marker.exists(); + if (!result_ok) { + throw new RuntimeException("Marker test failed for:" + marker.getPath()); + } + } + + private static void loadDexFile(String dexFile) throws Exception { + Class pathClassLoader = Class.forName("dalvik.system.PathClassLoader"); + if (pathClassLoader == null) { + throw new RuntimeException("Couldn't find path class loader class"); + } + Constructor constructor = + pathClassLoader.getDeclaredConstructor(String.class, ClassLoader.class); + constructor.newInstance( + dexFile, ClassLoader.getSystemClassLoader()); + } + + private static class VMRuntime { + private static final Method registerAppInfoMethod; + static { + try { + Class c = Class.forName("dalvik.system.VMRuntime"); + registerAppInfoMethod = c.getDeclaredMethod("registerAppInfo", + String.class, String.class, String[].class, String.class); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public static void registerAppInfo(String pkgName, String appDir, + String[] codePath, String foreignDexProfileDir) throws Exception { + registerAppInfoMethod.invoke(null, pkgName, appDir, codePath, foreignDexProfileDir); + } + } + + private static void copyFile(File fromFile, File toFile) throws Exception { + FileInputStream in = new FileInputStream(fromFile); + FileOutputStream out = new FileOutputStream(toFile); + try { + byte[] buffer = new byte[4096]; + int bytesRead; + while ((bytesRead = in.read(buffer)) >= 0) { + out.write(buffer, 0, bytesRead); + } + } finally { + out.flush(); + try { + out.getFD().sync(); + } catch (IOException e) { + } + out.close(); + in.close(); + } + } + + private static File createTempFile() throws Exception { + try { + return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX); + } catch (IOException e) { + System.setProperty("java.io.tmpdir", "/data/local/tmp"); + try { + return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX); + } catch (IOException e2) { + System.setProperty("java.io.tmpdir", "/sdcard"); + return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX); + } + } + } +} -- GitLab From 40e4ba2d2d433088f9c09a4a39c3b6d81dc3352b Mon Sep 17 00:00:00 2001 From: David Brazdil Date: Wed, 2 Mar 2016 13:25:57 +0000 Subject: [PATCH 096/204] Fix one more Checker test after switch to Jack Bug: 25635944 Change-Id: I61780b3505104f2b8a62bc2dee889b8cd444c696 --- test/565-checker-condition-liveness/src/Main.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/test/565-checker-condition-liveness/src/Main.java b/test/565-checker-condition-liveness/src/Main.java index dc4cb76258..acfcecdba8 100644 --- a/test/565-checker-condition-liveness/src/Main.java +++ b/test/565-checker-condition-liveness/src/Main.java @@ -28,10 +28,7 @@ public class Main { /// CHECK-EVAL: <> == <> + 1 public static int p(float arg) { - if (arg > 5.0f) { - return 0; - } - return -1; + return (arg > 5.0f) ? 0 : -1; } /// CHECK-START: void Main.main(java.lang.String[]) liveness (after) -- GitLab From c03d7b6bcac15ce8f829fee654b91a7e4583769d Mon Sep 17 00:00:00 2001 From: David Brazdil Date: Wed, 2 Mar 2016 12:18:03 +0000 Subject: [PATCH 097/204] oatdump: Add option to print just the header Sometimes that's all one needs. Change-Id: I0c442ed32f1662ad0b37dc2fccedd964ed1b1466 --- oatdump/oatdump.cc | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index e30b968a96..c1875366e9 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -361,6 +361,7 @@ class OatDumperOptions { const char* method_filter, bool list_classes, bool list_methods, + bool dump_header_only, const char* export_dex_location, uint32_t addr2instr) : dump_raw_mapping_table_(dump_raw_mapping_table), @@ -373,6 +374,7 @@ class OatDumperOptions { method_filter_(method_filter), list_classes_(list_classes), list_methods_(list_methods), + dump_header_only_(dump_header_only), export_dex_location_(export_dex_location), addr2instr_(addr2instr), class_loader_(nullptr) {} @@ -387,6 +389,7 @@ class OatDumperOptions { const char* const method_filter_; const bool list_classes_; const bool list_methods_; + const bool dump_header_only_; const char* const export_dex_location_; uint32_t addr2instr_; Handle* class_loader_; @@ -514,21 +517,24 @@ class OatDumper { os << StringPrintf("0x%08x\n\n", resolved_addr2instr_); } - for (size_t i = 0; i < oat_dex_files_.size(); i++) { - const OatFile::OatDexFile* oat_dex_file = oat_dex_files_[i]; - CHECK(oat_dex_file != nullptr); + if (!options_.dump_header_only_) { + for (size_t i = 0; i < oat_dex_files_.size(); i++) { + const OatFile::OatDexFile* oat_dex_file = oat_dex_files_[i]; + CHECK(oat_dex_file != nullptr); - // If file export selected skip file analysis - if (options_.export_dex_location_) { - if (!ExportDexFile(os, *oat_dex_file)) { - success = false; - } - } else { - if (!DumpOatDexFile(os, *oat_dex_file)) { - success = false; + // If file export selected skip file analysis + if (options_.export_dex_location_) { + if (!ExportDexFile(os, *oat_dex_file)) { + success = false; + } + } else { + if (!DumpOatDexFile(os, *oat_dex_file)) { + success = false; + } } } } + os << std::flush; return success; } @@ -2572,6 +2578,8 @@ struct OatdumpArgs : public CmdlineArgs { dump_code_info_stack_maps_ = true; } else if (option == "--no-disassemble") { disassemble_code_ = false; + } else if (option =="--header-only") { + dump_header_only_ = true; } else if (option.starts_with("--symbolize=")) { oat_filename_ = option.substr(strlen("--symbolize=")).data(); symbolize_ = true; @@ -2655,6 +2663,9 @@ struct OatdumpArgs : public CmdlineArgs { " --no-disassemble may be used to disable disassembly.\n" " Example: --no-disassemble\n" "\n" + " --header-only may be used to print only the oat header.\n" + " Example: --header-only\n" + "\n" " --list-classes may be used to list target file classes (can be used with filters).\n" " Example: --list-classes\n" " Example: --list-classes --class-filter=com.example.foo\n" @@ -2697,6 +2708,7 @@ struct OatdumpArgs : public CmdlineArgs { bool symbolize_ = false; bool list_classes_ = false; bool list_methods_ = false; + bool dump_header_only_ = false; uint32_t addr2instr_ = 0; const char* export_dex_location_ = nullptr; }; @@ -2719,6 +2731,7 @@ struct OatdumpMain : public CmdlineMain { args_->method_filter_, args_->list_classes_, args_->list_methods_, + args_->dump_header_only_, args_->export_dex_location_, args_->addr2instr_)); -- GitLab From 00b53b7f3f9ce5996b767b52c28dd846f47a723c Mon Sep 17 00:00:00 2001 From: Alexey Frunze Date: Tue, 2 Feb 2016 20:25:45 -0800 Subject: [PATCH 098/204] Fast ART MIPS64 interpreter Change-Id: I5dda522df0acf9f9df626fe4f5ecfe6c4df600d3 --- runtime/Android.mk | 3 +- runtime/interpreter/interpreter.cc | 18 +- runtime/interpreter/mterp/config_mips64 | 512 +- runtime/interpreter/mterp/mips64/alt_stub.S | 14 + runtime/interpreter/mterp/mips64/bincmp.S | 32 + runtime/interpreter/mterp/mips64/binop.S | 30 + runtime/interpreter/mterp/mips64/binop2addr.S | 30 + runtime/interpreter/mterp/mips64/binopLit16.S | 28 + runtime/interpreter/mterp/mips64/binopLit8.S | 29 + runtime/interpreter/mterp/mips64/binopWide.S | 30 + .../interpreter/mterp/mips64/binopWide2addr.S | 30 + runtime/interpreter/mterp/mips64/entry.S | 85 + runtime/interpreter/mterp/mips64/fallback.S | 2 + runtime/interpreter/mterp/mips64/fbinop.S | 18 + .../interpreter/mterp/mips64/fbinop2addr.S | 17 + runtime/interpreter/mterp/mips64/fbinopWide.S | 18 + .../mterp/mips64/fbinopWide2addr.S | 17 + runtime/interpreter/mterp/mips64/fcmp.S | 32 + runtime/interpreter/mterp/mips64/fcmpWide.S | 32 + runtime/interpreter/mterp/mips64/fcvtFooter.S | 18 + runtime/interpreter/mterp/mips64/fcvtHeader.S | 15 + runtime/interpreter/mterp/mips64/footer.S | 154 + runtime/interpreter/mterp/mips64/header.S | 285 + runtime/interpreter/mterp/mips64/invoke.S | 17 + .../interpreter/mterp/mips64/op_add_double.S | 1 + .../mterp/mips64/op_add_double_2addr.S | 1 + .../interpreter/mterp/mips64/op_add_float.S | 1 + .../mterp/mips64/op_add_float_2addr.S | 1 + runtime/interpreter/mterp/mips64/op_add_int.S | 1 + .../mterp/mips64/op_add_int_2addr.S | 1 + .../mterp/mips64/op_add_int_lit16.S | 1 + .../mterp/mips64/op_add_int_lit8.S | 1 + .../interpreter/mterp/mips64/op_add_long.S | 1 + .../mterp/mips64/op_add_long_2addr.S | 1 + runtime/interpreter/mterp/mips64/op_aget.S | 29 + .../mterp/mips64/op_aget_boolean.S | 1 + .../interpreter/mterp/mips64/op_aget_byte.S | 1 + .../interpreter/mterp/mips64/op_aget_char.S | 1 + .../interpreter/mterp/mips64/op_aget_object.S | 21 + .../interpreter/mterp/mips64/op_aget_short.S | 1 + .../interpreter/mterp/mips64/op_aget_wide.S | 21 + runtime/interpreter/mterp/mips64/op_and_int.S | 1 + .../mterp/mips64/op_and_int_2addr.S | 1 + .../mterp/mips64/op_and_int_lit16.S | 1 + .../mterp/mips64/op_and_int_lit8.S | 1 + .../interpreter/mterp/mips64/op_and_long.S | 1 + .../mterp/mips64/op_and_long_2addr.S | 1 + runtime/interpreter/mterp/mips64/op_aput.S | 29 + .../mterp/mips64/op_aput_boolean.S | 1 + .../interpreter/mterp/mips64/op_aput_byte.S | 1 + .../interpreter/mterp/mips64/op_aput_char.S | 1 + .../interpreter/mterp/mips64/op_aput_object.S | 14 + .../interpreter/mterp/mips64/op_aput_short.S | 1 + .../interpreter/mterp/mips64/op_aput_wide.S | 21 + .../mterp/mips64/op_array_length.S | 12 + .../interpreter/mterp/mips64/op_check_cast.S | 17 + .../interpreter/mterp/mips64/op_cmp_long.S | 13 + .../interpreter/mterp/mips64/op_cmpg_double.S | 1 + .../interpreter/mterp/mips64/op_cmpg_float.S | 1 + .../interpreter/mterp/mips64/op_cmpl_double.S | 1 + .../interpreter/mterp/mips64/op_cmpl_float.S | 1 + runtime/interpreter/mterp/mips64/op_const.S | 9 + .../interpreter/mterp/mips64/op_const_16.S | 7 + runtime/interpreter/mterp/mips64/op_const_4.S | 8 + .../interpreter/mterp/mips64/op_const_class.S | 13 + .../mterp/mips64/op_const_high16.S | 8 + .../mterp/mips64/op_const_string.S | 13 + .../mterp/mips64/op_const_string_jumbo.S | 15 + .../interpreter/mterp/mips64/op_const_wide.S | 13 + .../mterp/mips64/op_const_wide_16.S | 7 + .../mterp/mips64/op_const_wide_32.S | 9 + .../mterp/mips64/op_const_wide_high16.S | 8 + .../interpreter/mterp/mips64/op_div_double.S | 1 + .../mterp/mips64/op_div_double_2addr.S | 1 + .../interpreter/mterp/mips64/op_div_float.S | 1 + .../mterp/mips64/op_div_float_2addr.S | 1 + runtime/interpreter/mterp/mips64/op_div_int.S | 1 + .../mterp/mips64/op_div_int_2addr.S | 1 + .../mterp/mips64/op_div_int_lit16.S | 1 + .../mterp/mips64/op_div_int_lit8.S | 1 + .../interpreter/mterp/mips64/op_div_long.S | 1 + .../mterp/mips64/op_div_long_2addr.S | 1 + .../mterp/mips64/op_double_to_float.S | 8 + .../mterp/mips64/op_double_to_int.S | 23 + .../mterp/mips64/op_double_to_long.S | 23 + .../mterp/mips64/op_fill_array_data.S | 14 + .../mterp/mips64/op_filled_new_array.S | 18 + .../mterp/mips64/op_filled_new_array_range.S | 1 + .../mterp/mips64/op_float_to_double.S | 8 + .../mterp/mips64/op_float_to_int.S | 23 + .../mterp/mips64/op_float_to_long.S | 23 + runtime/interpreter/mterp/mips64/op_goto.S | 23 + runtime/interpreter/mterp/mips64/op_goto_16.S | 22 + runtime/interpreter/mterp/mips64/op_goto_32.S | 27 + runtime/interpreter/mterp/mips64/op_if_eq.S | 1 + runtime/interpreter/mterp/mips64/op_if_eqz.S | 1 + runtime/interpreter/mterp/mips64/op_if_ge.S | 1 + runtime/interpreter/mterp/mips64/op_if_gez.S | 1 + runtime/interpreter/mterp/mips64/op_if_gt.S | 1 + runtime/interpreter/mterp/mips64/op_if_gtz.S | 1 + runtime/interpreter/mterp/mips64/op_if_le.S | 1 + runtime/interpreter/mterp/mips64/op_if_lez.S | 1 + runtime/interpreter/mterp/mips64/op_if_lt.S | 1 + runtime/interpreter/mterp/mips64/op_if_ltz.S | 1 + runtime/interpreter/mterp/mips64/op_if_ne.S | 1 + runtime/interpreter/mterp/mips64/op_if_nez.S | 1 + runtime/interpreter/mterp/mips64/op_iget.S | 26 + .../mterp/mips64/op_iget_boolean.S | 1 + .../mterp/mips64/op_iget_boolean_quick.S | 1 + .../interpreter/mterp/mips64/op_iget_byte.S | 1 + .../mterp/mips64/op_iget_byte_quick.S | 1 + .../interpreter/mterp/mips64/op_iget_char.S | 1 + .../mterp/mips64/op_iget_char_quick.S | 1 + .../interpreter/mterp/mips64/op_iget_object.S | 1 + .../mterp/mips64/op_iget_object_quick.S | 16 + .../interpreter/mterp/mips64/op_iget_quick.S | 14 + .../interpreter/mterp/mips64/op_iget_short.S | 1 + .../mterp/mips64/op_iget_short_quick.S | 1 + .../interpreter/mterp/mips64/op_iget_wide.S | 21 + .../mterp/mips64/op_iget_wide_quick.S | 14 + .../interpreter/mterp/mips64/op_instance_of.S | 23 + .../interpreter/mterp/mips64/op_int_to_byte.S | 1 + .../interpreter/mterp/mips64/op_int_to_char.S | 1 + .../mterp/mips64/op_int_to_double.S | 8 + .../mterp/mips64/op_int_to_float.S | 8 + .../interpreter/mterp/mips64/op_int_to_long.S | 8 + .../mterp/mips64/op_int_to_short.S | 1 + .../mterp/mips64/op_invoke_direct.S | 1 + .../mterp/mips64/op_invoke_direct_range.S | 1 + .../mterp/mips64/op_invoke_interface.S | 8 + .../mterp/mips64/op_invoke_interface_range.S | 1 + .../mterp/mips64/op_invoke_static.S | 1 + .../mterp/mips64/op_invoke_static_range.S | 1 + .../mterp/mips64/op_invoke_super.S | 8 + .../mterp/mips64/op_invoke_super_range.S | 1 + .../mterp/mips64/op_invoke_virtual.S | 8 + .../mterp/mips64/op_invoke_virtual_quick.S | 1 + .../mterp/mips64/op_invoke_virtual_range.S | 1 + .../mips64/op_invoke_virtual_range_quick.S | 1 + runtime/interpreter/mterp/mips64/op_iput.S | 21 + .../mterp/mips64/op_iput_boolean.S | 1 + .../mterp/mips64/op_iput_boolean_quick.S | 1 + .../interpreter/mterp/mips64/op_iput_byte.S | 1 + .../mterp/mips64/op_iput_byte_quick.S | 1 + .../interpreter/mterp/mips64/op_iput_char.S | 1 + .../mterp/mips64/op_iput_char_quick.S | 1 + .../interpreter/mterp/mips64/op_iput_object.S | 11 + .../mterp/mips64/op_iput_object_quick.S | 10 + .../interpreter/mterp/mips64/op_iput_quick.S | 14 + .../interpreter/mterp/mips64/op_iput_short.S | 1 + .../mterp/mips64/op_iput_short_quick.S | 1 + .../interpreter/mterp/mips64/op_iput_wide.S | 15 + .../mterp/mips64/op_iput_wide_quick.S | 14 + .../mterp/mips64/op_long_to_double.S | 8 + .../mterp/mips64/op_long_to_float.S | 8 + .../interpreter/mterp/mips64/op_long_to_int.S | 2 + .../mterp/mips64/op_monitor_enter.S | 14 + .../mterp/mips64/op_monitor_exit.S | 18 + runtime/interpreter/mterp/mips64/op_move.S | 14 + runtime/interpreter/mterp/mips64/op_move_16.S | 14 + .../mterp/mips64/op_move_exception.S | 8 + .../interpreter/mterp/mips64/op_move_from16.S | 14 + .../interpreter/mterp/mips64/op_move_object.S | 1 + .../mterp/mips64/op_move_object_16.S | 1 + .../mterp/mips64/op_move_object_from16.S | 1 + .../interpreter/mterp/mips64/op_move_result.S | 14 + .../mterp/mips64/op_move_result_object.S | 1 + .../mterp/mips64/op_move_result_wide.S | 9 + .../interpreter/mterp/mips64/op_move_wide.S | 9 + .../mterp/mips64/op_move_wide_16.S | 9 + .../mterp/mips64/op_move_wide_from16.S | 9 + .../interpreter/mterp/mips64/op_mul_double.S | 1 + .../mterp/mips64/op_mul_double_2addr.S | 1 + .../interpreter/mterp/mips64/op_mul_float.S | 1 + .../mterp/mips64/op_mul_float_2addr.S | 1 + runtime/interpreter/mterp/mips64/op_mul_int.S | 1 + .../mterp/mips64/op_mul_int_2addr.S | 1 + .../mterp/mips64/op_mul_int_lit16.S | 1 + .../mterp/mips64/op_mul_int_lit8.S | 1 + .../interpreter/mterp/mips64/op_mul_long.S | 1 + .../mterp/mips64/op_mul_long_2addr.S | 1 + .../interpreter/mterp/mips64/op_neg_double.S | 3 + .../interpreter/mterp/mips64/op_neg_float.S | 3 + runtime/interpreter/mterp/mips64/op_neg_int.S | 1 + .../interpreter/mterp/mips64/op_neg_long.S | 1 + .../interpreter/mterp/mips64/op_new_array.S | 19 + .../mterp/mips64/op_new_instance.S | 14 + runtime/interpreter/mterp/mips64/op_nop.S | 3 + runtime/interpreter/mterp/mips64/op_not_int.S | 1 + .../interpreter/mterp/mips64/op_not_long.S | 1 + runtime/interpreter/mterp/mips64/op_or_int.S | 1 + .../mterp/mips64/op_or_int_2addr.S | 1 + .../mterp/mips64/op_or_int_lit16.S | 1 + .../interpreter/mterp/mips64/op_or_int_lit8.S | 1 + runtime/interpreter/mterp/mips64/op_or_long.S | 1 + .../mterp/mips64/op_or_long_2addr.S | 1 + .../mterp/mips64/op_packed_switch.S | 31 + .../interpreter/mterp/mips64/op_rem_double.S | 12 + .../mterp/mips64/op_rem_double_2addr.S | 12 + .../interpreter/mterp/mips64/op_rem_float.S | 12 + .../mterp/mips64/op_rem_float_2addr.S | 12 + runtime/interpreter/mterp/mips64/op_rem_int.S | 1 + .../mterp/mips64/op_rem_int_2addr.S | 1 + .../mterp/mips64/op_rem_int_lit16.S | 1 + .../mterp/mips64/op_rem_int_lit8.S | 1 + .../interpreter/mterp/mips64/op_rem_long.S | 1 + .../mterp/mips64/op_rem_long_2addr.S | 1 + runtime/interpreter/mterp/mips64/op_return.S | 18 + .../mterp/mips64/op_return_object.S | 1 + .../interpreter/mterp/mips64/op_return_void.S | 11 + .../mterp/mips64/op_return_void_no_barrier.S | 9 + .../interpreter/mterp/mips64/op_return_wide.S | 17 + .../interpreter/mterp/mips64/op_rsub_int.S | 1 + .../mterp/mips64/op_rsub_int_lit8.S | 1 + runtime/interpreter/mterp/mips64/op_sget.S | 26 + .../mterp/mips64/op_sget_boolean.S | 1 + .../interpreter/mterp/mips64/op_sget_byte.S | 1 + .../interpreter/mterp/mips64/op_sget_char.S | 1 + .../interpreter/mterp/mips64/op_sget_object.S | 1 + .../interpreter/mterp/mips64/op_sget_short.S | 1 + .../interpreter/mterp/mips64/op_sget_wide.S | 18 + runtime/interpreter/mterp/mips64/op_shl_int.S | 1 + .../mterp/mips64/op_shl_int_2addr.S | 1 + .../mterp/mips64/op_shl_int_lit8.S | 1 + .../interpreter/mterp/mips64/op_shl_long.S | 1 + .../mterp/mips64/op_shl_long_2addr.S | 1 + runtime/interpreter/mterp/mips64/op_shr_int.S | 1 + .../mterp/mips64/op_shr_int_2addr.S | 1 + .../mterp/mips64/op_shr_int_lit8.S | 1 + .../interpreter/mterp/mips64/op_shr_long.S | 1 + .../mterp/mips64/op_shr_long_2addr.S | 1 + .../mterp/mips64/op_sparse_switch.S | 1 + runtime/interpreter/mterp/mips64/op_sput.S | 20 + .../mterp/mips64/op_sput_boolean.S | 1 + .../interpreter/mterp/mips64/op_sput_byte.S | 1 + .../interpreter/mterp/mips64/op_sput_char.S | 1 + .../interpreter/mterp/mips64/op_sput_object.S | 11 + .../interpreter/mterp/mips64/op_sput_short.S | 1 + .../interpreter/mterp/mips64/op_sput_wide.S | 18 + .../interpreter/mterp/mips64/op_sub_double.S | 1 + .../mterp/mips64/op_sub_double_2addr.S | 1 + .../interpreter/mterp/mips64/op_sub_float.S | 1 + .../mterp/mips64/op_sub_float_2addr.S | 1 + runtime/interpreter/mterp/mips64/op_sub_int.S | 1 + .../mterp/mips64/op_sub_int_2addr.S | 1 + .../interpreter/mterp/mips64/op_sub_long.S | 1 + .../mterp/mips64/op_sub_long_2addr.S | 1 + runtime/interpreter/mterp/mips64/op_throw.S | 10 + .../interpreter/mterp/mips64/op_unused_3e.S | 1 + .../interpreter/mterp/mips64/op_unused_3f.S | 1 + .../interpreter/mterp/mips64/op_unused_40.S | 1 + .../interpreter/mterp/mips64/op_unused_41.S | 1 + .../interpreter/mterp/mips64/op_unused_42.S | 1 + .../interpreter/mterp/mips64/op_unused_43.S | 1 + .../interpreter/mterp/mips64/op_unused_79.S | 1 + .../interpreter/mterp/mips64/op_unused_7a.S | 1 + .../interpreter/mterp/mips64/op_unused_f4.S | 1 + .../interpreter/mterp/mips64/op_unused_fa.S | 1 + .../interpreter/mterp/mips64/op_unused_fb.S | 1 + .../interpreter/mterp/mips64/op_unused_fc.S | 1 + .../interpreter/mterp/mips64/op_unused_fd.S | 1 + .../interpreter/mterp/mips64/op_unused_fe.S | 1 + .../interpreter/mterp/mips64/op_unused_ff.S | 1 + .../interpreter/mterp/mips64/op_ushr_int.S | 1 + .../mterp/mips64/op_ushr_int_2addr.S | 1 + .../mterp/mips64/op_ushr_int_lit8.S | 1 + .../interpreter/mterp/mips64/op_ushr_long.S | 1 + .../mterp/mips64/op_ushr_long_2addr.S | 1 + runtime/interpreter/mterp/mips64/op_xor_int.S | 1 + .../mterp/mips64/op_xor_int_2addr.S | 1 + .../mterp/mips64/op_xor_int_lit16.S | 1 + .../mterp/mips64/op_xor_int_lit8.S | 1 + .../interpreter/mterp/mips64/op_xor_long.S | 1 + .../mterp/mips64/op_xor_long_2addr.S | 1 + runtime/interpreter/mterp/mips64/unop.S | 18 + runtime/interpreter/mterp/mips64/unopWide.S | 17 + runtime/interpreter/mterp/mips64/unused.S | 4 + runtime/interpreter/mterp/mips64/zcmp.S | 30 + runtime/interpreter/mterp/out/mterp_mips64.S | 12362 ++++++++++++++++ runtime/interpreter/mterp/rebuild.sh | 3 +- 280 files changed, 14967 insertions(+), 275 deletions(-) create mode 100644 runtime/interpreter/mterp/mips64/alt_stub.S create mode 100644 runtime/interpreter/mterp/mips64/bincmp.S create mode 100644 runtime/interpreter/mterp/mips64/binop.S create mode 100644 runtime/interpreter/mterp/mips64/binop2addr.S create mode 100644 runtime/interpreter/mterp/mips64/binopLit16.S create mode 100644 runtime/interpreter/mterp/mips64/binopLit8.S create mode 100644 runtime/interpreter/mterp/mips64/binopWide.S create mode 100644 runtime/interpreter/mterp/mips64/binopWide2addr.S create mode 100644 runtime/interpreter/mterp/mips64/entry.S create mode 100644 runtime/interpreter/mterp/mips64/fallback.S create mode 100644 runtime/interpreter/mterp/mips64/fbinop.S create mode 100644 runtime/interpreter/mterp/mips64/fbinop2addr.S create mode 100644 runtime/interpreter/mterp/mips64/fbinopWide.S create mode 100644 runtime/interpreter/mterp/mips64/fbinopWide2addr.S create mode 100644 runtime/interpreter/mterp/mips64/fcmp.S create mode 100644 runtime/interpreter/mterp/mips64/fcmpWide.S create mode 100644 runtime/interpreter/mterp/mips64/fcvtFooter.S create mode 100644 runtime/interpreter/mterp/mips64/fcvtHeader.S create mode 100644 runtime/interpreter/mterp/mips64/footer.S create mode 100644 runtime/interpreter/mterp/mips64/header.S create mode 100644 runtime/interpreter/mterp/mips64/invoke.S create mode 100644 runtime/interpreter/mterp/mips64/op_add_double.S create mode 100644 runtime/interpreter/mterp/mips64/op_add_double_2addr.S create mode 100644 runtime/interpreter/mterp/mips64/op_add_float.S create mode 100644 runtime/interpreter/mterp/mips64/op_add_float_2addr.S create mode 100644 runtime/interpreter/mterp/mips64/op_add_int.S create mode 100644 runtime/interpreter/mterp/mips64/op_add_int_2addr.S create mode 100644 runtime/interpreter/mterp/mips64/op_add_int_lit16.S create mode 100644 runtime/interpreter/mterp/mips64/op_add_int_lit8.S create mode 100644 runtime/interpreter/mterp/mips64/op_add_long.S create mode 100644 runtime/interpreter/mterp/mips64/op_add_long_2addr.S create mode 100644 runtime/interpreter/mterp/mips64/op_aget.S create mode 100644 runtime/interpreter/mterp/mips64/op_aget_boolean.S create mode 100644 runtime/interpreter/mterp/mips64/op_aget_byte.S create mode 100644 runtime/interpreter/mterp/mips64/op_aget_char.S create mode 100644 runtime/interpreter/mterp/mips64/op_aget_object.S create mode 100644 runtime/interpreter/mterp/mips64/op_aget_short.S create mode 100644 runtime/interpreter/mterp/mips64/op_aget_wide.S create mode 100644 runtime/interpreter/mterp/mips64/op_and_int.S create mode 100644 runtime/interpreter/mterp/mips64/op_and_int_2addr.S create mode 100644 runtime/interpreter/mterp/mips64/op_and_int_lit16.S create mode 100644 runtime/interpreter/mterp/mips64/op_and_int_lit8.S create mode 100644 runtime/interpreter/mterp/mips64/op_and_long.S create mode 100644 runtime/interpreter/mterp/mips64/op_and_long_2addr.S create mode 100644 runtime/interpreter/mterp/mips64/op_aput.S create mode 100644 runtime/interpreter/mterp/mips64/op_aput_boolean.S create mode 100644 runtime/interpreter/mterp/mips64/op_aput_byte.S create mode 100644 runtime/interpreter/mterp/mips64/op_aput_char.S create mode 100644 runtime/interpreter/mterp/mips64/op_aput_object.S create mode 100644 runtime/interpreter/mterp/mips64/op_aput_short.S create mode 100644 runtime/interpreter/mterp/mips64/op_aput_wide.S create mode 100644 runtime/interpreter/mterp/mips64/op_array_length.S create mode 100644 runtime/interpreter/mterp/mips64/op_check_cast.S create mode 100644 runtime/interpreter/mterp/mips64/op_cmp_long.S create mode 100644 runtime/interpreter/mterp/mips64/op_cmpg_double.S create mode 100644 runtime/interpreter/mterp/mips64/op_cmpg_float.S create mode 100644 runtime/interpreter/mterp/mips64/op_cmpl_double.S create mode 100644 runtime/interpreter/mterp/mips64/op_cmpl_float.S create mode 100644 runtime/interpreter/mterp/mips64/op_const.S create mode 100644 runtime/interpreter/mterp/mips64/op_const_16.S create mode 100644 runtime/interpreter/mterp/mips64/op_const_4.S create mode 100644 runtime/interpreter/mterp/mips64/op_const_class.S create mode 100644 runtime/interpreter/mterp/mips64/op_const_high16.S create mode 100644 runtime/interpreter/mterp/mips64/op_const_string.S create mode 100644 runtime/interpreter/mterp/mips64/op_const_string_jumbo.S create mode 100644 runtime/interpreter/mterp/mips64/op_const_wide.S create mode 100644 runtime/interpreter/mterp/mips64/op_const_wide_16.S create mode 100644 runtime/interpreter/mterp/mips64/op_const_wide_32.S create mode 100644 runtime/interpreter/mterp/mips64/op_const_wide_high16.S create mode 100644 runtime/interpreter/mterp/mips64/op_div_double.S create mode 100644 runtime/interpreter/mterp/mips64/op_div_double_2addr.S create mode 100644 runtime/interpreter/mterp/mips64/op_div_float.S create mode 100644 runtime/interpreter/mterp/mips64/op_div_float_2addr.S create mode 100644 runtime/interpreter/mterp/mips64/op_div_int.S create mode 100644 runtime/interpreter/mterp/mips64/op_div_int_2addr.S create mode 100644 runtime/interpreter/mterp/mips64/op_div_int_lit16.S create mode 100644 runtime/interpreter/mterp/mips64/op_div_int_lit8.S create mode 100644 runtime/interpreter/mterp/mips64/op_div_long.S create mode 100644 runtime/interpreter/mterp/mips64/op_div_long_2addr.S create mode 100644 runtime/interpreter/mterp/mips64/op_double_to_float.S create mode 100644 runtime/interpreter/mterp/mips64/op_double_to_int.S create mode 100644 runtime/interpreter/mterp/mips64/op_double_to_long.S create mode 100644 runtime/interpreter/mterp/mips64/op_fill_array_data.S create mode 100644 runtime/interpreter/mterp/mips64/op_filled_new_array.S create mode 100644 runtime/interpreter/mterp/mips64/op_filled_new_array_range.S create mode 100644 runtime/interpreter/mterp/mips64/op_float_to_double.S create mode 100644 runtime/interpreter/mterp/mips64/op_float_to_int.S create mode 100644 runtime/interpreter/mterp/mips64/op_float_to_long.S create mode 100644 runtime/interpreter/mterp/mips64/op_goto.S create mode 100644 runtime/interpreter/mterp/mips64/op_goto_16.S create mode 100644 runtime/interpreter/mterp/mips64/op_goto_32.S create mode 100644 runtime/interpreter/mterp/mips64/op_if_eq.S create mode 100644 runtime/interpreter/mterp/mips64/op_if_eqz.S create mode 100644 runtime/interpreter/mterp/mips64/op_if_ge.S create mode 100644 runtime/interpreter/mterp/mips64/op_if_gez.S create mode 100644 runtime/interpreter/mterp/mips64/op_if_gt.S create mode 100644 runtime/interpreter/mterp/mips64/op_if_gtz.S create mode 100644 runtime/interpreter/mterp/mips64/op_if_le.S create mode 100644 runtime/interpreter/mterp/mips64/op_if_lez.S create mode 100644 runtime/interpreter/mterp/mips64/op_if_lt.S create mode 100644 runtime/interpreter/mterp/mips64/op_if_ltz.S create mode 100644 runtime/interpreter/mterp/mips64/op_if_ne.S create mode 100644 runtime/interpreter/mterp/mips64/op_if_nez.S create mode 100644 runtime/interpreter/mterp/mips64/op_iget.S create mode 100644 runtime/interpreter/mterp/mips64/op_iget_boolean.S create mode 100644 runtime/interpreter/mterp/mips64/op_iget_boolean_quick.S create mode 100644 runtime/interpreter/mterp/mips64/op_iget_byte.S create mode 100644 runtime/interpreter/mterp/mips64/op_iget_byte_quick.S create mode 100644 runtime/interpreter/mterp/mips64/op_iget_char.S create mode 100644 runtime/interpreter/mterp/mips64/op_iget_char_quick.S create mode 100644 runtime/interpreter/mterp/mips64/op_iget_object.S create mode 100644 runtime/interpreter/mterp/mips64/op_iget_object_quick.S create mode 100644 runtime/interpreter/mterp/mips64/op_iget_quick.S create mode 100644 runtime/interpreter/mterp/mips64/op_iget_short.S create mode 100644 runtime/interpreter/mterp/mips64/op_iget_short_quick.S create mode 100644 runtime/interpreter/mterp/mips64/op_iget_wide.S create mode 100644 runtime/interpreter/mterp/mips64/op_iget_wide_quick.S create mode 100644 runtime/interpreter/mterp/mips64/op_instance_of.S create mode 100644 runtime/interpreter/mterp/mips64/op_int_to_byte.S create mode 100644 runtime/interpreter/mterp/mips64/op_int_to_char.S create mode 100644 runtime/interpreter/mterp/mips64/op_int_to_double.S create mode 100644 runtime/interpreter/mterp/mips64/op_int_to_float.S create mode 100644 runtime/interpreter/mterp/mips64/op_int_to_long.S create mode 100644 runtime/interpreter/mterp/mips64/op_int_to_short.S create mode 100644 runtime/interpreter/mterp/mips64/op_invoke_direct.S create mode 100644 runtime/interpreter/mterp/mips64/op_invoke_direct_range.S create mode 100644 runtime/interpreter/mterp/mips64/op_invoke_interface.S create mode 100644 runtime/interpreter/mterp/mips64/op_invoke_interface_range.S create mode 100644 runtime/interpreter/mterp/mips64/op_invoke_static.S create mode 100644 runtime/interpreter/mterp/mips64/op_invoke_static_range.S create mode 100644 runtime/interpreter/mterp/mips64/op_invoke_super.S create mode 100644 runtime/interpreter/mterp/mips64/op_invoke_super_range.S create mode 100644 runtime/interpreter/mterp/mips64/op_invoke_virtual.S create mode 100644 runtime/interpreter/mterp/mips64/op_invoke_virtual_quick.S create mode 100644 runtime/interpreter/mterp/mips64/op_invoke_virtual_range.S create mode 100644 runtime/interpreter/mterp/mips64/op_invoke_virtual_range_quick.S create mode 100644 runtime/interpreter/mterp/mips64/op_iput.S create mode 100644 runtime/interpreter/mterp/mips64/op_iput_boolean.S create mode 100644 runtime/interpreter/mterp/mips64/op_iput_boolean_quick.S create mode 100644 runtime/interpreter/mterp/mips64/op_iput_byte.S create mode 100644 runtime/interpreter/mterp/mips64/op_iput_byte_quick.S create mode 100644 runtime/interpreter/mterp/mips64/op_iput_char.S create mode 100644 runtime/interpreter/mterp/mips64/op_iput_char_quick.S create mode 100644 runtime/interpreter/mterp/mips64/op_iput_object.S create mode 100644 runtime/interpreter/mterp/mips64/op_iput_object_quick.S create mode 100644 runtime/interpreter/mterp/mips64/op_iput_quick.S create mode 100644 runtime/interpreter/mterp/mips64/op_iput_short.S create mode 100644 runtime/interpreter/mterp/mips64/op_iput_short_quick.S create mode 100644 runtime/interpreter/mterp/mips64/op_iput_wide.S create mode 100644 runtime/interpreter/mterp/mips64/op_iput_wide_quick.S create mode 100644 runtime/interpreter/mterp/mips64/op_long_to_double.S create mode 100644 runtime/interpreter/mterp/mips64/op_long_to_float.S create mode 100644 runtime/interpreter/mterp/mips64/op_long_to_int.S create mode 100644 runtime/interpreter/mterp/mips64/op_monitor_enter.S create mode 100644 runtime/interpreter/mterp/mips64/op_monitor_exit.S create mode 100644 runtime/interpreter/mterp/mips64/op_move.S create mode 100644 runtime/interpreter/mterp/mips64/op_move_16.S create mode 100644 runtime/interpreter/mterp/mips64/op_move_exception.S create mode 100644 runtime/interpreter/mterp/mips64/op_move_from16.S create mode 100644 runtime/interpreter/mterp/mips64/op_move_object.S create mode 100644 runtime/interpreter/mterp/mips64/op_move_object_16.S create mode 100644 runtime/interpreter/mterp/mips64/op_move_object_from16.S create mode 100644 runtime/interpreter/mterp/mips64/op_move_result.S create mode 100644 runtime/interpreter/mterp/mips64/op_move_result_object.S create mode 100644 runtime/interpreter/mterp/mips64/op_move_result_wide.S create mode 100644 runtime/interpreter/mterp/mips64/op_move_wide.S create mode 100644 runtime/interpreter/mterp/mips64/op_move_wide_16.S create mode 100644 runtime/interpreter/mterp/mips64/op_move_wide_from16.S create mode 100644 runtime/interpreter/mterp/mips64/op_mul_double.S create mode 100644 runtime/interpreter/mterp/mips64/op_mul_double_2addr.S create mode 100644 runtime/interpreter/mterp/mips64/op_mul_float.S create mode 100644 runtime/interpreter/mterp/mips64/op_mul_float_2addr.S create mode 100644 runtime/interpreter/mterp/mips64/op_mul_int.S create mode 100644 runtime/interpreter/mterp/mips64/op_mul_int_2addr.S create mode 100644 runtime/interpreter/mterp/mips64/op_mul_int_lit16.S create mode 100644 runtime/interpreter/mterp/mips64/op_mul_int_lit8.S create mode 100644 runtime/interpreter/mterp/mips64/op_mul_long.S create mode 100644 runtime/interpreter/mterp/mips64/op_mul_long_2addr.S create mode 100644 runtime/interpreter/mterp/mips64/op_neg_double.S create mode 100644 runtime/interpreter/mterp/mips64/op_neg_float.S create mode 100644 runtime/interpreter/mterp/mips64/op_neg_int.S create mode 100644 runtime/interpreter/mterp/mips64/op_neg_long.S create mode 100644 runtime/interpreter/mterp/mips64/op_new_array.S create mode 100644 runtime/interpreter/mterp/mips64/op_new_instance.S create mode 100644 runtime/interpreter/mterp/mips64/op_nop.S create mode 100644 runtime/interpreter/mterp/mips64/op_not_int.S create mode 100644 runtime/interpreter/mterp/mips64/op_not_long.S create mode 100644 runtime/interpreter/mterp/mips64/op_or_int.S create mode 100644 runtime/interpreter/mterp/mips64/op_or_int_2addr.S create mode 100644 runtime/interpreter/mterp/mips64/op_or_int_lit16.S create mode 100644 runtime/interpreter/mterp/mips64/op_or_int_lit8.S create mode 100644 runtime/interpreter/mterp/mips64/op_or_long.S create mode 100644 runtime/interpreter/mterp/mips64/op_or_long_2addr.S create mode 100644 runtime/interpreter/mterp/mips64/op_packed_switch.S create mode 100644 runtime/interpreter/mterp/mips64/op_rem_double.S create mode 100644 runtime/interpreter/mterp/mips64/op_rem_double_2addr.S create mode 100644 runtime/interpreter/mterp/mips64/op_rem_float.S create mode 100644 runtime/interpreter/mterp/mips64/op_rem_float_2addr.S create mode 100644 runtime/interpreter/mterp/mips64/op_rem_int.S create mode 100644 runtime/interpreter/mterp/mips64/op_rem_int_2addr.S create mode 100644 runtime/interpreter/mterp/mips64/op_rem_int_lit16.S create mode 100644 runtime/interpreter/mterp/mips64/op_rem_int_lit8.S create mode 100644 runtime/interpreter/mterp/mips64/op_rem_long.S create mode 100644 runtime/interpreter/mterp/mips64/op_rem_long_2addr.S create mode 100644 runtime/interpreter/mterp/mips64/op_return.S create mode 100644 runtime/interpreter/mterp/mips64/op_return_object.S create mode 100644 runtime/interpreter/mterp/mips64/op_return_void.S create mode 100644 runtime/interpreter/mterp/mips64/op_return_void_no_barrier.S create mode 100644 runtime/interpreter/mterp/mips64/op_return_wide.S create mode 100644 runtime/interpreter/mterp/mips64/op_rsub_int.S create mode 100644 runtime/interpreter/mterp/mips64/op_rsub_int_lit8.S create mode 100644 runtime/interpreter/mterp/mips64/op_sget.S create mode 100644 runtime/interpreter/mterp/mips64/op_sget_boolean.S create mode 100644 runtime/interpreter/mterp/mips64/op_sget_byte.S create mode 100644 runtime/interpreter/mterp/mips64/op_sget_char.S create mode 100644 runtime/interpreter/mterp/mips64/op_sget_object.S create mode 100644 runtime/interpreter/mterp/mips64/op_sget_short.S create mode 100644 runtime/interpreter/mterp/mips64/op_sget_wide.S create mode 100644 runtime/interpreter/mterp/mips64/op_shl_int.S create mode 100644 runtime/interpreter/mterp/mips64/op_shl_int_2addr.S create mode 100644 runtime/interpreter/mterp/mips64/op_shl_int_lit8.S create mode 100644 runtime/interpreter/mterp/mips64/op_shl_long.S create mode 100644 runtime/interpreter/mterp/mips64/op_shl_long_2addr.S create mode 100644 runtime/interpreter/mterp/mips64/op_shr_int.S create mode 100644 runtime/interpreter/mterp/mips64/op_shr_int_2addr.S create mode 100644 runtime/interpreter/mterp/mips64/op_shr_int_lit8.S create mode 100644 runtime/interpreter/mterp/mips64/op_shr_long.S create mode 100644 runtime/interpreter/mterp/mips64/op_shr_long_2addr.S create mode 100644 runtime/interpreter/mterp/mips64/op_sparse_switch.S create mode 100644 runtime/interpreter/mterp/mips64/op_sput.S create mode 100644 runtime/interpreter/mterp/mips64/op_sput_boolean.S create mode 100644 runtime/interpreter/mterp/mips64/op_sput_byte.S create mode 100644 runtime/interpreter/mterp/mips64/op_sput_char.S create mode 100644 runtime/interpreter/mterp/mips64/op_sput_object.S create mode 100644 runtime/interpreter/mterp/mips64/op_sput_short.S create mode 100644 runtime/interpreter/mterp/mips64/op_sput_wide.S create mode 100644 runtime/interpreter/mterp/mips64/op_sub_double.S create mode 100644 runtime/interpreter/mterp/mips64/op_sub_double_2addr.S create mode 100644 runtime/interpreter/mterp/mips64/op_sub_float.S create mode 100644 runtime/interpreter/mterp/mips64/op_sub_float_2addr.S create mode 100644 runtime/interpreter/mterp/mips64/op_sub_int.S create mode 100644 runtime/interpreter/mterp/mips64/op_sub_int_2addr.S create mode 100644 runtime/interpreter/mterp/mips64/op_sub_long.S create mode 100644 runtime/interpreter/mterp/mips64/op_sub_long_2addr.S create mode 100644 runtime/interpreter/mterp/mips64/op_throw.S create mode 100644 runtime/interpreter/mterp/mips64/op_unused_3e.S create mode 100644 runtime/interpreter/mterp/mips64/op_unused_3f.S create mode 100644 runtime/interpreter/mterp/mips64/op_unused_40.S create mode 100644 runtime/interpreter/mterp/mips64/op_unused_41.S create mode 100644 runtime/interpreter/mterp/mips64/op_unused_42.S create mode 100644 runtime/interpreter/mterp/mips64/op_unused_43.S create mode 100644 runtime/interpreter/mterp/mips64/op_unused_79.S create mode 100644 runtime/interpreter/mterp/mips64/op_unused_7a.S create mode 100644 runtime/interpreter/mterp/mips64/op_unused_f4.S create mode 100644 runtime/interpreter/mterp/mips64/op_unused_fa.S create mode 100644 runtime/interpreter/mterp/mips64/op_unused_fb.S create mode 100644 runtime/interpreter/mterp/mips64/op_unused_fc.S create mode 100644 runtime/interpreter/mterp/mips64/op_unused_fd.S create mode 100644 runtime/interpreter/mterp/mips64/op_unused_fe.S create mode 100644 runtime/interpreter/mterp/mips64/op_unused_ff.S create mode 100644 runtime/interpreter/mterp/mips64/op_ushr_int.S create mode 100644 runtime/interpreter/mterp/mips64/op_ushr_int_2addr.S create mode 100644 runtime/interpreter/mterp/mips64/op_ushr_int_lit8.S create mode 100644 runtime/interpreter/mterp/mips64/op_ushr_long.S create mode 100644 runtime/interpreter/mterp/mips64/op_ushr_long_2addr.S create mode 100644 runtime/interpreter/mterp/mips64/op_xor_int.S create mode 100644 runtime/interpreter/mterp/mips64/op_xor_int_2addr.S create mode 100644 runtime/interpreter/mterp/mips64/op_xor_int_lit16.S create mode 100644 runtime/interpreter/mterp/mips64/op_xor_int_lit8.S create mode 100644 runtime/interpreter/mterp/mips64/op_xor_long.S create mode 100644 runtime/interpreter/mterp/mips64/op_xor_long_2addr.S create mode 100644 runtime/interpreter/mterp/mips64/unop.S create mode 100644 runtime/interpreter/mterp/mips64/unopWide.S create mode 100644 runtime/interpreter/mterp/mips64/unused.S create mode 100644 runtime/interpreter/mterp/mips64/zcmp.S create mode 100644 runtime/interpreter/mterp/out/mterp_mips64.S diff --git a/runtime/Android.mk b/runtime/Android.mk index 88118501a0..500fa1420e 100644 --- a/runtime/Android.mk +++ b/runtime/Android.mk @@ -318,7 +318,8 @@ LIBART_TARGET_SRC_FILES_mips := \ arch/mips/fault_handler_mips.cc LIBART_TARGET_SRC_FILES_mips64 := \ - interpreter/mterp/mterp_stub.cc \ + interpreter/mterp/mterp.cc \ + interpreter/mterp/out/mterp_mips64.S \ arch/mips64/context_mips64.cc \ arch/mips64/entrypoints_init_mips64.cc \ arch/mips64/jni_entrypoints_mips64.S \ diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc index bfb1f9de06..aa8e37afb0 100644 --- a/runtime/interpreter/interpreter.cc +++ b/runtime/interpreter/interpreter.cc @@ -240,20 +240,10 @@ static std::ostream& operator<<(std::ostream& os, const InterpreterImplKind& rhs return os; } -#if !defined(__clang__) -#if (defined(__arm__) || defined(__i386__) || defined(__aarch64__) || defined(__x86_64__) || (defined(__mips__) && !defined(__LP64__))) -// TODO: remove when all targets implemented. static constexpr InterpreterImplKind kInterpreterImplKind = kMterpImplKind; -#else -static constexpr InterpreterImplKind kInterpreterImplKind = kComputedGotoImplKind; -#endif -#else + +#if defined(__clang__) // Clang 3.4 fails to build the goto interpreter implementation. -#if (defined(__arm__) || defined(__i386__) || defined(__aarch64__) || defined(__x86_64__) || (defined(__mips__) && !defined(__LP64__))) -static constexpr InterpreterImplKind kInterpreterImplKind = kMterpImplKind; -#else -static constexpr InterpreterImplKind kInterpreterImplKind = kSwitchImplKind; -#endif template JValue ExecuteGotoImpl(Thread*, const DexFile::CodeItem*, ShadowFrame&, JValue) { LOG(FATAL) << "UNREACHABLE"; @@ -327,12 +317,8 @@ static inline JValue Execute(Thread* self, const DexFile::CodeItem* code_item, while (true) { // Mterp does not support all instrumentation/debugging. if (MterpShouldSwitchInterpreters()) { -#if !defined(__clang__) - return ExecuteGotoImpl(self, code_item, shadow_frame, result_register); -#else return ExecuteSwitchImpl(self, code_item, shadow_frame, result_register, false); -#endif } bool returned = ExecuteMterpImpl(self, code_item, &shadow_frame, &result_register); if (returned) { diff --git a/runtime/interpreter/mterp/config_mips64 b/runtime/interpreter/mterp/config_mips64 index f804ce5566..c40c007eec 100644 --- a/runtime/interpreter/mterp/config_mips64 +++ b/runtime/interpreter/mterp/config_mips64 @@ -36,262 +36,262 @@ op-start mips64 # (override example:) op OP_SUB_FLOAT_2ADDR arm-vfp # (fallback example:) op OP_SUB_FLOAT_2ADDR FALLBACK - op op_nop FALLBACK - op op_move FALLBACK - op op_move_from16 FALLBACK - op op_move_16 FALLBACK - op op_move_wide FALLBACK - op op_move_wide_from16 FALLBACK - op op_move_wide_16 FALLBACK - op op_move_object FALLBACK - op op_move_object_from16 FALLBACK - op op_move_object_16 FALLBACK - op op_move_result FALLBACK - op op_move_result_wide FALLBACK - op op_move_result_object FALLBACK - op op_move_exception FALLBACK - op op_return_void FALLBACK - op op_return FALLBACK - op op_return_wide FALLBACK - op op_return_object FALLBACK - op op_const_4 FALLBACK - op op_const_16 FALLBACK - op op_const FALLBACK - op op_const_high16 FALLBACK - op op_const_wide_16 FALLBACK - op op_const_wide_32 FALLBACK - op op_const_wide FALLBACK - op op_const_wide_high16 FALLBACK - op op_const_string FALLBACK - op op_const_string_jumbo FALLBACK - op op_const_class FALLBACK - op op_monitor_enter FALLBACK - op op_monitor_exit FALLBACK - op op_check_cast FALLBACK - op op_instance_of FALLBACK - op op_array_length FALLBACK - op op_new_instance FALLBACK - op op_new_array FALLBACK - op op_filled_new_array FALLBACK - op op_filled_new_array_range FALLBACK - op op_fill_array_data FALLBACK - op op_throw FALLBACK - op op_goto FALLBACK - op op_goto_16 FALLBACK - op op_goto_32 FALLBACK - op op_packed_switch FALLBACK - op op_sparse_switch FALLBACK - op op_cmpl_float FALLBACK - op op_cmpg_float FALLBACK - op op_cmpl_double FALLBACK - op op_cmpg_double FALLBACK - op op_cmp_long FALLBACK - op op_if_eq FALLBACK - op op_if_ne FALLBACK - op op_if_lt FALLBACK - op op_if_ge FALLBACK - op op_if_gt FALLBACK - op op_if_le FALLBACK - op op_if_eqz FALLBACK - op op_if_nez FALLBACK - op op_if_ltz FALLBACK - op op_if_gez FALLBACK - op op_if_gtz FALLBACK - op op_if_lez FALLBACK - op_unused_3e FALLBACK - op_unused_3f FALLBACK - op_unused_40 FALLBACK - op_unused_41 FALLBACK - op_unused_42 FALLBACK - op_unused_43 FALLBACK - op op_aget FALLBACK - op op_aget_wide FALLBACK - op op_aget_object FALLBACK - op op_aget_boolean FALLBACK - op op_aget_byte FALLBACK - op op_aget_char FALLBACK - op op_aget_short FALLBACK - op op_aput FALLBACK - op op_aput_wide FALLBACK - op op_aput_object FALLBACK - op op_aput_boolean FALLBACK - op op_aput_byte FALLBACK - op op_aput_char FALLBACK - op op_aput_short FALLBACK - op op_iget FALLBACK - op op_iget_wide FALLBACK - op op_iget_object FALLBACK - op op_iget_boolean FALLBACK - op op_iget_byte FALLBACK - op op_iget_char FALLBACK - op op_iget_short FALLBACK - op op_iput FALLBACK - op op_iput_wide FALLBACK - op op_iput_object FALLBACK - op op_iput_boolean FALLBACK - op op_iput_byte FALLBACK - op op_iput_char FALLBACK - op op_iput_short FALLBACK - op op_sget FALLBACK - op op_sget_wide FALLBACK - op op_sget_object FALLBACK - op op_sget_boolean FALLBACK - op op_sget_byte FALLBACK - op op_sget_char FALLBACK - op op_sget_short FALLBACK - op op_sput FALLBACK - op op_sput_wide FALLBACK - op op_sput_object FALLBACK - op op_sput_boolean FALLBACK - op op_sput_byte FALLBACK - op op_sput_char FALLBACK - op op_sput_short FALLBACK - op op_invoke_virtual FALLBACK - op op_invoke_super FALLBACK - op op_invoke_direct FALLBACK - op op_invoke_static FALLBACK - op op_invoke_interface FALLBACK - op op_return_void_no_barrier FALLBACK - op op_invoke_virtual_range FALLBACK - op op_invoke_super_range FALLBACK - op op_invoke_direct_range FALLBACK - op op_invoke_static_range FALLBACK - op op_invoke_interface_range FALLBACK - op_unused_79 FALLBACK - op_unused_7a FALLBACK - op op_neg_int FALLBACK - op op_not_int FALLBACK - op op_neg_long FALLBACK - op op_not_long FALLBACK - op op_neg_float FALLBACK - op op_neg_double FALLBACK - op op_int_to_long FALLBACK - op op_int_to_float FALLBACK - op op_int_to_double FALLBACK - op op_long_to_int FALLBACK - op op_long_to_float FALLBACK - op op_long_to_double FALLBACK - op op_float_to_int FALLBACK - op op_float_to_long FALLBACK - op op_float_to_double FALLBACK - op op_double_to_int FALLBACK - op op_double_to_long FALLBACK - op op_double_to_float FALLBACK - op op_int_to_byte FALLBACK - op op_int_to_char FALLBACK - op op_int_to_short FALLBACK - op op_add_int FALLBACK - op op_sub_int FALLBACK - op op_mul_int FALLBACK - op op_div_int FALLBACK - op op_rem_int FALLBACK - op op_and_int FALLBACK - op op_or_int FALLBACK - op op_xor_int FALLBACK - op op_shl_int FALLBACK - op op_shr_int FALLBACK - op op_ushr_int FALLBACK - op op_add_long FALLBACK - op op_sub_long FALLBACK - op op_mul_long FALLBACK - op op_div_long FALLBACK - op op_rem_long FALLBACK - op op_and_long FALLBACK - op op_or_long FALLBACK - op op_xor_long FALLBACK - op op_shl_long FALLBACK - op op_shr_long FALLBACK - op op_ushr_long FALLBACK - op op_add_float FALLBACK - op op_sub_float FALLBACK - op op_mul_float FALLBACK - op op_div_float FALLBACK - op op_rem_float FALLBACK - op op_add_double FALLBACK - op op_sub_double FALLBACK - op op_mul_double FALLBACK - op op_div_double FALLBACK - op op_rem_double FALLBACK - op op_add_int_2addr FALLBACK - op op_sub_int_2addr FALLBACK - op op_mul_int_2addr FALLBACK - op op_div_int_2addr FALLBACK - op op_rem_int_2addr FALLBACK - op op_and_int_2addr FALLBACK - op op_or_int_2addr FALLBACK - op op_xor_int_2addr FALLBACK - op op_shl_int_2addr FALLBACK - op op_shr_int_2addr FALLBACK - op op_ushr_int_2addr FALLBACK - op op_add_long_2addr FALLBACK - op op_sub_long_2addr FALLBACK - op op_mul_long_2addr FALLBACK - op op_div_long_2addr FALLBACK - op op_rem_long_2addr FALLBACK - op op_and_long_2addr FALLBACK - op op_or_long_2addr FALLBACK - op op_xor_long_2addr FALLBACK - op op_shl_long_2addr FALLBACK - op op_shr_long_2addr FALLBACK - op op_ushr_long_2addr FALLBACK - op op_add_float_2addr FALLBACK - op op_sub_float_2addr FALLBACK - op op_mul_float_2addr FALLBACK - op op_div_float_2addr FALLBACK - op op_rem_float_2addr FALLBACK - op op_add_double_2addr FALLBACK - op op_sub_double_2addr FALLBACK - op op_mul_double_2addr FALLBACK - op op_div_double_2addr FALLBACK - op op_rem_double_2addr FALLBACK - op op_add_int_lit16 FALLBACK - op op_rsub_int FALLBACK - op op_mul_int_lit16 FALLBACK - op op_div_int_lit16 FALLBACK - op op_rem_int_lit16 FALLBACK - op op_and_int_lit16 FALLBACK - op op_or_int_lit16 FALLBACK - op op_xor_int_lit16 FALLBACK - op op_add_int_lit8 FALLBACK - op op_rsub_int_lit8 FALLBACK - op op_mul_int_lit8 FALLBACK - op op_div_int_lit8 FALLBACK - op op_rem_int_lit8 FALLBACK - op op_and_int_lit8 FALLBACK - op op_or_int_lit8 FALLBACK - op op_xor_int_lit8 FALLBACK - op op_shl_int_lit8 FALLBACK - op op_shr_int_lit8 FALLBACK - op op_ushr_int_lit8 FALLBACK - op op_iget_quick FALLBACK - op op_iget_wide_quick FALLBACK - op op_iget_object_quick FALLBACK - op op_iput_quick FALLBACK - op op_iput_wide_quick FALLBACK - op op_iput_object_quick FALLBACK - op op_invoke_virtual_quick FALLBACK - op op_invoke_virtual_range_quick FALLBACK - op op_iput_boolean_quick FALLBACK - op op_iput_byte_quick FALLBACK - op op_iput_char_quick FALLBACK - op op_iput_short_quick FALLBACK - op op_iget_boolean_quick FALLBACK - op op_iget_byte_quick FALLBACK - op op_iget_char_quick FALLBACK - op op_iget_short_quick FALLBACK - op_unused_f3 FALLBACK - op_unused_f4 FALLBACK - op_unused_f5 FALLBACK - op_unused_f6 FALLBACK - op_unused_f7 FALLBACK - op_unused_f8 FALLBACK - op_unused_f9 FALLBACK - op_unused_fa FALLBACK - op_unused_fb FALLBACK - op_unused_fc FALLBACK - op_unused_fd FALLBACK - op_unused_fe FALLBACK - op_unused_ff FALLBACK + # op op_nop FALLBACK + # op op_move FALLBACK + # op op_move_from16 FALLBACK + # op op_move_16 FALLBACK + # op op_move_wide FALLBACK + # op op_move_wide_from16 FALLBACK + # op op_move_wide_16 FALLBACK + # op op_move_object FALLBACK + # op op_move_object_from16 FALLBACK + # op op_move_object_16 FALLBACK + # op op_move_result FALLBACK + # op op_move_result_wide FALLBACK + # op op_move_result_object FALLBACK + # op op_move_exception FALLBACK + # op op_return_void FALLBACK + # op op_return FALLBACK + # op op_return_wide FALLBACK + # op op_return_object FALLBACK + # op op_const_4 FALLBACK + # op op_const_16 FALLBACK + # op op_const FALLBACK + # op op_const_high16 FALLBACK + # op op_const_wide_16 FALLBACK + # op op_const_wide_32 FALLBACK + # op op_const_wide FALLBACK + # op op_const_wide_high16 FALLBACK + # op op_const_string FALLBACK + # op op_const_string_jumbo FALLBACK + # op op_const_class FALLBACK + # op op_monitor_enter FALLBACK + # op op_monitor_exit FALLBACK + # op op_check_cast FALLBACK + # op op_instance_of FALLBACK + # op op_array_length FALLBACK + # op op_new_instance FALLBACK + # op op_new_array FALLBACK + # op op_filled_new_array FALLBACK + # op op_filled_new_array_range FALLBACK + # op op_fill_array_data FALLBACK + # op op_throw FALLBACK + # op op_goto FALLBACK + # op op_goto_16 FALLBACK + # op op_goto_32 FALLBACK + # op op_packed_switch FALLBACK + # op op_sparse_switch FALLBACK + # op op_cmpl_float FALLBACK + # op op_cmpg_float FALLBACK + # op op_cmpl_double FALLBACK + # op op_cmpg_double FALLBACK + # op op_cmp_long FALLBACK + # op op_if_eq FALLBACK + # op op_if_ne FALLBACK + # op op_if_lt FALLBACK + # op op_if_ge FALLBACK + # op op_if_gt FALLBACK + # op op_if_le FALLBACK + # op op_if_eqz FALLBACK + # op op_if_nez FALLBACK + # op op_if_ltz FALLBACK + # op op_if_gez FALLBACK + # op op_if_gtz FALLBACK + # op op_if_lez FALLBACK + # op op_unused_3e FALLBACK + # op op_unused_3f FALLBACK + # op op_unused_40 FALLBACK + # op op_unused_41 FALLBACK + # op op_unused_42 FALLBACK + # op op_unused_43 FALLBACK + # op op_aget FALLBACK + # op op_aget_wide FALLBACK + # op op_aget_object FALLBACK + # op op_aget_boolean FALLBACK + # op op_aget_byte FALLBACK + # op op_aget_char FALLBACK + # op op_aget_short FALLBACK + # op op_aput FALLBACK + # op op_aput_wide FALLBACK + # op op_aput_object FALLBACK + # op op_aput_boolean FALLBACK + # op op_aput_byte FALLBACK + # op op_aput_char FALLBACK + # op op_aput_short FALLBACK + # op op_iget FALLBACK + # op op_iget_wide FALLBACK + # op op_iget_object FALLBACK + # op op_iget_boolean FALLBACK + # op op_iget_byte FALLBACK + # op op_iget_char FALLBACK + # op op_iget_short FALLBACK + # op op_iput FALLBACK + # op op_iput_wide FALLBACK + # op op_iput_object FALLBACK + # op op_iput_boolean FALLBACK + # op op_iput_byte FALLBACK + # op op_iput_char FALLBACK + # op op_iput_short FALLBACK + # op op_sget FALLBACK + # op op_sget_wide FALLBACK + # op op_sget_object FALLBACK + # op op_sget_boolean FALLBACK + # op op_sget_byte FALLBACK + # op op_sget_char FALLBACK + # op op_sget_short FALLBACK + # op op_sput FALLBACK + # op op_sput_wide FALLBACK + # op op_sput_object FALLBACK + # op op_sput_boolean FALLBACK + # op op_sput_byte FALLBACK + # op op_sput_char FALLBACK + # op op_sput_short FALLBACK + # op op_invoke_virtual FALLBACK + # op op_invoke_super FALLBACK + # op op_invoke_direct FALLBACK + # op op_invoke_static FALLBACK + # op op_invoke_interface FALLBACK + # op op_return_void_no_barrier FALLBACK + # op op_invoke_virtual_range FALLBACK + # op op_invoke_super_range FALLBACK + # op op_invoke_direct_range FALLBACK + # op op_invoke_static_range FALLBACK + # op op_invoke_interface_range FALLBACK + # op op_unused_79 FALLBACK + # op op_unused_7a FALLBACK + # op op_neg_int FALLBACK + # op op_not_int FALLBACK + # op op_neg_long FALLBACK + # op op_not_long FALLBACK + # op op_neg_float FALLBACK + # op op_neg_double FALLBACK + # op op_int_to_long FALLBACK + # op op_int_to_float FALLBACK + # op op_int_to_double FALLBACK + # op op_long_to_int FALLBACK + # op op_long_to_float FALLBACK + # op op_long_to_double FALLBACK + # op op_float_to_int FALLBACK + # op op_float_to_long FALLBACK + # op op_float_to_double FALLBACK + # op op_double_to_int FALLBACK + # op op_double_to_long FALLBACK + # op op_double_to_float FALLBACK + # op op_int_to_byte FALLBACK + # op op_int_to_char FALLBACK + # op op_int_to_short FALLBACK + # op op_add_int FALLBACK + # op op_sub_int FALLBACK + # op op_mul_int FALLBACK + # op op_div_int FALLBACK + # op op_rem_int FALLBACK + # op op_and_int FALLBACK + # op op_or_int FALLBACK + # op op_xor_int FALLBACK + # op op_shl_int FALLBACK + # op op_shr_int FALLBACK + # op op_ushr_int FALLBACK + # op op_add_long FALLBACK + # op op_sub_long FALLBACK + # op op_mul_long FALLBACK + # op op_div_long FALLBACK + # op op_rem_long FALLBACK + # op op_and_long FALLBACK + # op op_or_long FALLBACK + # op op_xor_long FALLBACK + # op op_shl_long FALLBACK + # op op_shr_long FALLBACK + # op op_ushr_long FALLBACK + # op op_add_float FALLBACK + # op op_sub_float FALLBACK + # op op_mul_float FALLBACK + # op op_div_float FALLBACK + # op op_rem_float FALLBACK + # op op_add_double FALLBACK + # op op_sub_double FALLBACK + # op op_mul_double FALLBACK + # op op_div_double FALLBACK + # op op_rem_double FALLBACK + # op op_add_int_2addr FALLBACK + # op op_sub_int_2addr FALLBACK + # op op_mul_int_2addr FALLBACK + # op op_div_int_2addr FALLBACK + # op op_rem_int_2addr FALLBACK + # op op_and_int_2addr FALLBACK + # op op_or_int_2addr FALLBACK + # op op_xor_int_2addr FALLBACK + # op op_shl_int_2addr FALLBACK + # op op_shr_int_2addr FALLBACK + # op op_ushr_int_2addr FALLBACK + # op op_add_long_2addr FALLBACK + # op op_sub_long_2addr FALLBACK + # op op_mul_long_2addr FALLBACK + # op op_div_long_2addr FALLBACK + # op op_rem_long_2addr FALLBACK + # op op_and_long_2addr FALLBACK + # op op_or_long_2addr FALLBACK + # op op_xor_long_2addr FALLBACK + # op op_shl_long_2addr FALLBACK + # op op_shr_long_2addr FALLBACK + # op op_ushr_long_2addr FALLBACK + # op op_add_float_2addr FALLBACK + # op op_sub_float_2addr FALLBACK + # op op_mul_float_2addr FALLBACK + # op op_div_float_2addr FALLBACK + # op op_rem_float_2addr FALLBACK + # op op_add_double_2addr FALLBACK + # op op_sub_double_2addr FALLBACK + # op op_mul_double_2addr FALLBACK + # op op_div_double_2addr FALLBACK + # op op_rem_double_2addr FALLBACK + # op op_add_int_lit16 FALLBACK + # op op_rsub_int FALLBACK + # op op_mul_int_lit16 FALLBACK + # op op_div_int_lit16 FALLBACK + # op op_rem_int_lit16 FALLBACK + # op op_and_int_lit16 FALLBACK + # op op_or_int_lit16 FALLBACK + # op op_xor_int_lit16 FALLBACK + # op op_add_int_lit8 FALLBACK + # op op_rsub_int_lit8 FALLBACK + # op op_mul_int_lit8 FALLBACK + # op op_div_int_lit8 FALLBACK + # op op_rem_int_lit8 FALLBACK + # op op_and_int_lit8 FALLBACK + # op op_or_int_lit8 FALLBACK + # op op_xor_int_lit8 FALLBACK + # op op_shl_int_lit8 FALLBACK + # op op_shr_int_lit8 FALLBACK + # op op_ushr_int_lit8 FALLBACK + # op op_iget_quick FALLBACK + # op op_iget_wide_quick FALLBACK + # op op_iget_object_quick FALLBACK + # op op_iput_quick FALLBACK + # op op_iput_wide_quick FALLBACK + # op op_iput_object_quick FALLBACK + # op op_invoke_virtual_quick FALLBACK + # op op_invoke_virtual_range_quick FALLBACK + # op op_iput_boolean_quick FALLBACK + # op op_iput_byte_quick FALLBACK + # op op_iput_char_quick FALLBACK + # op op_iput_short_quick FALLBACK + # op op_iget_boolean_quick FALLBACK + # op op_iget_byte_quick FALLBACK + # op op_iget_char_quick FALLBACK + # op op_iget_short_quick FALLBACK + op op_invoke_lambda FALLBACK + # op op_unused_f4 FALLBACK + op op_capture_variable FALLBACK + op op_create_lambda FALLBACK + op op_liberate_variable FALLBACK + op op_box_lambda FALLBACK + op op_unbox_lambda FALLBACK + # op op_unused_fa FALLBACK + # op op_unused_fb FALLBACK + # op op_unused_fc FALLBACK + # op op_unused_fd FALLBACK + # op op_unused_fe FALLBACK + # op op_unused_ff FALLBACK op-end # common subroutines for asm diff --git a/runtime/interpreter/mterp/mips64/alt_stub.S b/runtime/interpreter/mterp/mips64/alt_stub.S new file mode 100644 index 0000000000..bd76a1b464 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/alt_stub.S @@ -0,0 +1,14 @@ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (${opnum} * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. diff --git a/runtime/interpreter/mterp/mips64/bincmp.S b/runtime/interpreter/mterp/mips64/bincmp.S new file mode 100644 index 0000000000..d39c9009b7 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/bincmp.S @@ -0,0 +1,32 @@ + /* + * Generic two-operand compare-and-branch operation. Provide a "condition" + * fragment that specifies the comparison to perform, e.g. for + * "if-le" you would use "le". + * + * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le + */ + /* if-cmp vA, vB, +CCCC */ + lh a4, 2(rPC) # a4 <- sign-extended CCCC + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG a0, a2 # a0 <- vA + GET_VREG a1, a3 # a1 <- vB + + b${condition}c a0, a1, 1f + li a4, 2 # offset if branch not taken +1: + + dlsa rPC, a4, rPC, 1 # rPC <- rPC + CCCC * 2 + FETCH_INST # load rINST + +#if MTERP_SUSPEND + bgez a4, 2f # CCCC * 2 >= 0 => no suspend check + REFRESH_IBASE +2: +#else + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + bltz a4, MterpCheckSuspendAndContinue +#endif + + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/binop.S b/runtime/interpreter/mterp/mips64/binop.S new file mode 100644 index 0000000000..fab48b73b3 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/binop.S @@ -0,0 +1,30 @@ +%default {"preinstr":"", "result":"a0", "chkzero":"0"} + /* + * Generic 32-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int, + * xor-int, shl-int, shr-int, ushr-int + */ + /* binop vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG a0, a2 # a0 <- vBB + GET_VREG a1, a3 # a1 <- vCC + .if $chkzero + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + $preinstr # optional op + $instr # $result <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG $result, a4 # vAA <- $result + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/binop2addr.S b/runtime/interpreter/mterp/mips64/binop2addr.S new file mode 100644 index 0000000000..1ae73f51d4 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/binop2addr.S @@ -0,0 +1,30 @@ +%default {"preinstr":"", "result":"a0", "chkzero":"0"} + /* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vB (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, + * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, + * shl-int/2addr, shr-int/2addr, ushr-int/2addr + */ + /* binop/2addr vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG a0, a2 # a0 <- vA + GET_VREG a1, a3 # a1 <- vB + .if $chkzero + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + $preinstr # optional op + $instr # $result <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG $result, a2 # vA <- $result + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/binopLit16.S b/runtime/interpreter/mterp/mips64/binopLit16.S new file mode 100644 index 0000000000..925775824c --- /dev/null +++ b/runtime/interpreter/mterp/mips64/binopLit16.S @@ -0,0 +1,28 @@ +%default {"preinstr":"", "result":"a0", "chkzero":"0"} + /* + * Generic 32-bit "lit16" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * CCCC (a1). Useful for integer division and modulus. + * + * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16, + * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16 + */ + /* binop/lit16 vA, vB, #+CCCC */ + lh a1, 2(rPC) # a1 <- sign-extended CCCC + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG a0, a3 # a0 <- vB + .if $chkzero + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + $preinstr # optional op + $instr # $result <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG $result, a2 # vA <- $result + GOTO_OPCODE v0 # jump to next instruction + diff --git a/runtime/interpreter/mterp/mips64/binopLit8.S b/runtime/interpreter/mterp/mips64/binopLit8.S new file mode 100644 index 0000000000..f4a0bba9b9 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/binopLit8.S @@ -0,0 +1,29 @@ +%default {"preinstr":"", "result":"a0", "chkzero":"0"} + /* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * CC (a1). Useful for integer division and modulus. + * + * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8, + * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + /* binop/lit8 vAA, vBB, #+CC */ + lbu a3, 2(rPC) # a3 <- BB + lb a1, 3(rPC) # a1 <- sign-extended CC + srl a2, rINST, 8 # a2 <- AA + GET_VREG a0, a3 # a0 <- vBB + .if $chkzero + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + $preinstr # optional op + $instr # $result <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG $result, a2 # vAA <- $result + GOTO_OPCODE v0 # jump to next instruction + diff --git a/runtime/interpreter/mterp/mips64/binopWide.S b/runtime/interpreter/mterp/mips64/binopWide.S new file mode 100644 index 0000000000..732f0d60f9 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/binopWide.S @@ -0,0 +1,30 @@ +%default {"preinstr":"", "result":"a0", "chkzero":"0"} + /* + * Generic 64-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (LONG_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-long, sub-long, mul-long, div-long, rem-long, and-long, or-long, + * xor-long, shl-long, shr-long, ushr-long + */ + /* binop vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG_WIDE a0, a2 # a0 <- vBB + GET_VREG_WIDE a1, a3 # a1 <- vCC + .if $chkzero + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + $preinstr # optional op + $instr # $result <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE $result, a4 # vAA <- $result + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/binopWide2addr.S b/runtime/interpreter/mterp/mips64/binopWide2addr.S new file mode 100644 index 0000000000..45d8d82960 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/binopWide2addr.S @@ -0,0 +1,30 @@ +%default {"preinstr":"", "result":"a0", "chkzero":"0"} + /* + * Generic 64-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vB (a1). Useful for integer division and modulus. Note that we + * *don't* check for (LONG_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-long/2addr, sub-long/2addr, mul-long/2addr, div-long/2addr, + * rem-long/2addr, and-long/2addr, or-long/2addr, xor-long/2addr, + * shl-long/2addr, shr-long/2addr, ushr-long/2addr + */ + /* binop/2addr vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG_WIDE a0, a2 # a0 <- vA + GET_VREG_WIDE a1, a3 # a1 <- vB + .if $chkzero + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + $preinstr # optional op + $instr # $result <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE $result, a2 # vA <- $result + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/entry.S b/runtime/interpreter/mterp/mips64/entry.S new file mode 100644 index 0000000000..ae6c26b706 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/entry.S @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Interpreter entry point. + */ + + .set reorder + + .text + .global ExecuteMterpImpl + .type ExecuteMterpImpl, %function + .balign 16 +/* + * On entry: + * a0 Thread* self + * a1 code_item + * a2 ShadowFrame + * a3 JValue* result_register + * + */ +ExecuteMterpImpl: + .cfi_startproc + .cpsetup t9, t8, ExecuteMterpImpl + + .cfi_def_cfa sp, 0 + daddu sp, sp, -STACK_SIZE + .cfi_adjust_cfa_offset STACK_SIZE + + sd t8, STACK_OFFSET_GP(sp) + .cfi_rel_offset 28, STACK_OFFSET_GP + sd ra, STACK_OFFSET_RA(sp) + .cfi_rel_offset 31, STACK_OFFSET_RA + + sd s0, STACK_OFFSET_S0(sp) + .cfi_rel_offset 16, STACK_OFFSET_S0 + sd s1, STACK_OFFSET_S1(sp) + .cfi_rel_offset 17, STACK_OFFSET_S1 + sd s2, STACK_OFFSET_S2(sp) + .cfi_rel_offset 18, STACK_OFFSET_S2 + sd s3, STACK_OFFSET_S3(sp) + .cfi_rel_offset 19, STACK_OFFSET_S3 + sd s4, STACK_OFFSET_S4(sp) + .cfi_rel_offset 20, STACK_OFFSET_S4 + sd s5, STACK_OFFSET_S5(sp) + .cfi_rel_offset 21, STACK_OFFSET_S5 + + /* Remember the return register */ + sd a3, SHADOWFRAME_RESULT_REGISTER_OFFSET(a2) + + /* Remember the code_item */ + sd a1, SHADOWFRAME_CODE_ITEM_OFFSET(a2) + + /* set up "named" registers */ + move rSELF, a0 + daddu rFP, a2, SHADOWFRAME_VREGS_OFFSET + lw v0, SHADOWFRAME_NUMBER_OF_VREGS_OFFSET(a2) + dlsa rREFS, v0, rFP, 2 + daddu rPC, a1, CODEITEM_INSNS_OFFSET + lw v0, SHADOWFRAME_DEX_PC_OFFSET(a2) + dlsa rPC, v0, rPC, 1 + EXPORT_PC + + /* Starting ibase */ + REFRESH_IBASE + + /* start executing the instruction at rPC */ + FETCH_INST + GET_INST_OPCODE v0 + GOTO_OPCODE v0 + + /* NOTE: no fallthrough */ diff --git a/runtime/interpreter/mterp/mips64/fallback.S b/runtime/interpreter/mterp/mips64/fallback.S new file mode 100644 index 0000000000..560b994b08 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/fallback.S @@ -0,0 +1,2 @@ +/* Transfer stub to alternate interpreter */ + b MterpFallback diff --git a/runtime/interpreter/mterp/mips64/fbinop.S b/runtime/interpreter/mterp/mips64/fbinop.S new file mode 100644 index 0000000000..f19dd1c3d9 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/fbinop.S @@ -0,0 +1,18 @@ +%default {} + /*: + * Generic 32-bit floating-point operation. + * + * For: add-float, sub-float, mul-float, div-float. + * form: f0, f0, f1 + */ + /* binop vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG_FLOAT f0, a2 # f0 <- vBB + GET_VREG_FLOAT f1, a3 # f1 <- vCC + $instr # f0 <- f0 op f1 + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_FLOAT f0, a4 # vAA <- f0 + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/fbinop2addr.S b/runtime/interpreter/mterp/mips64/fbinop2addr.S new file mode 100644 index 0000000000..2e2cd7e8e9 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/fbinop2addr.S @@ -0,0 +1,17 @@ +%default {} + /*: + * Generic 32-bit "/2addr" floating-point operation. + * + * For: add-float/2addr, sub-float/2addr, mul-float/2addr, div-float/2addr. + * form: f0, f0, f1 + */ + /* binop/2addr vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG_FLOAT f0, a2 # f0 <- vA + GET_VREG_FLOAT f1, a3 # f1 <- vB + $instr # f0 <- f0 op f1 + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_FLOAT f0, a2 # vA <- f0 + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/fbinopWide.S b/runtime/interpreter/mterp/mips64/fbinopWide.S new file mode 100644 index 0000000000..8915c9447c --- /dev/null +++ b/runtime/interpreter/mterp/mips64/fbinopWide.S @@ -0,0 +1,18 @@ +%default {} + /*: + * Generic 64-bit floating-point operation. + * + * For: add-double, sub-double, mul-double, div-double. + * form: f0, f0, f1 + */ + /* binop vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG_DOUBLE f0, a2 # f0 <- vBB + GET_VREG_DOUBLE f1, a3 # f1 <- vCC + $instr # f0 <- f0 op f1 + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_DOUBLE f0, a4 # vAA <- f0 + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/fbinopWide2addr.S b/runtime/interpreter/mterp/mips64/fbinopWide2addr.S new file mode 100644 index 0000000000..a3f4eaa8cc --- /dev/null +++ b/runtime/interpreter/mterp/mips64/fbinopWide2addr.S @@ -0,0 +1,17 @@ +%default {} + /*: + * Generic 64-bit "/2addr" floating-point operation. + * + * For: add-double/2addr, sub-double/2addr, mul-double/2addr, div-double/2addr. + * form: f0, f0, f1 + */ + /* binop/2addr vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG_DOUBLE f0, a2 # f0 <- vA + GET_VREG_DOUBLE f1, a3 # f1 <- vB + $instr # f0 <- f0 op f1 + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_DOUBLE f0, a2 # vA <- f0 + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/fcmp.S b/runtime/interpreter/mterp/mips64/fcmp.S new file mode 100644 index 0000000000..2e1a3e4c3d --- /dev/null +++ b/runtime/interpreter/mterp/mips64/fcmp.S @@ -0,0 +1,32 @@ +%default {} + /* + * Compare two floating-point values. Puts 0, 1, or -1 into the + * destination register based on the results of the comparison. + * + * For: cmpl-float, cmpg-float + */ + /* op vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG_FLOAT f0, a2 # f0 <- vBB + GET_VREG_FLOAT f1, a3 # f1 <- vCC + cmp.eq.s f2, f0, f1 + li a0, 0 + bc1nez f2, 1f # done if vBB == vCC (ordered) + .if $gt_bias + cmp.lt.s f2, f0, f1 + li a0, -1 + bc1nez f2, 1f # done if vBB < vCC (ordered) + li a0, 1 # vBB > vCC or unordered + .else + cmp.lt.s f2, f1, f0 + li a0, 1 + bc1nez f2, 1f # done if vBB > vCC (ordered) + li a0, -1 # vBB < vCC or unordered + .endif +1: + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a4 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/fcmpWide.S b/runtime/interpreter/mterp/mips64/fcmpWide.S new file mode 100644 index 0000000000..2a3a341a3d --- /dev/null +++ b/runtime/interpreter/mterp/mips64/fcmpWide.S @@ -0,0 +1,32 @@ +%default {} + /* + * Compare two floating-point values. Puts 0, 1, or -1 into the + * destination register based on the results of the comparison. + * + * For: cmpl-double, cmpg-double + */ + /* op vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG_DOUBLE f0, a2 # f0 <- vBB + GET_VREG_DOUBLE f1, a3 # f1 <- vCC + cmp.eq.d f2, f0, f1 + li a0, 0 + bc1nez f2, 1f # done if vBB == vCC (ordered) + .if $gt_bias + cmp.lt.d f2, f0, f1 + li a0, -1 + bc1nez f2, 1f # done if vBB < vCC (ordered) + li a0, 1 # vBB > vCC or unordered + .else + cmp.lt.d f2, f1, f0 + li a0, 1 + bc1nez f2, 1f # done if vBB > vCC (ordered) + li a0, -1 # vBB < vCC or unordered + .endif +1: + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a4 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/fcvtFooter.S b/runtime/interpreter/mterp/mips64/fcvtFooter.S new file mode 100644 index 0000000000..06e9507817 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/fcvtFooter.S @@ -0,0 +1,18 @@ + /* + * Stores a specified register containing the result of conversion + * from or to a floating-point type and jumps to the next instruction. + * + * Expects a1 to contain the destination Dalvik register number. + * a1 is set up by fcvtHeader.S. + * + * For: int-to-float, int-to-double, long-to-float, long-to-double, + * float-to-int, float-to-long, float-to-double, double-to-int, + * double-to-long, double-to-float, neg-float, neg-double. + * + * Note that this file can't be included after a break in other files + * and in those files its contents appear as a copy. + * See: float-to-int, float-to-long, double-to-int, double-to-long. + */ + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG$suffix $valreg, a1 + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/fcvtHeader.S b/runtime/interpreter/mterp/mips64/fcvtHeader.S new file mode 100644 index 0000000000..8742e42c39 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/fcvtHeader.S @@ -0,0 +1,15 @@ + /* + * Loads a specified register from vB. Used primarily for conversions + * from or to a floating-point type. + * + * Sets up a1 = A and a2 = B. a2 is later used by fcvtFooter.S to + * store the result in vA and jump to the next instruction. + * + * For: int-to-float, int-to-double, long-to-float, long-to-double, + * float-to-int, float-to-long, float-to-double, double-to-int, + * double-to-long, double-to-float, neg-float, neg-double. + */ + ext a1, rINST, 8, 4 # a1 <- A + srl a2, rINST, 12 # a2 <- B + GET_VREG$suffix $valreg, a2 + FETCH_ADVANCE_INST 1 # advance rPC, load rINST diff --git a/runtime/interpreter/mterp/mips64/footer.S b/runtime/interpreter/mterp/mips64/footer.S new file mode 100644 index 0000000000..1a2e22b672 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/footer.S @@ -0,0 +1,154 @@ +/* + * We've detected a condition that will result in an exception, but the exception + * has not yet been thrown. Just bail out to the reference interpreter to deal with it. + * TUNING: for consistency, we may want to just go ahead and handle these here. + */ + + .extern MterpLogDivideByZeroException +common_errDivideByZero: + EXPORT_PC +#if MTERP_LOGGING + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + jal MterpLogDivideByZeroException +#endif + b MterpCommonFallback + + .extern MterpLogArrayIndexException +common_errArrayIndex: + EXPORT_PC +#if MTERP_LOGGING + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + jal MterpLogArrayIndexException +#endif + b MterpCommonFallback + + .extern MterpLogNullObjectException +common_errNullObject: + EXPORT_PC +#if MTERP_LOGGING + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + jal MterpLogNullObjectException +#endif + b MterpCommonFallback + +/* + * If we're here, something is out of the ordinary. If there is a pending + * exception, handle it. Otherwise, roll back and retry with the reference + * interpreter. + */ +MterpPossibleException: + ld a0, THREAD_EXCEPTION_OFFSET(rSELF) + beqzc a0, MterpFallback # If not, fall back to reference interpreter. + /* intentional fallthrough - handle pending exception. */ +/* + * On return from a runtime helper routine, we've found a pending exception. + * Can we handle it here - or need to bail out to caller? + * + */ + .extern MterpHandleException +MterpException: + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + jal MterpHandleException # (self, shadow_frame) + beqzc v0, MterpExceptionReturn # no local catch, back to caller. + ld a0, OFF_FP_CODE_ITEM(rFP) + lwu a1, OFF_FP_DEX_PC(rFP) + REFRESH_IBASE + daddu rPC, a0, CODEITEM_INSNS_OFFSET + dlsa rPC, a1, rPC, 1 # generate new dex_pc_ptr + sd rPC, OFF_FP_DEX_PC_PTR(rFP) + /* resume execution at catch block */ + FETCH_INST + GET_INST_OPCODE v0 + GOTO_OPCODE v0 + /* NOTE: no fallthrough */ + +/* + * Check for suspend check request. Assumes rINST already loaded, rPC advanced and + * still needs to get the opcode and branch to it, and flags are in ra. + */ + .extern MterpSuspendCheck +MterpCheckSuspendAndContinue: + REFRESH_IBASE + and ra, ra, (THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + bnez ra, check1 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction +check1: + EXPORT_PC + move a0, rSELF + jal MterpSuspendCheck # (self) + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + +/* + * Bail out to reference interpreter. + */ + .extern MterpLogFallback +MterpFallback: + EXPORT_PC +#if MTERP_LOGGING + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + jal MterpLogFallback +#endif +MterpCommonFallback: + li v0, 0 # signal retry with reference interpreter. + b MterpDone + +/* + * We pushed some registers on the stack in ExecuteMterpImpl, then saved + * SP and RA. Here we restore SP, restore the registers, and then restore + * RA to PC. + * + * On entry: + * uint32_t* rFP (should still be live, pointer to base of vregs) + */ +MterpExceptionReturn: + li v0, 1 # signal return to caller. + b MterpDone +/* + * Returned value is expected in a0 and if it's not 64-bit, the 32 most + * significant bits of a0 must be 0. + */ +MterpReturn: + ld a2, OFF_FP_RESULT_REGISTER(rFP) + lw ra, THREAD_FLAGS_OFFSET(rSELF) + sd a0, 0(a2) + move a0, rSELF + and ra, ra, (THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + beqzc ra, check2 + jal MterpSuspendCheck # (self) +check2: + li v0, 1 # signal return to caller. +MterpDone: + ld s5, STACK_OFFSET_S5(sp) + .cfi_restore 21 + ld s4, STACK_OFFSET_S4(sp) + .cfi_restore 20 + ld s3, STACK_OFFSET_S3(sp) + .cfi_restore 19 + ld s2, STACK_OFFSET_S2(sp) + .cfi_restore 18 + ld s1, STACK_OFFSET_S1(sp) + .cfi_restore 17 + ld s0, STACK_OFFSET_S0(sp) + .cfi_restore 16 + + ld ra, STACK_OFFSET_RA(sp) + .cfi_restore 31 + + ld t8, STACK_OFFSET_GP(sp) + .cpreturn + .cfi_restore 28 + + .set noreorder + jr ra + daddu sp, sp, STACK_SIZE + .cfi_adjust_cfa_offset -STACK_SIZE + + .cfi_endproc + .size ExecuteMterpImpl, .-ExecuteMterpImpl diff --git a/runtime/interpreter/mterp/mips64/header.S b/runtime/interpreter/mterp/mips64/header.S new file mode 100644 index 0000000000..4c3ca9efff --- /dev/null +++ b/runtime/interpreter/mterp/mips64/header.S @@ -0,0 +1,285 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +/* TODO: add the missing file and use its FP register definitions. */ +/* #include */ +/* FP register definitions */ +#define f0 $$f0 +#define f1 $$f1 +#define f2 $$f2 +#define f3 $$f3 +#define f12 $$f12 +#define f13 $$f13 + +/* + * It looks like the GNU assembler currently does not support the blec and bgtc + * idioms, which should translate into bgec and bltc respectively with swapped + * left and right register operands. + * TODO: remove these macros when the assembler is fixed. + */ +.macro blec lreg, rreg, target + bgec \rreg, \lreg, \target +.endm +.macro bgtc lreg, rreg, target + bltc \rreg, \lreg, \target +.endm + +/* +Mterp and MIPS64 notes: + +The following registers have fixed assignments: + + reg nick purpose + s0 rPC interpreted program counter, used for fetching instructions + s1 rFP interpreted frame pointer, used for accessing locals and args + s2 rSELF self (Thread) pointer + s3 rINST first 16-bit code unit of current instruction + s4 rIBASE interpreted instruction base pointer, used for computed goto + s5 rREFS base of object references in shadow frame (ideally, we'll get rid of this later). +*/ + +/* During bringup, we'll use the shadow frame model instead of rFP */ +/* single-purpose registers, given names for clarity */ +#define rPC s0 +#define rFP s1 +#define rSELF s2 +#define rINST s3 +#define rIBASE s4 +#define rREFS s5 + +/* + * This is a #include, not a %include, because we want the C pre-processor + * to expand the macros into assembler assignment statements. + */ +#include "asm_support.h" + +/* + * Instead of holding a pointer to the shadow frame, we keep rFP at the base of the vregs. So, + * to access other shadow frame fields, we need to use a backwards offset. Define those here. + */ +#define OFF_FP(a) (a - SHADOWFRAME_VREGS_OFFSET) +#define OFF_FP_NUMBER_OF_VREGS OFF_FP(SHADOWFRAME_NUMBER_OF_VREGS_OFFSET) +#define OFF_FP_DEX_PC OFF_FP(SHADOWFRAME_DEX_PC_OFFSET) +#define OFF_FP_LINK OFF_FP(SHADOWFRAME_LINK_OFFSET) +#define OFF_FP_METHOD OFF_FP(SHADOWFRAME_METHOD_OFFSET) +#define OFF_FP_RESULT_REGISTER OFF_FP(SHADOWFRAME_RESULT_REGISTER_OFFSET) +#define OFF_FP_DEX_PC_PTR OFF_FP(SHADOWFRAME_DEX_PC_PTR_OFFSET) +#define OFF_FP_CODE_ITEM OFF_FP(SHADOWFRAME_CODE_ITEM_OFFSET) +#define OFF_FP_SHADOWFRAME (-SHADOWFRAME_VREGS_OFFSET) + +/* + * + * The reference interpreter performs explicit suspect checks, which is somewhat wasteful. + * Dalvik's interpreter folded suspend checks into the jump table mechanism, and eventually + * mterp should do so as well. + */ +#define MTERP_SUSPEND 0 + +#define MTERP_LOGGING 0 + +/* + * "export" the PC to dex_pc field in the shadow frame, f/b/o future exception objects. Must + * be done *before* something throws. + * + * It's okay to do this more than once. + * + * NOTE: the fast interpreter keeps track of dex pc as a direct pointer to the mapped + * dex byte codes. However, the rest of the runtime expects dex pc to be an instruction + * offset into the code_items_[] array. For effiency, we will "export" the + * current dex pc as a direct pointer using the EXPORT_PC macro, and rely on GetDexPC + * to convert to a dex pc when needed. + */ +.macro EXPORT_PC + sd rPC, OFF_FP_DEX_PC_PTR(rFP) +.endm + +/* + * Refresh handler table. + */ +.macro REFRESH_IBASE + ld rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) +.endm + +/* + * Fetch the next instruction from rPC into rINST. Does not advance rPC. + */ +.macro FETCH_INST + lhu rINST, 0(rPC) +.endm + +/* Advance rPC by some number of code units. */ +.macro ADVANCE count + daddu rPC, rPC, (\count) * 2 +.endm + +/* + * Fetch the next instruction from the specified offset. Advances rPC + * to point to the next instruction. + * + * This must come AFTER anything that can throw an exception, or the + * exception catch may miss. (This also implies that it must come after + * EXPORT_PC.) + */ +.macro FETCH_ADVANCE_INST count + ADVANCE \count + FETCH_INST +.endm + +/* + * Similar to FETCH_ADVANCE_INST, but does not update rPC. Used to load + * rINST ahead of possible exception point. Be sure to manually advance rPC + * later. + */ +.macro PREFETCH_INST count + lhu rINST, ((\count) * 2)(rPC) +.endm + +/* + * Put the instruction's opcode field into the specified register. + */ +.macro GET_INST_OPCODE reg + and \reg, rINST, 255 +.endm + +/* + * Begin executing the opcode in _reg. + */ +.macro GOTO_OPCODE reg + .set noat + sll AT, \reg, 7 + daddu AT, rIBASE, AT + jic AT, 0 + .set at +.endm + +/* + * Get/set the 32-bit value from a Dalvik register. + * Note, GET_VREG does sign extension to 64 bits while + * GET_VREG_U does zero extension to 64 bits. + * One is useful for arithmetic while the other is + * useful for storing the result value as 64-bit. + */ +.macro GET_VREG reg, vreg + .set noat + dlsa AT, \vreg, rFP, 2 + lw \reg, 0(AT) + .set at +.endm +.macro GET_VREG_U reg, vreg + .set noat + dlsa AT, \vreg, rFP, 2 + lwu \reg, 0(AT) + .set at +.endm +.macro GET_VREG_FLOAT reg, vreg + .set noat + dlsa AT, \vreg, rFP, 2 + lwc1 \reg, 0(AT) + .set at +.endm +.macro SET_VREG reg, vreg + .set noat + dlsa AT, \vreg, rFP, 2 + sw \reg, 0(AT) + dlsa AT, \vreg, rREFS, 2 + sw zero, 0(AT) + .set at +.endm +.macro SET_VREG_OBJECT reg, vreg + .set noat + dlsa AT, \vreg, rFP, 2 + sw \reg, 0(AT) + dlsa AT, \vreg, rREFS, 2 + sw \reg, 0(AT) + .set at +.endm +.macro SET_VREG_FLOAT reg, vreg + .set noat + dlsa AT, \vreg, rFP, 2 + swc1 \reg, 0(AT) + dlsa AT, \vreg, rREFS, 2 + sw zero, 0(AT) + .set at +.endm + +/* + * Get/set the 64-bit value from a Dalvik register. + * Avoid unaligned memory accesses. + * Note, SET_VREG_WIDE clobbers the register containing the value being stored. + * Note, SET_VREG_DOUBLE clobbers the register containing the Dalvik register number. + */ +.macro GET_VREG_WIDE reg, vreg + .set noat + dlsa AT, \vreg, rFP, 2 + lw \reg, 0(AT) + lw AT, 4(AT) + dinsu \reg, AT, 32, 32 + .set at +.endm +.macro GET_VREG_DOUBLE reg, vreg + .set noat + dlsa AT, \vreg, rFP, 2 + lwc1 \reg, 0(AT) + lw AT, 4(AT) + mthc1 AT, \reg + .set at +.endm +.macro SET_VREG_WIDE reg, vreg + .set noat + dlsa AT, \vreg, rFP, 2 + sw \reg, 0(AT) + drotr32 \reg, \reg, 0 + sw \reg, 4(AT) + dlsa AT, \vreg, rREFS, 2 + sw zero, 0(AT) + sw zero, 4(AT) + .set at +.endm +.macro SET_VREG_DOUBLE reg, vreg + .set noat + dlsa AT, \vreg, rREFS, 2 + sw zero, 0(AT) + sw zero, 4(AT) + dlsa AT, \vreg, rFP, 2 + swc1 \reg, 0(AT) + mfhc1 \vreg, \reg + sw \vreg, 4(AT) + .set at +.endm + +/* + * On-stack offsets for spilling/unspilling callee-saved registers + * and the frame size. + */ +#define STACK_OFFSET_RA 0 +#define STACK_OFFSET_GP 8 +#define STACK_OFFSET_S0 16 +#define STACK_OFFSET_S1 24 +#define STACK_OFFSET_S2 32 +#define STACK_OFFSET_S3 40 +#define STACK_OFFSET_S4 48 +#define STACK_OFFSET_S5 56 +#define STACK_SIZE 64 + +/* Constants for float/double_to_int/long conversions */ +#define INT_MIN 0x80000000 +#define INT_MIN_AS_FLOAT 0xCF000000 +#define INT_MIN_AS_DOUBLE 0xC1E0000000000000 +#define LONG_MIN 0x8000000000000000 +#define LONG_MIN_AS_FLOAT 0xDF000000 +#define LONG_MIN_AS_DOUBLE 0xC3E0000000000000 diff --git a/runtime/interpreter/mterp/mips64/invoke.S b/runtime/interpreter/mterp/mips64/invoke.S new file mode 100644 index 0000000000..4ae4fb1c1d --- /dev/null +++ b/runtime/interpreter/mterp/mips64/invoke.S @@ -0,0 +1,17 @@ +%default { "helper":"UndefinedInvokeHandler" } + /* + * Generic invoke handler wrapper. + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + .extern $helper + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rPC + move a3, rINST + jal $helper + beqzc v0, MterpException + FETCH_ADVANCE_INST 3 + GET_INST_OPCODE v0 + GOTO_OPCODE v0 diff --git a/runtime/interpreter/mterp/mips64/op_add_double.S b/runtime/interpreter/mterp/mips64/op_add_double.S new file mode 100644 index 0000000000..1520e325f7 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_add_double.S @@ -0,0 +1 @@ +%include "mips64/fbinopWide.S" {"instr":"add.d f0, f0, f1"} diff --git a/runtime/interpreter/mterp/mips64/op_add_double_2addr.S b/runtime/interpreter/mterp/mips64/op_add_double_2addr.S new file mode 100644 index 0000000000..c14382ef20 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_add_double_2addr.S @@ -0,0 +1 @@ +%include "mips64/fbinopWide2addr.S" {"instr":"add.d f0, f0, f1"} diff --git a/runtime/interpreter/mterp/mips64/op_add_float.S b/runtime/interpreter/mterp/mips64/op_add_float.S new file mode 100644 index 0000000000..c6ed558dc3 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_add_float.S @@ -0,0 +1 @@ +%include "mips64/fbinop.S" {"instr":"add.s f0, f0, f1"} diff --git a/runtime/interpreter/mterp/mips64/op_add_float_2addr.S b/runtime/interpreter/mterp/mips64/op_add_float_2addr.S new file mode 100644 index 0000000000..4c20547b22 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_add_float_2addr.S @@ -0,0 +1 @@ +%include "mips64/fbinop2addr.S" {"instr":"add.s f0, f0, f1"} diff --git a/runtime/interpreter/mterp/mips64/op_add_int.S b/runtime/interpreter/mterp/mips64/op_add_int.S new file mode 100644 index 0000000000..6e569de71a --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_add_int.S @@ -0,0 +1 @@ +%include "mips64/binop.S" {"instr":"addu a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_add_int_2addr.S b/runtime/interpreter/mterp/mips64/op_add_int_2addr.S new file mode 100644 index 0000000000..2a84124a3a --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_add_int_2addr.S @@ -0,0 +1 @@ +%include "mips64/binop2addr.S" {"instr":"addu a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_add_int_lit16.S b/runtime/interpreter/mterp/mips64/op_add_int_lit16.S new file mode 100644 index 0000000000..94b053bba3 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_add_int_lit16.S @@ -0,0 +1 @@ +%include "mips64/binopLit16.S" {"instr":"addu a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_add_int_lit8.S b/runtime/interpreter/mterp/mips64/op_add_int_lit8.S new file mode 100644 index 0000000000..3b6d734723 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_add_int_lit8.S @@ -0,0 +1 @@ +%include "mips64/binopLit8.S" {"instr":"addu a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_add_long.S b/runtime/interpreter/mterp/mips64/op_add_long.S new file mode 100644 index 0000000000..c8d702f29f --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_add_long.S @@ -0,0 +1 @@ +%include "mips64/binopWide.S" {"instr":"daddu a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_add_long_2addr.S b/runtime/interpreter/mterp/mips64/op_add_long_2addr.S new file mode 100644 index 0000000000..928ff54565 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_add_long_2addr.S @@ -0,0 +1 @@ +%include "mips64/binopWide2addr.S" {"instr":"daddu a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_aget.S b/runtime/interpreter/mterp/mips64/op_aget.S new file mode 100644 index 0000000000..0472a0616b --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_aget.S @@ -0,0 +1,29 @@ +%default { "load":"lw", "shift":"2", "data_offset":"MIRROR_INT_ARRAY_DATA_OFFSET" } + /* + * Array get, 32 bits or less. vAA <- vBB[vCC]. + * + * for: aget, aget-boolean, aget-byte, aget-char, aget-short + * + * NOTE: assumes data offset for arrays is the same for all non-wide types. + * If this changes, specialize. + */ + /* op vAA, vBB, vCC */ + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + srl a4, rINST, 8 # a4 <- AA + GET_VREG_U a0, a2 # a0 <- vBB (array object) + GET_VREG a1, a3 # a1 <- vCC (requested index) + beqz a0, common_errNullObject # bail if null array object + lw a3, MIRROR_ARRAY_LENGTH_OFFSET(a0) # a3 <- arrayObj->length + .if $shift + # [d]lsa does not support shift count of 0. + dlsa a0, a1, a0, $shift # a0 <- arrayObj + index*width + .else + daddu a0, a1, a0 # a0 <- arrayObj + index*width + .endif + bgeu a1, a3, common_errArrayIndex # unsigned compare: index >= length, bail + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + $load a2, $data_offset(a0) # a2 <- vBB[vCC] + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a2, a4 # vAA <- a2 + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_aget_boolean.S b/runtime/interpreter/mterp/mips64/op_aget_boolean.S new file mode 100644 index 0000000000..d5be01b7c5 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_aget_boolean.S @@ -0,0 +1 @@ +%include "mips64/op_aget.S" { "load":"lbu", "shift":"0", "data_offset":"MIRROR_BOOLEAN_ARRAY_DATA_OFFSET" } diff --git a/runtime/interpreter/mterp/mips64/op_aget_byte.S b/runtime/interpreter/mterp/mips64/op_aget_byte.S new file mode 100644 index 0000000000..084de8d4df --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_aget_byte.S @@ -0,0 +1 @@ +%include "mips64/op_aget.S" { "load":"lb", "shift":"0", "data_offset":"MIRROR_BYTE_ARRAY_DATA_OFFSET" } diff --git a/runtime/interpreter/mterp/mips64/op_aget_char.S b/runtime/interpreter/mterp/mips64/op_aget_char.S new file mode 100644 index 0000000000..6c99ed52ad --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_aget_char.S @@ -0,0 +1 @@ +%include "mips64/op_aget.S" { "load":"lhu", "shift":"1", "data_offset":"MIRROR_CHAR_ARRAY_DATA_OFFSET" } diff --git a/runtime/interpreter/mterp/mips64/op_aget_object.S b/runtime/interpreter/mterp/mips64/op_aget_object.S new file mode 100644 index 0000000000..6374a05e7b --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_aget_object.S @@ -0,0 +1,21 @@ + /* + * Array object get. vAA <- vBB[vCC]. + * + * for: aget-object + */ + /* op vAA, vBB, vCC */ + .extern artAGetObjectFromMterp + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + EXPORT_PC + GET_VREG_U a0, a2 # a0 <- vBB (array object) + GET_VREG a1, a3 # a1 <- vCC (requested index) + jal artAGetObjectFromMterp # (array, index) + ld a1, THREAD_EXCEPTION_OFFSET(rSELF) + srl a4, rINST, 8 # a4 <- AA + PREFETCH_INST 2 + bnez a1, MterpException + SET_VREG_OBJECT v0, a4 # vAA <- v0 + ADVANCE 2 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_aget_short.S b/runtime/interpreter/mterp/mips64/op_aget_short.S new file mode 100644 index 0000000000..0158b0a1a1 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_aget_short.S @@ -0,0 +1 @@ +%include "mips64/op_aget.S" { "load":"lh", "shift":"1", "data_offset":"MIRROR_SHORT_ARRAY_DATA_OFFSET" } diff --git a/runtime/interpreter/mterp/mips64/op_aget_wide.S b/runtime/interpreter/mterp/mips64/op_aget_wide.S new file mode 100644 index 0000000000..0945acae5a --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_aget_wide.S @@ -0,0 +1,21 @@ + /* + * Array get, 64 bits. vAA <- vBB[vCC]. + * + */ + /* aget-wide vAA, vBB, vCC */ + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + srl a4, rINST, 8 # a4 <- AA + GET_VREG_U a0, a2 # a0 <- vBB (array object) + GET_VREG a1, a3 # a1 <- vCC (requested index) + beqz a0, common_errNullObject # bail if null array object + lw a3, MIRROR_ARRAY_LENGTH_OFFSET(a0) # a3 <- arrayObj->length + dlsa a0, a1, a0, 3 # a0 <- arrayObj + index*width + bgeu a1, a3, common_errArrayIndex # unsigned compare: index >= length, bail + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + lw a2, MIRROR_WIDE_ARRAY_DATA_OFFSET(a0) + lw a3, (MIRROR_WIDE_ARRAY_DATA_OFFSET+4)(a0) + dinsu a2, a3, 32, 32 # a2 <- vBB[vCC] + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a2, a4 # vAA <- a2 + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_and_int.S b/runtime/interpreter/mterp/mips64/op_and_int.S new file mode 100644 index 0000000000..f0792a8351 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_and_int.S @@ -0,0 +1 @@ +%include "mips64/binop.S" {"instr":"and a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_and_int_2addr.S b/runtime/interpreter/mterp/mips64/op_and_int_2addr.S new file mode 100644 index 0000000000..08dc615518 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_and_int_2addr.S @@ -0,0 +1 @@ +%include "mips64/binop2addr.S" {"instr":"and a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_and_int_lit16.S b/runtime/interpreter/mterp/mips64/op_and_int_lit16.S new file mode 100644 index 0000000000..65d28ad20c --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_and_int_lit16.S @@ -0,0 +1 @@ +%include "mips64/binopLit16.S" {"instr":"and a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_and_int_lit8.S b/runtime/interpreter/mterp/mips64/op_and_int_lit8.S new file mode 100644 index 0000000000..ab84bb7ce2 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_and_int_lit8.S @@ -0,0 +1 @@ +%include "mips64/binopLit8.S" {"instr":"and a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_and_long.S b/runtime/interpreter/mterp/mips64/op_and_long.S new file mode 100644 index 0000000000..e383ba00ca --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_and_long.S @@ -0,0 +1 @@ +%include "mips64/binopWide.S" {"instr":"and a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_and_long_2addr.S b/runtime/interpreter/mterp/mips64/op_and_long_2addr.S new file mode 100644 index 0000000000..f863bb9275 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_and_long_2addr.S @@ -0,0 +1 @@ +%include "mips64/binopWide2addr.S" {"instr":"and a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_aput.S b/runtime/interpreter/mterp/mips64/op_aput.S new file mode 100644 index 0000000000..9bfda97d05 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_aput.S @@ -0,0 +1,29 @@ +%default { "store":"sw", "shift":"2", "data_offset":"MIRROR_INT_ARRAY_DATA_OFFSET" } + /* + * Array put, 32 bits or less. vBB[vCC] <- vAA. + * + * for: aput, aput-boolean, aput-byte, aput-char, aput-short + * + * NOTE: this assumes data offset for arrays is the same for all non-wide types. + * If this changes, specialize. + */ + /* op vAA, vBB, vCC */ + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + srl a4, rINST, 8 # a4 <- AA + GET_VREG_U a0, a2 # a0 <- vBB (array object) + GET_VREG a1, a3 # a1 <- vCC (requested index) + beqz a0, common_errNullObject # bail if null array object + lw a3, MIRROR_ARRAY_LENGTH_OFFSET(a0) # a3 <- arrayObj->length + .if $shift + # [d]lsa does not support shift count of 0. + dlsa a0, a1, a0, $shift # a0 <- arrayObj + index*width + .else + daddu a0, a1, a0 # a0 <- arrayObj + index*width + .endif + bgeu a1, a3, common_errArrayIndex # unsigned compare: index >= length, bail + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_VREG a2, a4 # a2 <- vAA + GET_INST_OPCODE v0 # extract opcode from rINST + $store a2, $data_offset(a0) # vBB[vCC] <- a2 + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_aput_boolean.S b/runtime/interpreter/mterp/mips64/op_aput_boolean.S new file mode 100644 index 0000000000..6707a1f11d --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_aput_boolean.S @@ -0,0 +1 @@ +%include "mips64/op_aput.S" { "store":"sb", "shift":"0", "data_offset":"MIRROR_BOOLEAN_ARRAY_DATA_OFFSET" } diff --git a/runtime/interpreter/mterp/mips64/op_aput_byte.S b/runtime/interpreter/mterp/mips64/op_aput_byte.S new file mode 100644 index 0000000000..7b9ce48379 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_aput_byte.S @@ -0,0 +1 @@ +%include "mips64/op_aput.S" { "store":"sb", "shift":"0", "data_offset":"MIRROR_BYTE_ARRAY_DATA_OFFSET" } diff --git a/runtime/interpreter/mterp/mips64/op_aput_char.S b/runtime/interpreter/mterp/mips64/op_aput_char.S new file mode 100644 index 0000000000..82bc8f7818 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_aput_char.S @@ -0,0 +1 @@ +%include "mips64/op_aput.S" { "store":"sh", "shift":"1", "data_offset":"MIRROR_CHAR_ARRAY_DATA_OFFSET" } diff --git a/runtime/interpreter/mterp/mips64/op_aput_object.S b/runtime/interpreter/mterp/mips64/op_aput_object.S new file mode 100644 index 0000000000..b132456a18 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_aput_object.S @@ -0,0 +1,14 @@ + /* + * Store an object into an array. vBB[vCC] <- vAA. + */ + /* op vAA, vBB, vCC */ + .extern MterpAputObject + EXPORT_PC + daddu a0, rFP, OFF_FP_SHADOWFRAME + move a1, rPC + move a2, rINST + jal MterpAputObject + beqzc v0, MterpPossibleException + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_aput_short.S b/runtime/interpreter/mterp/mips64/op_aput_short.S new file mode 100644 index 0000000000..a7af2945b1 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_aput_short.S @@ -0,0 +1 @@ +%include "mips64/op_aput.S" { "store":"sh", "shift":"1", "data_offset":"MIRROR_SHORT_ARRAY_DATA_OFFSET" } diff --git a/runtime/interpreter/mterp/mips64/op_aput_wide.S b/runtime/interpreter/mterp/mips64/op_aput_wide.S new file mode 100644 index 0000000000..a1d7a3b51e --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_aput_wide.S @@ -0,0 +1,21 @@ + /* + * Array put, 64 bits. vBB[vCC] <- vAA. + * + */ + /* aput-wide vAA, vBB, vCC */ + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + srl a4, rINST, 8 # a4 <- AA + GET_VREG_U a0, a2 # a0 <- vBB (array object) + GET_VREG a1, a3 # a1 <- vCC (requested index) + beqz a0, common_errNullObject # bail if null array object + lw a3, MIRROR_ARRAY_LENGTH_OFFSET(a0) # a3 <- arrayObj->length + dlsa a0, a1, a0, 3 # a0 <- arrayObj + index*width + bgeu a1, a3, common_errArrayIndex # unsigned compare: index >= length, bail + GET_VREG_WIDE a2, a4 # a2 <- vAA + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + sw a2, MIRROR_WIDE_ARRAY_DATA_OFFSET(a0) + dsrl32 a2, a2, 0 + sw a2, (MIRROR_WIDE_ARRAY_DATA_OFFSET+4)(a0) # vBB[vCC] <- a2 + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_array_length.S b/runtime/interpreter/mterp/mips64/op_array_length.S new file mode 100644 index 0000000000..2d9e172d18 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_array_length.S @@ -0,0 +1,12 @@ + /* + * Return the length of an array. + */ + srl a1, rINST, 12 # a1 <- B + GET_VREG_U a0, a1 # a0 <- vB (object ref) + ext a2, rINST, 8, 4 # a2 <- A + beqz a0, common_errNullObject # yup, fail + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + lw a3, MIRROR_ARRAY_LENGTH_OFFSET(a0) # a3 <- array length + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a3, a2 # vB <- length + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_check_cast.S b/runtime/interpreter/mterp/mips64/op_check_cast.S new file mode 100644 index 0000000000..472595d824 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_check_cast.S @@ -0,0 +1,17 @@ + /* + * Check to see if a cast from one class to another is allowed. + */ + /* check-cast vAA, class//BBBB */ + .extern MterpCheckCast + EXPORT_PC + lhu a0, 2(rPC) # a0 <- BBBB + srl a1, rINST, 8 # a1 <- AA + dlsa a1, a1, rFP, 2 # a1 <- &object + ld a2, OFF_FP_METHOD(rFP) # a2 <- method + move a3, rSELF # a3 <- self + jal MterpCheckCast # (index, &obj, method, self) + PREFETCH_INST 2 + bnez v0, MterpPossibleException + ADVANCE 2 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_cmp_long.S b/runtime/interpreter/mterp/mips64/op_cmp_long.S new file mode 100644 index 0000000000..6e9376cfab --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_cmp_long.S @@ -0,0 +1,13 @@ + /* cmp-long vAA, vBB, vCC */ + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + srl a4, rINST, 8 # a4 <- AA + GET_VREG_WIDE a0, a2 # a0 <- vBB + GET_VREG_WIDE a1, a3 # a1 <- vCC + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + slt a2, a0, a1 + slt a0, a1, a0 + subu a0, a0, a2 + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a4 # vAA <- result + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_cmpg_double.S b/runtime/interpreter/mterp/mips64/op_cmpg_double.S new file mode 100644 index 0000000000..a8e2ef9867 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_cmpg_double.S @@ -0,0 +1 @@ +%include "mips64/fcmpWide.S" {"gt_bias":"1"} diff --git a/runtime/interpreter/mterp/mips64/op_cmpg_float.S b/runtime/interpreter/mterp/mips64/op_cmpg_float.S new file mode 100644 index 0000000000..0c93eac7de --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_cmpg_float.S @@ -0,0 +1 @@ +%include "mips64/fcmp.S" {"gt_bias":"1"} diff --git a/runtime/interpreter/mterp/mips64/op_cmpl_double.S b/runtime/interpreter/mterp/mips64/op_cmpl_double.S new file mode 100644 index 0000000000..9111b067f6 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_cmpl_double.S @@ -0,0 +1 @@ +%include "mips64/fcmpWide.S" {"gt_bias":"0"} diff --git a/runtime/interpreter/mterp/mips64/op_cmpl_float.S b/runtime/interpreter/mterp/mips64/op_cmpl_float.S new file mode 100644 index 0000000000..b047451842 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_cmpl_float.S @@ -0,0 +1 @@ +%include "mips64/fcmp.S" {"gt_bias":"0"} diff --git a/runtime/interpreter/mterp/mips64/op_const.S b/runtime/interpreter/mterp/mips64/op_const.S new file mode 100644 index 0000000000..4b0d69b763 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_const.S @@ -0,0 +1,9 @@ + /* const vAA, #+BBBBbbbb */ + srl a2, rINST, 8 # a2 <- AA + lh a0, 2(rPC) # a0 <- bbbb (low) + lh a1, 4(rPC) # a1 <- BBBB (high) + FETCH_ADVANCE_INST 3 # advance rPC, load rINST + ins a0, a1, 16, 16 # a0 = BBBBbbbb + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vAA <- +BBBBbbbb + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_const_16.S b/runtime/interpreter/mterp/mips64/op_const_16.S new file mode 100644 index 0000000000..51e68a7df7 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_const_16.S @@ -0,0 +1,7 @@ + /* const/16 vAA, #+BBBB */ + srl a2, rINST, 8 # a2 <- AA + lh a0, 2(rPC) # a0 <- sign-extended BBBB + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vAA <- +BBBB + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_const_4.S b/runtime/interpreter/mterp/mips64/op_const_4.S new file mode 100644 index 0000000000..0a58bff7b7 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_const_4.S @@ -0,0 +1,8 @@ + /* const/4 vA, #+B */ + ext a2, rINST, 8, 4 # a2 <- A + seh a0, rINST # sign extend B in rINST + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + sra a0, a0, 12 # shift B into its final position + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vA <- +B + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_const_class.S b/runtime/interpreter/mterp/mips64/op_const_class.S new file mode 100644 index 0000000000..adf79df38e --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_const_class.S @@ -0,0 +1,13 @@ + /* const/class vAA, Class//BBBB */ + .extern MterpConstClass + EXPORT_PC + lhu a0, 2(rPC) # a0 <- BBBB + srl a1, rINST, 8 # a1 <- AA + daddu a2, rFP, OFF_FP_SHADOWFRAME + move a3, rSELF + jal MterpConstClass # (index, tgt_reg, shadow_frame, self) + PREFETCH_INST 2 # load rINST + bnez v0, MterpPossibleException # let reference interpreter deal with it. + ADVANCE 2 # advance rPC + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_const_high16.S b/runtime/interpreter/mterp/mips64/op_const_high16.S new file mode 100644 index 0000000000..43effb6f60 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_const_high16.S @@ -0,0 +1,8 @@ + /* const/high16 vAA, #+BBBB0000 */ + srl a2, rINST, 8 # a2 <- AA + lh a0, 2(rPC) # a0 <- BBBB + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + sll a0, a0, 16 # a0 <- BBBB0000 + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vAA <- +BBBB0000 + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_const_string.S b/runtime/interpreter/mterp/mips64/op_const_string.S new file mode 100644 index 0000000000..4684c11854 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_const_string.S @@ -0,0 +1,13 @@ + /* const/string vAA, String//BBBB */ + .extern MterpConstString + EXPORT_PC + lhu a0, 2(rPC) # a0 <- BBBB + srl a1, rINST, 8 # a1 <- AA + daddu a2, rFP, OFF_FP_SHADOWFRAME + move a3, rSELF + jal MterpConstString # (index, tgt_reg, shadow_frame, self) + PREFETCH_INST 2 # load rINST + bnez v0, MterpPossibleException # let reference interpreter deal with it. + ADVANCE 2 # advance rPC + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_const_string_jumbo.S b/runtime/interpreter/mterp/mips64/op_const_string_jumbo.S new file mode 100644 index 0000000000..47f2101c88 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_const_string_jumbo.S @@ -0,0 +1,15 @@ + /* const/string vAA, String//BBBBBBBB */ + .extern MterpConstString + EXPORT_PC + lh a0, 2(rPC) # a0 <- bbbb (low) + lh a4, 4(rPC) # a4 <- BBBB (high) + srl a1, rINST, 8 # a1 <- AA + ins a0, a4, 16, 16 # a0 <- BBBBbbbb + daddu a2, rFP, OFF_FP_SHADOWFRAME + move a3, rSELF + jal MterpConstString # (index, tgt_reg, shadow_frame, self) + PREFETCH_INST 3 # load rINST + bnez v0, MterpPossibleException # let reference interpreter deal with it. + ADVANCE 3 # advance rPC + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_const_wide.S b/runtime/interpreter/mterp/mips64/op_const_wide.S new file mode 100644 index 0000000000..f7eaf7c231 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_const_wide.S @@ -0,0 +1,13 @@ + /* const-wide vAA, #+HHHHhhhhBBBBbbbb */ + srl a4, rINST, 8 # a4 <- AA + lh a0, 2(rPC) # a0 <- bbbb (low) + lh a1, 4(rPC) # a1 <- BBBB (low middle) + lh a2, 6(rPC) # a2 <- hhhh (high middle) + lh a3, 8(rPC) # a3 <- HHHH (high) + FETCH_ADVANCE_INST 5 # advance rPC, load rINST + ins a0, a1, 16, 16 # a0 = BBBBbbbb + ins a2, a3, 16, 16 # a2 = HHHHhhhh + dinsu a0, a2, 32, 32 # a0 = HHHHhhhhBBBBbbbb + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a4 # vAA <- +HHHHhhhhBBBBbbbb + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_const_wide_16.S b/runtime/interpreter/mterp/mips64/op_const_wide_16.S new file mode 100644 index 0000000000..3a70937973 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_const_wide_16.S @@ -0,0 +1,7 @@ + /* const-wide/16 vAA, #+BBBB */ + srl a2, rINST, 8 # a2 <- AA + lh a0, 2(rPC) # a0 <- sign-extended BBBB + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a2 # vAA <- +BBBB + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_const_wide_32.S b/runtime/interpreter/mterp/mips64/op_const_wide_32.S new file mode 100644 index 0000000000..867197ce13 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_const_wide_32.S @@ -0,0 +1,9 @@ + /* const-wide/32 vAA, #+BBBBbbbb */ + srl a2, rINST, 8 # a2 <- AA + lh a0, 2(rPC) # a0 <- bbbb (low) + lh a1, 4(rPC) # a1 <- BBBB (high) + FETCH_ADVANCE_INST 3 # advance rPC, load rINST + ins a0, a1, 16, 16 # a0 = BBBBbbbb + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a2 # vAA <- +BBBBbbbb + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_const_wide_high16.S b/runtime/interpreter/mterp/mips64/op_const_wide_high16.S new file mode 100644 index 0000000000..d741631bcb --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_const_wide_high16.S @@ -0,0 +1,8 @@ + /* const-wide/high16 vAA, #+BBBB000000000000 */ + srl a2, rINST, 8 # a2 <- AA + lh a0, 2(rPC) # a0 <- BBBB + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + dsll32 a0, a0, 16 # a0 <- BBBB000000000000 + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a2 # vAA <- +BBBB000000000000 + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_div_double.S b/runtime/interpreter/mterp/mips64/op_div_double.S new file mode 100644 index 0000000000..44998f0c29 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_div_double.S @@ -0,0 +1 @@ +%include "mips64/fbinopWide.S" {"instr":"div.d f0, f0, f1"} diff --git a/runtime/interpreter/mterp/mips64/op_div_double_2addr.S b/runtime/interpreter/mterp/mips64/op_div_double_2addr.S new file mode 100644 index 0000000000..396af798f6 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_div_double_2addr.S @@ -0,0 +1 @@ +%include "mips64/fbinopWide2addr.S" {"instr":"div.d f0, f0, f1"} diff --git a/runtime/interpreter/mterp/mips64/op_div_float.S b/runtime/interpreter/mterp/mips64/op_div_float.S new file mode 100644 index 0000000000..7b09d52f02 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_div_float.S @@ -0,0 +1 @@ +%include "mips64/fbinop.S" {"instr":"div.s f0, f0, f1"} diff --git a/runtime/interpreter/mterp/mips64/op_div_float_2addr.S b/runtime/interpreter/mterp/mips64/op_div_float_2addr.S new file mode 100644 index 0000000000..e74fddae6d --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_div_float_2addr.S @@ -0,0 +1 @@ +%include "mips64/fbinop2addr.S" {"instr":"div.s f0, f0, f1"} diff --git a/runtime/interpreter/mterp/mips64/op_div_int.S b/runtime/interpreter/mterp/mips64/op_div_int.S new file mode 100644 index 0000000000..fb04acbff8 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_div_int.S @@ -0,0 +1 @@ +%include "mips64/binop.S" {"instr":"div a0, a0, a1", "chkzero":"1"} diff --git a/runtime/interpreter/mterp/mips64/op_div_int_2addr.S b/runtime/interpreter/mterp/mips64/op_div_int_2addr.S new file mode 100644 index 0000000000..db29b844fb --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_div_int_2addr.S @@ -0,0 +1 @@ +%include "mips64/binop2addr.S" {"instr":"div a0, a0, a1", "chkzero":"1"} diff --git a/runtime/interpreter/mterp/mips64/op_div_int_lit16.S b/runtime/interpreter/mterp/mips64/op_div_int_lit16.S new file mode 100644 index 0000000000..e903ddee2c --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_div_int_lit16.S @@ -0,0 +1 @@ +%include "mips64/binopLit16.S" {"instr":"div a0, a0, a1", "chkzero":"1"} diff --git a/runtime/interpreter/mterp/mips64/op_div_int_lit8.S b/runtime/interpreter/mterp/mips64/op_div_int_lit8.S new file mode 100644 index 0000000000..055960546f --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_div_int_lit8.S @@ -0,0 +1 @@ +%include "mips64/binopLit8.S" {"instr":"div a0, a0, a1", "chkzero":"1"} diff --git a/runtime/interpreter/mterp/mips64/op_div_long.S b/runtime/interpreter/mterp/mips64/op_div_long.S new file mode 100644 index 0000000000..01fc2b281a --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_div_long.S @@ -0,0 +1 @@ +%include "mips64/binopWide.S" {"instr":"ddiv a0, a0, a1", "chkzero":"1"} diff --git a/runtime/interpreter/mterp/mips64/op_div_long_2addr.S b/runtime/interpreter/mterp/mips64/op_div_long_2addr.S new file mode 100644 index 0000000000..9627ab8a24 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_div_long_2addr.S @@ -0,0 +1 @@ +%include "mips64/binopWide2addr.S" {"instr":"ddiv a0, a0, a1", "chkzero":"1"} diff --git a/runtime/interpreter/mterp/mips64/op_double_to_float.S b/runtime/interpreter/mterp/mips64/op_double_to_float.S new file mode 100644 index 0000000000..2b2acee591 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_double_to_float.S @@ -0,0 +1,8 @@ + /* + * Conversion from or to floating-point happens in a floating-point register. + * Therefore we load the input and store the output into or from a + * floating-point register irrespective of the type. + */ +%include "mips64/fcvtHeader.S" { "suffix":"_DOUBLE", "valreg":"f0" } + cvt.s.d f0, f0 +%include "mips64/fcvtFooter.S" { "suffix":"_FLOAT", "valreg":"f0" } diff --git a/runtime/interpreter/mterp/mips64/op_double_to_int.S b/runtime/interpreter/mterp/mips64/op_double_to_int.S new file mode 100644 index 0000000000..aa2cbcad38 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_double_to_int.S @@ -0,0 +1,23 @@ +%include "mips64/fcvtHeader.S" { "suffix":"_DOUBLE", "valreg":"f0" } + /* + * TODO: simplify this when the MIPS64R6 emulator + * supports NAN2008=1. + */ + dli t0, INT_MIN_AS_DOUBLE + dmtc1 t0, f1 + cmp.le.d f1, f1, f0 + bc1nez f1, .L${opcode}_trunc + cmp.eq.d f1, f0, f0 + li t0, INT_MIN + mfc1 t1, f1 + and t0, t0, t1 + b .L${opcode}_done +%break +.L${opcode}_trunc: + trunc.w.d f0, f0 + mfc1 t0, f0 +.L${opcode}_done: + /* Can't include fcvtFooter.S after break */ + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG t0, a1 + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_double_to_long.S b/runtime/interpreter/mterp/mips64/op_double_to_long.S new file mode 100644 index 0000000000..777cfeb6c8 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_double_to_long.S @@ -0,0 +1,23 @@ +%include "mips64/fcvtHeader.S" { "suffix":"_DOUBLE", "valreg":"f0" } + /* + * TODO: simplify this when the MIPS64R6 emulator + * supports NAN2008=1. + */ + dli t0, LONG_MIN_AS_DOUBLE + dmtc1 t0, f1 + cmp.le.d f1, f1, f0 + bc1nez f1, .L${opcode}_trunc + cmp.eq.d f1, f0, f0 + dli t0, LONG_MIN + mfc1 t1, f1 + and t0, t0, t1 + b .L${opcode}_done +%break +.L${opcode}_trunc: + trunc.l.d f0, f0 + dmfc1 t0, f0 +.L${opcode}_done: + /* Can't include fcvtFooter.S after break */ + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE t0, a1 + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_fill_array_data.S b/runtime/interpreter/mterp/mips64/op_fill_array_data.S new file mode 100644 index 0000000000..c90f0b90ad --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_fill_array_data.S @@ -0,0 +1,14 @@ + /* fill-array-data vAA, +BBBBBBBB */ + .extern MterpFillArrayData + EXPORT_PC + lh a1, 2(rPC) # a1 <- bbbb (lo) + lh a0, 4(rPC) # a0 <- BBBB (hi) + srl a3, rINST, 8 # a3 <- AA + ins a1, a0, 16, 16 # a1 <- BBBBbbbb + GET_VREG_U a0, a3 # a0 <- vAA (array object) + dlsa a1, a1, rPC, 1 # a1 <- PC + BBBBbbbb*2 (array data off.) + jal MterpFillArrayData # (obj, payload) + beqzc v0, MterpPossibleException # exception? + FETCH_ADVANCE_INST 3 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_filled_new_array.S b/runtime/interpreter/mterp/mips64/op_filled_new_array.S new file mode 100644 index 0000000000..35f55c27a6 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_filled_new_array.S @@ -0,0 +1,18 @@ +%default { "helper":"MterpFilledNewArray" } + /* + * Create a new array with elements filled from registers. + * + * for: filled-new-array, filled-new-array/range + */ + /* op vB, {vD, vE, vF, vG, vA}, class//CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, type//BBBB */ + .extern $helper + EXPORT_PC + daddu a0, rFP, OFF_FP_SHADOWFRAME + move a1, rPC + move a2, rSELF + jal $helper + beqzc v0, MterpPossibleException + FETCH_ADVANCE_INST 3 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_filled_new_array_range.S b/runtime/interpreter/mterp/mips64/op_filled_new_array_range.S new file mode 100644 index 0000000000..a4e18f68d6 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_filled_new_array_range.S @@ -0,0 +1 @@ +%include "mips64/op_filled_new_array.S" { "helper":"MterpFilledNewArrayRange" } diff --git a/runtime/interpreter/mterp/mips64/op_float_to_double.S b/runtime/interpreter/mterp/mips64/op_float_to_double.S new file mode 100644 index 0000000000..6accfeeff6 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_float_to_double.S @@ -0,0 +1,8 @@ + /* + * Conversion from or to floating-point happens in a floating-point register. + * Therefore we load the input and store the output into or from a + * floating-point register irrespective of the type. + */ +%include "mips64/fcvtHeader.S" { "suffix":"_FLOAT", "valreg":"f0" } + cvt.d.s f0, f0 +%include "mips64/fcvtFooter.S" { "suffix":"_DOUBLE", "valreg":"f0" } diff --git a/runtime/interpreter/mterp/mips64/op_float_to_int.S b/runtime/interpreter/mterp/mips64/op_float_to_int.S new file mode 100644 index 0000000000..d957540a7b --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_float_to_int.S @@ -0,0 +1,23 @@ +%include "mips64/fcvtHeader.S" { "suffix":"_FLOAT", "valreg":"f0" } + /* + * TODO: simplify this when the MIPS64R6 emulator + * supports NAN2008=1. + */ + li t0, INT_MIN_AS_FLOAT + mtc1 t0, f1 + cmp.le.s f1, f1, f0 + bc1nez f1, .L${opcode}_trunc + cmp.eq.s f1, f0, f0 + li t0, INT_MIN + mfc1 t1, f1 + and t0, t0, t1 + b .L${opcode}_done +%break +.L${opcode}_trunc: + trunc.w.s f0, f0 + mfc1 t0, f0 +.L${opcode}_done: + /* Can't include fcvtFooter.S after break */ + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG t0, a1 + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_float_to_long.S b/runtime/interpreter/mterp/mips64/op_float_to_long.S new file mode 100644 index 0000000000..5d036c8455 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_float_to_long.S @@ -0,0 +1,23 @@ +%include "mips64/fcvtHeader.S" { "suffix":"_FLOAT", "valreg":"f0" } + /* + * TODO: simplify this when the MIPS64R6 emulator + * supports NAN2008=1. + */ + li t0, LONG_MIN_AS_FLOAT + mtc1 t0, f1 + cmp.le.s f1, f1, f0 + bc1nez f1, .L${opcode}_trunc + cmp.eq.s f1, f0, f0 + dli t0, LONG_MIN + mfc1 t1, f1 + and t0, t0, t1 + b .L${opcode}_done +%break +.L${opcode}_trunc: + trunc.l.s f0, f0 + dmfc1 t0, f0 +.L${opcode}_done: + /* Can't include fcvtFooter.S after break */ + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE t0, a1 + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_goto.S b/runtime/interpreter/mterp/mips64/op_goto.S new file mode 100644 index 0000000000..f2df3e4112 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_goto.S @@ -0,0 +1,23 @@ + /* + * Unconditional branch, 8-bit offset. + * + * The branch distance is a signed code-unit offset, which we need to + * double to get a byte offset. + */ + /* goto +AA */ + srl a0, rINST, 8 + seb a0, a0 # a0 <- sign-extended AA + dlsa rPC, a0, rPC, 1 # rPC <- rPC + AA * 2 + FETCH_INST # load rINST + +#if MTERP_SUSPEND + bgez a0, 1f # AA * 2 >= 0 => no suspend check + REFRESH_IBASE +1: +#else + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + bltz a0, MterpCheckSuspendAndContinue +#endif + + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_goto_16.S b/runtime/interpreter/mterp/mips64/op_goto_16.S new file mode 100644 index 0000000000..cbf8cf2994 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_goto_16.S @@ -0,0 +1,22 @@ + /* + * Unconditional branch, 16-bit offset. + * + * The branch distance is a signed code-unit offset, which we need to + * double to get a byte offset. + */ + /* goto/16 +AAAA */ + lh a0, 2(rPC) # a0 <- sign-extended AAAA + dlsa rPC, a0, rPC, 1 # rPC <- rPC + AAAA * 2 + FETCH_INST # load rINST + +#if MTERP_SUSPEND + bgez a0, 1f # AA * 2 >= 0 => no suspend check + REFRESH_IBASE +1: +#else + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + bltz a0, MterpCheckSuspendAndContinue +#endif + + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_goto_32.S b/runtime/interpreter/mterp/mips64/op_goto_32.S new file mode 100644 index 0000000000..4a1feac1ac --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_goto_32.S @@ -0,0 +1,27 @@ + /* + * Unconditional branch, 32-bit offset. + * + * The branch distance is a signed code-unit offset, which we need to + * double to get a byte offset. + * + * Unlike most opcodes, this one is allowed to branch to itself, so + * our "backward branch" test must be "<=0" instead of "<0". + */ + /* goto/32 +AAAAAAAA */ + lh a0, 2(rPC) # a0 <- aaaa (low) + lh a1, 4(rPC) # a1 <- AAAA (high) + ins a0, a1, 16, 16 # a0 = sign-extended AAAAaaaa + dlsa rPC, a0, rPC, 1 # rPC <- rPC + AAAAAAAA * 2 + FETCH_INST # load rINST + +#if MTERP_SUSPEND + bgtz a0, 1f # AA * 2 > 0 => no suspend check + REFRESH_IBASE +1: +#else + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + blez a0, MterpCheckSuspendAndContinue +#endif + + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_if_eq.S b/runtime/interpreter/mterp/mips64/op_if_eq.S new file mode 100644 index 0000000000..aa35cadf17 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_if_eq.S @@ -0,0 +1 @@ +%include "mips64/bincmp.S" { "condition":"eq" } diff --git a/runtime/interpreter/mterp/mips64/op_if_eqz.S b/runtime/interpreter/mterp/mips64/op_if_eqz.S new file mode 100644 index 0000000000..0fe34187a0 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_if_eqz.S @@ -0,0 +1 @@ +%include "mips64/zcmp.S" { "condition":"eq" } diff --git a/runtime/interpreter/mterp/mips64/op_if_ge.S b/runtime/interpreter/mterp/mips64/op_if_ge.S new file mode 100644 index 0000000000..59fdcc5b33 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_if_ge.S @@ -0,0 +1 @@ +%include "mips64/bincmp.S" { "condition":"ge" } diff --git a/runtime/interpreter/mterp/mips64/op_if_gez.S b/runtime/interpreter/mterp/mips64/op_if_gez.S new file mode 100644 index 0000000000..57f1f66ecd --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_if_gez.S @@ -0,0 +1 @@ +%include "mips64/zcmp.S" { "condition":"ge" } diff --git a/runtime/interpreter/mterp/mips64/op_if_gt.S b/runtime/interpreter/mterp/mips64/op_if_gt.S new file mode 100644 index 0000000000..26cc1195b5 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_if_gt.S @@ -0,0 +1 @@ +%include "mips64/bincmp.S" { "condition":"gt" } diff --git a/runtime/interpreter/mterp/mips64/op_if_gtz.S b/runtime/interpreter/mterp/mips64/op_if_gtz.S new file mode 100644 index 0000000000..69fcacb82d --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_if_gtz.S @@ -0,0 +1 @@ +%include "mips64/zcmp.S" { "condition":"gt" } diff --git a/runtime/interpreter/mterp/mips64/op_if_le.S b/runtime/interpreter/mterp/mips64/op_if_le.S new file mode 100644 index 0000000000..a7fce17c40 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_if_le.S @@ -0,0 +1 @@ +%include "mips64/bincmp.S" { "condition":"le" } diff --git a/runtime/interpreter/mterp/mips64/op_if_lez.S b/runtime/interpreter/mterp/mips64/op_if_lez.S new file mode 100644 index 0000000000..f3edcc6d99 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_if_lez.S @@ -0,0 +1 @@ +%include "mips64/zcmp.S" { "condition":"le" } diff --git a/runtime/interpreter/mterp/mips64/op_if_lt.S b/runtime/interpreter/mterp/mips64/op_if_lt.S new file mode 100644 index 0000000000..a975a31b57 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_if_lt.S @@ -0,0 +1 @@ +%include "mips64/bincmp.S" { "condition":"lt" } diff --git a/runtime/interpreter/mterp/mips64/op_if_ltz.S b/runtime/interpreter/mterp/mips64/op_if_ltz.S new file mode 100644 index 0000000000..c1d730d43f --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_if_ltz.S @@ -0,0 +1 @@ +%include "mips64/zcmp.S" { "condition":"lt" } diff --git a/runtime/interpreter/mterp/mips64/op_if_ne.S b/runtime/interpreter/mterp/mips64/op_if_ne.S new file mode 100644 index 0000000000..f143ee917e --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_if_ne.S @@ -0,0 +1 @@ +%include "mips64/bincmp.S" { "condition":"ne" } diff --git a/runtime/interpreter/mterp/mips64/op_if_nez.S b/runtime/interpreter/mterp/mips64/op_if_nez.S new file mode 100644 index 0000000000..1856b96dbc --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_if_nez.S @@ -0,0 +1 @@ +%include "mips64/zcmp.S" { "condition":"ne" } diff --git a/runtime/interpreter/mterp/mips64/op_iget.S b/runtime/interpreter/mterp/mips64/op_iget.S new file mode 100644 index 0000000000..ade4b31b80 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_iget.S @@ -0,0 +1,26 @@ +%default { "is_object":"0", "helper":"artGet32InstanceFromCode"} + /* + * General instance field get. + * + * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short + */ + .extern $helper + EXPORT_PC + lhu a0, 2(rPC) # a0 <- field ref CCCC + srl a1, rINST, 12 # a1 <- B + GET_VREG_U a1, a1 # a1 <- fp[B], the object pointer + ld a2, OFF_FP_METHOD(rFP) # a2 <- referrer + move a3, rSELF # a3 <- self + jal $helper + ld a3, THREAD_EXCEPTION_OFFSET(rSELF) + ext a2, rINST, 8, 4 # a2 <- A + PREFETCH_INST 2 + bnez a3, MterpPossibleException # bail out + .if $is_object + SET_VREG_OBJECT v0, a2 # fp[A] <- v0 + .else + SET_VREG v0, a2 # fp[A] <- v0 + .endif + ADVANCE 2 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_iget_boolean.S b/runtime/interpreter/mterp/mips64/op_iget_boolean.S new file mode 100644 index 0000000000..cb2c8bef07 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_iget_boolean.S @@ -0,0 +1 @@ +%include "mips64/op_iget.S" { "helper":"artGetBooleanInstanceFromCode" } diff --git a/runtime/interpreter/mterp/mips64/op_iget_boolean_quick.S b/runtime/interpreter/mterp/mips64/op_iget_boolean_quick.S new file mode 100644 index 0000000000..979dc7079e --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_iget_boolean_quick.S @@ -0,0 +1 @@ +%include "mips64/op_iget_quick.S" { "load":"lbu" } diff --git a/runtime/interpreter/mterp/mips64/op_iget_byte.S b/runtime/interpreter/mterp/mips64/op_iget_byte.S new file mode 100644 index 0000000000..099d8d0362 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_iget_byte.S @@ -0,0 +1 @@ +%include "mips64/op_iget.S" { "helper":"artGetByteInstanceFromCode" } diff --git a/runtime/interpreter/mterp/mips64/op_iget_byte_quick.S b/runtime/interpreter/mterp/mips64/op_iget_byte_quick.S new file mode 100644 index 0000000000..cb35556721 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_iget_byte_quick.S @@ -0,0 +1 @@ +%include "mips64/op_iget_quick.S" { "load":"lb" } diff --git a/runtime/interpreter/mterp/mips64/op_iget_char.S b/runtime/interpreter/mterp/mips64/op_iget_char.S new file mode 100644 index 0000000000..927b7affa6 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_iget_char.S @@ -0,0 +1 @@ +%include "mips64/op_iget.S" { "helper":"artGetCharInstanceFromCode" } diff --git a/runtime/interpreter/mterp/mips64/op_iget_char_quick.S b/runtime/interpreter/mterp/mips64/op_iget_char_quick.S new file mode 100644 index 0000000000..603456775b --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_iget_char_quick.S @@ -0,0 +1 @@ +%include "mips64/op_iget_quick.S" { "load":"lhu" } diff --git a/runtime/interpreter/mterp/mips64/op_iget_object.S b/runtime/interpreter/mterp/mips64/op_iget_object.S new file mode 100644 index 0000000000..c658556992 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_iget_object.S @@ -0,0 +1 @@ +%include "mips64/op_iget.S" { "is_object":"1", "helper":"artGetObjInstanceFromCode" } diff --git a/runtime/interpreter/mterp/mips64/op_iget_object_quick.S b/runtime/interpreter/mterp/mips64/op_iget_object_quick.S new file mode 100644 index 0000000000..171d54301b --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_iget_object_quick.S @@ -0,0 +1,16 @@ + /* For: iget-object-quick */ + /* op vA, vB, offset//CCCC */ + .extern artIGetObjectFromMterp + srl a2, rINST, 12 # a2 <- B + lhu a1, 2(rPC) # a1 <- field byte offset + EXPORT_PC + GET_VREG_U a0, a2 # a0 <- object we're operating on + jal artIGetObjectFromMterp # (obj, offset) + ld a3, THREAD_EXCEPTION_OFFSET(rSELF) + ext a2, rINST, 8, 4 # a2 <- A + PREFETCH_INST 2 + bnez a3, MterpPossibleException # bail out + SET_VREG_OBJECT v0, a2 # fp[A] <- v0 + ADVANCE 2 # advance rPC + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_iget_quick.S b/runtime/interpreter/mterp/mips64/op_iget_quick.S new file mode 100644 index 0000000000..fee6ab738c --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_iget_quick.S @@ -0,0 +1,14 @@ +%default { "load":"lw" } + /* For: iget-quick, iget-boolean-quick, iget-byte-quick, iget-char-quick, iget-short-quick */ + /* op vA, vB, offset//CCCC */ + srl a2, rINST, 12 # a2 <- B + lhu a1, 2(rPC) # a1 <- field byte offset + GET_VREG_U a3, a2 # a3 <- object we're operating on + ext a4, rINST, 8, 4 # a4 <- A + daddu a1, a1, a3 + beqz a3, common_errNullObject # object was null + $load a0, 0(a1) # a0 <- obj.field + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + SET_VREG a0, a4 # fp[A] <- a0 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_iget_short.S b/runtime/interpreter/mterp/mips64/op_iget_short.S new file mode 100644 index 0000000000..28b5093b6d --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_iget_short.S @@ -0,0 +1 @@ +%include "mips64/op_iget.S" { "helper":"artGetShortInstanceFromCode" } diff --git a/runtime/interpreter/mterp/mips64/op_iget_short_quick.S b/runtime/interpreter/mterp/mips64/op_iget_short_quick.S new file mode 100644 index 0000000000..6e152dbf48 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_iget_short_quick.S @@ -0,0 +1 @@ +%include "mips64/op_iget_quick.S" { "load":"lh" } diff --git a/runtime/interpreter/mterp/mips64/op_iget_wide.S b/runtime/interpreter/mterp/mips64/op_iget_wide.S new file mode 100644 index 0000000000..85cf6705a7 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_iget_wide.S @@ -0,0 +1,21 @@ + /* + * 64-bit instance field get. + * + * for: iget-wide + */ + .extern artGet64InstanceFromCode + EXPORT_PC + lhu a0, 2(rPC) # a0 <- field ref CCCC + srl a1, rINST, 12 # a1 <- B + GET_VREG_U a1, a1 # a1 <- fp[B], the object pointer + ld a2, OFF_FP_METHOD(rFP) # a2 <- referrer + move a3, rSELF # a3 <- self + jal artGet64InstanceFromCode + ld a3, THREAD_EXCEPTION_OFFSET(rSELF) + ext a2, rINST, 8, 4 # a2 <- A + PREFETCH_INST 2 + bnez a3, MterpPossibleException # bail out + SET_VREG_WIDE v0, a2 # fp[A] <- v0 + ADVANCE 2 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_iget_wide_quick.S b/runtime/interpreter/mterp/mips64/op_iget_wide_quick.S new file mode 100644 index 0000000000..2adc6adf15 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_iget_wide_quick.S @@ -0,0 +1,14 @@ + /* iget-wide-quick vA, vB, offset//CCCC */ + srl a2, rINST, 12 # a2 <- B + lhu a4, 2(rPC) # a4 <- field byte offset + GET_VREG_U a3, a2 # a3 <- object we're operating on + ext a2, rINST, 8, 4 # a2 <- A + beqz a3, common_errNullObject # object was null + daddu a4, a3, a4 # create direct pointer + lw a0, 0(a4) + lw a1, 4(a4) + dinsu a0, a1, 32, 32 + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + SET_VREG_WIDE a0, a2 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_instance_of.S b/runtime/interpreter/mterp/mips64/op_instance_of.S new file mode 100644 index 0000000000..39a5dc7c26 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_instance_of.S @@ -0,0 +1,23 @@ + /* + * Check to see if an object reference is an instance of a class. + * + * Most common situation is a non-null object, being compared against + * an already-resolved class. + */ + /* instance-of vA, vB, class//CCCC */ + .extern MterpInstanceOf + EXPORT_PC + lhu a0, 2(rPC) # a0 <- CCCC + srl a1, rINST, 12 # a1 <- B + dlsa a1, a1, rFP, 2 # a1 <- &object + ld a2, OFF_FP_METHOD(rFP) # a2 <- method + move a3, rSELF # a3 <- self + jal MterpInstanceOf # (index, &obj, method, self) + ld a1, THREAD_EXCEPTION_OFFSET(rSELF) + ext a2, rINST, 8, 4 # a2 <- A + PREFETCH_INST 2 + bnez a1, MterpException + ADVANCE 2 # advance rPC + SET_VREG v0, a2 # vA <- v0 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_int_to_byte.S b/runtime/interpreter/mterp/mips64/op_int_to_byte.S new file mode 100644 index 0000000000..1993e076a6 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_int_to_byte.S @@ -0,0 +1 @@ +%include "mips64/unop.S" {"instr":"seb a0, a0"} diff --git a/runtime/interpreter/mterp/mips64/op_int_to_char.S b/runtime/interpreter/mterp/mips64/op_int_to_char.S new file mode 100644 index 0000000000..8f03acd3f6 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_int_to_char.S @@ -0,0 +1 @@ +%include "mips64/unop.S" {"instr":"and a0, a0, 0xffff"} diff --git a/runtime/interpreter/mterp/mips64/op_int_to_double.S b/runtime/interpreter/mterp/mips64/op_int_to_double.S new file mode 100644 index 0000000000..6df71be394 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_int_to_double.S @@ -0,0 +1,8 @@ + /* + * Conversion from or to floating-point happens in a floating-point register. + * Therefore we load the input and store the output into or from a + * floating-point register irrespective of the type. + */ +%include "mips64/fcvtHeader.S" { "suffix":"_FLOAT", "valreg":"f0" } + cvt.d.w f0, f0 +%include "mips64/fcvtFooter.S" { "suffix":"_DOUBLE", "valreg":"f0" } diff --git a/runtime/interpreter/mterp/mips64/op_int_to_float.S b/runtime/interpreter/mterp/mips64/op_int_to_float.S new file mode 100644 index 0000000000..77e9eba53a --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_int_to_float.S @@ -0,0 +1,8 @@ + /* + * Conversion from or to floating-point happens in a floating-point register. + * Therefore we load the input and store the output into or from a + * floating-point register irrespective of the type. + */ +%include "mips64/fcvtHeader.S" { "suffix":"_FLOAT", "valreg":"f0" } + cvt.s.w f0, f0 +%include "mips64/fcvtFooter.S" { "suffix":"_FLOAT", "valreg":"f0" } diff --git a/runtime/interpreter/mterp/mips64/op_int_to_long.S b/runtime/interpreter/mterp/mips64/op_int_to_long.S new file mode 100644 index 0000000000..7b9ad86fdc --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_int_to_long.S @@ -0,0 +1,8 @@ + /* int-to-long vA, vB */ + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG a0, a3 # a0 <- vB (sign-extended to 64 bits) + ext a2, rINST, 8, 4 # a2 <- A + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a2 # vA <- vB + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_int_to_short.S b/runtime/interpreter/mterp/mips64/op_int_to_short.S new file mode 100644 index 0000000000..4a3f2346cf --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_int_to_short.S @@ -0,0 +1 @@ +%include "mips64/unop.S" {"instr":"seh a0, a0"} diff --git a/runtime/interpreter/mterp/mips64/op_invoke_direct.S b/runtime/interpreter/mterp/mips64/op_invoke_direct.S new file mode 100644 index 0000000000..5047118e48 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_invoke_direct.S @@ -0,0 +1 @@ +%include "mips64/invoke.S" { "helper":"MterpInvokeDirect" } diff --git a/runtime/interpreter/mterp/mips64/op_invoke_direct_range.S b/runtime/interpreter/mterp/mips64/op_invoke_direct_range.S new file mode 100644 index 0000000000..5c9b95f5be --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_invoke_direct_range.S @@ -0,0 +1 @@ +%include "mips64/invoke.S" { "helper":"MterpInvokeDirectRange" } diff --git a/runtime/interpreter/mterp/mips64/op_invoke_interface.S b/runtime/interpreter/mterp/mips64/op_invoke_interface.S new file mode 100644 index 0000000000..ed148adcbb --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_invoke_interface.S @@ -0,0 +1,8 @@ +%include "mips64/invoke.S" { "helper":"MterpInvokeInterface" } + /* + * Handle an interface method call. + * + * for: invoke-interface, invoke-interface/range + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ diff --git a/runtime/interpreter/mterp/mips64/op_invoke_interface_range.S b/runtime/interpreter/mterp/mips64/op_invoke_interface_range.S new file mode 100644 index 0000000000..91c231e0f4 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_invoke_interface_range.S @@ -0,0 +1 @@ +%include "mips64/invoke.S" { "helper":"MterpInvokeInterfaceRange" } diff --git a/runtime/interpreter/mterp/mips64/op_invoke_static.S b/runtime/interpreter/mterp/mips64/op_invoke_static.S new file mode 100644 index 0000000000..44f5cb7a78 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_invoke_static.S @@ -0,0 +1 @@ +%include "mips64/invoke.S" { "helper":"MterpInvokeStatic" } diff --git a/runtime/interpreter/mterp/mips64/op_invoke_static_range.S b/runtime/interpreter/mterp/mips64/op_invoke_static_range.S new file mode 100644 index 0000000000..289e5aa977 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_invoke_static_range.S @@ -0,0 +1 @@ +%include "mips64/invoke.S" { "helper":"MterpInvokeStaticRange" } diff --git a/runtime/interpreter/mterp/mips64/op_invoke_super.S b/runtime/interpreter/mterp/mips64/op_invoke_super.S new file mode 100644 index 0000000000..b13fffe714 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_invoke_super.S @@ -0,0 +1,8 @@ +%include "mips64/invoke.S" { "helper":"MterpInvokeSuper" } + /* + * Handle a "super" method call. + * + * for: invoke-super, invoke-super/range + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ diff --git a/runtime/interpreter/mterp/mips64/op_invoke_super_range.S b/runtime/interpreter/mterp/mips64/op_invoke_super_range.S new file mode 100644 index 0000000000..350b9757ba --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_invoke_super_range.S @@ -0,0 +1 @@ +%include "mips64/invoke.S" { "helper":"MterpInvokeSuperRange" } diff --git a/runtime/interpreter/mterp/mips64/op_invoke_virtual.S b/runtime/interpreter/mterp/mips64/op_invoke_virtual.S new file mode 100644 index 0000000000..0d26cda812 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_invoke_virtual.S @@ -0,0 +1,8 @@ +%include "mips64/invoke.S" { "helper":"MterpInvokeVirtual" } + /* + * Handle a virtual method call. + * + * for: invoke-virtual, invoke-virtual/range + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ diff --git a/runtime/interpreter/mterp/mips64/op_invoke_virtual_quick.S b/runtime/interpreter/mterp/mips64/op_invoke_virtual_quick.S new file mode 100644 index 0000000000..f39562c199 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_invoke_virtual_quick.S @@ -0,0 +1 @@ +%include "mips64/invoke.S" { "helper":"MterpInvokeVirtualQuick" } diff --git a/runtime/interpreter/mterp/mips64/op_invoke_virtual_range.S b/runtime/interpreter/mterp/mips64/op_invoke_virtual_range.S new file mode 100644 index 0000000000..0bb43f8fcc --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_invoke_virtual_range.S @@ -0,0 +1 @@ +%include "mips64/invoke.S" { "helper":"MterpInvokeVirtualRange" } diff --git a/runtime/interpreter/mterp/mips64/op_invoke_virtual_range_quick.S b/runtime/interpreter/mterp/mips64/op_invoke_virtual_range_quick.S new file mode 100644 index 0000000000..c4488513bd --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_invoke_virtual_range_quick.S @@ -0,0 +1 @@ +%include "mips64/invoke.S" { "helper":"MterpInvokeVirtualQuickRange" } diff --git a/runtime/interpreter/mterp/mips64/op_iput.S b/runtime/interpreter/mterp/mips64/op_iput.S new file mode 100644 index 0000000000..a906a0fc82 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_iput.S @@ -0,0 +1,21 @@ +%default { "helper":"artSet32InstanceFromMterp" } + /* + * General 32-bit instance field put. + * + * for: iput, iput-boolean, iput-byte, iput-char, iput-short + */ + /* op vA, vB, field//CCCC */ + .extern $helper + EXPORT_PC + lhu a0, 2(rPC) # a0 <- field ref CCCC + srl a1, rINST, 12 # a1 <- B + GET_VREG_U a1, a1 # a1 <- fp[B], the object pointer + ext a2, rINST, 8, 4 # a2 <- A + GET_VREG a2, a2 # a2 <- fp[A] + ld a3, OFF_FP_METHOD(rFP) # a3 <- referrer + PREFETCH_INST 2 + jal $helper + bnez v0, MterpPossibleException # bail out + ADVANCE 2 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_iput_boolean.S b/runtime/interpreter/mterp/mips64/op_iput_boolean.S new file mode 100644 index 0000000000..3034fa59d5 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_iput_boolean.S @@ -0,0 +1 @@ +%include "mips64/op_iput.S" { "helper":"artSet8InstanceFromMterp" } diff --git a/runtime/interpreter/mterp/mips64/op_iput_boolean_quick.S b/runtime/interpreter/mterp/mips64/op_iput_boolean_quick.S new file mode 100644 index 0000000000..df99948e40 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_iput_boolean_quick.S @@ -0,0 +1 @@ +%include "mips64/op_iput_quick.S" { "store":"sb" } diff --git a/runtime/interpreter/mterp/mips64/op_iput_byte.S b/runtime/interpreter/mterp/mips64/op_iput_byte.S new file mode 100644 index 0000000000..3034fa59d5 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_iput_byte.S @@ -0,0 +1 @@ +%include "mips64/op_iput.S" { "helper":"artSet8InstanceFromMterp" } diff --git a/runtime/interpreter/mterp/mips64/op_iput_byte_quick.S b/runtime/interpreter/mterp/mips64/op_iput_byte_quick.S new file mode 100644 index 0000000000..df99948e40 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_iput_byte_quick.S @@ -0,0 +1 @@ +%include "mips64/op_iput_quick.S" { "store":"sb" } diff --git a/runtime/interpreter/mterp/mips64/op_iput_char.S b/runtime/interpreter/mterp/mips64/op_iput_char.S new file mode 100644 index 0000000000..4c2fa28776 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_iput_char.S @@ -0,0 +1 @@ +%include "mips64/op_iput.S" { "helper":"artSet16InstanceFromMterp" } diff --git a/runtime/interpreter/mterp/mips64/op_iput_char_quick.S b/runtime/interpreter/mterp/mips64/op_iput_char_quick.S new file mode 100644 index 0000000000..a6286b7b97 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_iput_char_quick.S @@ -0,0 +1 @@ +%include "mips64/op_iput_quick.S" { "store":"sh" } diff --git a/runtime/interpreter/mterp/mips64/op_iput_object.S b/runtime/interpreter/mterp/mips64/op_iput_object.S new file mode 100644 index 0000000000..9a42f54669 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_iput_object.S @@ -0,0 +1,11 @@ + .extern MterpIputObject + EXPORT_PC + daddu a0, rFP, OFF_FP_SHADOWFRAME + move a1, rPC + move a2, rINST + move a3, rSELF + jal MterpIputObject + beqzc v0, MterpException + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_iput_object_quick.S b/runtime/interpreter/mterp/mips64/op_iput_object_quick.S new file mode 100644 index 0000000000..658ef42a19 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_iput_object_quick.S @@ -0,0 +1,10 @@ + .extern MterpIputObjectQuick + EXPORT_PC + daddu a0, rFP, OFF_FP_SHADOWFRAME + move a1, rPC + move a2, rINST + jal MterpIputObjectQuick + beqzc v0, MterpException + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_iput_quick.S b/runtime/interpreter/mterp/mips64/op_iput_quick.S new file mode 100644 index 0000000000..b95adfcd4f --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_iput_quick.S @@ -0,0 +1,14 @@ +%default { "store":"sw" } + /* For: iput-quick, iput-boolean-quick, iput-byte-quick, iput-char-quick, iput-short-quick */ + /* op vA, vB, offset//CCCC */ + srl a2, rINST, 12 # a2 <- B + lhu a1, 2(rPC) # a1 <- field byte offset + GET_VREG_U a3, a2 # a3 <- fp[B], the object pointer + ext a2, rINST, 8, 4 # a2 <- A + beqz a3, common_errNullObject # object was null + GET_VREG a0, a2 # a0 <- fp[A] + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + daddu a1, a1, a3 + $store a0, 0(a1) # obj.field <- a0 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_iput_short.S b/runtime/interpreter/mterp/mips64/op_iput_short.S new file mode 100644 index 0000000000..4c2fa28776 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_iput_short.S @@ -0,0 +1 @@ +%include "mips64/op_iput.S" { "helper":"artSet16InstanceFromMterp" } diff --git a/runtime/interpreter/mterp/mips64/op_iput_short_quick.S b/runtime/interpreter/mterp/mips64/op_iput_short_quick.S new file mode 100644 index 0000000000..a6286b7b97 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_iput_short_quick.S @@ -0,0 +1 @@ +%include "mips64/op_iput_quick.S" { "store":"sh" } diff --git a/runtime/interpreter/mterp/mips64/op_iput_wide.S b/runtime/interpreter/mterp/mips64/op_iput_wide.S new file mode 100644 index 0000000000..9b790f812a --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_iput_wide.S @@ -0,0 +1,15 @@ + /* iput-wide vA, vB, field//CCCC */ + .extern artSet64InstanceFromMterp + EXPORT_PC + lhu a0, 2(rPC) # a0 <- field ref CCCC + srl a1, rINST, 12 # a1 <- B + GET_VREG_U a1, a1 # a1 <- fp[B], the object pointer + ext a2, rINST, 8, 4 # a2 <- A + dlsa a2, a2, rFP, 2 # a2 <- &fp[A] + ld a3, OFF_FP_METHOD(rFP) # a3 <- referrer + PREFETCH_INST 2 + jal artSet64InstanceFromMterp + bnez v0, MterpPossibleException # bail out + ADVANCE 2 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_iput_wide_quick.S b/runtime/interpreter/mterp/mips64/op_iput_wide_quick.S new file mode 100644 index 0000000000..95a8ad8f9c --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_iput_wide_quick.S @@ -0,0 +1,14 @@ + /* iput-wide-quick vA, vB, offset//CCCC */ + srl a2, rINST, 12 # a2 <- B + lhu a3, 2(rPC) # a3 <- field byte offset + GET_VREG_U a2, a2 # a2 <- fp[B], the object pointer + ext a0, rINST, 8, 4 # a0 <- A + beqz a2, common_errNullObject # object was null + GET_VREG_WIDE a0, a0 # a0 <- fp[A] + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + daddu a1, a2, a3 # create a direct pointer + sw a0, 0(a1) + dsrl32 a0, a0, 0 + sw a0, 4(a1) + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_long_to_double.S b/runtime/interpreter/mterp/mips64/op_long_to_double.S new file mode 100644 index 0000000000..8503e769b9 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_long_to_double.S @@ -0,0 +1,8 @@ + /* + * Conversion from or to floating-point happens in a floating-point register. + * Therefore we load the input and store the output into or from a + * floating-point register irrespective of the type. + */ +%include "mips64/fcvtHeader.S" { "suffix":"_DOUBLE", "valreg":"f0" } + cvt.d.l f0, f0 +%include "mips64/fcvtFooter.S" { "suffix":"_DOUBLE", "valreg":"f0" } diff --git a/runtime/interpreter/mterp/mips64/op_long_to_float.S b/runtime/interpreter/mterp/mips64/op_long_to_float.S new file mode 100644 index 0000000000..31f5c0e9b0 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_long_to_float.S @@ -0,0 +1,8 @@ + /* + * Conversion from or to floating-point happens in a floating-point register. + * Therefore we load the input and store the output into or from a + * floating-point register irrespective of the type. + */ +%include "mips64/fcvtHeader.S" { "suffix":"_DOUBLE", "valreg":"f0" } + cvt.s.l f0, f0 +%include "mips64/fcvtFooter.S" { "suffix":"_FLOAT", "valreg":"f0" } diff --git a/runtime/interpreter/mterp/mips64/op_long_to_int.S b/runtime/interpreter/mterp/mips64/op_long_to_int.S new file mode 100644 index 0000000000..4ef4b512dc --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_long_to_int.S @@ -0,0 +1,2 @@ +/* we ignore the high word, making this equivalent to a 32-bit reg move */ +%include "mips64/op_move.S" diff --git a/runtime/interpreter/mterp/mips64/op_monitor_enter.S b/runtime/interpreter/mterp/mips64/op_monitor_enter.S new file mode 100644 index 0000000000..36ae50346e --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_monitor_enter.S @@ -0,0 +1,14 @@ + /* + * Synchronize on an object. + */ + /* monitor-enter vAA */ + .extern artLockObjectFromCode + EXPORT_PC + srl a2, rINST, 8 # a2 <- AA + GET_VREG_U a0, a2 # a0 <- vAA (object) + move a1, rSELF # a1 <- self + jal artLockObjectFromCode + bnezc v0, MterpException + FETCH_ADVANCE_INST 1 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_monitor_exit.S b/runtime/interpreter/mterp/mips64/op_monitor_exit.S new file mode 100644 index 0000000000..9945952017 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_monitor_exit.S @@ -0,0 +1,18 @@ + /* + * Unlock an object. + * + * Exceptions that occur when unlocking a monitor need to appear as + * if they happened at the following instruction. See the Dalvik + * instruction spec. + */ + /* monitor-exit vAA */ + .extern artUnlockObjectFromCode + EXPORT_PC + srl a2, rINST, 8 # a2 <- AA + GET_VREG_U a0, a2 # a0 <- vAA (object) + move a1, rSELF # a1 <- self + jal artUnlockObjectFromCode # v0 <- success for unlock(self, obj) + bnezc v0, MterpException + FETCH_ADVANCE_INST 1 # before throw: advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_move.S b/runtime/interpreter/mterp/mips64/op_move.S new file mode 100644 index 0000000000..c79f6cde8d --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_move.S @@ -0,0 +1,14 @@ +%default { "is_object":"0" } + /* for move, move-object, long-to-int */ + /* op vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + GET_VREG a0, a3 # a0 <- vB + GET_INST_OPCODE v0 # extract opcode from rINST + .if $is_object + SET_VREG_OBJECT a0, a2 # vA <- vB + .else + SET_VREG a0, a2 # vA <- vB + .endif + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_move_16.S b/runtime/interpreter/mterp/mips64/op_move_16.S new file mode 100644 index 0000000000..9d5c4dce8c --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_move_16.S @@ -0,0 +1,14 @@ +%default { "is_object":"0" } + /* for: move/16, move-object/16 */ + /* op vAAAA, vBBBB */ + lhu a3, 4(rPC) # a3 <- BBBB + lhu a2, 2(rPC) # a2 <- AAAA + FETCH_ADVANCE_INST 3 # advance rPC, load rINST + GET_VREG a0, a3 # a0 <- vBBBB + GET_INST_OPCODE v0 # extract opcode from rINST + .if $is_object + SET_VREG_OBJECT a0, a2 # vAAAA <- vBBBB + .else + SET_VREG a0, a2 # vAAAA <- vBBBB + .endif + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_move_exception.S b/runtime/interpreter/mterp/mips64/op_move_exception.S new file mode 100644 index 0000000000..d226718c8f --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_move_exception.S @@ -0,0 +1,8 @@ + /* move-exception vAA */ + srl a2, rINST, 8 # a2 <- AA + ld a0, THREAD_EXCEPTION_OFFSET(rSELF) # load exception obj + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + SET_VREG_OBJECT a0, a2 # vAA <- exception obj + GET_INST_OPCODE v0 # extract opcode from rINST + sd zero, THREAD_EXCEPTION_OFFSET(rSELF) # clear exception + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_move_from16.S b/runtime/interpreter/mterp/mips64/op_move_from16.S new file mode 100644 index 0000000000..6d6bde007f --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_move_from16.S @@ -0,0 +1,14 @@ +%default { "is_object":"0" } + /* for: move/from16, move-object/from16 */ + /* op vAA, vBBBB */ + lhu a3, 2(rPC) # a3 <- BBBB + srl a2, rINST, 8 # a2 <- AA + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_VREG a0, a3 # a0 <- vBBBB + GET_INST_OPCODE v0 # extract opcode from rINST + .if $is_object + SET_VREG_OBJECT a0, a2 # vAA <- vBBBB + .else + SET_VREG a0, a2 # vAA <- vBBBB + .endif + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_move_object.S b/runtime/interpreter/mterp/mips64/op_move_object.S new file mode 100644 index 0000000000..47e0272a6c --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_move_object.S @@ -0,0 +1 @@ +%include "mips64/op_move.S" {"is_object":"1"} diff --git a/runtime/interpreter/mterp/mips64/op_move_object_16.S b/runtime/interpreter/mterp/mips64/op_move_object_16.S new file mode 100644 index 0000000000..a777dcdaf8 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_move_object_16.S @@ -0,0 +1 @@ +%include "mips64/op_move_16.S" {"is_object":"1"} diff --git a/runtime/interpreter/mterp/mips64/op_move_object_from16.S b/runtime/interpreter/mterp/mips64/op_move_object_from16.S new file mode 100644 index 0000000000..ab55ebd646 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_move_object_from16.S @@ -0,0 +1 @@ +%include "mips64/op_move_from16.S" {"is_object":"1"} diff --git a/runtime/interpreter/mterp/mips64/op_move_result.S b/runtime/interpreter/mterp/mips64/op_move_result.S new file mode 100644 index 0000000000..1ec28cb6d8 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_move_result.S @@ -0,0 +1,14 @@ +%default { "is_object":"0" } + /* for: move-result, move-result-object */ + /* op vAA */ + srl a2, rINST, 8 # a2 <- AA + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + ld a0, OFF_FP_RESULT_REGISTER(rFP) # get pointer to result JType + lw a0, 0(a0) # a0 <- result.i + GET_INST_OPCODE v0 # extract opcode from rINST + .if $is_object + SET_VREG_OBJECT a0, a2 # vAA <- result + .else + SET_VREG a0, a2 # vAA <- result + .endif + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_move_result_object.S b/runtime/interpreter/mterp/mips64/op_move_result_object.S new file mode 100644 index 0000000000..e76bc22c11 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_move_result_object.S @@ -0,0 +1 @@ +%include "mips64/op_move_result.S" {"is_object":"1"} diff --git a/runtime/interpreter/mterp/mips64/op_move_result_wide.S b/runtime/interpreter/mterp/mips64/op_move_result_wide.S new file mode 100644 index 0000000000..3ba0d7288b --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_move_result_wide.S @@ -0,0 +1,9 @@ + /* for: move-result-wide */ + /* op vAA */ + srl a2, rINST, 8 # a2 <- AA + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + ld a0, OFF_FP_RESULT_REGISTER(rFP) # get pointer to result JType + ld a0, 0(a0) # a0 <- result.j + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a2 # vAA <- result + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_move_wide.S b/runtime/interpreter/mterp/mips64/op_move_wide.S new file mode 100644 index 0000000000..ea23f87ff0 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_move_wide.S @@ -0,0 +1,9 @@ + /* move-wide vA, vB */ + /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */ + ext a3, rINST, 12, 4 # a3 <- B + ext a2, rINST, 8, 4 # a2 <- A + GET_VREG_WIDE a0, a3 # a0 <- vB + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a2 # vA <- vB + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_move_wide_16.S b/runtime/interpreter/mterp/mips64/op_move_wide_16.S new file mode 100644 index 0000000000..8ec606834b --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_move_wide_16.S @@ -0,0 +1,9 @@ + /* move-wide/16 vAAAA, vBBBB */ + /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */ + lhu a3, 4(rPC) # a3 <- BBBB + lhu a2, 2(rPC) # a2 <- AAAA + GET_VREG_WIDE a0, a3 # a0 <- vBBBB + FETCH_ADVANCE_INST 3 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a2 # vAAAA <- vBBBB + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_move_wide_from16.S b/runtime/interpreter/mterp/mips64/op_move_wide_from16.S new file mode 100644 index 0000000000..11d5603fe1 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_move_wide_from16.S @@ -0,0 +1,9 @@ + /* move-wide/from16 vAA, vBBBB */ + /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */ + lhu a3, 2(rPC) # a3 <- BBBB + srl a2, rINST, 8 # a2 <- AA + GET_VREG_WIDE a0, a3 # a0 <- vBBBB + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a2 # vAA <- vBBBB + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_mul_double.S b/runtime/interpreter/mterp/mips64/op_mul_double.S new file mode 100644 index 0000000000..e7e17f7ece --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_mul_double.S @@ -0,0 +1 @@ +%include "mips64/fbinopWide.S" {"instr":"mul.d f0, f0, f1"} diff --git a/runtime/interpreter/mterp/mips64/op_mul_double_2addr.S b/runtime/interpreter/mterp/mips64/op_mul_double_2addr.S new file mode 100644 index 0000000000..f404d4688d --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_mul_double_2addr.S @@ -0,0 +1 @@ +%include "mips64/fbinopWide2addr.S" {"instr":"mul.d f0, f0, f1"} diff --git a/runtime/interpreter/mterp/mips64/op_mul_float.S b/runtime/interpreter/mterp/mips64/op_mul_float.S new file mode 100644 index 0000000000..9a695fca16 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_mul_float.S @@ -0,0 +1 @@ +%include "mips64/fbinop.S" {"instr":"mul.s f0, f0, f1"} diff --git a/runtime/interpreter/mterp/mips64/op_mul_float_2addr.S b/runtime/interpreter/mterp/mips64/op_mul_float_2addr.S new file mode 100644 index 0000000000..a134a34253 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_mul_float_2addr.S @@ -0,0 +1 @@ +%include "mips64/fbinop2addr.S" {"instr":"mul.s f0, f0, f1"} diff --git a/runtime/interpreter/mterp/mips64/op_mul_int.S b/runtime/interpreter/mterp/mips64/op_mul_int.S new file mode 100644 index 0000000000..e1b90ff4e9 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_mul_int.S @@ -0,0 +1 @@ +%include "mips64/binop.S" {"instr":"mul a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_mul_int_2addr.S b/runtime/interpreter/mterp/mips64/op_mul_int_2addr.S new file mode 100644 index 0000000000..c0c4063d54 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_mul_int_2addr.S @@ -0,0 +1 @@ +%include "mips64/binop2addr.S" {"instr":"mul a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_mul_int_lit16.S b/runtime/interpreter/mterp/mips64/op_mul_int_lit16.S new file mode 100644 index 0000000000..bb4fff8747 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_mul_int_lit16.S @@ -0,0 +1 @@ +%include "mips64/binopLit16.S" {"instr":"mul a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_mul_int_lit8.S b/runtime/interpreter/mterp/mips64/op_mul_int_lit8.S new file mode 100644 index 0000000000..da11ea9295 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_mul_int_lit8.S @@ -0,0 +1 @@ +%include "mips64/binopLit8.S" {"instr":"mul a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_mul_long.S b/runtime/interpreter/mterp/mips64/op_mul_long.S new file mode 100644 index 0000000000..ec32850606 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_mul_long.S @@ -0,0 +1 @@ +%include "mips64/binopWide.S" {"instr":"dmul a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_mul_long_2addr.S b/runtime/interpreter/mterp/mips64/op_mul_long_2addr.S new file mode 100644 index 0000000000..eb50cda03c --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_mul_long_2addr.S @@ -0,0 +1 @@ +%include "mips64/binopWide2addr.S" {"instr":"dmul a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_neg_double.S b/runtime/interpreter/mterp/mips64/op_neg_double.S new file mode 100644 index 0000000000..a135d61173 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_neg_double.S @@ -0,0 +1,3 @@ +%include "mips64/fcvtHeader.S" { "suffix":"_DOUBLE", "valreg":"f0" } + neg.d f0, f0 +%include "mips64/fcvtFooter.S" { "suffix":"_DOUBLE", "valreg":"f0" } diff --git a/runtime/interpreter/mterp/mips64/op_neg_float.S b/runtime/interpreter/mterp/mips64/op_neg_float.S new file mode 100644 index 0000000000..78019f03d8 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_neg_float.S @@ -0,0 +1,3 @@ +%include "mips64/fcvtHeader.S" { "suffix":"_FLOAT", "valreg":"f0" } + neg.s f0, f0 +%include "mips64/fcvtFooter.S" { "suffix":"_FLOAT", "valreg":"f0" } diff --git a/runtime/interpreter/mterp/mips64/op_neg_int.S b/runtime/interpreter/mterp/mips64/op_neg_int.S new file mode 100644 index 0000000000..31538c0caa --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_neg_int.S @@ -0,0 +1 @@ +%include "mips64/unop.S" {"instr":"subu a0, zero, a0"} diff --git a/runtime/interpreter/mterp/mips64/op_neg_long.S b/runtime/interpreter/mterp/mips64/op_neg_long.S new file mode 100644 index 0000000000..bc80d0623f --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_neg_long.S @@ -0,0 +1 @@ +%include "mips64/unopWide.S" {"instr":"dsubu a0, zero, a0"} diff --git a/runtime/interpreter/mterp/mips64/op_new_array.S b/runtime/interpreter/mterp/mips64/op_new_array.S new file mode 100644 index 0000000000..d78b4ac32e --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_new_array.S @@ -0,0 +1,19 @@ + /* + * Allocate an array of objects, specified with the array class + * and a count. + * + * The verifier guarantees that this is an array class, so we don't + * check for it here. + */ + /* new-array vA, vB, class//CCCC */ + .extern MterpNewArray + EXPORT_PC + daddu a0, rFP, OFF_FP_SHADOWFRAME + move a1, rPC + move a2, rINST + move a3, rSELF + jal MterpNewArray + beqzc v0, MterpPossibleException + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_new_instance.S b/runtime/interpreter/mterp/mips64/op_new_instance.S new file mode 100644 index 0000000000..cc5e13e00d --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_new_instance.S @@ -0,0 +1,14 @@ + /* + * Create a new instance of a class. + */ + /* new-instance vAA, class//BBBB */ + .extern MterpNewInstance + EXPORT_PC + daddu a0, rFP, OFF_FP_SHADOWFRAME + move a1, rSELF + move a2, rINST + jal MterpNewInstance # (shadow_frame, self, inst_data) + beqzc v0, MterpPossibleException + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_nop.S b/runtime/interpreter/mterp/mips64/op_nop.S new file mode 100644 index 0000000000..cc803a791a --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_nop.S @@ -0,0 +1,3 @@ + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_not_int.S b/runtime/interpreter/mterp/mips64/op_not_int.S new file mode 100644 index 0000000000..59540950cd --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_not_int.S @@ -0,0 +1 @@ +%include "mips64/unop.S" {"instr":"nor a0, zero, a0"} diff --git a/runtime/interpreter/mterp/mips64/op_not_long.S b/runtime/interpreter/mterp/mips64/op_not_long.S new file mode 100644 index 0000000000..c8f5da7e82 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_not_long.S @@ -0,0 +1 @@ +%include "mips64/unopWide.S" {"instr":"nor a0, zero, a0"} diff --git a/runtime/interpreter/mterp/mips64/op_or_int.S b/runtime/interpreter/mterp/mips64/op_or_int.S new file mode 100644 index 0000000000..0102355c55 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_or_int.S @@ -0,0 +1 @@ +%include "mips64/binop.S" {"instr":"or a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_or_int_2addr.S b/runtime/interpreter/mterp/mips64/op_or_int_2addr.S new file mode 100644 index 0000000000..eed89008c0 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_or_int_2addr.S @@ -0,0 +1 @@ +%include "mips64/binop2addr.S" {"instr":"or a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_or_int_lit16.S b/runtime/interpreter/mterp/mips64/op_or_int_lit16.S new file mode 100644 index 0000000000..16a0f3e1a2 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_or_int_lit16.S @@ -0,0 +1 @@ +%include "mips64/binopLit16.S" {"instr":"or a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_or_int_lit8.S b/runtime/interpreter/mterp/mips64/op_or_int_lit8.S new file mode 100644 index 0000000000..dbbf7904c6 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_or_int_lit8.S @@ -0,0 +1 @@ +%include "mips64/binopLit8.S" {"instr":"or a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_or_long.S b/runtime/interpreter/mterp/mips64/op_or_long.S new file mode 100644 index 0000000000..e6f8639e52 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_or_long.S @@ -0,0 +1 @@ +%include "mips64/binopWide.S" {"instr":"or a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_or_long_2addr.S b/runtime/interpreter/mterp/mips64/op_or_long_2addr.S new file mode 100644 index 0000000000..ad5e6c8e99 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_or_long_2addr.S @@ -0,0 +1 @@ +%include "mips64/binopWide2addr.S" {"instr":"or a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_packed_switch.S b/runtime/interpreter/mterp/mips64/op_packed_switch.S new file mode 100644 index 0000000000..cdbdf75321 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_packed_switch.S @@ -0,0 +1,31 @@ +%default { "func":"MterpDoPackedSwitch" } + /* + * Handle a packed-switch or sparse-switch instruction. In both cases + * we decode it and hand it off to a helper function. + * + * We don't really expect backward branches in a switch statement, but + * they're perfectly legal, so we check for them here. + * + * for: packed-switch, sparse-switch + */ + /* op vAA, +BBBBBBBB */ + .extern $func + lh a0, 2(rPC) # a0 <- bbbb (lo) + lh a1, 4(rPC) # a1 <- BBBB (hi) + srl a3, rINST, 8 # a3 <- AA + ins a0, a1, 16, 16 # a0 <- BBBBbbbb + GET_VREG a1, a3 # a1 <- vAA + dlsa a0, a0, rPC, 1 # a0 <- PC + BBBBbbbb*2 + jal $func # v0 <- code-unit branch offset + dlsa rPC, v0, rPC, 1 # rPC <- rPC + offset * 2 + FETCH_INST # load rINST +#if MTERP_SUSPEND + bgtz v0, 1f # offset * 2 > 0 => no suspend check + REFRESH_IBASE +1: +#else + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + blez v0, MterpCheckSuspendAndContinue +#endif + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_rem_double.S b/runtime/interpreter/mterp/mips64/op_rem_double.S new file mode 100644 index 0000000000..ba61cfdc71 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_rem_double.S @@ -0,0 +1,12 @@ + /* rem-double vAA, vBB, vCC */ + .extern fmod + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG_DOUBLE f12, a2 # f12 <- vBB + GET_VREG_DOUBLE f13, a3 # f13 <- vCC + jal fmod # f0 <- f12 op f13 + srl a4, rINST, 8 # a4 <- AA + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_DOUBLE f0, a4 # vAA <- f0 + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_rem_double_2addr.S b/runtime/interpreter/mterp/mips64/op_rem_double_2addr.S new file mode 100644 index 0000000000..c649f0d62c --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_rem_double_2addr.S @@ -0,0 +1,12 @@ + /* rem-double/2addr vA, vB */ + .extern fmod + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG_DOUBLE f12, a2 # f12 <- vA + GET_VREG_DOUBLE f13, a3 # f13 <- vB + jal fmod # f0 <- f12 op f13 + ext a2, rINST, 8, 4 # a2 <- A + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_DOUBLE f0, a2 # vA <- f0 + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_rem_float.S b/runtime/interpreter/mterp/mips64/op_rem_float.S new file mode 100644 index 0000000000..3967b0b02c --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_rem_float.S @@ -0,0 +1,12 @@ + /* rem-float vAA, vBB, vCC */ + .extern fmodf + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG_FLOAT f12, a2 # f12 <- vBB + GET_VREG_FLOAT f13, a3 # f13 <- vCC + jal fmodf # f0 <- f12 op f13 + srl a4, rINST, 8 # a4 <- AA + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_FLOAT f0, a4 # vAA <- f0 + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_rem_float_2addr.S b/runtime/interpreter/mterp/mips64/op_rem_float_2addr.S new file mode 100644 index 0000000000..3fed41e851 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_rem_float_2addr.S @@ -0,0 +1,12 @@ + /* rem-float/2addr vA, vB */ + .extern fmodf + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG_FLOAT f12, a2 # f12 <- vA + GET_VREG_FLOAT f13, a3 # f13 <- vB + jal fmodf # f0 <- f12 op f13 + ext a2, rINST, 8, 4 # a2 <- A + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_FLOAT f0, a2 # vA <- f0 + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_rem_int.S b/runtime/interpreter/mterp/mips64/op_rem_int.S new file mode 100644 index 0000000000..c05e9c49fc --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_rem_int.S @@ -0,0 +1 @@ +%include "mips64/binop.S" {"instr":"mod a0, a0, a1", "chkzero":"1"} diff --git a/runtime/interpreter/mterp/mips64/op_rem_int_2addr.S b/runtime/interpreter/mterp/mips64/op_rem_int_2addr.S new file mode 100644 index 0000000000..a4e162d3fa --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_rem_int_2addr.S @@ -0,0 +1 @@ +%include "mips64/binop2addr.S" {"instr":"mod a0, a0, a1", "chkzero":"1"} diff --git a/runtime/interpreter/mterp/mips64/op_rem_int_lit16.S b/runtime/interpreter/mterp/mips64/op_rem_int_lit16.S new file mode 100644 index 0000000000..3284f1473c --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_rem_int_lit16.S @@ -0,0 +1 @@ +%include "mips64/binopLit16.S" {"instr":"mod a0, a0, a1", "chkzero":"1"} diff --git a/runtime/interpreter/mterp/mips64/op_rem_int_lit8.S b/runtime/interpreter/mterp/mips64/op_rem_int_lit8.S new file mode 100644 index 0000000000..1e6a584be5 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_rem_int_lit8.S @@ -0,0 +1 @@ +%include "mips64/binopLit8.S" {"instr":"mod a0, a0, a1", "chkzero":"1"} diff --git a/runtime/interpreter/mterp/mips64/op_rem_long.S b/runtime/interpreter/mterp/mips64/op_rem_long.S new file mode 100644 index 0000000000..32b2d1916d --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_rem_long.S @@ -0,0 +1 @@ +%include "mips64/binopWide.S" {"instr":"dmod a0, a0, a1", "chkzero":"1"} diff --git a/runtime/interpreter/mterp/mips64/op_rem_long_2addr.S b/runtime/interpreter/mterp/mips64/op_rem_long_2addr.S new file mode 100644 index 0000000000..ad658e1fde --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_rem_long_2addr.S @@ -0,0 +1 @@ +%include "mips64/binopWide2addr.S" {"instr":"dmod a0, a0, a1", "chkzero":"1"} diff --git a/runtime/interpreter/mterp/mips64/op_return.S b/runtime/interpreter/mterp/mips64/op_return.S new file mode 100644 index 0000000000..ec986b8066 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_return.S @@ -0,0 +1,18 @@ + /* + * Return a 32-bit value. + * + * for: return, return-object + */ + /* op vAA */ + .extern MterpThreadFenceForConstructor + .extern MterpSuspendCheck + jal MterpThreadFenceForConstructor + lw ra, THREAD_FLAGS_OFFSET(rSELF) + move a0, rSELF + and ra, ra, (THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + beqzc ra, 1f + jal MterpSuspendCheck # (self) +1: + srl a2, rINST, 8 # a2 <- AA + GET_VREG_U a0, a2 # a0 <- vAA + b MterpReturn diff --git a/runtime/interpreter/mterp/mips64/op_return_object.S b/runtime/interpreter/mterp/mips64/op_return_object.S new file mode 100644 index 0000000000..67f1871e3d --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_return_object.S @@ -0,0 +1 @@ +%include "mips64/op_return.S" diff --git a/runtime/interpreter/mterp/mips64/op_return_void.S b/runtime/interpreter/mterp/mips64/op_return_void.S new file mode 100644 index 0000000000..05253aea05 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_return_void.S @@ -0,0 +1,11 @@ + .extern MterpThreadFenceForConstructor + .extern MterpSuspendCheck + jal MterpThreadFenceForConstructor + lw ra, THREAD_FLAGS_OFFSET(rSELF) + move a0, rSELF + and ra, ra, (THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + beqzc ra, 1f + jal MterpSuspendCheck # (self) +1: + li a0, 0 + b MterpReturn diff --git a/runtime/interpreter/mterp/mips64/op_return_void_no_barrier.S b/runtime/interpreter/mterp/mips64/op_return_void_no_barrier.S new file mode 100644 index 0000000000..f67e811c70 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_return_void_no_barrier.S @@ -0,0 +1,9 @@ + .extern MterpSuspendCheck + lw ra, THREAD_FLAGS_OFFSET(rSELF) + move a0, rSELF + and ra, ra, (THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + beqzc ra, 1f + jal MterpSuspendCheck # (self) +1: + li a0, 0 + b MterpReturn diff --git a/runtime/interpreter/mterp/mips64/op_return_wide.S b/runtime/interpreter/mterp/mips64/op_return_wide.S new file mode 100644 index 0000000000..544e02794d --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_return_wide.S @@ -0,0 +1,17 @@ + /* + * Return a 64-bit value. + */ + /* return-wide vAA */ + /* op vAA */ + .extern MterpThreadFenceForConstructor + .extern MterpSuspendCheck + jal MterpThreadFenceForConstructor + lw ra, THREAD_FLAGS_OFFSET(rSELF) + move a0, rSELF + and ra, ra, (THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + beqzc ra, 1f + jal MterpSuspendCheck # (self) +1: + srl a2, rINST, 8 # a2 <- AA + GET_VREG_WIDE a0, a2 # a0 <- vAA + b MterpReturn diff --git a/runtime/interpreter/mterp/mips64/op_rsub_int.S b/runtime/interpreter/mterp/mips64/op_rsub_int.S new file mode 100644 index 0000000000..fa31a0af5f --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_rsub_int.S @@ -0,0 +1 @@ +%include "mips64/binopLit16.S" {"instr":"subu a0, a1, a0"} diff --git a/runtime/interpreter/mterp/mips64/op_rsub_int_lit8.S b/runtime/interpreter/mterp/mips64/op_rsub_int_lit8.S new file mode 100644 index 0000000000..c31ff32060 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_rsub_int_lit8.S @@ -0,0 +1 @@ +%include "mips64/binopLit8.S" {"instr":"subu a0, a1, a0"} diff --git a/runtime/interpreter/mterp/mips64/op_sget.S b/runtime/interpreter/mterp/mips64/op_sget.S new file mode 100644 index 0000000000..bd2cfe3778 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_sget.S @@ -0,0 +1,26 @@ +%default { "is_object":"0", "helper":"artGet32StaticFromCode", "extend":"" } + /* + * General SGET handler wrapper. + * + * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short + */ + /* op vAA, field//BBBB */ + .extern $helper + EXPORT_PC + lhu a0, 2(rPC) # a0 <- field ref BBBB + ld a1, OFF_FP_METHOD(rFP) + move a2, rSELF + jal $helper + ld a3, THREAD_EXCEPTION_OFFSET(rSELF) + srl a2, rINST, 8 # a2 <- AA + $extend + PREFETCH_INST 2 + bnez a3, MterpException # bail out + .if $is_object + SET_VREG_OBJECT v0, a2 # fp[AA] <- v0 + .else + SET_VREG v0, a2 # fp[AA] <- v0 + .endif + ADVANCE 2 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 diff --git a/runtime/interpreter/mterp/mips64/op_sget_boolean.S b/runtime/interpreter/mterp/mips64/op_sget_boolean.S new file mode 100644 index 0000000000..e7b1844d86 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_sget_boolean.S @@ -0,0 +1 @@ +%include "mips64/op_sget.S" {"helper":"artGetBooleanStaticFromCode", "extend":"and v0, v0, 0xff"} diff --git a/runtime/interpreter/mterp/mips64/op_sget_byte.S b/runtime/interpreter/mterp/mips64/op_sget_byte.S new file mode 100644 index 0000000000..52a2e4a5d5 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_sget_byte.S @@ -0,0 +1 @@ +%include "mips64/op_sget.S" {"helper":"artGetByteStaticFromCode", "extend":"seb v0, v0"} diff --git a/runtime/interpreter/mterp/mips64/op_sget_char.S b/runtime/interpreter/mterp/mips64/op_sget_char.S new file mode 100644 index 0000000000..873d82a0d6 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_sget_char.S @@ -0,0 +1 @@ +%include "mips64/op_sget.S" {"helper":"artGetCharStaticFromCode", "extend":"and v0, v0, 0xffff"} diff --git a/runtime/interpreter/mterp/mips64/op_sget_object.S b/runtime/interpreter/mterp/mips64/op_sget_object.S new file mode 100644 index 0000000000..3108417e00 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_sget_object.S @@ -0,0 +1 @@ +%include "mips64/op_sget.S" {"is_object":"1", "helper":"artGetObjStaticFromCode"} diff --git a/runtime/interpreter/mterp/mips64/op_sget_short.S b/runtime/interpreter/mterp/mips64/op_sget_short.S new file mode 100644 index 0000000000..fed4e76baa --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_sget_short.S @@ -0,0 +1 @@ +%include "mips64/op_sget.S" {"helper":"artGetShortStaticFromCode", "extend":"seh v0, v0"} diff --git a/runtime/interpreter/mterp/mips64/op_sget_wide.S b/runtime/interpreter/mterp/mips64/op_sget_wide.S new file mode 100644 index 0000000000..77124d1d8d --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_sget_wide.S @@ -0,0 +1,18 @@ + /* + * SGET_WIDE handler wrapper. + * + */ + /* sget-wide vAA, field//BBBB */ + .extern artGet64StaticFromCode + EXPORT_PC + lhu a0, 2(rPC) # a0 <- field ref BBBB + ld a1, OFF_FP_METHOD(rFP) + move a2, rSELF + jal artGet64StaticFromCode + ld a3, THREAD_EXCEPTION_OFFSET(rSELF) + srl a4, rINST, 8 # a4 <- AA + bnez a3, MterpException # bail out + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + SET_VREG_WIDE v0, a4 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_shl_int.S b/runtime/interpreter/mterp/mips64/op_shl_int.S new file mode 100644 index 0000000000..784481f335 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_shl_int.S @@ -0,0 +1 @@ +%include "mips64/binop.S" {"instr":"sll a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_shl_int_2addr.S b/runtime/interpreter/mterp/mips64/op_shl_int_2addr.S new file mode 100644 index 0000000000..a6c8a78ff6 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_shl_int_2addr.S @@ -0,0 +1 @@ +%include "mips64/binop2addr.S" {"instr":"sll a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_shl_int_lit8.S b/runtime/interpreter/mterp/mips64/op_shl_int_lit8.S new file mode 100644 index 0000000000..36ef207edf --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_shl_int_lit8.S @@ -0,0 +1 @@ +%include "mips64/binopLit8.S" {"instr":"sll a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_shl_long.S b/runtime/interpreter/mterp/mips64/op_shl_long.S new file mode 100644 index 0000000000..225a2cbc2a --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_shl_long.S @@ -0,0 +1 @@ +%include "mips64/binopWide.S" {"instr":"dsll a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_shl_long_2addr.S b/runtime/interpreter/mterp/mips64/op_shl_long_2addr.S new file mode 100644 index 0000000000..c04d8823f4 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_shl_long_2addr.S @@ -0,0 +1 @@ +%include "mips64/binopWide2addr.S" {"instr":"dsll a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_shr_int.S b/runtime/interpreter/mterp/mips64/op_shr_int.S new file mode 100644 index 0000000000..eded0373b1 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_shr_int.S @@ -0,0 +1 @@ +%include "mips64/binop.S" {"instr":"sra a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_shr_int_2addr.S b/runtime/interpreter/mterp/mips64/op_shr_int_2addr.S new file mode 100644 index 0000000000..5b4d96f187 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_shr_int_2addr.S @@ -0,0 +1 @@ +%include "mips64/binop2addr.S" {"instr":"sra a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_shr_int_lit8.S b/runtime/interpreter/mterp/mips64/op_shr_int_lit8.S new file mode 100644 index 0000000000..175eb8633a --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_shr_int_lit8.S @@ -0,0 +1 @@ +%include "mips64/binopLit8.S" {"instr":"sra a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_shr_long.S b/runtime/interpreter/mterp/mips64/op_shr_long.S new file mode 100644 index 0000000000..0db38c8510 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_shr_long.S @@ -0,0 +1 @@ +%include "mips64/binopWide.S" {"instr":"dsra a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_shr_long_2addr.S b/runtime/interpreter/mterp/mips64/op_shr_long_2addr.S new file mode 100644 index 0000000000..48131ad7e4 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_shr_long_2addr.S @@ -0,0 +1 @@ +%include "mips64/binopWide2addr.S" {"instr":"dsra a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_sparse_switch.S b/runtime/interpreter/mterp/mips64/op_sparse_switch.S new file mode 100644 index 0000000000..b065aaa95b --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_sparse_switch.S @@ -0,0 +1 @@ +%include "mips64/op_packed_switch.S" { "func":"MterpDoSparseSwitch" } diff --git a/runtime/interpreter/mterp/mips64/op_sput.S b/runtime/interpreter/mterp/mips64/op_sput.S new file mode 100644 index 0000000000..142f18f3ba --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_sput.S @@ -0,0 +1,20 @@ +%default { "helper":"artSet32StaticFromCode" } + /* + * General SPUT handler wrapper. + * + * for: sput, sput-boolean, sput-byte, sput-char, sput-short + */ + /* op vAA, field//BBBB */ + .extern $helper + EXPORT_PC + lhu a0, 2(rPC) # a0 <- field ref BBBB + srl a3, rINST, 8 # a3 <- AA + GET_VREG a1, a3 # a1 <- fp[AA] + ld a2, OFF_FP_METHOD(rFP) + move a3, rSELF + PREFETCH_INST 2 # Get next inst, but don't advance rPC + jal $helper + bnezc v0, MterpException # 0 on success + ADVANCE 2 # Past exception point - now advance rPC + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_sput_boolean.S b/runtime/interpreter/mterp/mips64/op_sput_boolean.S new file mode 100644 index 0000000000..f5b8dbf433 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_sput_boolean.S @@ -0,0 +1 @@ +%include "mips64/op_sput.S" {"helper":"artSet8StaticFromCode"} diff --git a/runtime/interpreter/mterp/mips64/op_sput_byte.S b/runtime/interpreter/mterp/mips64/op_sput_byte.S new file mode 100644 index 0000000000..f5b8dbf433 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_sput_byte.S @@ -0,0 +1 @@ +%include "mips64/op_sput.S" {"helper":"artSet8StaticFromCode"} diff --git a/runtime/interpreter/mterp/mips64/op_sput_char.S b/runtime/interpreter/mterp/mips64/op_sput_char.S new file mode 100644 index 0000000000..c4d195c82f --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_sput_char.S @@ -0,0 +1 @@ +%include "mips64/op_sput.S" {"helper":"artSet16StaticFromCode"} diff --git a/runtime/interpreter/mterp/mips64/op_sput_object.S b/runtime/interpreter/mterp/mips64/op_sput_object.S new file mode 100644 index 0000000000..ef4c685116 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_sput_object.S @@ -0,0 +1,11 @@ + .extern MterpSputObject + EXPORT_PC + daddu a0, rFP, OFF_FP_SHADOWFRAME + move a1, rPC + move a2, rINST + move a3, rSELF + jal MterpSputObject + beqzc v0, MterpException + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_sput_short.S b/runtime/interpreter/mterp/mips64/op_sput_short.S new file mode 100644 index 0000000000..c4d195c82f --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_sput_short.S @@ -0,0 +1 @@ +%include "mips64/op_sput.S" {"helper":"artSet16StaticFromCode"} diff --git a/runtime/interpreter/mterp/mips64/op_sput_wide.S b/runtime/interpreter/mterp/mips64/op_sput_wide.S new file mode 100644 index 0000000000..828ddc15e7 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_sput_wide.S @@ -0,0 +1,18 @@ + /* + * SPUT_WIDE handler wrapper. + * + */ + /* sput-wide vAA, field//BBBB */ + .extern artSet64IndirectStaticFromMterp + EXPORT_PC + lhu a0, 2(rPC) # a0 <- field ref BBBB + ld a1, OFF_FP_METHOD(rFP) + srl a2, rINST, 8 # a2 <- AA + dlsa a2, a2, rFP, 2 + move a3, rSELF + PREFETCH_INST 2 # Get next inst, but don't advance rPC + jal artSet64IndirectStaticFromMterp + bnezc v0, MterpException # 0 on success, -1 on failure + ADVANCE 2 # Past exception point - now advance rPC + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_sub_double.S b/runtime/interpreter/mterp/mips64/op_sub_double.S new file mode 100644 index 0000000000..40a6c89a10 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_sub_double.S @@ -0,0 +1 @@ +%include "mips64/fbinopWide.S" {"instr":"sub.d f0, f0, f1"} diff --git a/runtime/interpreter/mterp/mips64/op_sub_double_2addr.S b/runtime/interpreter/mterp/mips64/op_sub_double_2addr.S new file mode 100644 index 0000000000..984737e553 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_sub_double_2addr.S @@ -0,0 +1 @@ +%include "mips64/fbinopWide2addr.S" {"instr":"sub.d f0, f0, f1"} diff --git a/runtime/interpreter/mterp/mips64/op_sub_float.S b/runtime/interpreter/mterp/mips64/op_sub_float.S new file mode 100644 index 0000000000..9010592116 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_sub_float.S @@ -0,0 +1 @@ +%include "mips64/fbinop.S" {"instr":"sub.s f0, f0, f1"} diff --git a/runtime/interpreter/mterp/mips64/op_sub_float_2addr.S b/runtime/interpreter/mterp/mips64/op_sub_float_2addr.S new file mode 100644 index 0000000000..e7d4ffe1ae --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_sub_float_2addr.S @@ -0,0 +1 @@ +%include "mips64/fbinop2addr.S" {"instr":"sub.s f0, f0, f1"} diff --git a/runtime/interpreter/mterp/mips64/op_sub_int.S b/runtime/interpreter/mterp/mips64/op_sub_int.S new file mode 100644 index 0000000000..609ea0575d --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_sub_int.S @@ -0,0 +1 @@ +%include "mips64/binop.S" {"instr":"subu a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_sub_int_2addr.S b/runtime/interpreter/mterp/mips64/op_sub_int_2addr.S new file mode 100644 index 0000000000..ba2f1e875b --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_sub_int_2addr.S @@ -0,0 +1 @@ +%include "mips64/binop2addr.S" {"instr":"subu a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_sub_long.S b/runtime/interpreter/mterp/mips64/op_sub_long.S new file mode 100644 index 0000000000..09a6afd26e --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_sub_long.S @@ -0,0 +1 @@ +%include "mips64/binopWide.S" {"instr":"dsubu a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_sub_long_2addr.S b/runtime/interpreter/mterp/mips64/op_sub_long_2addr.S new file mode 100644 index 0000000000..b9ec82a19b --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_sub_long_2addr.S @@ -0,0 +1 @@ +%include "mips64/binopWide2addr.S" {"instr":"dsubu a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_throw.S b/runtime/interpreter/mterp/mips64/op_throw.S new file mode 100644 index 0000000000..6418d57ecc --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_throw.S @@ -0,0 +1,10 @@ + /* + * Throw an exception object in the current thread. + */ + /* throw vAA */ + EXPORT_PC + srl a2, rINST, 8 # a2 <- AA + GET_VREG_U a0, a2 # a0 <- vAA (exception object) + beqzc a0, common_errNullObject + sd a0, THREAD_EXCEPTION_OFFSET(rSELF) # thread->exception <- obj + b MterpException diff --git a/runtime/interpreter/mterp/mips64/op_unused_3e.S b/runtime/interpreter/mterp/mips64/op_unused_3e.S new file mode 100644 index 0000000000..29463d73fc --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_unused_3e.S @@ -0,0 +1 @@ +%include "mips64/unused.S" diff --git a/runtime/interpreter/mterp/mips64/op_unused_3f.S b/runtime/interpreter/mterp/mips64/op_unused_3f.S new file mode 100644 index 0000000000..29463d73fc --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_unused_3f.S @@ -0,0 +1 @@ +%include "mips64/unused.S" diff --git a/runtime/interpreter/mterp/mips64/op_unused_40.S b/runtime/interpreter/mterp/mips64/op_unused_40.S new file mode 100644 index 0000000000..29463d73fc --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_unused_40.S @@ -0,0 +1 @@ +%include "mips64/unused.S" diff --git a/runtime/interpreter/mterp/mips64/op_unused_41.S b/runtime/interpreter/mterp/mips64/op_unused_41.S new file mode 100644 index 0000000000..29463d73fc --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_unused_41.S @@ -0,0 +1 @@ +%include "mips64/unused.S" diff --git a/runtime/interpreter/mterp/mips64/op_unused_42.S b/runtime/interpreter/mterp/mips64/op_unused_42.S new file mode 100644 index 0000000000..29463d73fc --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_unused_42.S @@ -0,0 +1 @@ +%include "mips64/unused.S" diff --git a/runtime/interpreter/mterp/mips64/op_unused_43.S b/runtime/interpreter/mterp/mips64/op_unused_43.S new file mode 100644 index 0000000000..29463d73fc --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_unused_43.S @@ -0,0 +1 @@ +%include "mips64/unused.S" diff --git a/runtime/interpreter/mterp/mips64/op_unused_79.S b/runtime/interpreter/mterp/mips64/op_unused_79.S new file mode 100644 index 0000000000..29463d73fc --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_unused_79.S @@ -0,0 +1 @@ +%include "mips64/unused.S" diff --git a/runtime/interpreter/mterp/mips64/op_unused_7a.S b/runtime/interpreter/mterp/mips64/op_unused_7a.S new file mode 100644 index 0000000000..29463d73fc --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_unused_7a.S @@ -0,0 +1 @@ +%include "mips64/unused.S" diff --git a/runtime/interpreter/mterp/mips64/op_unused_f4.S b/runtime/interpreter/mterp/mips64/op_unused_f4.S new file mode 100644 index 0000000000..29463d73fc --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_unused_f4.S @@ -0,0 +1 @@ +%include "mips64/unused.S" diff --git a/runtime/interpreter/mterp/mips64/op_unused_fa.S b/runtime/interpreter/mterp/mips64/op_unused_fa.S new file mode 100644 index 0000000000..29463d73fc --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_unused_fa.S @@ -0,0 +1 @@ +%include "mips64/unused.S" diff --git a/runtime/interpreter/mterp/mips64/op_unused_fb.S b/runtime/interpreter/mterp/mips64/op_unused_fb.S new file mode 100644 index 0000000000..29463d73fc --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_unused_fb.S @@ -0,0 +1 @@ +%include "mips64/unused.S" diff --git a/runtime/interpreter/mterp/mips64/op_unused_fc.S b/runtime/interpreter/mterp/mips64/op_unused_fc.S new file mode 100644 index 0000000000..29463d73fc --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_unused_fc.S @@ -0,0 +1 @@ +%include "mips64/unused.S" diff --git a/runtime/interpreter/mterp/mips64/op_unused_fd.S b/runtime/interpreter/mterp/mips64/op_unused_fd.S new file mode 100644 index 0000000000..29463d73fc --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_unused_fd.S @@ -0,0 +1 @@ +%include "mips64/unused.S" diff --git a/runtime/interpreter/mterp/mips64/op_unused_fe.S b/runtime/interpreter/mterp/mips64/op_unused_fe.S new file mode 100644 index 0000000000..29463d73fc --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_unused_fe.S @@ -0,0 +1 @@ +%include "mips64/unused.S" diff --git a/runtime/interpreter/mterp/mips64/op_unused_ff.S b/runtime/interpreter/mterp/mips64/op_unused_ff.S new file mode 100644 index 0000000000..29463d73fc --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_unused_ff.S @@ -0,0 +1 @@ +%include "mips64/unused.S" diff --git a/runtime/interpreter/mterp/mips64/op_ushr_int.S b/runtime/interpreter/mterp/mips64/op_ushr_int.S new file mode 100644 index 0000000000..37c90cb7ec --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_ushr_int.S @@ -0,0 +1 @@ +%include "mips64/binop.S" {"instr":"srl a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_ushr_int_2addr.S b/runtime/interpreter/mterp/mips64/op_ushr_int_2addr.S new file mode 100644 index 0000000000..d6bf4135dc --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_ushr_int_2addr.S @@ -0,0 +1 @@ +%include "mips64/binop2addr.S" {"instr":"srl a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_ushr_int_lit8.S b/runtime/interpreter/mterp/mips64/op_ushr_int_lit8.S new file mode 100644 index 0000000000..2a2d843c8a --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_ushr_int_lit8.S @@ -0,0 +1 @@ +%include "mips64/binopLit8.S" {"instr":"srl a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_ushr_long.S b/runtime/interpreter/mterp/mips64/op_ushr_long.S new file mode 100644 index 0000000000..e724405f1f --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_ushr_long.S @@ -0,0 +1 @@ +%include "mips64/binopWide.S" {"instr":"dsrl a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_ushr_long_2addr.S b/runtime/interpreter/mterp/mips64/op_ushr_long_2addr.S new file mode 100644 index 0000000000..d2cf135566 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_ushr_long_2addr.S @@ -0,0 +1 @@ +%include "mips64/binopWide2addr.S" {"instr":"dsrl a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_xor_int.S b/runtime/interpreter/mterp/mips64/op_xor_int.S new file mode 100644 index 0000000000..ee25ebc925 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_xor_int.S @@ -0,0 +1 @@ +%include "mips64/binop.S" {"instr":"xor a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_xor_int_2addr.S b/runtime/interpreter/mterp/mips64/op_xor_int_2addr.S new file mode 100644 index 0000000000..0f0496729a --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_xor_int_2addr.S @@ -0,0 +1 @@ +%include "mips64/binop2addr.S" {"instr":"xor a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_xor_int_lit16.S b/runtime/interpreter/mterp/mips64/op_xor_int_lit16.S new file mode 100644 index 0000000000..ecb21aee07 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_xor_int_lit16.S @@ -0,0 +1 @@ +%include "mips64/binopLit16.S" {"instr":"xor a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_xor_int_lit8.S b/runtime/interpreter/mterp/mips64/op_xor_int_lit8.S new file mode 100644 index 0000000000..115ae99917 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_xor_int_lit8.S @@ -0,0 +1 @@ +%include "mips64/binopLit8.S" {"instr":"xor a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_xor_long.S b/runtime/interpreter/mterp/mips64/op_xor_long.S new file mode 100644 index 0000000000..7ebabc2710 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_xor_long.S @@ -0,0 +1 @@ +%include "mips64/binopWide.S" {"instr":"xor a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_xor_long_2addr.S b/runtime/interpreter/mterp/mips64/op_xor_long_2addr.S new file mode 100644 index 0000000000..0f1919a21e --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_xor_long_2addr.S @@ -0,0 +1 @@ +%include "mips64/binopWide2addr.S" {"instr":"xor a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/unop.S b/runtime/interpreter/mterp/mips64/unop.S new file mode 100644 index 0000000000..e3f7ea0eda --- /dev/null +++ b/runtime/interpreter/mterp/mips64/unop.S @@ -0,0 +1,18 @@ +%default {"preinstr":""} + /* + * Generic 32-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "a0 = op a0". + * + * for: int-to-byte, int-to-char, int-to-short, + * not-int, neg-int + */ + /* unop vA, vB */ + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG a0, a3 # a0 <- vB + ext a2, rINST, 8, 4 # a2 <- A + $preinstr # optional op + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + $instr # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/unopWide.S b/runtime/interpreter/mterp/mips64/unopWide.S new file mode 100644 index 0000000000..c0dd1aa1d3 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/unopWide.S @@ -0,0 +1,17 @@ +%default {"preinstr":""} + /* + * Generic 64-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "a0 = op a0". + * + * For: not-long, neg-long + */ + /* unop vA, vB */ + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG_WIDE a0, a3 # a0 <- vB + ext a2, rINST, 8, 4 # a2 <- A + $preinstr # optional op + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + $instr # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/unused.S b/runtime/interpreter/mterp/mips64/unused.S new file mode 100644 index 0000000000..30d38bd6cd --- /dev/null +++ b/runtime/interpreter/mterp/mips64/unused.S @@ -0,0 +1,4 @@ +/* + * Bail to reference interpreter to throw. + */ + b MterpFallback diff --git a/runtime/interpreter/mterp/mips64/zcmp.S b/runtime/interpreter/mterp/mips64/zcmp.S new file mode 100644 index 0000000000..d7ad894485 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/zcmp.S @@ -0,0 +1,30 @@ + /* + * Generic one-operand compare-and-branch operation. Provide a "condition" + * fragment that specifies the comparison to perform, e.g. for + * "if-lez" you would use "le". + * + * For: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez + */ + /* if-cmp vAA, +BBBB */ + lh a4, 2(rPC) # a4 <- sign-extended BBBB + srl a2, rINST, 8 # a2 <- AA + GET_VREG a0, a2 # a0 <- vAA + + b${condition}zc a0, 1f + li a4, 2 # offset if branch not taken +1: + + dlsa rPC, a4, rPC, 1 # rPC <- rPC + BBBB * 2 + FETCH_INST # load rINST + +#if MTERP_SUSPEND + bgez a4, 2f # BBBB * 2 >= 0 => no suspend check + REFRESH_IBASE +2: +#else + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + bltz a4, MterpCheckSuspendAndContinue +#endif + + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/out/mterp_mips64.S b/runtime/interpreter/mterp/out/mterp_mips64.S new file mode 100644 index 0000000000..7cef823fff --- /dev/null +++ b/runtime/interpreter/mterp/out/mterp_mips64.S @@ -0,0 +1,12362 @@ +/* + * This file was generated automatically by gen-mterp.py for 'mips64'. + * + * --> DO NOT EDIT <-- + */ + +/* File: mips64/header.S */ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +/* TODO: add the missing file and use its FP register definitions. */ +/* #include */ +/* FP register definitions */ +#define f0 $f0 +#define f1 $f1 +#define f2 $f2 +#define f3 $f3 +#define f12 $f12 +#define f13 $f13 + +/* + * It looks like the GNU assembler currently does not support the blec and bgtc + * idioms, which should translate into bgec and bltc respectively with swapped + * left and right register operands. + * TODO: remove these macros when the assembler is fixed. + */ +.macro blec lreg, rreg, target + bgec \rreg, \lreg, \target +.endm +.macro bgtc lreg, rreg, target + bltc \rreg, \lreg, \target +.endm + +/* +Mterp and MIPS64 notes: + +The following registers have fixed assignments: + + reg nick purpose + s0 rPC interpreted program counter, used for fetching instructions + s1 rFP interpreted frame pointer, used for accessing locals and args + s2 rSELF self (Thread) pointer + s3 rINST first 16-bit code unit of current instruction + s4 rIBASE interpreted instruction base pointer, used for computed goto + s5 rREFS base of object references in shadow frame (ideally, we'll get rid of this later). +*/ + +/* During bringup, we'll use the shadow frame model instead of rFP */ +/* single-purpose registers, given names for clarity */ +#define rPC s0 +#define rFP s1 +#define rSELF s2 +#define rINST s3 +#define rIBASE s4 +#define rREFS s5 + +/* + * This is a #include, not a %include, because we want the C pre-processor + * to expand the macros into assembler assignment statements. + */ +#include "asm_support.h" + +/* + * Instead of holding a pointer to the shadow frame, we keep rFP at the base of the vregs. So, + * to access other shadow frame fields, we need to use a backwards offset. Define those here. + */ +#define OFF_FP(a) (a - SHADOWFRAME_VREGS_OFFSET) +#define OFF_FP_NUMBER_OF_VREGS OFF_FP(SHADOWFRAME_NUMBER_OF_VREGS_OFFSET) +#define OFF_FP_DEX_PC OFF_FP(SHADOWFRAME_DEX_PC_OFFSET) +#define OFF_FP_LINK OFF_FP(SHADOWFRAME_LINK_OFFSET) +#define OFF_FP_METHOD OFF_FP(SHADOWFRAME_METHOD_OFFSET) +#define OFF_FP_RESULT_REGISTER OFF_FP(SHADOWFRAME_RESULT_REGISTER_OFFSET) +#define OFF_FP_DEX_PC_PTR OFF_FP(SHADOWFRAME_DEX_PC_PTR_OFFSET) +#define OFF_FP_CODE_ITEM OFF_FP(SHADOWFRAME_CODE_ITEM_OFFSET) +#define OFF_FP_SHADOWFRAME (-SHADOWFRAME_VREGS_OFFSET) + +/* + * + * The reference interpreter performs explicit suspect checks, which is somewhat wasteful. + * Dalvik's interpreter folded suspend checks into the jump table mechanism, and eventually + * mterp should do so as well. + */ +#define MTERP_SUSPEND 0 + +#define MTERP_LOGGING 0 + +/* + * "export" the PC to dex_pc field in the shadow frame, f/b/o future exception objects. Must + * be done *before* something throws. + * + * It's okay to do this more than once. + * + * NOTE: the fast interpreter keeps track of dex pc as a direct pointer to the mapped + * dex byte codes. However, the rest of the runtime expects dex pc to be an instruction + * offset into the code_items_[] array. For effiency, we will "export" the + * current dex pc as a direct pointer using the EXPORT_PC macro, and rely on GetDexPC + * to convert to a dex pc when needed. + */ +.macro EXPORT_PC + sd rPC, OFF_FP_DEX_PC_PTR(rFP) +.endm + +/* + * Refresh handler table. + */ +.macro REFRESH_IBASE + ld rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) +.endm + +/* + * Fetch the next instruction from rPC into rINST. Does not advance rPC. + */ +.macro FETCH_INST + lhu rINST, 0(rPC) +.endm + +/* Advance rPC by some number of code units. */ +.macro ADVANCE count + daddu rPC, rPC, (\count) * 2 +.endm + +/* + * Fetch the next instruction from the specified offset. Advances rPC + * to point to the next instruction. + * + * This must come AFTER anything that can throw an exception, or the + * exception catch may miss. (This also implies that it must come after + * EXPORT_PC.) + */ +.macro FETCH_ADVANCE_INST count + ADVANCE \count + FETCH_INST +.endm + +/* + * Similar to FETCH_ADVANCE_INST, but does not update rPC. Used to load + * rINST ahead of possible exception point. Be sure to manually advance rPC + * later. + */ +.macro PREFETCH_INST count + lhu rINST, ((\count) * 2)(rPC) +.endm + +/* + * Put the instruction's opcode field into the specified register. + */ +.macro GET_INST_OPCODE reg + and \reg, rINST, 255 +.endm + +/* + * Begin executing the opcode in _reg. + */ +.macro GOTO_OPCODE reg + .set noat + sll AT, \reg, 7 + daddu AT, rIBASE, AT + jic AT, 0 + .set at +.endm + +/* + * Get/set the 32-bit value from a Dalvik register. + * Note, GET_VREG does sign extension to 64 bits while + * GET_VREG_U does zero extension to 64 bits. + * One is useful for arithmetic while the other is + * useful for storing the result value as 64-bit. + */ +.macro GET_VREG reg, vreg + .set noat + dlsa AT, \vreg, rFP, 2 + lw \reg, 0(AT) + .set at +.endm +.macro GET_VREG_U reg, vreg + .set noat + dlsa AT, \vreg, rFP, 2 + lwu \reg, 0(AT) + .set at +.endm +.macro GET_VREG_FLOAT reg, vreg + .set noat + dlsa AT, \vreg, rFP, 2 + lwc1 \reg, 0(AT) + .set at +.endm +.macro SET_VREG reg, vreg + .set noat + dlsa AT, \vreg, rFP, 2 + sw \reg, 0(AT) + dlsa AT, \vreg, rREFS, 2 + sw zero, 0(AT) + .set at +.endm +.macro SET_VREG_OBJECT reg, vreg + .set noat + dlsa AT, \vreg, rFP, 2 + sw \reg, 0(AT) + dlsa AT, \vreg, rREFS, 2 + sw \reg, 0(AT) + .set at +.endm +.macro SET_VREG_FLOAT reg, vreg + .set noat + dlsa AT, \vreg, rFP, 2 + swc1 \reg, 0(AT) + dlsa AT, \vreg, rREFS, 2 + sw zero, 0(AT) + .set at +.endm + +/* + * Get/set the 64-bit value from a Dalvik register. + * Avoid unaligned memory accesses. + * Note, SET_VREG_WIDE clobbers the register containing the value being stored. + * Note, SET_VREG_DOUBLE clobbers the register containing the Dalvik register number. + */ +.macro GET_VREG_WIDE reg, vreg + .set noat + dlsa AT, \vreg, rFP, 2 + lw \reg, 0(AT) + lw AT, 4(AT) + dinsu \reg, AT, 32, 32 + .set at +.endm +.macro GET_VREG_DOUBLE reg, vreg + .set noat + dlsa AT, \vreg, rFP, 2 + lwc1 \reg, 0(AT) + lw AT, 4(AT) + mthc1 AT, \reg + .set at +.endm +.macro SET_VREG_WIDE reg, vreg + .set noat + dlsa AT, \vreg, rFP, 2 + sw \reg, 0(AT) + drotr32 \reg, \reg, 0 + sw \reg, 4(AT) + dlsa AT, \vreg, rREFS, 2 + sw zero, 0(AT) + sw zero, 4(AT) + .set at +.endm +.macro SET_VREG_DOUBLE reg, vreg + .set noat + dlsa AT, \vreg, rREFS, 2 + sw zero, 0(AT) + sw zero, 4(AT) + dlsa AT, \vreg, rFP, 2 + swc1 \reg, 0(AT) + mfhc1 \vreg, \reg + sw \vreg, 4(AT) + .set at +.endm + +/* + * On-stack offsets for spilling/unspilling callee-saved registers + * and the frame size. + */ +#define STACK_OFFSET_RA 0 +#define STACK_OFFSET_GP 8 +#define STACK_OFFSET_S0 16 +#define STACK_OFFSET_S1 24 +#define STACK_OFFSET_S2 32 +#define STACK_OFFSET_S3 40 +#define STACK_OFFSET_S4 48 +#define STACK_OFFSET_S5 56 +#define STACK_SIZE 64 + +/* Constants for float/double_to_int/long conversions */ +#define INT_MIN 0x80000000 +#define INT_MIN_AS_FLOAT 0xCF000000 +#define INT_MIN_AS_DOUBLE 0xC1E0000000000000 +#define LONG_MIN 0x8000000000000000 +#define LONG_MIN_AS_FLOAT 0xDF000000 +#define LONG_MIN_AS_DOUBLE 0xC3E0000000000000 + +/* File: mips64/entry.S */ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Interpreter entry point. + */ + + .set reorder + + .text + .global ExecuteMterpImpl + .type ExecuteMterpImpl, %function + .balign 16 +/* + * On entry: + * a0 Thread* self + * a1 code_item + * a2 ShadowFrame + * a3 JValue* result_register + * + */ +ExecuteMterpImpl: + .cfi_startproc + .cpsetup t9, t8, ExecuteMterpImpl + + .cfi_def_cfa sp, 0 + daddu sp, sp, -STACK_SIZE + .cfi_adjust_cfa_offset STACK_SIZE + + sd t8, STACK_OFFSET_GP(sp) + .cfi_rel_offset 28, STACK_OFFSET_GP + sd ra, STACK_OFFSET_RA(sp) + .cfi_rel_offset 31, STACK_OFFSET_RA + + sd s0, STACK_OFFSET_S0(sp) + .cfi_rel_offset 16, STACK_OFFSET_S0 + sd s1, STACK_OFFSET_S1(sp) + .cfi_rel_offset 17, STACK_OFFSET_S1 + sd s2, STACK_OFFSET_S2(sp) + .cfi_rel_offset 18, STACK_OFFSET_S2 + sd s3, STACK_OFFSET_S3(sp) + .cfi_rel_offset 19, STACK_OFFSET_S3 + sd s4, STACK_OFFSET_S4(sp) + .cfi_rel_offset 20, STACK_OFFSET_S4 + sd s5, STACK_OFFSET_S5(sp) + .cfi_rel_offset 21, STACK_OFFSET_S5 + + /* Remember the return register */ + sd a3, SHADOWFRAME_RESULT_REGISTER_OFFSET(a2) + + /* Remember the code_item */ + sd a1, SHADOWFRAME_CODE_ITEM_OFFSET(a2) + + /* set up "named" registers */ + move rSELF, a0 + daddu rFP, a2, SHADOWFRAME_VREGS_OFFSET + lw v0, SHADOWFRAME_NUMBER_OF_VREGS_OFFSET(a2) + dlsa rREFS, v0, rFP, 2 + daddu rPC, a1, CODEITEM_INSNS_OFFSET + lw v0, SHADOWFRAME_DEX_PC_OFFSET(a2) + dlsa rPC, v0, rPC, 1 + EXPORT_PC + + /* Starting ibase */ + REFRESH_IBASE + + /* start executing the instruction at rPC */ + FETCH_INST + GET_INST_OPCODE v0 + GOTO_OPCODE v0 + + /* NOTE: no fallthrough */ + + + .global artMterpAsmInstructionStart + .type artMterpAsmInstructionStart, %function +artMterpAsmInstructionStart = .L_op_nop + .text + +/* ------------------------------ */ + .balign 128 +.L_op_nop: /* 0x00 */ +/* File: mips64/op_nop.S */ + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_move: /* 0x01 */ +/* File: mips64/op_move.S */ + /* for move, move-object, long-to-int */ + /* op vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + GET_VREG a0, a3 # a0 <- vB + GET_INST_OPCODE v0 # extract opcode from rINST + .if 0 + SET_VREG_OBJECT a0, a2 # vA <- vB + .else + SET_VREG a0, a2 # vA <- vB + .endif + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_move_from16: /* 0x02 */ +/* File: mips64/op_move_from16.S */ + /* for: move/from16, move-object/from16 */ + /* op vAA, vBBBB */ + lhu a3, 2(rPC) # a3 <- BBBB + srl a2, rINST, 8 # a2 <- AA + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_VREG a0, a3 # a0 <- vBBBB + GET_INST_OPCODE v0 # extract opcode from rINST + .if 0 + SET_VREG_OBJECT a0, a2 # vAA <- vBBBB + .else + SET_VREG a0, a2 # vAA <- vBBBB + .endif + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_move_16: /* 0x03 */ +/* File: mips64/op_move_16.S */ + /* for: move/16, move-object/16 */ + /* op vAAAA, vBBBB */ + lhu a3, 4(rPC) # a3 <- BBBB + lhu a2, 2(rPC) # a2 <- AAAA + FETCH_ADVANCE_INST 3 # advance rPC, load rINST + GET_VREG a0, a3 # a0 <- vBBBB + GET_INST_OPCODE v0 # extract opcode from rINST + .if 0 + SET_VREG_OBJECT a0, a2 # vAAAA <- vBBBB + .else + SET_VREG a0, a2 # vAAAA <- vBBBB + .endif + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_move_wide: /* 0x04 */ +/* File: mips64/op_move_wide.S */ + /* move-wide vA, vB */ + /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */ + ext a3, rINST, 12, 4 # a3 <- B + ext a2, rINST, 8, 4 # a2 <- A + GET_VREG_WIDE a0, a3 # a0 <- vB + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a2 # vA <- vB + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_move_wide_from16: /* 0x05 */ +/* File: mips64/op_move_wide_from16.S */ + /* move-wide/from16 vAA, vBBBB */ + /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */ + lhu a3, 2(rPC) # a3 <- BBBB + srl a2, rINST, 8 # a2 <- AA + GET_VREG_WIDE a0, a3 # a0 <- vBBBB + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a2 # vAA <- vBBBB + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_move_wide_16: /* 0x06 */ +/* File: mips64/op_move_wide_16.S */ + /* move-wide/16 vAAAA, vBBBB */ + /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */ + lhu a3, 4(rPC) # a3 <- BBBB + lhu a2, 2(rPC) # a2 <- AAAA + GET_VREG_WIDE a0, a3 # a0 <- vBBBB + FETCH_ADVANCE_INST 3 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a2 # vAAAA <- vBBBB + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_move_object: /* 0x07 */ +/* File: mips64/op_move_object.S */ +/* File: mips64/op_move.S */ + /* for move, move-object, long-to-int */ + /* op vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + GET_VREG a0, a3 # a0 <- vB + GET_INST_OPCODE v0 # extract opcode from rINST + .if 1 + SET_VREG_OBJECT a0, a2 # vA <- vB + .else + SET_VREG a0, a2 # vA <- vB + .endif + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_move_object_from16: /* 0x08 */ +/* File: mips64/op_move_object_from16.S */ +/* File: mips64/op_move_from16.S */ + /* for: move/from16, move-object/from16 */ + /* op vAA, vBBBB */ + lhu a3, 2(rPC) # a3 <- BBBB + srl a2, rINST, 8 # a2 <- AA + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_VREG a0, a3 # a0 <- vBBBB + GET_INST_OPCODE v0 # extract opcode from rINST + .if 1 + SET_VREG_OBJECT a0, a2 # vAA <- vBBBB + .else + SET_VREG a0, a2 # vAA <- vBBBB + .endif + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_move_object_16: /* 0x09 */ +/* File: mips64/op_move_object_16.S */ +/* File: mips64/op_move_16.S */ + /* for: move/16, move-object/16 */ + /* op vAAAA, vBBBB */ + lhu a3, 4(rPC) # a3 <- BBBB + lhu a2, 2(rPC) # a2 <- AAAA + FETCH_ADVANCE_INST 3 # advance rPC, load rINST + GET_VREG a0, a3 # a0 <- vBBBB + GET_INST_OPCODE v0 # extract opcode from rINST + .if 1 + SET_VREG_OBJECT a0, a2 # vAAAA <- vBBBB + .else + SET_VREG a0, a2 # vAAAA <- vBBBB + .endif + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_move_result: /* 0x0a */ +/* File: mips64/op_move_result.S */ + /* for: move-result, move-result-object */ + /* op vAA */ + srl a2, rINST, 8 # a2 <- AA + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + ld a0, OFF_FP_RESULT_REGISTER(rFP) # get pointer to result JType + lw a0, 0(a0) # a0 <- result.i + GET_INST_OPCODE v0 # extract opcode from rINST + .if 0 + SET_VREG_OBJECT a0, a2 # vAA <- result + .else + SET_VREG a0, a2 # vAA <- result + .endif + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_move_result_wide: /* 0x0b */ +/* File: mips64/op_move_result_wide.S */ + /* for: move-result-wide */ + /* op vAA */ + srl a2, rINST, 8 # a2 <- AA + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + ld a0, OFF_FP_RESULT_REGISTER(rFP) # get pointer to result JType + ld a0, 0(a0) # a0 <- result.j + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a2 # vAA <- result + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_move_result_object: /* 0x0c */ +/* File: mips64/op_move_result_object.S */ +/* File: mips64/op_move_result.S */ + /* for: move-result, move-result-object */ + /* op vAA */ + srl a2, rINST, 8 # a2 <- AA + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + ld a0, OFF_FP_RESULT_REGISTER(rFP) # get pointer to result JType + lw a0, 0(a0) # a0 <- result.i + GET_INST_OPCODE v0 # extract opcode from rINST + .if 1 + SET_VREG_OBJECT a0, a2 # vAA <- result + .else + SET_VREG a0, a2 # vAA <- result + .endif + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_move_exception: /* 0x0d */ +/* File: mips64/op_move_exception.S */ + /* move-exception vAA */ + srl a2, rINST, 8 # a2 <- AA + ld a0, THREAD_EXCEPTION_OFFSET(rSELF) # load exception obj + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + SET_VREG_OBJECT a0, a2 # vAA <- exception obj + GET_INST_OPCODE v0 # extract opcode from rINST + sd zero, THREAD_EXCEPTION_OFFSET(rSELF) # clear exception + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_return_void: /* 0x0e */ +/* File: mips64/op_return_void.S */ + .extern MterpThreadFenceForConstructor + .extern MterpSuspendCheck + jal MterpThreadFenceForConstructor + lw ra, THREAD_FLAGS_OFFSET(rSELF) + move a0, rSELF + and ra, ra, (THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + beqzc ra, 1f + jal MterpSuspendCheck # (self) +1: + li a0, 0 + b MterpReturn + +/* ------------------------------ */ + .balign 128 +.L_op_return: /* 0x0f */ +/* File: mips64/op_return.S */ + /* + * Return a 32-bit value. + * + * for: return, return-object + */ + /* op vAA */ + .extern MterpThreadFenceForConstructor + .extern MterpSuspendCheck + jal MterpThreadFenceForConstructor + lw ra, THREAD_FLAGS_OFFSET(rSELF) + move a0, rSELF + and ra, ra, (THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + beqzc ra, 1f + jal MterpSuspendCheck # (self) +1: + srl a2, rINST, 8 # a2 <- AA + GET_VREG_U a0, a2 # a0 <- vAA + b MterpReturn + +/* ------------------------------ */ + .balign 128 +.L_op_return_wide: /* 0x10 */ +/* File: mips64/op_return_wide.S */ + /* + * Return a 64-bit value. + */ + /* return-wide vAA */ + /* op vAA */ + .extern MterpThreadFenceForConstructor + .extern MterpSuspendCheck + jal MterpThreadFenceForConstructor + lw ra, THREAD_FLAGS_OFFSET(rSELF) + move a0, rSELF + and ra, ra, (THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + beqzc ra, 1f + jal MterpSuspendCheck # (self) +1: + srl a2, rINST, 8 # a2 <- AA + GET_VREG_WIDE a0, a2 # a0 <- vAA + b MterpReturn + +/* ------------------------------ */ + .balign 128 +.L_op_return_object: /* 0x11 */ +/* File: mips64/op_return_object.S */ +/* File: mips64/op_return.S */ + /* + * Return a 32-bit value. + * + * for: return, return-object + */ + /* op vAA */ + .extern MterpThreadFenceForConstructor + .extern MterpSuspendCheck + jal MterpThreadFenceForConstructor + lw ra, THREAD_FLAGS_OFFSET(rSELF) + move a0, rSELF + and ra, ra, (THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + beqzc ra, 1f + jal MterpSuspendCheck # (self) +1: + srl a2, rINST, 8 # a2 <- AA + GET_VREG_U a0, a2 # a0 <- vAA + b MterpReturn + + +/* ------------------------------ */ + .balign 128 +.L_op_const_4: /* 0x12 */ +/* File: mips64/op_const_4.S */ + /* const/4 vA, #+B */ + ext a2, rINST, 8, 4 # a2 <- A + seh a0, rINST # sign extend B in rINST + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + sra a0, a0, 12 # shift B into its final position + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vA <- +B + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_const_16: /* 0x13 */ +/* File: mips64/op_const_16.S */ + /* const/16 vAA, #+BBBB */ + srl a2, rINST, 8 # a2 <- AA + lh a0, 2(rPC) # a0 <- sign-extended BBBB + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vAA <- +BBBB + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_const: /* 0x14 */ +/* File: mips64/op_const.S */ + /* const vAA, #+BBBBbbbb */ + srl a2, rINST, 8 # a2 <- AA + lh a0, 2(rPC) # a0 <- bbbb (low) + lh a1, 4(rPC) # a1 <- BBBB (high) + FETCH_ADVANCE_INST 3 # advance rPC, load rINST + ins a0, a1, 16, 16 # a0 = BBBBbbbb + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vAA <- +BBBBbbbb + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_const_high16: /* 0x15 */ +/* File: mips64/op_const_high16.S */ + /* const/high16 vAA, #+BBBB0000 */ + srl a2, rINST, 8 # a2 <- AA + lh a0, 2(rPC) # a0 <- BBBB + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + sll a0, a0, 16 # a0 <- BBBB0000 + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vAA <- +BBBB0000 + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_const_wide_16: /* 0x16 */ +/* File: mips64/op_const_wide_16.S */ + /* const-wide/16 vAA, #+BBBB */ + srl a2, rINST, 8 # a2 <- AA + lh a0, 2(rPC) # a0 <- sign-extended BBBB + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a2 # vAA <- +BBBB + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_const_wide_32: /* 0x17 */ +/* File: mips64/op_const_wide_32.S */ + /* const-wide/32 vAA, #+BBBBbbbb */ + srl a2, rINST, 8 # a2 <- AA + lh a0, 2(rPC) # a0 <- bbbb (low) + lh a1, 4(rPC) # a1 <- BBBB (high) + FETCH_ADVANCE_INST 3 # advance rPC, load rINST + ins a0, a1, 16, 16 # a0 = BBBBbbbb + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a2 # vAA <- +BBBBbbbb + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_const_wide: /* 0x18 */ +/* File: mips64/op_const_wide.S */ + /* const-wide vAA, #+HHHHhhhhBBBBbbbb */ + srl a4, rINST, 8 # a4 <- AA + lh a0, 2(rPC) # a0 <- bbbb (low) + lh a1, 4(rPC) # a1 <- BBBB (low middle) + lh a2, 6(rPC) # a2 <- hhhh (high middle) + lh a3, 8(rPC) # a3 <- HHHH (high) + FETCH_ADVANCE_INST 5 # advance rPC, load rINST + ins a0, a1, 16, 16 # a0 = BBBBbbbb + ins a2, a3, 16, 16 # a2 = HHHHhhhh + dinsu a0, a2, 32, 32 # a0 = HHHHhhhhBBBBbbbb + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a4 # vAA <- +HHHHhhhhBBBBbbbb + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_const_wide_high16: /* 0x19 */ +/* File: mips64/op_const_wide_high16.S */ + /* const-wide/high16 vAA, #+BBBB000000000000 */ + srl a2, rINST, 8 # a2 <- AA + lh a0, 2(rPC) # a0 <- BBBB + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + dsll32 a0, a0, 16 # a0 <- BBBB000000000000 + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a2 # vAA <- +BBBB000000000000 + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_const_string: /* 0x1a */ +/* File: mips64/op_const_string.S */ + /* const/string vAA, String//BBBB */ + .extern MterpConstString + EXPORT_PC + lhu a0, 2(rPC) # a0 <- BBBB + srl a1, rINST, 8 # a1 <- AA + daddu a2, rFP, OFF_FP_SHADOWFRAME + move a3, rSELF + jal MterpConstString # (index, tgt_reg, shadow_frame, self) + PREFETCH_INST 2 # load rINST + bnez v0, MterpPossibleException # let reference interpreter deal with it. + ADVANCE 2 # advance rPC + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_const_string_jumbo: /* 0x1b */ +/* File: mips64/op_const_string_jumbo.S */ + /* const/string vAA, String//BBBBBBBB */ + .extern MterpConstString + EXPORT_PC + lh a0, 2(rPC) # a0 <- bbbb (low) + lh a4, 4(rPC) # a4 <- BBBB (high) + srl a1, rINST, 8 # a1 <- AA + ins a0, a4, 16, 16 # a0 <- BBBBbbbb + daddu a2, rFP, OFF_FP_SHADOWFRAME + move a3, rSELF + jal MterpConstString # (index, tgt_reg, shadow_frame, self) + PREFETCH_INST 3 # load rINST + bnez v0, MterpPossibleException # let reference interpreter deal with it. + ADVANCE 3 # advance rPC + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_const_class: /* 0x1c */ +/* File: mips64/op_const_class.S */ + /* const/class vAA, Class//BBBB */ + .extern MterpConstClass + EXPORT_PC + lhu a0, 2(rPC) # a0 <- BBBB + srl a1, rINST, 8 # a1 <- AA + daddu a2, rFP, OFF_FP_SHADOWFRAME + move a3, rSELF + jal MterpConstClass # (index, tgt_reg, shadow_frame, self) + PREFETCH_INST 2 # load rINST + bnez v0, MterpPossibleException # let reference interpreter deal with it. + ADVANCE 2 # advance rPC + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_monitor_enter: /* 0x1d */ +/* File: mips64/op_monitor_enter.S */ + /* + * Synchronize on an object. + */ + /* monitor-enter vAA */ + .extern artLockObjectFromCode + EXPORT_PC + srl a2, rINST, 8 # a2 <- AA + GET_VREG_U a0, a2 # a0 <- vAA (object) + move a1, rSELF # a1 <- self + jal artLockObjectFromCode + bnezc v0, MterpException + FETCH_ADVANCE_INST 1 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_monitor_exit: /* 0x1e */ +/* File: mips64/op_monitor_exit.S */ + /* + * Unlock an object. + * + * Exceptions that occur when unlocking a monitor need to appear as + * if they happened at the following instruction. See the Dalvik + * instruction spec. + */ + /* monitor-exit vAA */ + .extern artUnlockObjectFromCode + EXPORT_PC + srl a2, rINST, 8 # a2 <- AA + GET_VREG_U a0, a2 # a0 <- vAA (object) + move a1, rSELF # a1 <- self + jal artUnlockObjectFromCode # v0 <- success for unlock(self, obj) + bnezc v0, MterpException + FETCH_ADVANCE_INST 1 # before throw: advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_check_cast: /* 0x1f */ +/* File: mips64/op_check_cast.S */ + /* + * Check to see if a cast from one class to another is allowed. + */ + /* check-cast vAA, class//BBBB */ + .extern MterpCheckCast + EXPORT_PC + lhu a0, 2(rPC) # a0 <- BBBB + srl a1, rINST, 8 # a1 <- AA + dlsa a1, a1, rFP, 2 # a1 <- &object + ld a2, OFF_FP_METHOD(rFP) # a2 <- method + move a3, rSELF # a3 <- self + jal MterpCheckCast # (index, &obj, method, self) + PREFETCH_INST 2 + bnez v0, MterpPossibleException + ADVANCE 2 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_instance_of: /* 0x20 */ +/* File: mips64/op_instance_of.S */ + /* + * Check to see if an object reference is an instance of a class. + * + * Most common situation is a non-null object, being compared against + * an already-resolved class. + */ + /* instance-of vA, vB, class//CCCC */ + .extern MterpInstanceOf + EXPORT_PC + lhu a0, 2(rPC) # a0 <- CCCC + srl a1, rINST, 12 # a1 <- B + dlsa a1, a1, rFP, 2 # a1 <- &object + ld a2, OFF_FP_METHOD(rFP) # a2 <- method + move a3, rSELF # a3 <- self + jal MterpInstanceOf # (index, &obj, method, self) + ld a1, THREAD_EXCEPTION_OFFSET(rSELF) + ext a2, rINST, 8, 4 # a2 <- A + PREFETCH_INST 2 + bnez a1, MterpException + ADVANCE 2 # advance rPC + SET_VREG v0, a2 # vA <- v0 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_array_length: /* 0x21 */ +/* File: mips64/op_array_length.S */ + /* + * Return the length of an array. + */ + srl a1, rINST, 12 # a1 <- B + GET_VREG_U a0, a1 # a0 <- vB (object ref) + ext a2, rINST, 8, 4 # a2 <- A + beqz a0, common_errNullObject # yup, fail + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + lw a3, MIRROR_ARRAY_LENGTH_OFFSET(a0) # a3 <- array length + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a3, a2 # vB <- length + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_new_instance: /* 0x22 */ +/* File: mips64/op_new_instance.S */ + /* + * Create a new instance of a class. + */ + /* new-instance vAA, class//BBBB */ + .extern MterpNewInstance + EXPORT_PC + daddu a0, rFP, OFF_FP_SHADOWFRAME + move a1, rSELF + move a2, rINST + jal MterpNewInstance # (shadow_frame, self, inst_data) + beqzc v0, MterpPossibleException + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_new_array: /* 0x23 */ +/* File: mips64/op_new_array.S */ + /* + * Allocate an array of objects, specified with the array class + * and a count. + * + * The verifier guarantees that this is an array class, so we don't + * check for it here. + */ + /* new-array vA, vB, class//CCCC */ + .extern MterpNewArray + EXPORT_PC + daddu a0, rFP, OFF_FP_SHADOWFRAME + move a1, rPC + move a2, rINST + move a3, rSELF + jal MterpNewArray + beqzc v0, MterpPossibleException + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_filled_new_array: /* 0x24 */ +/* File: mips64/op_filled_new_array.S */ + /* + * Create a new array with elements filled from registers. + * + * for: filled-new-array, filled-new-array/range + */ + /* op vB, {vD, vE, vF, vG, vA}, class//CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, type//BBBB */ + .extern MterpFilledNewArray + EXPORT_PC + daddu a0, rFP, OFF_FP_SHADOWFRAME + move a1, rPC + move a2, rSELF + jal MterpFilledNewArray + beqzc v0, MterpPossibleException + FETCH_ADVANCE_INST 3 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_filled_new_array_range: /* 0x25 */ +/* File: mips64/op_filled_new_array_range.S */ +/* File: mips64/op_filled_new_array.S */ + /* + * Create a new array with elements filled from registers. + * + * for: filled-new-array, filled-new-array/range + */ + /* op vB, {vD, vE, vF, vG, vA}, class//CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, type//BBBB */ + .extern MterpFilledNewArrayRange + EXPORT_PC + daddu a0, rFP, OFF_FP_SHADOWFRAME + move a1, rPC + move a2, rSELF + jal MterpFilledNewArrayRange + beqzc v0, MterpPossibleException + FETCH_ADVANCE_INST 3 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_fill_array_data: /* 0x26 */ +/* File: mips64/op_fill_array_data.S */ + /* fill-array-data vAA, +BBBBBBBB */ + .extern MterpFillArrayData + EXPORT_PC + lh a1, 2(rPC) # a1 <- bbbb (lo) + lh a0, 4(rPC) # a0 <- BBBB (hi) + srl a3, rINST, 8 # a3 <- AA + ins a1, a0, 16, 16 # a1 <- BBBBbbbb + GET_VREG_U a0, a3 # a0 <- vAA (array object) + dlsa a1, a1, rPC, 1 # a1 <- PC + BBBBbbbb*2 (array data off.) + jal MterpFillArrayData # (obj, payload) + beqzc v0, MterpPossibleException # exception? + FETCH_ADVANCE_INST 3 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_throw: /* 0x27 */ +/* File: mips64/op_throw.S */ + /* + * Throw an exception object in the current thread. + */ + /* throw vAA */ + EXPORT_PC + srl a2, rINST, 8 # a2 <- AA + GET_VREG_U a0, a2 # a0 <- vAA (exception object) + beqzc a0, common_errNullObject + sd a0, THREAD_EXCEPTION_OFFSET(rSELF) # thread->exception <- obj + b MterpException + +/* ------------------------------ */ + .balign 128 +.L_op_goto: /* 0x28 */ +/* File: mips64/op_goto.S */ + /* + * Unconditional branch, 8-bit offset. + * + * The branch distance is a signed code-unit offset, which we need to + * double to get a byte offset. + */ + /* goto +AA */ + srl a0, rINST, 8 + seb a0, a0 # a0 <- sign-extended AA + dlsa rPC, a0, rPC, 1 # rPC <- rPC + AA * 2 + FETCH_INST # load rINST + +#if MTERP_SUSPEND + bgez a0, 1f # AA * 2 >= 0 => no suspend check + REFRESH_IBASE +1: +#else + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + bltz a0, MterpCheckSuspendAndContinue +#endif + + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_goto_16: /* 0x29 */ +/* File: mips64/op_goto_16.S */ + /* + * Unconditional branch, 16-bit offset. + * + * The branch distance is a signed code-unit offset, which we need to + * double to get a byte offset. + */ + /* goto/16 +AAAA */ + lh a0, 2(rPC) # a0 <- sign-extended AAAA + dlsa rPC, a0, rPC, 1 # rPC <- rPC + AAAA * 2 + FETCH_INST # load rINST + +#if MTERP_SUSPEND + bgez a0, 1f # AA * 2 >= 0 => no suspend check + REFRESH_IBASE +1: +#else + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + bltz a0, MterpCheckSuspendAndContinue +#endif + + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_goto_32: /* 0x2a */ +/* File: mips64/op_goto_32.S */ + /* + * Unconditional branch, 32-bit offset. + * + * The branch distance is a signed code-unit offset, which we need to + * double to get a byte offset. + * + * Unlike most opcodes, this one is allowed to branch to itself, so + * our "backward branch" test must be "<=0" instead of "<0". + */ + /* goto/32 +AAAAAAAA */ + lh a0, 2(rPC) # a0 <- aaaa (low) + lh a1, 4(rPC) # a1 <- AAAA (high) + ins a0, a1, 16, 16 # a0 = sign-extended AAAAaaaa + dlsa rPC, a0, rPC, 1 # rPC <- rPC + AAAAAAAA * 2 + FETCH_INST # load rINST + +#if MTERP_SUSPEND + bgtz a0, 1f # AA * 2 > 0 => no suspend check + REFRESH_IBASE +1: +#else + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + blez a0, MterpCheckSuspendAndContinue +#endif + + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_packed_switch: /* 0x2b */ +/* File: mips64/op_packed_switch.S */ + /* + * Handle a packed-switch or sparse-switch instruction. In both cases + * we decode it and hand it off to a helper function. + * + * We don't really expect backward branches in a switch statement, but + * they're perfectly legal, so we check for them here. + * + * for: packed-switch, sparse-switch + */ + /* op vAA, +BBBBBBBB */ + .extern MterpDoPackedSwitch + lh a0, 2(rPC) # a0 <- bbbb (lo) + lh a1, 4(rPC) # a1 <- BBBB (hi) + srl a3, rINST, 8 # a3 <- AA + ins a0, a1, 16, 16 # a0 <- BBBBbbbb + GET_VREG a1, a3 # a1 <- vAA + dlsa a0, a0, rPC, 1 # a0 <- PC + BBBBbbbb*2 + jal MterpDoPackedSwitch # v0 <- code-unit branch offset + dlsa rPC, v0, rPC, 1 # rPC <- rPC + offset * 2 + FETCH_INST # load rINST +#if MTERP_SUSPEND + bgtz v0, 1f # offset * 2 > 0 => no suspend check + REFRESH_IBASE +1: +#else + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + blez v0, MterpCheckSuspendAndContinue +#endif + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_sparse_switch: /* 0x2c */ +/* File: mips64/op_sparse_switch.S */ +/* File: mips64/op_packed_switch.S */ + /* + * Handle a packed-switch or sparse-switch instruction. In both cases + * we decode it and hand it off to a helper function. + * + * We don't really expect backward branches in a switch statement, but + * they're perfectly legal, so we check for them here. + * + * for: packed-switch, sparse-switch + */ + /* op vAA, +BBBBBBBB */ + .extern MterpDoSparseSwitch + lh a0, 2(rPC) # a0 <- bbbb (lo) + lh a1, 4(rPC) # a1 <- BBBB (hi) + srl a3, rINST, 8 # a3 <- AA + ins a0, a1, 16, 16 # a0 <- BBBBbbbb + GET_VREG a1, a3 # a1 <- vAA + dlsa a0, a0, rPC, 1 # a0 <- PC + BBBBbbbb*2 + jal MterpDoSparseSwitch # v0 <- code-unit branch offset + dlsa rPC, v0, rPC, 1 # rPC <- rPC + offset * 2 + FETCH_INST # load rINST +#if MTERP_SUSPEND + bgtz v0, 1f # offset * 2 > 0 => no suspend check + REFRESH_IBASE +1: +#else + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + blez v0, MterpCheckSuspendAndContinue +#endif + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_cmpl_float: /* 0x2d */ +/* File: mips64/op_cmpl_float.S */ +/* File: mips64/fcmp.S */ + /* + * Compare two floating-point values. Puts 0, 1, or -1 into the + * destination register based on the results of the comparison. + * + * For: cmpl-float, cmpg-float + */ + /* op vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG_FLOAT f0, a2 # f0 <- vBB + GET_VREG_FLOAT f1, a3 # f1 <- vCC + cmp.eq.s f2, f0, f1 + li a0, 0 + bc1nez f2, 1f # done if vBB == vCC (ordered) + .if 0 + cmp.lt.s f2, f0, f1 + li a0, -1 + bc1nez f2, 1f # done if vBB < vCC (ordered) + li a0, 1 # vBB > vCC or unordered + .else + cmp.lt.s f2, f1, f0 + li a0, 1 + bc1nez f2, 1f # done if vBB > vCC (ordered) + li a0, -1 # vBB < vCC or unordered + .endif +1: + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a4 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_cmpg_float: /* 0x2e */ +/* File: mips64/op_cmpg_float.S */ +/* File: mips64/fcmp.S */ + /* + * Compare two floating-point values. Puts 0, 1, or -1 into the + * destination register based on the results of the comparison. + * + * For: cmpl-float, cmpg-float + */ + /* op vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG_FLOAT f0, a2 # f0 <- vBB + GET_VREG_FLOAT f1, a3 # f1 <- vCC + cmp.eq.s f2, f0, f1 + li a0, 0 + bc1nez f2, 1f # done if vBB == vCC (ordered) + .if 1 + cmp.lt.s f2, f0, f1 + li a0, -1 + bc1nez f2, 1f # done if vBB < vCC (ordered) + li a0, 1 # vBB > vCC or unordered + .else + cmp.lt.s f2, f1, f0 + li a0, 1 + bc1nez f2, 1f # done if vBB > vCC (ordered) + li a0, -1 # vBB < vCC or unordered + .endif +1: + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a4 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_cmpl_double: /* 0x2f */ +/* File: mips64/op_cmpl_double.S */ +/* File: mips64/fcmpWide.S */ + /* + * Compare two floating-point values. Puts 0, 1, or -1 into the + * destination register based on the results of the comparison. + * + * For: cmpl-double, cmpg-double + */ + /* op vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG_DOUBLE f0, a2 # f0 <- vBB + GET_VREG_DOUBLE f1, a3 # f1 <- vCC + cmp.eq.d f2, f0, f1 + li a0, 0 + bc1nez f2, 1f # done if vBB == vCC (ordered) + .if 0 + cmp.lt.d f2, f0, f1 + li a0, -1 + bc1nez f2, 1f # done if vBB < vCC (ordered) + li a0, 1 # vBB > vCC or unordered + .else + cmp.lt.d f2, f1, f0 + li a0, 1 + bc1nez f2, 1f # done if vBB > vCC (ordered) + li a0, -1 # vBB < vCC or unordered + .endif +1: + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a4 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_cmpg_double: /* 0x30 */ +/* File: mips64/op_cmpg_double.S */ +/* File: mips64/fcmpWide.S */ + /* + * Compare two floating-point values. Puts 0, 1, or -1 into the + * destination register based on the results of the comparison. + * + * For: cmpl-double, cmpg-double + */ + /* op vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG_DOUBLE f0, a2 # f0 <- vBB + GET_VREG_DOUBLE f1, a3 # f1 <- vCC + cmp.eq.d f2, f0, f1 + li a0, 0 + bc1nez f2, 1f # done if vBB == vCC (ordered) + .if 1 + cmp.lt.d f2, f0, f1 + li a0, -1 + bc1nez f2, 1f # done if vBB < vCC (ordered) + li a0, 1 # vBB > vCC or unordered + .else + cmp.lt.d f2, f1, f0 + li a0, 1 + bc1nez f2, 1f # done if vBB > vCC (ordered) + li a0, -1 # vBB < vCC or unordered + .endif +1: + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a4 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_cmp_long: /* 0x31 */ +/* File: mips64/op_cmp_long.S */ + /* cmp-long vAA, vBB, vCC */ + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + srl a4, rINST, 8 # a4 <- AA + GET_VREG_WIDE a0, a2 # a0 <- vBB + GET_VREG_WIDE a1, a3 # a1 <- vCC + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + slt a2, a0, a1 + slt a0, a1, a0 + subu a0, a0, a2 + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a4 # vAA <- result + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_if_eq: /* 0x32 */ +/* File: mips64/op_if_eq.S */ +/* File: mips64/bincmp.S */ + /* + * Generic two-operand compare-and-branch operation. Provide a "condition" + * fragment that specifies the comparison to perform, e.g. for + * "if-le" you would use "le". + * + * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le + */ + /* if-cmp vA, vB, +CCCC */ + lh a4, 2(rPC) # a4 <- sign-extended CCCC + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG a0, a2 # a0 <- vA + GET_VREG a1, a3 # a1 <- vB + + beqc a0, a1, 1f + li a4, 2 # offset if branch not taken +1: + + dlsa rPC, a4, rPC, 1 # rPC <- rPC + CCCC * 2 + FETCH_INST # load rINST + +#if MTERP_SUSPEND + bgez a4, 2f # CCCC * 2 >= 0 => no suspend check + REFRESH_IBASE +2: +#else + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + bltz a4, MterpCheckSuspendAndContinue +#endif + + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_if_ne: /* 0x33 */ +/* File: mips64/op_if_ne.S */ +/* File: mips64/bincmp.S */ + /* + * Generic two-operand compare-and-branch operation. Provide a "condition" + * fragment that specifies the comparison to perform, e.g. for + * "if-le" you would use "le". + * + * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le + */ + /* if-cmp vA, vB, +CCCC */ + lh a4, 2(rPC) # a4 <- sign-extended CCCC + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG a0, a2 # a0 <- vA + GET_VREG a1, a3 # a1 <- vB + + bnec a0, a1, 1f + li a4, 2 # offset if branch not taken +1: + + dlsa rPC, a4, rPC, 1 # rPC <- rPC + CCCC * 2 + FETCH_INST # load rINST + +#if MTERP_SUSPEND + bgez a4, 2f # CCCC * 2 >= 0 => no suspend check + REFRESH_IBASE +2: +#else + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + bltz a4, MterpCheckSuspendAndContinue +#endif + + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_if_lt: /* 0x34 */ +/* File: mips64/op_if_lt.S */ +/* File: mips64/bincmp.S */ + /* + * Generic two-operand compare-and-branch operation. Provide a "condition" + * fragment that specifies the comparison to perform, e.g. for + * "if-le" you would use "le". + * + * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le + */ + /* if-cmp vA, vB, +CCCC */ + lh a4, 2(rPC) # a4 <- sign-extended CCCC + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG a0, a2 # a0 <- vA + GET_VREG a1, a3 # a1 <- vB + + bltc a0, a1, 1f + li a4, 2 # offset if branch not taken +1: + + dlsa rPC, a4, rPC, 1 # rPC <- rPC + CCCC * 2 + FETCH_INST # load rINST + +#if MTERP_SUSPEND + bgez a4, 2f # CCCC * 2 >= 0 => no suspend check + REFRESH_IBASE +2: +#else + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + bltz a4, MterpCheckSuspendAndContinue +#endif + + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_if_ge: /* 0x35 */ +/* File: mips64/op_if_ge.S */ +/* File: mips64/bincmp.S */ + /* + * Generic two-operand compare-and-branch operation. Provide a "condition" + * fragment that specifies the comparison to perform, e.g. for + * "if-le" you would use "le". + * + * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le + */ + /* if-cmp vA, vB, +CCCC */ + lh a4, 2(rPC) # a4 <- sign-extended CCCC + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG a0, a2 # a0 <- vA + GET_VREG a1, a3 # a1 <- vB + + bgec a0, a1, 1f + li a4, 2 # offset if branch not taken +1: + + dlsa rPC, a4, rPC, 1 # rPC <- rPC + CCCC * 2 + FETCH_INST # load rINST + +#if MTERP_SUSPEND + bgez a4, 2f # CCCC * 2 >= 0 => no suspend check + REFRESH_IBASE +2: +#else + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + bltz a4, MterpCheckSuspendAndContinue +#endif + + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_if_gt: /* 0x36 */ +/* File: mips64/op_if_gt.S */ +/* File: mips64/bincmp.S */ + /* + * Generic two-operand compare-and-branch operation. Provide a "condition" + * fragment that specifies the comparison to perform, e.g. for + * "if-le" you would use "le". + * + * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le + */ + /* if-cmp vA, vB, +CCCC */ + lh a4, 2(rPC) # a4 <- sign-extended CCCC + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG a0, a2 # a0 <- vA + GET_VREG a1, a3 # a1 <- vB + + bgtc a0, a1, 1f + li a4, 2 # offset if branch not taken +1: + + dlsa rPC, a4, rPC, 1 # rPC <- rPC + CCCC * 2 + FETCH_INST # load rINST + +#if MTERP_SUSPEND + bgez a4, 2f # CCCC * 2 >= 0 => no suspend check + REFRESH_IBASE +2: +#else + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + bltz a4, MterpCheckSuspendAndContinue +#endif + + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_if_le: /* 0x37 */ +/* File: mips64/op_if_le.S */ +/* File: mips64/bincmp.S */ + /* + * Generic two-operand compare-and-branch operation. Provide a "condition" + * fragment that specifies the comparison to perform, e.g. for + * "if-le" you would use "le". + * + * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le + */ + /* if-cmp vA, vB, +CCCC */ + lh a4, 2(rPC) # a4 <- sign-extended CCCC + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG a0, a2 # a0 <- vA + GET_VREG a1, a3 # a1 <- vB + + blec a0, a1, 1f + li a4, 2 # offset if branch not taken +1: + + dlsa rPC, a4, rPC, 1 # rPC <- rPC + CCCC * 2 + FETCH_INST # load rINST + +#if MTERP_SUSPEND + bgez a4, 2f # CCCC * 2 >= 0 => no suspend check + REFRESH_IBASE +2: +#else + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + bltz a4, MterpCheckSuspendAndContinue +#endif + + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_if_eqz: /* 0x38 */ +/* File: mips64/op_if_eqz.S */ +/* File: mips64/zcmp.S */ + /* + * Generic one-operand compare-and-branch operation. Provide a "condition" + * fragment that specifies the comparison to perform, e.g. for + * "if-lez" you would use "le". + * + * For: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez + */ + /* if-cmp vAA, +BBBB */ + lh a4, 2(rPC) # a4 <- sign-extended BBBB + srl a2, rINST, 8 # a2 <- AA + GET_VREG a0, a2 # a0 <- vAA + + beqzc a0, 1f + li a4, 2 # offset if branch not taken +1: + + dlsa rPC, a4, rPC, 1 # rPC <- rPC + BBBB * 2 + FETCH_INST # load rINST + +#if MTERP_SUSPEND + bgez a4, 2f # BBBB * 2 >= 0 => no suspend check + REFRESH_IBASE +2: +#else + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + bltz a4, MterpCheckSuspendAndContinue +#endif + + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_if_nez: /* 0x39 */ +/* File: mips64/op_if_nez.S */ +/* File: mips64/zcmp.S */ + /* + * Generic one-operand compare-and-branch operation. Provide a "condition" + * fragment that specifies the comparison to perform, e.g. for + * "if-lez" you would use "le". + * + * For: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez + */ + /* if-cmp vAA, +BBBB */ + lh a4, 2(rPC) # a4 <- sign-extended BBBB + srl a2, rINST, 8 # a2 <- AA + GET_VREG a0, a2 # a0 <- vAA + + bnezc a0, 1f + li a4, 2 # offset if branch not taken +1: + + dlsa rPC, a4, rPC, 1 # rPC <- rPC + BBBB * 2 + FETCH_INST # load rINST + +#if MTERP_SUSPEND + bgez a4, 2f # BBBB * 2 >= 0 => no suspend check + REFRESH_IBASE +2: +#else + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + bltz a4, MterpCheckSuspendAndContinue +#endif + + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_if_ltz: /* 0x3a */ +/* File: mips64/op_if_ltz.S */ +/* File: mips64/zcmp.S */ + /* + * Generic one-operand compare-and-branch operation. Provide a "condition" + * fragment that specifies the comparison to perform, e.g. for + * "if-lez" you would use "le". + * + * For: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez + */ + /* if-cmp vAA, +BBBB */ + lh a4, 2(rPC) # a4 <- sign-extended BBBB + srl a2, rINST, 8 # a2 <- AA + GET_VREG a0, a2 # a0 <- vAA + + bltzc a0, 1f + li a4, 2 # offset if branch not taken +1: + + dlsa rPC, a4, rPC, 1 # rPC <- rPC + BBBB * 2 + FETCH_INST # load rINST + +#if MTERP_SUSPEND + bgez a4, 2f # BBBB * 2 >= 0 => no suspend check + REFRESH_IBASE +2: +#else + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + bltz a4, MterpCheckSuspendAndContinue +#endif + + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_if_gez: /* 0x3b */ +/* File: mips64/op_if_gez.S */ +/* File: mips64/zcmp.S */ + /* + * Generic one-operand compare-and-branch operation. Provide a "condition" + * fragment that specifies the comparison to perform, e.g. for + * "if-lez" you would use "le". + * + * For: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez + */ + /* if-cmp vAA, +BBBB */ + lh a4, 2(rPC) # a4 <- sign-extended BBBB + srl a2, rINST, 8 # a2 <- AA + GET_VREG a0, a2 # a0 <- vAA + + bgezc a0, 1f + li a4, 2 # offset if branch not taken +1: + + dlsa rPC, a4, rPC, 1 # rPC <- rPC + BBBB * 2 + FETCH_INST # load rINST + +#if MTERP_SUSPEND + bgez a4, 2f # BBBB * 2 >= 0 => no suspend check + REFRESH_IBASE +2: +#else + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + bltz a4, MterpCheckSuspendAndContinue +#endif + + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_if_gtz: /* 0x3c */ +/* File: mips64/op_if_gtz.S */ +/* File: mips64/zcmp.S */ + /* + * Generic one-operand compare-and-branch operation. Provide a "condition" + * fragment that specifies the comparison to perform, e.g. for + * "if-lez" you would use "le". + * + * For: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez + */ + /* if-cmp vAA, +BBBB */ + lh a4, 2(rPC) # a4 <- sign-extended BBBB + srl a2, rINST, 8 # a2 <- AA + GET_VREG a0, a2 # a0 <- vAA + + bgtzc a0, 1f + li a4, 2 # offset if branch not taken +1: + + dlsa rPC, a4, rPC, 1 # rPC <- rPC + BBBB * 2 + FETCH_INST # load rINST + +#if MTERP_SUSPEND + bgez a4, 2f # BBBB * 2 >= 0 => no suspend check + REFRESH_IBASE +2: +#else + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + bltz a4, MterpCheckSuspendAndContinue +#endif + + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_if_lez: /* 0x3d */ +/* File: mips64/op_if_lez.S */ +/* File: mips64/zcmp.S */ + /* + * Generic one-operand compare-and-branch operation. Provide a "condition" + * fragment that specifies the comparison to perform, e.g. for + * "if-lez" you would use "le". + * + * For: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez + */ + /* if-cmp vAA, +BBBB */ + lh a4, 2(rPC) # a4 <- sign-extended BBBB + srl a2, rINST, 8 # a2 <- AA + GET_VREG a0, a2 # a0 <- vAA + + blezc a0, 1f + li a4, 2 # offset if branch not taken +1: + + dlsa rPC, a4, rPC, 1 # rPC <- rPC + BBBB * 2 + FETCH_INST # load rINST + +#if MTERP_SUSPEND + bgez a4, 2f # BBBB * 2 >= 0 => no suspend check + REFRESH_IBASE +2: +#else + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + bltz a4, MterpCheckSuspendAndContinue +#endif + + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_3e: /* 0x3e */ +/* File: mips64/op_unused_3e.S */ +/* File: mips64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + b MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_3f: /* 0x3f */ +/* File: mips64/op_unused_3f.S */ +/* File: mips64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + b MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_40: /* 0x40 */ +/* File: mips64/op_unused_40.S */ +/* File: mips64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + b MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_41: /* 0x41 */ +/* File: mips64/op_unused_41.S */ +/* File: mips64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + b MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_42: /* 0x42 */ +/* File: mips64/op_unused_42.S */ +/* File: mips64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + b MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_43: /* 0x43 */ +/* File: mips64/op_unused_43.S */ +/* File: mips64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + b MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_aget: /* 0x44 */ +/* File: mips64/op_aget.S */ + /* + * Array get, 32 bits or less. vAA <- vBB[vCC]. + * + * for: aget, aget-boolean, aget-byte, aget-char, aget-short + * + * NOTE: assumes data offset for arrays is the same for all non-wide types. + * If this changes, specialize. + */ + /* op vAA, vBB, vCC */ + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + srl a4, rINST, 8 # a4 <- AA + GET_VREG_U a0, a2 # a0 <- vBB (array object) + GET_VREG a1, a3 # a1 <- vCC (requested index) + beqz a0, common_errNullObject # bail if null array object + lw a3, MIRROR_ARRAY_LENGTH_OFFSET(a0) # a3 <- arrayObj->length + .if 2 + # [d]lsa does not support shift count of 0. + dlsa a0, a1, a0, 2 # a0 <- arrayObj + index*width + .else + daddu a0, a1, a0 # a0 <- arrayObj + index*width + .endif + bgeu a1, a3, common_errArrayIndex # unsigned compare: index >= length, bail + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + lw a2, MIRROR_INT_ARRAY_DATA_OFFSET(a0) # a2 <- vBB[vCC] + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a2, a4 # vAA <- a2 + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_aget_wide: /* 0x45 */ +/* File: mips64/op_aget_wide.S */ + /* + * Array get, 64 bits. vAA <- vBB[vCC]. + * + */ + /* aget-wide vAA, vBB, vCC */ + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + srl a4, rINST, 8 # a4 <- AA + GET_VREG_U a0, a2 # a0 <- vBB (array object) + GET_VREG a1, a3 # a1 <- vCC (requested index) + beqz a0, common_errNullObject # bail if null array object + lw a3, MIRROR_ARRAY_LENGTH_OFFSET(a0) # a3 <- arrayObj->length + dlsa a0, a1, a0, 3 # a0 <- arrayObj + index*width + bgeu a1, a3, common_errArrayIndex # unsigned compare: index >= length, bail + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + lw a2, MIRROR_WIDE_ARRAY_DATA_OFFSET(a0) + lw a3, (MIRROR_WIDE_ARRAY_DATA_OFFSET+4)(a0) + dinsu a2, a3, 32, 32 # a2 <- vBB[vCC] + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a2, a4 # vAA <- a2 + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_aget_object: /* 0x46 */ +/* File: mips64/op_aget_object.S */ + /* + * Array object get. vAA <- vBB[vCC]. + * + * for: aget-object + */ + /* op vAA, vBB, vCC */ + .extern artAGetObjectFromMterp + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + EXPORT_PC + GET_VREG_U a0, a2 # a0 <- vBB (array object) + GET_VREG a1, a3 # a1 <- vCC (requested index) + jal artAGetObjectFromMterp # (array, index) + ld a1, THREAD_EXCEPTION_OFFSET(rSELF) + srl a4, rINST, 8 # a4 <- AA + PREFETCH_INST 2 + bnez a1, MterpException + SET_VREG_OBJECT v0, a4 # vAA <- v0 + ADVANCE 2 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_aget_boolean: /* 0x47 */ +/* File: mips64/op_aget_boolean.S */ +/* File: mips64/op_aget.S */ + /* + * Array get, 32 bits or less. vAA <- vBB[vCC]. + * + * for: aget, aget-boolean, aget-byte, aget-char, aget-short + * + * NOTE: assumes data offset for arrays is the same for all non-wide types. + * If this changes, specialize. + */ + /* op vAA, vBB, vCC */ + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + srl a4, rINST, 8 # a4 <- AA + GET_VREG_U a0, a2 # a0 <- vBB (array object) + GET_VREG a1, a3 # a1 <- vCC (requested index) + beqz a0, common_errNullObject # bail if null array object + lw a3, MIRROR_ARRAY_LENGTH_OFFSET(a0) # a3 <- arrayObj->length + .if 0 + # [d]lsa does not support shift count of 0. + dlsa a0, a1, a0, 0 # a0 <- arrayObj + index*width + .else + daddu a0, a1, a0 # a0 <- arrayObj + index*width + .endif + bgeu a1, a3, common_errArrayIndex # unsigned compare: index >= length, bail + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + lbu a2, MIRROR_BOOLEAN_ARRAY_DATA_OFFSET(a0) # a2 <- vBB[vCC] + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a2, a4 # vAA <- a2 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_aget_byte: /* 0x48 */ +/* File: mips64/op_aget_byte.S */ +/* File: mips64/op_aget.S */ + /* + * Array get, 32 bits or less. vAA <- vBB[vCC]. + * + * for: aget, aget-boolean, aget-byte, aget-char, aget-short + * + * NOTE: assumes data offset for arrays is the same for all non-wide types. + * If this changes, specialize. + */ + /* op vAA, vBB, vCC */ + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + srl a4, rINST, 8 # a4 <- AA + GET_VREG_U a0, a2 # a0 <- vBB (array object) + GET_VREG a1, a3 # a1 <- vCC (requested index) + beqz a0, common_errNullObject # bail if null array object + lw a3, MIRROR_ARRAY_LENGTH_OFFSET(a0) # a3 <- arrayObj->length + .if 0 + # [d]lsa does not support shift count of 0. + dlsa a0, a1, a0, 0 # a0 <- arrayObj + index*width + .else + daddu a0, a1, a0 # a0 <- arrayObj + index*width + .endif + bgeu a1, a3, common_errArrayIndex # unsigned compare: index >= length, bail + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + lb a2, MIRROR_BYTE_ARRAY_DATA_OFFSET(a0) # a2 <- vBB[vCC] + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a2, a4 # vAA <- a2 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_aget_char: /* 0x49 */ +/* File: mips64/op_aget_char.S */ +/* File: mips64/op_aget.S */ + /* + * Array get, 32 bits or less. vAA <- vBB[vCC]. + * + * for: aget, aget-boolean, aget-byte, aget-char, aget-short + * + * NOTE: assumes data offset for arrays is the same for all non-wide types. + * If this changes, specialize. + */ + /* op vAA, vBB, vCC */ + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + srl a4, rINST, 8 # a4 <- AA + GET_VREG_U a0, a2 # a0 <- vBB (array object) + GET_VREG a1, a3 # a1 <- vCC (requested index) + beqz a0, common_errNullObject # bail if null array object + lw a3, MIRROR_ARRAY_LENGTH_OFFSET(a0) # a3 <- arrayObj->length + .if 1 + # [d]lsa does not support shift count of 0. + dlsa a0, a1, a0, 1 # a0 <- arrayObj + index*width + .else + daddu a0, a1, a0 # a0 <- arrayObj + index*width + .endif + bgeu a1, a3, common_errArrayIndex # unsigned compare: index >= length, bail + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + lhu a2, MIRROR_CHAR_ARRAY_DATA_OFFSET(a0) # a2 <- vBB[vCC] + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a2, a4 # vAA <- a2 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_aget_short: /* 0x4a */ +/* File: mips64/op_aget_short.S */ +/* File: mips64/op_aget.S */ + /* + * Array get, 32 bits or less. vAA <- vBB[vCC]. + * + * for: aget, aget-boolean, aget-byte, aget-char, aget-short + * + * NOTE: assumes data offset for arrays is the same for all non-wide types. + * If this changes, specialize. + */ + /* op vAA, vBB, vCC */ + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + srl a4, rINST, 8 # a4 <- AA + GET_VREG_U a0, a2 # a0 <- vBB (array object) + GET_VREG a1, a3 # a1 <- vCC (requested index) + beqz a0, common_errNullObject # bail if null array object + lw a3, MIRROR_ARRAY_LENGTH_OFFSET(a0) # a3 <- arrayObj->length + .if 1 + # [d]lsa does not support shift count of 0. + dlsa a0, a1, a0, 1 # a0 <- arrayObj + index*width + .else + daddu a0, a1, a0 # a0 <- arrayObj + index*width + .endif + bgeu a1, a3, common_errArrayIndex # unsigned compare: index >= length, bail + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + lh a2, MIRROR_SHORT_ARRAY_DATA_OFFSET(a0) # a2 <- vBB[vCC] + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a2, a4 # vAA <- a2 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_aput: /* 0x4b */ +/* File: mips64/op_aput.S */ + /* + * Array put, 32 bits or less. vBB[vCC] <- vAA. + * + * for: aput, aput-boolean, aput-byte, aput-char, aput-short + * + * NOTE: this assumes data offset for arrays is the same for all non-wide types. + * If this changes, specialize. + */ + /* op vAA, vBB, vCC */ + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + srl a4, rINST, 8 # a4 <- AA + GET_VREG_U a0, a2 # a0 <- vBB (array object) + GET_VREG a1, a3 # a1 <- vCC (requested index) + beqz a0, common_errNullObject # bail if null array object + lw a3, MIRROR_ARRAY_LENGTH_OFFSET(a0) # a3 <- arrayObj->length + .if 2 + # [d]lsa does not support shift count of 0. + dlsa a0, a1, a0, 2 # a0 <- arrayObj + index*width + .else + daddu a0, a1, a0 # a0 <- arrayObj + index*width + .endif + bgeu a1, a3, common_errArrayIndex # unsigned compare: index >= length, bail + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_VREG a2, a4 # a2 <- vAA + GET_INST_OPCODE v0 # extract opcode from rINST + sw a2, MIRROR_INT_ARRAY_DATA_OFFSET(a0) # vBB[vCC] <- a2 + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_aput_wide: /* 0x4c */ +/* File: mips64/op_aput_wide.S */ + /* + * Array put, 64 bits. vBB[vCC] <- vAA. + * + */ + /* aput-wide vAA, vBB, vCC */ + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + srl a4, rINST, 8 # a4 <- AA + GET_VREG_U a0, a2 # a0 <- vBB (array object) + GET_VREG a1, a3 # a1 <- vCC (requested index) + beqz a0, common_errNullObject # bail if null array object + lw a3, MIRROR_ARRAY_LENGTH_OFFSET(a0) # a3 <- arrayObj->length + dlsa a0, a1, a0, 3 # a0 <- arrayObj + index*width + bgeu a1, a3, common_errArrayIndex # unsigned compare: index >= length, bail + GET_VREG_WIDE a2, a4 # a2 <- vAA + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + sw a2, MIRROR_WIDE_ARRAY_DATA_OFFSET(a0) + dsrl32 a2, a2, 0 + sw a2, (MIRROR_WIDE_ARRAY_DATA_OFFSET+4)(a0) # vBB[vCC] <- a2 + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_aput_object: /* 0x4d */ +/* File: mips64/op_aput_object.S */ + /* + * Store an object into an array. vBB[vCC] <- vAA. + */ + /* op vAA, vBB, vCC */ + .extern MterpAputObject + EXPORT_PC + daddu a0, rFP, OFF_FP_SHADOWFRAME + move a1, rPC + move a2, rINST + jal MterpAputObject + beqzc v0, MterpPossibleException + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_aput_boolean: /* 0x4e */ +/* File: mips64/op_aput_boolean.S */ +/* File: mips64/op_aput.S */ + /* + * Array put, 32 bits or less. vBB[vCC] <- vAA. + * + * for: aput, aput-boolean, aput-byte, aput-char, aput-short + * + * NOTE: this assumes data offset for arrays is the same for all non-wide types. + * If this changes, specialize. + */ + /* op vAA, vBB, vCC */ + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + srl a4, rINST, 8 # a4 <- AA + GET_VREG_U a0, a2 # a0 <- vBB (array object) + GET_VREG a1, a3 # a1 <- vCC (requested index) + beqz a0, common_errNullObject # bail if null array object + lw a3, MIRROR_ARRAY_LENGTH_OFFSET(a0) # a3 <- arrayObj->length + .if 0 + # [d]lsa does not support shift count of 0. + dlsa a0, a1, a0, 0 # a0 <- arrayObj + index*width + .else + daddu a0, a1, a0 # a0 <- arrayObj + index*width + .endif + bgeu a1, a3, common_errArrayIndex # unsigned compare: index >= length, bail + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_VREG a2, a4 # a2 <- vAA + GET_INST_OPCODE v0 # extract opcode from rINST + sb a2, MIRROR_BOOLEAN_ARRAY_DATA_OFFSET(a0) # vBB[vCC] <- a2 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_aput_byte: /* 0x4f */ +/* File: mips64/op_aput_byte.S */ +/* File: mips64/op_aput.S */ + /* + * Array put, 32 bits or less. vBB[vCC] <- vAA. + * + * for: aput, aput-boolean, aput-byte, aput-char, aput-short + * + * NOTE: this assumes data offset for arrays is the same for all non-wide types. + * If this changes, specialize. + */ + /* op vAA, vBB, vCC */ + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + srl a4, rINST, 8 # a4 <- AA + GET_VREG_U a0, a2 # a0 <- vBB (array object) + GET_VREG a1, a3 # a1 <- vCC (requested index) + beqz a0, common_errNullObject # bail if null array object + lw a3, MIRROR_ARRAY_LENGTH_OFFSET(a0) # a3 <- arrayObj->length + .if 0 + # [d]lsa does not support shift count of 0. + dlsa a0, a1, a0, 0 # a0 <- arrayObj + index*width + .else + daddu a0, a1, a0 # a0 <- arrayObj + index*width + .endif + bgeu a1, a3, common_errArrayIndex # unsigned compare: index >= length, bail + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_VREG a2, a4 # a2 <- vAA + GET_INST_OPCODE v0 # extract opcode from rINST + sb a2, MIRROR_BYTE_ARRAY_DATA_OFFSET(a0) # vBB[vCC] <- a2 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_aput_char: /* 0x50 */ +/* File: mips64/op_aput_char.S */ +/* File: mips64/op_aput.S */ + /* + * Array put, 32 bits or less. vBB[vCC] <- vAA. + * + * for: aput, aput-boolean, aput-byte, aput-char, aput-short + * + * NOTE: this assumes data offset for arrays is the same for all non-wide types. + * If this changes, specialize. + */ + /* op vAA, vBB, vCC */ + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + srl a4, rINST, 8 # a4 <- AA + GET_VREG_U a0, a2 # a0 <- vBB (array object) + GET_VREG a1, a3 # a1 <- vCC (requested index) + beqz a0, common_errNullObject # bail if null array object + lw a3, MIRROR_ARRAY_LENGTH_OFFSET(a0) # a3 <- arrayObj->length + .if 1 + # [d]lsa does not support shift count of 0. + dlsa a0, a1, a0, 1 # a0 <- arrayObj + index*width + .else + daddu a0, a1, a0 # a0 <- arrayObj + index*width + .endif + bgeu a1, a3, common_errArrayIndex # unsigned compare: index >= length, bail + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_VREG a2, a4 # a2 <- vAA + GET_INST_OPCODE v0 # extract opcode from rINST + sh a2, MIRROR_CHAR_ARRAY_DATA_OFFSET(a0) # vBB[vCC] <- a2 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_aput_short: /* 0x51 */ +/* File: mips64/op_aput_short.S */ +/* File: mips64/op_aput.S */ + /* + * Array put, 32 bits or less. vBB[vCC] <- vAA. + * + * for: aput, aput-boolean, aput-byte, aput-char, aput-short + * + * NOTE: this assumes data offset for arrays is the same for all non-wide types. + * If this changes, specialize. + */ + /* op vAA, vBB, vCC */ + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + srl a4, rINST, 8 # a4 <- AA + GET_VREG_U a0, a2 # a0 <- vBB (array object) + GET_VREG a1, a3 # a1 <- vCC (requested index) + beqz a0, common_errNullObject # bail if null array object + lw a3, MIRROR_ARRAY_LENGTH_OFFSET(a0) # a3 <- arrayObj->length + .if 1 + # [d]lsa does not support shift count of 0. + dlsa a0, a1, a0, 1 # a0 <- arrayObj + index*width + .else + daddu a0, a1, a0 # a0 <- arrayObj + index*width + .endif + bgeu a1, a3, common_errArrayIndex # unsigned compare: index >= length, bail + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_VREG a2, a4 # a2 <- vAA + GET_INST_OPCODE v0 # extract opcode from rINST + sh a2, MIRROR_SHORT_ARRAY_DATA_OFFSET(a0) # vBB[vCC] <- a2 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_iget: /* 0x52 */ +/* File: mips64/op_iget.S */ + /* + * General instance field get. + * + * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short + */ + .extern artGet32InstanceFromCode + EXPORT_PC + lhu a0, 2(rPC) # a0 <- field ref CCCC + srl a1, rINST, 12 # a1 <- B + GET_VREG_U a1, a1 # a1 <- fp[B], the object pointer + ld a2, OFF_FP_METHOD(rFP) # a2 <- referrer + move a3, rSELF # a3 <- self + jal artGet32InstanceFromCode + ld a3, THREAD_EXCEPTION_OFFSET(rSELF) + ext a2, rINST, 8, 4 # a2 <- A + PREFETCH_INST 2 + bnez a3, MterpPossibleException # bail out + .if 0 + SET_VREG_OBJECT v0, a2 # fp[A] <- v0 + .else + SET_VREG v0, a2 # fp[A] <- v0 + .endif + ADVANCE 2 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_iget_wide: /* 0x53 */ +/* File: mips64/op_iget_wide.S */ + /* + * 64-bit instance field get. + * + * for: iget-wide + */ + .extern artGet64InstanceFromCode + EXPORT_PC + lhu a0, 2(rPC) # a0 <- field ref CCCC + srl a1, rINST, 12 # a1 <- B + GET_VREG_U a1, a1 # a1 <- fp[B], the object pointer + ld a2, OFF_FP_METHOD(rFP) # a2 <- referrer + move a3, rSELF # a3 <- self + jal artGet64InstanceFromCode + ld a3, THREAD_EXCEPTION_OFFSET(rSELF) + ext a2, rINST, 8, 4 # a2 <- A + PREFETCH_INST 2 + bnez a3, MterpPossibleException # bail out + SET_VREG_WIDE v0, a2 # fp[A] <- v0 + ADVANCE 2 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_iget_object: /* 0x54 */ +/* File: mips64/op_iget_object.S */ +/* File: mips64/op_iget.S */ + /* + * General instance field get. + * + * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short + */ + .extern artGetObjInstanceFromCode + EXPORT_PC + lhu a0, 2(rPC) # a0 <- field ref CCCC + srl a1, rINST, 12 # a1 <- B + GET_VREG_U a1, a1 # a1 <- fp[B], the object pointer + ld a2, OFF_FP_METHOD(rFP) # a2 <- referrer + move a3, rSELF # a3 <- self + jal artGetObjInstanceFromCode + ld a3, THREAD_EXCEPTION_OFFSET(rSELF) + ext a2, rINST, 8, 4 # a2 <- A + PREFETCH_INST 2 + bnez a3, MterpPossibleException # bail out + .if 1 + SET_VREG_OBJECT v0, a2 # fp[A] <- v0 + .else + SET_VREG v0, a2 # fp[A] <- v0 + .endif + ADVANCE 2 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_boolean: /* 0x55 */ +/* File: mips64/op_iget_boolean.S */ +/* File: mips64/op_iget.S */ + /* + * General instance field get. + * + * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short + */ + .extern artGetBooleanInstanceFromCode + EXPORT_PC + lhu a0, 2(rPC) # a0 <- field ref CCCC + srl a1, rINST, 12 # a1 <- B + GET_VREG_U a1, a1 # a1 <- fp[B], the object pointer + ld a2, OFF_FP_METHOD(rFP) # a2 <- referrer + move a3, rSELF # a3 <- self + jal artGetBooleanInstanceFromCode + ld a3, THREAD_EXCEPTION_OFFSET(rSELF) + ext a2, rINST, 8, 4 # a2 <- A + PREFETCH_INST 2 + bnez a3, MterpPossibleException # bail out + .if 0 + SET_VREG_OBJECT v0, a2 # fp[A] <- v0 + .else + SET_VREG v0, a2 # fp[A] <- v0 + .endif + ADVANCE 2 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_byte: /* 0x56 */ +/* File: mips64/op_iget_byte.S */ +/* File: mips64/op_iget.S */ + /* + * General instance field get. + * + * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short + */ + .extern artGetByteInstanceFromCode + EXPORT_PC + lhu a0, 2(rPC) # a0 <- field ref CCCC + srl a1, rINST, 12 # a1 <- B + GET_VREG_U a1, a1 # a1 <- fp[B], the object pointer + ld a2, OFF_FP_METHOD(rFP) # a2 <- referrer + move a3, rSELF # a3 <- self + jal artGetByteInstanceFromCode + ld a3, THREAD_EXCEPTION_OFFSET(rSELF) + ext a2, rINST, 8, 4 # a2 <- A + PREFETCH_INST 2 + bnez a3, MterpPossibleException # bail out + .if 0 + SET_VREG_OBJECT v0, a2 # fp[A] <- v0 + .else + SET_VREG v0, a2 # fp[A] <- v0 + .endif + ADVANCE 2 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_char: /* 0x57 */ +/* File: mips64/op_iget_char.S */ +/* File: mips64/op_iget.S */ + /* + * General instance field get. + * + * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short + */ + .extern artGetCharInstanceFromCode + EXPORT_PC + lhu a0, 2(rPC) # a0 <- field ref CCCC + srl a1, rINST, 12 # a1 <- B + GET_VREG_U a1, a1 # a1 <- fp[B], the object pointer + ld a2, OFF_FP_METHOD(rFP) # a2 <- referrer + move a3, rSELF # a3 <- self + jal artGetCharInstanceFromCode + ld a3, THREAD_EXCEPTION_OFFSET(rSELF) + ext a2, rINST, 8, 4 # a2 <- A + PREFETCH_INST 2 + bnez a3, MterpPossibleException # bail out + .if 0 + SET_VREG_OBJECT v0, a2 # fp[A] <- v0 + .else + SET_VREG v0, a2 # fp[A] <- v0 + .endif + ADVANCE 2 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_short: /* 0x58 */ +/* File: mips64/op_iget_short.S */ +/* File: mips64/op_iget.S */ + /* + * General instance field get. + * + * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short + */ + .extern artGetShortInstanceFromCode + EXPORT_PC + lhu a0, 2(rPC) # a0 <- field ref CCCC + srl a1, rINST, 12 # a1 <- B + GET_VREG_U a1, a1 # a1 <- fp[B], the object pointer + ld a2, OFF_FP_METHOD(rFP) # a2 <- referrer + move a3, rSELF # a3 <- self + jal artGetShortInstanceFromCode + ld a3, THREAD_EXCEPTION_OFFSET(rSELF) + ext a2, rINST, 8, 4 # a2 <- A + PREFETCH_INST 2 + bnez a3, MterpPossibleException # bail out + .if 0 + SET_VREG_OBJECT v0, a2 # fp[A] <- v0 + .else + SET_VREG v0, a2 # fp[A] <- v0 + .endif + ADVANCE 2 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_iput: /* 0x59 */ +/* File: mips64/op_iput.S */ + /* + * General 32-bit instance field put. + * + * for: iput, iput-boolean, iput-byte, iput-char, iput-short + */ + /* op vA, vB, field//CCCC */ + .extern artSet32InstanceFromMterp + EXPORT_PC + lhu a0, 2(rPC) # a0 <- field ref CCCC + srl a1, rINST, 12 # a1 <- B + GET_VREG_U a1, a1 # a1 <- fp[B], the object pointer + ext a2, rINST, 8, 4 # a2 <- A + GET_VREG a2, a2 # a2 <- fp[A] + ld a3, OFF_FP_METHOD(rFP) # a3 <- referrer + PREFETCH_INST 2 + jal artSet32InstanceFromMterp + bnez v0, MterpPossibleException # bail out + ADVANCE 2 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_iput_wide: /* 0x5a */ +/* File: mips64/op_iput_wide.S */ + /* iput-wide vA, vB, field//CCCC */ + .extern artSet64InstanceFromMterp + EXPORT_PC + lhu a0, 2(rPC) # a0 <- field ref CCCC + srl a1, rINST, 12 # a1 <- B + GET_VREG_U a1, a1 # a1 <- fp[B], the object pointer + ext a2, rINST, 8, 4 # a2 <- A + dlsa a2, a2, rFP, 2 # a2 <- &fp[A] + ld a3, OFF_FP_METHOD(rFP) # a3 <- referrer + PREFETCH_INST 2 + jal artSet64InstanceFromMterp + bnez v0, MterpPossibleException # bail out + ADVANCE 2 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_iput_object: /* 0x5b */ +/* File: mips64/op_iput_object.S */ + .extern MterpIputObject + EXPORT_PC + daddu a0, rFP, OFF_FP_SHADOWFRAME + move a1, rPC + move a2, rINST + move a3, rSELF + jal MterpIputObject + beqzc v0, MterpException + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_iput_boolean: /* 0x5c */ +/* File: mips64/op_iput_boolean.S */ +/* File: mips64/op_iput.S */ + /* + * General 32-bit instance field put. + * + * for: iput, iput-boolean, iput-byte, iput-char, iput-short + */ + /* op vA, vB, field//CCCC */ + .extern artSet8InstanceFromMterp + EXPORT_PC + lhu a0, 2(rPC) # a0 <- field ref CCCC + srl a1, rINST, 12 # a1 <- B + GET_VREG_U a1, a1 # a1 <- fp[B], the object pointer + ext a2, rINST, 8, 4 # a2 <- A + GET_VREG a2, a2 # a2 <- fp[A] + ld a3, OFF_FP_METHOD(rFP) # a3 <- referrer + PREFETCH_INST 2 + jal artSet8InstanceFromMterp + bnez v0, MterpPossibleException # bail out + ADVANCE 2 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_iput_byte: /* 0x5d */ +/* File: mips64/op_iput_byte.S */ +/* File: mips64/op_iput.S */ + /* + * General 32-bit instance field put. + * + * for: iput, iput-boolean, iput-byte, iput-char, iput-short + */ + /* op vA, vB, field//CCCC */ + .extern artSet8InstanceFromMterp + EXPORT_PC + lhu a0, 2(rPC) # a0 <- field ref CCCC + srl a1, rINST, 12 # a1 <- B + GET_VREG_U a1, a1 # a1 <- fp[B], the object pointer + ext a2, rINST, 8, 4 # a2 <- A + GET_VREG a2, a2 # a2 <- fp[A] + ld a3, OFF_FP_METHOD(rFP) # a3 <- referrer + PREFETCH_INST 2 + jal artSet8InstanceFromMterp + bnez v0, MterpPossibleException # bail out + ADVANCE 2 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_iput_char: /* 0x5e */ +/* File: mips64/op_iput_char.S */ +/* File: mips64/op_iput.S */ + /* + * General 32-bit instance field put. + * + * for: iput, iput-boolean, iput-byte, iput-char, iput-short + */ + /* op vA, vB, field//CCCC */ + .extern artSet16InstanceFromMterp + EXPORT_PC + lhu a0, 2(rPC) # a0 <- field ref CCCC + srl a1, rINST, 12 # a1 <- B + GET_VREG_U a1, a1 # a1 <- fp[B], the object pointer + ext a2, rINST, 8, 4 # a2 <- A + GET_VREG a2, a2 # a2 <- fp[A] + ld a3, OFF_FP_METHOD(rFP) # a3 <- referrer + PREFETCH_INST 2 + jal artSet16InstanceFromMterp + bnez v0, MterpPossibleException # bail out + ADVANCE 2 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_iput_short: /* 0x5f */ +/* File: mips64/op_iput_short.S */ +/* File: mips64/op_iput.S */ + /* + * General 32-bit instance field put. + * + * for: iput, iput-boolean, iput-byte, iput-char, iput-short + */ + /* op vA, vB, field//CCCC */ + .extern artSet16InstanceFromMterp + EXPORT_PC + lhu a0, 2(rPC) # a0 <- field ref CCCC + srl a1, rINST, 12 # a1 <- B + GET_VREG_U a1, a1 # a1 <- fp[B], the object pointer + ext a2, rINST, 8, 4 # a2 <- A + GET_VREG a2, a2 # a2 <- fp[A] + ld a3, OFF_FP_METHOD(rFP) # a3 <- referrer + PREFETCH_INST 2 + jal artSet16InstanceFromMterp + bnez v0, MterpPossibleException # bail out + ADVANCE 2 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_sget: /* 0x60 */ +/* File: mips64/op_sget.S */ + /* + * General SGET handler wrapper. + * + * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short + */ + /* op vAA, field//BBBB */ + .extern artGet32StaticFromCode + EXPORT_PC + lhu a0, 2(rPC) # a0 <- field ref BBBB + ld a1, OFF_FP_METHOD(rFP) + move a2, rSELF + jal artGet32StaticFromCode + ld a3, THREAD_EXCEPTION_OFFSET(rSELF) + srl a2, rINST, 8 # a2 <- AA + + PREFETCH_INST 2 + bnez a3, MterpException # bail out + .if 0 + SET_VREG_OBJECT v0, a2 # fp[AA] <- v0 + .else + SET_VREG v0, a2 # fp[AA] <- v0 + .endif + ADVANCE 2 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 + +/* ------------------------------ */ + .balign 128 +.L_op_sget_wide: /* 0x61 */ +/* File: mips64/op_sget_wide.S */ + /* + * SGET_WIDE handler wrapper. + * + */ + /* sget-wide vAA, field//BBBB */ + .extern artGet64StaticFromCode + EXPORT_PC + lhu a0, 2(rPC) # a0 <- field ref BBBB + ld a1, OFF_FP_METHOD(rFP) + move a2, rSELF + jal artGet64StaticFromCode + ld a3, THREAD_EXCEPTION_OFFSET(rSELF) + srl a4, rINST, 8 # a4 <- AA + bnez a3, MterpException # bail out + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + SET_VREG_WIDE v0, a4 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_sget_object: /* 0x62 */ +/* File: mips64/op_sget_object.S */ +/* File: mips64/op_sget.S */ + /* + * General SGET handler wrapper. + * + * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short + */ + /* op vAA, field//BBBB */ + .extern artGetObjStaticFromCode + EXPORT_PC + lhu a0, 2(rPC) # a0 <- field ref BBBB + ld a1, OFF_FP_METHOD(rFP) + move a2, rSELF + jal artGetObjStaticFromCode + ld a3, THREAD_EXCEPTION_OFFSET(rSELF) + srl a2, rINST, 8 # a2 <- AA + + PREFETCH_INST 2 + bnez a3, MterpException # bail out + .if 1 + SET_VREG_OBJECT v0, a2 # fp[AA] <- v0 + .else + SET_VREG v0, a2 # fp[AA] <- v0 + .endif + ADVANCE 2 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 + + +/* ------------------------------ */ + .balign 128 +.L_op_sget_boolean: /* 0x63 */ +/* File: mips64/op_sget_boolean.S */ +/* File: mips64/op_sget.S */ + /* + * General SGET handler wrapper. + * + * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short + */ + /* op vAA, field//BBBB */ + .extern artGetBooleanStaticFromCode + EXPORT_PC + lhu a0, 2(rPC) # a0 <- field ref BBBB + ld a1, OFF_FP_METHOD(rFP) + move a2, rSELF + jal artGetBooleanStaticFromCode + ld a3, THREAD_EXCEPTION_OFFSET(rSELF) + srl a2, rINST, 8 # a2 <- AA + and v0, v0, 0xff + PREFETCH_INST 2 + bnez a3, MterpException # bail out + .if 0 + SET_VREG_OBJECT v0, a2 # fp[AA] <- v0 + .else + SET_VREG v0, a2 # fp[AA] <- v0 + .endif + ADVANCE 2 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 + + +/* ------------------------------ */ + .balign 128 +.L_op_sget_byte: /* 0x64 */ +/* File: mips64/op_sget_byte.S */ +/* File: mips64/op_sget.S */ + /* + * General SGET handler wrapper. + * + * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short + */ + /* op vAA, field//BBBB */ + .extern artGetByteStaticFromCode + EXPORT_PC + lhu a0, 2(rPC) # a0 <- field ref BBBB + ld a1, OFF_FP_METHOD(rFP) + move a2, rSELF + jal artGetByteStaticFromCode + ld a3, THREAD_EXCEPTION_OFFSET(rSELF) + srl a2, rINST, 8 # a2 <- AA + seb v0, v0 + PREFETCH_INST 2 + bnez a3, MterpException # bail out + .if 0 + SET_VREG_OBJECT v0, a2 # fp[AA] <- v0 + .else + SET_VREG v0, a2 # fp[AA] <- v0 + .endif + ADVANCE 2 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 + + +/* ------------------------------ */ + .balign 128 +.L_op_sget_char: /* 0x65 */ +/* File: mips64/op_sget_char.S */ +/* File: mips64/op_sget.S */ + /* + * General SGET handler wrapper. + * + * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short + */ + /* op vAA, field//BBBB */ + .extern artGetCharStaticFromCode + EXPORT_PC + lhu a0, 2(rPC) # a0 <- field ref BBBB + ld a1, OFF_FP_METHOD(rFP) + move a2, rSELF + jal artGetCharStaticFromCode + ld a3, THREAD_EXCEPTION_OFFSET(rSELF) + srl a2, rINST, 8 # a2 <- AA + and v0, v0, 0xffff + PREFETCH_INST 2 + bnez a3, MterpException # bail out + .if 0 + SET_VREG_OBJECT v0, a2 # fp[AA] <- v0 + .else + SET_VREG v0, a2 # fp[AA] <- v0 + .endif + ADVANCE 2 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 + + +/* ------------------------------ */ + .balign 128 +.L_op_sget_short: /* 0x66 */ +/* File: mips64/op_sget_short.S */ +/* File: mips64/op_sget.S */ + /* + * General SGET handler wrapper. + * + * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short + */ + /* op vAA, field//BBBB */ + .extern artGetShortStaticFromCode + EXPORT_PC + lhu a0, 2(rPC) # a0 <- field ref BBBB + ld a1, OFF_FP_METHOD(rFP) + move a2, rSELF + jal artGetShortStaticFromCode + ld a3, THREAD_EXCEPTION_OFFSET(rSELF) + srl a2, rINST, 8 # a2 <- AA + seh v0, v0 + PREFETCH_INST 2 + bnez a3, MterpException # bail out + .if 0 + SET_VREG_OBJECT v0, a2 # fp[AA] <- v0 + .else + SET_VREG v0, a2 # fp[AA] <- v0 + .endif + ADVANCE 2 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 + + +/* ------------------------------ */ + .balign 128 +.L_op_sput: /* 0x67 */ +/* File: mips64/op_sput.S */ + /* + * General SPUT handler wrapper. + * + * for: sput, sput-boolean, sput-byte, sput-char, sput-short + */ + /* op vAA, field//BBBB */ + .extern artSet32StaticFromCode + EXPORT_PC + lhu a0, 2(rPC) # a0 <- field ref BBBB + srl a3, rINST, 8 # a3 <- AA + GET_VREG a1, a3 # a1 <- fp[AA] + ld a2, OFF_FP_METHOD(rFP) + move a3, rSELF + PREFETCH_INST 2 # Get next inst, but don't advance rPC + jal artSet32StaticFromCode + bnezc v0, MterpException # 0 on success + ADVANCE 2 # Past exception point - now advance rPC + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_sput_wide: /* 0x68 */ +/* File: mips64/op_sput_wide.S */ + /* + * SPUT_WIDE handler wrapper. + * + */ + /* sput-wide vAA, field//BBBB */ + .extern artSet64IndirectStaticFromMterp + EXPORT_PC + lhu a0, 2(rPC) # a0 <- field ref BBBB + ld a1, OFF_FP_METHOD(rFP) + srl a2, rINST, 8 # a2 <- AA + dlsa a2, a2, rFP, 2 + move a3, rSELF + PREFETCH_INST 2 # Get next inst, but don't advance rPC + jal artSet64IndirectStaticFromMterp + bnezc v0, MterpException # 0 on success, -1 on failure + ADVANCE 2 # Past exception point - now advance rPC + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_sput_object: /* 0x69 */ +/* File: mips64/op_sput_object.S */ + .extern MterpSputObject + EXPORT_PC + daddu a0, rFP, OFF_FP_SHADOWFRAME + move a1, rPC + move a2, rINST + move a3, rSELF + jal MterpSputObject + beqzc v0, MterpException + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_sput_boolean: /* 0x6a */ +/* File: mips64/op_sput_boolean.S */ +/* File: mips64/op_sput.S */ + /* + * General SPUT handler wrapper. + * + * for: sput, sput-boolean, sput-byte, sput-char, sput-short + */ + /* op vAA, field//BBBB */ + .extern artSet8StaticFromCode + EXPORT_PC + lhu a0, 2(rPC) # a0 <- field ref BBBB + srl a3, rINST, 8 # a3 <- AA + GET_VREG a1, a3 # a1 <- fp[AA] + ld a2, OFF_FP_METHOD(rFP) + move a3, rSELF + PREFETCH_INST 2 # Get next inst, but don't advance rPC + jal artSet8StaticFromCode + bnezc v0, MterpException # 0 on success + ADVANCE 2 # Past exception point - now advance rPC + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_sput_byte: /* 0x6b */ +/* File: mips64/op_sput_byte.S */ +/* File: mips64/op_sput.S */ + /* + * General SPUT handler wrapper. + * + * for: sput, sput-boolean, sput-byte, sput-char, sput-short + */ + /* op vAA, field//BBBB */ + .extern artSet8StaticFromCode + EXPORT_PC + lhu a0, 2(rPC) # a0 <- field ref BBBB + srl a3, rINST, 8 # a3 <- AA + GET_VREG a1, a3 # a1 <- fp[AA] + ld a2, OFF_FP_METHOD(rFP) + move a3, rSELF + PREFETCH_INST 2 # Get next inst, but don't advance rPC + jal artSet8StaticFromCode + bnezc v0, MterpException # 0 on success + ADVANCE 2 # Past exception point - now advance rPC + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_sput_char: /* 0x6c */ +/* File: mips64/op_sput_char.S */ +/* File: mips64/op_sput.S */ + /* + * General SPUT handler wrapper. + * + * for: sput, sput-boolean, sput-byte, sput-char, sput-short + */ + /* op vAA, field//BBBB */ + .extern artSet16StaticFromCode + EXPORT_PC + lhu a0, 2(rPC) # a0 <- field ref BBBB + srl a3, rINST, 8 # a3 <- AA + GET_VREG a1, a3 # a1 <- fp[AA] + ld a2, OFF_FP_METHOD(rFP) + move a3, rSELF + PREFETCH_INST 2 # Get next inst, but don't advance rPC + jal artSet16StaticFromCode + bnezc v0, MterpException # 0 on success + ADVANCE 2 # Past exception point - now advance rPC + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_sput_short: /* 0x6d */ +/* File: mips64/op_sput_short.S */ +/* File: mips64/op_sput.S */ + /* + * General SPUT handler wrapper. + * + * for: sput, sput-boolean, sput-byte, sput-char, sput-short + */ + /* op vAA, field//BBBB */ + .extern artSet16StaticFromCode + EXPORT_PC + lhu a0, 2(rPC) # a0 <- field ref BBBB + srl a3, rINST, 8 # a3 <- AA + GET_VREG a1, a3 # a1 <- fp[AA] + ld a2, OFF_FP_METHOD(rFP) + move a3, rSELF + PREFETCH_INST 2 # Get next inst, but don't advance rPC + jal artSet16StaticFromCode + bnezc v0, MterpException # 0 on success + ADVANCE 2 # Past exception point - now advance rPC + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_virtual: /* 0x6e */ +/* File: mips64/op_invoke_virtual.S */ +/* File: mips64/invoke.S */ + /* + * Generic invoke handler wrapper. + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + .extern MterpInvokeVirtual + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rPC + move a3, rINST + jal MterpInvokeVirtual + beqzc v0, MterpException + FETCH_ADVANCE_INST 3 + GET_INST_OPCODE v0 + GOTO_OPCODE v0 + + /* + * Handle a virtual method call. + * + * for: invoke-virtual, invoke-virtual/range + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_super: /* 0x6f */ +/* File: mips64/op_invoke_super.S */ +/* File: mips64/invoke.S */ + /* + * Generic invoke handler wrapper. + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + .extern MterpInvokeSuper + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rPC + move a3, rINST + jal MterpInvokeSuper + beqzc v0, MterpException + FETCH_ADVANCE_INST 3 + GET_INST_OPCODE v0 + GOTO_OPCODE v0 + + /* + * Handle a "super" method call. + * + * for: invoke-super, invoke-super/range + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_direct: /* 0x70 */ +/* File: mips64/op_invoke_direct.S */ +/* File: mips64/invoke.S */ + /* + * Generic invoke handler wrapper. + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + .extern MterpInvokeDirect + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rPC + move a3, rINST + jal MterpInvokeDirect + beqzc v0, MterpException + FETCH_ADVANCE_INST 3 + GET_INST_OPCODE v0 + GOTO_OPCODE v0 + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_static: /* 0x71 */ +/* File: mips64/op_invoke_static.S */ +/* File: mips64/invoke.S */ + /* + * Generic invoke handler wrapper. + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + .extern MterpInvokeStatic + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rPC + move a3, rINST + jal MterpInvokeStatic + beqzc v0, MterpException + FETCH_ADVANCE_INST 3 + GET_INST_OPCODE v0 + GOTO_OPCODE v0 + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_interface: /* 0x72 */ +/* File: mips64/op_invoke_interface.S */ +/* File: mips64/invoke.S */ + /* + * Generic invoke handler wrapper. + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + .extern MterpInvokeInterface + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rPC + move a3, rINST + jal MterpInvokeInterface + beqzc v0, MterpException + FETCH_ADVANCE_INST 3 + GET_INST_OPCODE v0 + GOTO_OPCODE v0 + + /* + * Handle an interface method call. + * + * for: invoke-interface, invoke-interface/range + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + +/* ------------------------------ */ + .balign 128 +.L_op_return_void_no_barrier: /* 0x73 */ +/* File: mips64/op_return_void_no_barrier.S */ + .extern MterpSuspendCheck + lw ra, THREAD_FLAGS_OFFSET(rSELF) + move a0, rSELF + and ra, ra, (THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + beqzc ra, 1f + jal MterpSuspendCheck # (self) +1: + li a0, 0 + b MterpReturn + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_virtual_range: /* 0x74 */ +/* File: mips64/op_invoke_virtual_range.S */ +/* File: mips64/invoke.S */ + /* + * Generic invoke handler wrapper. + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + .extern MterpInvokeVirtualRange + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rPC + move a3, rINST + jal MterpInvokeVirtualRange + beqzc v0, MterpException + FETCH_ADVANCE_INST 3 + GET_INST_OPCODE v0 + GOTO_OPCODE v0 + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_super_range: /* 0x75 */ +/* File: mips64/op_invoke_super_range.S */ +/* File: mips64/invoke.S */ + /* + * Generic invoke handler wrapper. + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + .extern MterpInvokeSuperRange + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rPC + move a3, rINST + jal MterpInvokeSuperRange + beqzc v0, MterpException + FETCH_ADVANCE_INST 3 + GET_INST_OPCODE v0 + GOTO_OPCODE v0 + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_direct_range: /* 0x76 */ +/* File: mips64/op_invoke_direct_range.S */ +/* File: mips64/invoke.S */ + /* + * Generic invoke handler wrapper. + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + .extern MterpInvokeDirectRange + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rPC + move a3, rINST + jal MterpInvokeDirectRange + beqzc v0, MterpException + FETCH_ADVANCE_INST 3 + GET_INST_OPCODE v0 + GOTO_OPCODE v0 + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_static_range: /* 0x77 */ +/* File: mips64/op_invoke_static_range.S */ +/* File: mips64/invoke.S */ + /* + * Generic invoke handler wrapper. + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + .extern MterpInvokeStaticRange + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rPC + move a3, rINST + jal MterpInvokeStaticRange + beqzc v0, MterpException + FETCH_ADVANCE_INST 3 + GET_INST_OPCODE v0 + GOTO_OPCODE v0 + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_interface_range: /* 0x78 */ +/* File: mips64/op_invoke_interface_range.S */ +/* File: mips64/invoke.S */ + /* + * Generic invoke handler wrapper. + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + .extern MterpInvokeInterfaceRange + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rPC + move a3, rINST + jal MterpInvokeInterfaceRange + beqzc v0, MterpException + FETCH_ADVANCE_INST 3 + GET_INST_OPCODE v0 + GOTO_OPCODE v0 + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_79: /* 0x79 */ +/* File: mips64/op_unused_79.S */ +/* File: mips64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + b MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_7a: /* 0x7a */ +/* File: mips64/op_unused_7a.S */ +/* File: mips64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + b MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_neg_int: /* 0x7b */ +/* File: mips64/op_neg_int.S */ +/* File: mips64/unop.S */ + /* + * Generic 32-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "a0 = op a0". + * + * for: int-to-byte, int-to-char, int-to-short, + * not-int, neg-int + */ + /* unop vA, vB */ + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG a0, a3 # a0 <- vB + ext a2, rINST, 8, 4 # a2 <- A + # optional op + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + subu a0, zero, a0 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_not_int: /* 0x7c */ +/* File: mips64/op_not_int.S */ +/* File: mips64/unop.S */ + /* + * Generic 32-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "a0 = op a0". + * + * for: int-to-byte, int-to-char, int-to-short, + * not-int, neg-int + */ + /* unop vA, vB */ + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG a0, a3 # a0 <- vB + ext a2, rINST, 8, 4 # a2 <- A + # optional op + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + nor a0, zero, a0 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_neg_long: /* 0x7d */ +/* File: mips64/op_neg_long.S */ +/* File: mips64/unopWide.S */ + /* + * Generic 64-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "a0 = op a0". + * + * For: not-long, neg-long + */ + /* unop vA, vB */ + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG_WIDE a0, a3 # a0 <- vB + ext a2, rINST, 8, 4 # a2 <- A + # optional op + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + dsubu a0, zero, a0 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_not_long: /* 0x7e */ +/* File: mips64/op_not_long.S */ +/* File: mips64/unopWide.S */ + /* + * Generic 64-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "a0 = op a0". + * + * For: not-long, neg-long + */ + /* unop vA, vB */ + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG_WIDE a0, a3 # a0 <- vB + ext a2, rINST, 8, 4 # a2 <- A + # optional op + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + nor a0, zero, a0 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_neg_float: /* 0x7f */ +/* File: mips64/op_neg_float.S */ +/* File: mips64/fcvtHeader.S */ + /* + * Loads a specified register from vB. Used primarily for conversions + * from or to a floating-point type. + * + * Sets up a1 = A and a2 = B. a2 is later used by fcvtFooter.S to + * store the result in vA and jump to the next instruction. + * + * For: int-to-float, int-to-double, long-to-float, long-to-double, + * float-to-int, float-to-long, float-to-double, double-to-int, + * double-to-long, double-to-float, neg-float, neg-double. + */ + ext a1, rINST, 8, 4 # a1 <- A + srl a2, rINST, 12 # a2 <- B + GET_VREG_FLOAT f0, a2 + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + + neg.s f0, f0 +/* File: mips64/fcvtFooter.S */ + /* + * Stores a specified register containing the result of conversion + * from or to a floating-point type and jumps to the next instruction. + * + * Expects a1 to contain the destination Dalvik register number. + * a1 is set up by fcvtHeader.S. + * + * For: int-to-float, int-to-double, long-to-float, long-to-double, + * float-to-int, float-to-long, float-to-double, double-to-int, + * double-to-long, double-to-float, neg-float, neg-double. + * + * Note that this file can't be included after a break in other files + * and in those files its contents appear as a copy. + * See: float-to-int, float-to-long, double-to-int, double-to-long. + */ + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_FLOAT f0, a1 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_neg_double: /* 0x80 */ +/* File: mips64/op_neg_double.S */ +/* File: mips64/fcvtHeader.S */ + /* + * Loads a specified register from vB. Used primarily for conversions + * from or to a floating-point type. + * + * Sets up a1 = A and a2 = B. a2 is later used by fcvtFooter.S to + * store the result in vA and jump to the next instruction. + * + * For: int-to-float, int-to-double, long-to-float, long-to-double, + * float-to-int, float-to-long, float-to-double, double-to-int, + * double-to-long, double-to-float, neg-float, neg-double. + */ + ext a1, rINST, 8, 4 # a1 <- A + srl a2, rINST, 12 # a2 <- B + GET_VREG_DOUBLE f0, a2 + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + + neg.d f0, f0 +/* File: mips64/fcvtFooter.S */ + /* + * Stores a specified register containing the result of conversion + * from or to a floating-point type and jumps to the next instruction. + * + * Expects a1 to contain the destination Dalvik register number. + * a1 is set up by fcvtHeader.S. + * + * For: int-to-float, int-to-double, long-to-float, long-to-double, + * float-to-int, float-to-long, float-to-double, double-to-int, + * double-to-long, double-to-float, neg-float, neg-double. + * + * Note that this file can't be included after a break in other files + * and in those files its contents appear as a copy. + * See: float-to-int, float-to-long, double-to-int, double-to-long. + */ + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_DOUBLE f0, a1 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_int_to_long: /* 0x81 */ +/* File: mips64/op_int_to_long.S */ + /* int-to-long vA, vB */ + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG a0, a3 # a0 <- vB (sign-extended to 64 bits) + ext a2, rINST, 8, 4 # a2 <- A + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a2 # vA <- vB + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_int_to_float: /* 0x82 */ +/* File: mips64/op_int_to_float.S */ + /* + * Conversion from or to floating-point happens in a floating-point register. + * Therefore we load the input and store the output into or from a + * floating-point register irrespective of the type. + */ +/* File: mips64/fcvtHeader.S */ + /* + * Loads a specified register from vB. Used primarily for conversions + * from or to a floating-point type. + * + * Sets up a1 = A and a2 = B. a2 is later used by fcvtFooter.S to + * store the result in vA and jump to the next instruction. + * + * For: int-to-float, int-to-double, long-to-float, long-to-double, + * float-to-int, float-to-long, float-to-double, double-to-int, + * double-to-long, double-to-float, neg-float, neg-double. + */ + ext a1, rINST, 8, 4 # a1 <- A + srl a2, rINST, 12 # a2 <- B + GET_VREG_FLOAT f0, a2 + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + + cvt.s.w f0, f0 +/* File: mips64/fcvtFooter.S */ + /* + * Stores a specified register containing the result of conversion + * from or to a floating-point type and jumps to the next instruction. + * + * Expects a1 to contain the destination Dalvik register number. + * a1 is set up by fcvtHeader.S. + * + * For: int-to-float, int-to-double, long-to-float, long-to-double, + * float-to-int, float-to-long, float-to-double, double-to-int, + * double-to-long, double-to-float, neg-float, neg-double. + * + * Note that this file can't be included after a break in other files + * and in those files its contents appear as a copy. + * See: float-to-int, float-to-long, double-to-int, double-to-long. + */ + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_FLOAT f0, a1 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_int_to_double: /* 0x83 */ +/* File: mips64/op_int_to_double.S */ + /* + * Conversion from or to floating-point happens in a floating-point register. + * Therefore we load the input and store the output into or from a + * floating-point register irrespective of the type. + */ +/* File: mips64/fcvtHeader.S */ + /* + * Loads a specified register from vB. Used primarily for conversions + * from or to a floating-point type. + * + * Sets up a1 = A and a2 = B. a2 is later used by fcvtFooter.S to + * store the result in vA and jump to the next instruction. + * + * For: int-to-float, int-to-double, long-to-float, long-to-double, + * float-to-int, float-to-long, float-to-double, double-to-int, + * double-to-long, double-to-float, neg-float, neg-double. + */ + ext a1, rINST, 8, 4 # a1 <- A + srl a2, rINST, 12 # a2 <- B + GET_VREG_FLOAT f0, a2 + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + + cvt.d.w f0, f0 +/* File: mips64/fcvtFooter.S */ + /* + * Stores a specified register containing the result of conversion + * from or to a floating-point type and jumps to the next instruction. + * + * Expects a1 to contain the destination Dalvik register number. + * a1 is set up by fcvtHeader.S. + * + * For: int-to-float, int-to-double, long-to-float, long-to-double, + * float-to-int, float-to-long, float-to-double, double-to-int, + * double-to-long, double-to-float, neg-float, neg-double. + * + * Note that this file can't be included after a break in other files + * and in those files its contents appear as a copy. + * See: float-to-int, float-to-long, double-to-int, double-to-long. + */ + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_DOUBLE f0, a1 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_long_to_int: /* 0x84 */ +/* File: mips64/op_long_to_int.S */ +/* we ignore the high word, making this equivalent to a 32-bit reg move */ +/* File: mips64/op_move.S */ + /* for move, move-object, long-to-int */ + /* op vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + GET_VREG a0, a3 # a0 <- vB + GET_INST_OPCODE v0 # extract opcode from rINST + .if 0 + SET_VREG_OBJECT a0, a2 # vA <- vB + .else + SET_VREG a0, a2 # vA <- vB + .endif + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_long_to_float: /* 0x85 */ +/* File: mips64/op_long_to_float.S */ + /* + * Conversion from or to floating-point happens in a floating-point register. + * Therefore we load the input and store the output into or from a + * floating-point register irrespective of the type. + */ +/* File: mips64/fcvtHeader.S */ + /* + * Loads a specified register from vB. Used primarily for conversions + * from or to a floating-point type. + * + * Sets up a1 = A and a2 = B. a2 is later used by fcvtFooter.S to + * store the result in vA and jump to the next instruction. + * + * For: int-to-float, int-to-double, long-to-float, long-to-double, + * float-to-int, float-to-long, float-to-double, double-to-int, + * double-to-long, double-to-float, neg-float, neg-double. + */ + ext a1, rINST, 8, 4 # a1 <- A + srl a2, rINST, 12 # a2 <- B + GET_VREG_DOUBLE f0, a2 + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + + cvt.s.l f0, f0 +/* File: mips64/fcvtFooter.S */ + /* + * Stores a specified register containing the result of conversion + * from or to a floating-point type and jumps to the next instruction. + * + * Expects a1 to contain the destination Dalvik register number. + * a1 is set up by fcvtHeader.S. + * + * For: int-to-float, int-to-double, long-to-float, long-to-double, + * float-to-int, float-to-long, float-to-double, double-to-int, + * double-to-long, double-to-float, neg-float, neg-double. + * + * Note that this file can't be included after a break in other files + * and in those files its contents appear as a copy. + * See: float-to-int, float-to-long, double-to-int, double-to-long. + */ + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_FLOAT f0, a1 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_long_to_double: /* 0x86 */ +/* File: mips64/op_long_to_double.S */ + /* + * Conversion from or to floating-point happens in a floating-point register. + * Therefore we load the input and store the output into or from a + * floating-point register irrespective of the type. + */ +/* File: mips64/fcvtHeader.S */ + /* + * Loads a specified register from vB. Used primarily for conversions + * from or to a floating-point type. + * + * Sets up a1 = A and a2 = B. a2 is later used by fcvtFooter.S to + * store the result in vA and jump to the next instruction. + * + * For: int-to-float, int-to-double, long-to-float, long-to-double, + * float-to-int, float-to-long, float-to-double, double-to-int, + * double-to-long, double-to-float, neg-float, neg-double. + */ + ext a1, rINST, 8, 4 # a1 <- A + srl a2, rINST, 12 # a2 <- B + GET_VREG_DOUBLE f0, a2 + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + + cvt.d.l f0, f0 +/* File: mips64/fcvtFooter.S */ + /* + * Stores a specified register containing the result of conversion + * from or to a floating-point type and jumps to the next instruction. + * + * Expects a1 to contain the destination Dalvik register number. + * a1 is set up by fcvtHeader.S. + * + * For: int-to-float, int-to-double, long-to-float, long-to-double, + * float-to-int, float-to-long, float-to-double, double-to-int, + * double-to-long, double-to-float, neg-float, neg-double. + * + * Note that this file can't be included after a break in other files + * and in those files its contents appear as a copy. + * See: float-to-int, float-to-long, double-to-int, double-to-long. + */ + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_DOUBLE f0, a1 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_float_to_int: /* 0x87 */ +/* File: mips64/op_float_to_int.S */ +/* File: mips64/fcvtHeader.S */ + /* + * Loads a specified register from vB. Used primarily for conversions + * from or to a floating-point type. + * + * Sets up a1 = A and a2 = B. a2 is later used by fcvtFooter.S to + * store the result in vA and jump to the next instruction. + * + * For: int-to-float, int-to-double, long-to-float, long-to-double, + * float-to-int, float-to-long, float-to-double, double-to-int, + * double-to-long, double-to-float, neg-float, neg-double. + */ + ext a1, rINST, 8, 4 # a1 <- A + srl a2, rINST, 12 # a2 <- B + GET_VREG_FLOAT f0, a2 + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + + /* + * TODO: simplify this when the MIPS64R6 emulator + * supports NAN2008=1. + */ + li t0, INT_MIN_AS_FLOAT + mtc1 t0, f1 + cmp.le.s f1, f1, f0 + bc1nez f1, .Lop_float_to_int_trunc + cmp.eq.s f1, f0, f0 + li t0, INT_MIN + mfc1 t1, f1 + and t0, t0, t1 + b .Lop_float_to_int_done + +/* ------------------------------ */ + .balign 128 +.L_op_float_to_long: /* 0x88 */ +/* File: mips64/op_float_to_long.S */ +/* File: mips64/fcvtHeader.S */ + /* + * Loads a specified register from vB. Used primarily for conversions + * from or to a floating-point type. + * + * Sets up a1 = A and a2 = B. a2 is later used by fcvtFooter.S to + * store the result in vA and jump to the next instruction. + * + * For: int-to-float, int-to-double, long-to-float, long-to-double, + * float-to-int, float-to-long, float-to-double, double-to-int, + * double-to-long, double-to-float, neg-float, neg-double. + */ + ext a1, rINST, 8, 4 # a1 <- A + srl a2, rINST, 12 # a2 <- B + GET_VREG_FLOAT f0, a2 + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + + /* + * TODO: simplify this when the MIPS64R6 emulator + * supports NAN2008=1. + */ + li t0, LONG_MIN_AS_FLOAT + mtc1 t0, f1 + cmp.le.s f1, f1, f0 + bc1nez f1, .Lop_float_to_long_trunc + cmp.eq.s f1, f0, f0 + dli t0, LONG_MIN + mfc1 t1, f1 + and t0, t0, t1 + b .Lop_float_to_long_done + +/* ------------------------------ */ + .balign 128 +.L_op_float_to_double: /* 0x89 */ +/* File: mips64/op_float_to_double.S */ + /* + * Conversion from or to floating-point happens in a floating-point register. + * Therefore we load the input and store the output into or from a + * floating-point register irrespective of the type. + */ +/* File: mips64/fcvtHeader.S */ + /* + * Loads a specified register from vB. Used primarily for conversions + * from or to a floating-point type. + * + * Sets up a1 = A and a2 = B. a2 is later used by fcvtFooter.S to + * store the result in vA and jump to the next instruction. + * + * For: int-to-float, int-to-double, long-to-float, long-to-double, + * float-to-int, float-to-long, float-to-double, double-to-int, + * double-to-long, double-to-float, neg-float, neg-double. + */ + ext a1, rINST, 8, 4 # a1 <- A + srl a2, rINST, 12 # a2 <- B + GET_VREG_FLOAT f0, a2 + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + + cvt.d.s f0, f0 +/* File: mips64/fcvtFooter.S */ + /* + * Stores a specified register containing the result of conversion + * from or to a floating-point type and jumps to the next instruction. + * + * Expects a1 to contain the destination Dalvik register number. + * a1 is set up by fcvtHeader.S. + * + * For: int-to-float, int-to-double, long-to-float, long-to-double, + * float-to-int, float-to-long, float-to-double, double-to-int, + * double-to-long, double-to-float, neg-float, neg-double. + * + * Note that this file can't be included after a break in other files + * and in those files its contents appear as a copy. + * See: float-to-int, float-to-long, double-to-int, double-to-long. + */ + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_DOUBLE f0, a1 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_double_to_int: /* 0x8a */ +/* File: mips64/op_double_to_int.S */ +/* File: mips64/fcvtHeader.S */ + /* + * Loads a specified register from vB. Used primarily for conversions + * from or to a floating-point type. + * + * Sets up a1 = A and a2 = B. a2 is later used by fcvtFooter.S to + * store the result in vA and jump to the next instruction. + * + * For: int-to-float, int-to-double, long-to-float, long-to-double, + * float-to-int, float-to-long, float-to-double, double-to-int, + * double-to-long, double-to-float, neg-float, neg-double. + */ + ext a1, rINST, 8, 4 # a1 <- A + srl a2, rINST, 12 # a2 <- B + GET_VREG_DOUBLE f0, a2 + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + + /* + * TODO: simplify this when the MIPS64R6 emulator + * supports NAN2008=1. + */ + dli t0, INT_MIN_AS_DOUBLE + dmtc1 t0, f1 + cmp.le.d f1, f1, f0 + bc1nez f1, .Lop_double_to_int_trunc + cmp.eq.d f1, f0, f0 + li t0, INT_MIN + mfc1 t1, f1 + and t0, t0, t1 + b .Lop_double_to_int_done + +/* ------------------------------ */ + .balign 128 +.L_op_double_to_long: /* 0x8b */ +/* File: mips64/op_double_to_long.S */ +/* File: mips64/fcvtHeader.S */ + /* + * Loads a specified register from vB. Used primarily for conversions + * from or to a floating-point type. + * + * Sets up a1 = A and a2 = B. a2 is later used by fcvtFooter.S to + * store the result in vA and jump to the next instruction. + * + * For: int-to-float, int-to-double, long-to-float, long-to-double, + * float-to-int, float-to-long, float-to-double, double-to-int, + * double-to-long, double-to-float, neg-float, neg-double. + */ + ext a1, rINST, 8, 4 # a1 <- A + srl a2, rINST, 12 # a2 <- B + GET_VREG_DOUBLE f0, a2 + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + + /* + * TODO: simplify this when the MIPS64R6 emulator + * supports NAN2008=1. + */ + dli t0, LONG_MIN_AS_DOUBLE + dmtc1 t0, f1 + cmp.le.d f1, f1, f0 + bc1nez f1, .Lop_double_to_long_trunc + cmp.eq.d f1, f0, f0 + dli t0, LONG_MIN + mfc1 t1, f1 + and t0, t0, t1 + b .Lop_double_to_long_done + +/* ------------------------------ */ + .balign 128 +.L_op_double_to_float: /* 0x8c */ +/* File: mips64/op_double_to_float.S */ + /* + * Conversion from or to floating-point happens in a floating-point register. + * Therefore we load the input and store the output into or from a + * floating-point register irrespective of the type. + */ +/* File: mips64/fcvtHeader.S */ + /* + * Loads a specified register from vB. Used primarily for conversions + * from or to a floating-point type. + * + * Sets up a1 = A and a2 = B. a2 is later used by fcvtFooter.S to + * store the result in vA and jump to the next instruction. + * + * For: int-to-float, int-to-double, long-to-float, long-to-double, + * float-to-int, float-to-long, float-to-double, double-to-int, + * double-to-long, double-to-float, neg-float, neg-double. + */ + ext a1, rINST, 8, 4 # a1 <- A + srl a2, rINST, 12 # a2 <- B + GET_VREG_DOUBLE f0, a2 + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + + cvt.s.d f0, f0 +/* File: mips64/fcvtFooter.S */ + /* + * Stores a specified register containing the result of conversion + * from or to a floating-point type and jumps to the next instruction. + * + * Expects a1 to contain the destination Dalvik register number. + * a1 is set up by fcvtHeader.S. + * + * For: int-to-float, int-to-double, long-to-float, long-to-double, + * float-to-int, float-to-long, float-to-double, double-to-int, + * double-to-long, double-to-float, neg-float, neg-double. + * + * Note that this file can't be included after a break in other files + * and in those files its contents appear as a copy. + * See: float-to-int, float-to-long, double-to-int, double-to-long. + */ + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_FLOAT f0, a1 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_int_to_byte: /* 0x8d */ +/* File: mips64/op_int_to_byte.S */ +/* File: mips64/unop.S */ + /* + * Generic 32-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "a0 = op a0". + * + * for: int-to-byte, int-to-char, int-to-short, + * not-int, neg-int + */ + /* unop vA, vB */ + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG a0, a3 # a0 <- vB + ext a2, rINST, 8, 4 # a2 <- A + # optional op + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + seb a0, a0 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_int_to_char: /* 0x8e */ +/* File: mips64/op_int_to_char.S */ +/* File: mips64/unop.S */ + /* + * Generic 32-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "a0 = op a0". + * + * for: int-to-byte, int-to-char, int-to-short, + * not-int, neg-int + */ + /* unop vA, vB */ + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG a0, a3 # a0 <- vB + ext a2, rINST, 8, 4 # a2 <- A + # optional op + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + and a0, a0, 0xffff # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_int_to_short: /* 0x8f */ +/* File: mips64/op_int_to_short.S */ +/* File: mips64/unop.S */ + /* + * Generic 32-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "a0 = op a0". + * + * for: int-to-byte, int-to-char, int-to-short, + * not-int, neg-int + */ + /* unop vA, vB */ + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG a0, a3 # a0 <- vB + ext a2, rINST, 8, 4 # a2 <- A + # optional op + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + seh a0, a0 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_add_int: /* 0x90 */ +/* File: mips64/op_add_int.S */ +/* File: mips64/binop.S */ + /* + * Generic 32-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int, + * xor-int, shl-int, shr-int, ushr-int + */ + /* binop vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG a0, a2 # a0 <- vBB + GET_VREG a1, a3 # a1 <- vCC + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + addu a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a4 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_sub_int: /* 0x91 */ +/* File: mips64/op_sub_int.S */ +/* File: mips64/binop.S */ + /* + * Generic 32-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int, + * xor-int, shl-int, shr-int, ushr-int + */ + /* binop vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG a0, a2 # a0 <- vBB + GET_VREG a1, a3 # a1 <- vCC + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + subu a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a4 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_int: /* 0x92 */ +/* File: mips64/op_mul_int.S */ +/* File: mips64/binop.S */ + /* + * Generic 32-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int, + * xor-int, shl-int, shr-int, ushr-int + */ + /* binop vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG a0, a2 # a0 <- vBB + GET_VREG a1, a3 # a1 <- vCC + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + mul a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a4 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_div_int: /* 0x93 */ +/* File: mips64/op_div_int.S */ +/* File: mips64/binop.S */ + /* + * Generic 32-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int, + * xor-int, shl-int, shr-int, ushr-int + */ + /* binop vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG a0, a2 # a0 <- vBB + GET_VREG a1, a3 # a1 <- vCC + .if 1 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + div a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a4 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_rem_int: /* 0x94 */ +/* File: mips64/op_rem_int.S */ +/* File: mips64/binop.S */ + /* + * Generic 32-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int, + * xor-int, shl-int, shr-int, ushr-int + */ + /* binop vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG a0, a2 # a0 <- vBB + GET_VREG a1, a3 # a1 <- vCC + .if 1 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + mod a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a4 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_and_int: /* 0x95 */ +/* File: mips64/op_and_int.S */ +/* File: mips64/binop.S */ + /* + * Generic 32-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int, + * xor-int, shl-int, shr-int, ushr-int + */ + /* binop vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG a0, a2 # a0 <- vBB + GET_VREG a1, a3 # a1 <- vCC + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + and a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a4 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_or_int: /* 0x96 */ +/* File: mips64/op_or_int.S */ +/* File: mips64/binop.S */ + /* + * Generic 32-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int, + * xor-int, shl-int, shr-int, ushr-int + */ + /* binop vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG a0, a2 # a0 <- vBB + GET_VREG a1, a3 # a1 <- vCC + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + or a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a4 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_xor_int: /* 0x97 */ +/* File: mips64/op_xor_int.S */ +/* File: mips64/binop.S */ + /* + * Generic 32-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int, + * xor-int, shl-int, shr-int, ushr-int + */ + /* binop vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG a0, a2 # a0 <- vBB + GET_VREG a1, a3 # a1 <- vCC + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + xor a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a4 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_shl_int: /* 0x98 */ +/* File: mips64/op_shl_int.S */ +/* File: mips64/binop.S */ + /* + * Generic 32-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int, + * xor-int, shl-int, shr-int, ushr-int + */ + /* binop vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG a0, a2 # a0 <- vBB + GET_VREG a1, a3 # a1 <- vCC + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + sll a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a4 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_shr_int: /* 0x99 */ +/* File: mips64/op_shr_int.S */ +/* File: mips64/binop.S */ + /* + * Generic 32-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int, + * xor-int, shl-int, shr-int, ushr-int + */ + /* binop vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG a0, a2 # a0 <- vBB + GET_VREG a1, a3 # a1 <- vCC + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + sra a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a4 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_ushr_int: /* 0x9a */ +/* File: mips64/op_ushr_int.S */ +/* File: mips64/binop.S */ + /* + * Generic 32-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int, + * xor-int, shl-int, shr-int, ushr-int + */ + /* binop vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG a0, a2 # a0 <- vBB + GET_VREG a1, a3 # a1 <- vCC + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + srl a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a4 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_add_long: /* 0x9b */ +/* File: mips64/op_add_long.S */ +/* File: mips64/binopWide.S */ + /* + * Generic 64-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (LONG_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-long, sub-long, mul-long, div-long, rem-long, and-long, or-long, + * xor-long, shl-long, shr-long, ushr-long + */ + /* binop vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG_WIDE a0, a2 # a0 <- vBB + GET_VREG_WIDE a1, a3 # a1 <- vCC + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + daddu a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a4 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_sub_long: /* 0x9c */ +/* File: mips64/op_sub_long.S */ +/* File: mips64/binopWide.S */ + /* + * Generic 64-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (LONG_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-long, sub-long, mul-long, div-long, rem-long, and-long, or-long, + * xor-long, shl-long, shr-long, ushr-long + */ + /* binop vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG_WIDE a0, a2 # a0 <- vBB + GET_VREG_WIDE a1, a3 # a1 <- vCC + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + dsubu a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a4 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_long: /* 0x9d */ +/* File: mips64/op_mul_long.S */ +/* File: mips64/binopWide.S */ + /* + * Generic 64-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (LONG_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-long, sub-long, mul-long, div-long, rem-long, and-long, or-long, + * xor-long, shl-long, shr-long, ushr-long + */ + /* binop vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG_WIDE a0, a2 # a0 <- vBB + GET_VREG_WIDE a1, a3 # a1 <- vCC + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + dmul a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a4 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_div_long: /* 0x9e */ +/* File: mips64/op_div_long.S */ +/* File: mips64/binopWide.S */ + /* + * Generic 64-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (LONG_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-long, sub-long, mul-long, div-long, rem-long, and-long, or-long, + * xor-long, shl-long, shr-long, ushr-long + */ + /* binop vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG_WIDE a0, a2 # a0 <- vBB + GET_VREG_WIDE a1, a3 # a1 <- vCC + .if 1 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + ddiv a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a4 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_rem_long: /* 0x9f */ +/* File: mips64/op_rem_long.S */ +/* File: mips64/binopWide.S */ + /* + * Generic 64-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (LONG_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-long, sub-long, mul-long, div-long, rem-long, and-long, or-long, + * xor-long, shl-long, shr-long, ushr-long + */ + /* binop vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG_WIDE a0, a2 # a0 <- vBB + GET_VREG_WIDE a1, a3 # a1 <- vCC + .if 1 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + dmod a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a4 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_and_long: /* 0xa0 */ +/* File: mips64/op_and_long.S */ +/* File: mips64/binopWide.S */ + /* + * Generic 64-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (LONG_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-long, sub-long, mul-long, div-long, rem-long, and-long, or-long, + * xor-long, shl-long, shr-long, ushr-long + */ + /* binop vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG_WIDE a0, a2 # a0 <- vBB + GET_VREG_WIDE a1, a3 # a1 <- vCC + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + and a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a4 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_or_long: /* 0xa1 */ +/* File: mips64/op_or_long.S */ +/* File: mips64/binopWide.S */ + /* + * Generic 64-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (LONG_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-long, sub-long, mul-long, div-long, rem-long, and-long, or-long, + * xor-long, shl-long, shr-long, ushr-long + */ + /* binop vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG_WIDE a0, a2 # a0 <- vBB + GET_VREG_WIDE a1, a3 # a1 <- vCC + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + or a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a4 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_xor_long: /* 0xa2 */ +/* File: mips64/op_xor_long.S */ +/* File: mips64/binopWide.S */ + /* + * Generic 64-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (LONG_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-long, sub-long, mul-long, div-long, rem-long, and-long, or-long, + * xor-long, shl-long, shr-long, ushr-long + */ + /* binop vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG_WIDE a0, a2 # a0 <- vBB + GET_VREG_WIDE a1, a3 # a1 <- vCC + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + xor a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a4 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_shl_long: /* 0xa3 */ +/* File: mips64/op_shl_long.S */ +/* File: mips64/binopWide.S */ + /* + * Generic 64-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (LONG_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-long, sub-long, mul-long, div-long, rem-long, and-long, or-long, + * xor-long, shl-long, shr-long, ushr-long + */ + /* binop vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG_WIDE a0, a2 # a0 <- vBB + GET_VREG_WIDE a1, a3 # a1 <- vCC + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + dsll a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a4 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_shr_long: /* 0xa4 */ +/* File: mips64/op_shr_long.S */ +/* File: mips64/binopWide.S */ + /* + * Generic 64-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (LONG_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-long, sub-long, mul-long, div-long, rem-long, and-long, or-long, + * xor-long, shl-long, shr-long, ushr-long + */ + /* binop vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG_WIDE a0, a2 # a0 <- vBB + GET_VREG_WIDE a1, a3 # a1 <- vCC + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + dsra a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a4 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_ushr_long: /* 0xa5 */ +/* File: mips64/op_ushr_long.S */ +/* File: mips64/binopWide.S */ + /* + * Generic 64-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (LONG_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-long, sub-long, mul-long, div-long, rem-long, and-long, or-long, + * xor-long, shl-long, shr-long, ushr-long + */ + /* binop vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG_WIDE a0, a2 # a0 <- vBB + GET_VREG_WIDE a1, a3 # a1 <- vCC + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + dsrl a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a4 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_add_float: /* 0xa6 */ +/* File: mips64/op_add_float.S */ +/* File: mips64/fbinop.S */ + /*: + * Generic 32-bit floating-point operation. + * + * For: add-float, sub-float, mul-float, div-float. + * form: f0, f0, f1 + */ + /* binop vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG_FLOAT f0, a2 # f0 <- vBB + GET_VREG_FLOAT f1, a3 # f1 <- vCC + add.s f0, f0, f1 # f0 <- f0 op f1 + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_FLOAT f0, a4 # vAA <- f0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_sub_float: /* 0xa7 */ +/* File: mips64/op_sub_float.S */ +/* File: mips64/fbinop.S */ + /*: + * Generic 32-bit floating-point operation. + * + * For: add-float, sub-float, mul-float, div-float. + * form: f0, f0, f1 + */ + /* binop vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG_FLOAT f0, a2 # f0 <- vBB + GET_VREG_FLOAT f1, a3 # f1 <- vCC + sub.s f0, f0, f1 # f0 <- f0 op f1 + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_FLOAT f0, a4 # vAA <- f0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_float: /* 0xa8 */ +/* File: mips64/op_mul_float.S */ +/* File: mips64/fbinop.S */ + /*: + * Generic 32-bit floating-point operation. + * + * For: add-float, sub-float, mul-float, div-float. + * form: f0, f0, f1 + */ + /* binop vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG_FLOAT f0, a2 # f0 <- vBB + GET_VREG_FLOAT f1, a3 # f1 <- vCC + mul.s f0, f0, f1 # f0 <- f0 op f1 + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_FLOAT f0, a4 # vAA <- f0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_div_float: /* 0xa9 */ +/* File: mips64/op_div_float.S */ +/* File: mips64/fbinop.S */ + /*: + * Generic 32-bit floating-point operation. + * + * For: add-float, sub-float, mul-float, div-float. + * form: f0, f0, f1 + */ + /* binop vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG_FLOAT f0, a2 # f0 <- vBB + GET_VREG_FLOAT f1, a3 # f1 <- vCC + div.s f0, f0, f1 # f0 <- f0 op f1 + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_FLOAT f0, a4 # vAA <- f0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_rem_float: /* 0xaa */ +/* File: mips64/op_rem_float.S */ + /* rem-float vAA, vBB, vCC */ + .extern fmodf + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG_FLOAT f12, a2 # f12 <- vBB + GET_VREG_FLOAT f13, a3 # f13 <- vCC + jal fmodf # f0 <- f12 op f13 + srl a4, rINST, 8 # a4 <- AA + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_FLOAT f0, a4 # vAA <- f0 + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_add_double: /* 0xab */ +/* File: mips64/op_add_double.S */ +/* File: mips64/fbinopWide.S */ + /*: + * Generic 64-bit floating-point operation. + * + * For: add-double, sub-double, mul-double, div-double. + * form: f0, f0, f1 + */ + /* binop vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG_DOUBLE f0, a2 # f0 <- vBB + GET_VREG_DOUBLE f1, a3 # f1 <- vCC + add.d f0, f0, f1 # f0 <- f0 op f1 + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_DOUBLE f0, a4 # vAA <- f0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_sub_double: /* 0xac */ +/* File: mips64/op_sub_double.S */ +/* File: mips64/fbinopWide.S */ + /*: + * Generic 64-bit floating-point operation. + * + * For: add-double, sub-double, mul-double, div-double. + * form: f0, f0, f1 + */ + /* binop vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG_DOUBLE f0, a2 # f0 <- vBB + GET_VREG_DOUBLE f1, a3 # f1 <- vCC + sub.d f0, f0, f1 # f0 <- f0 op f1 + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_DOUBLE f0, a4 # vAA <- f0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_double: /* 0xad */ +/* File: mips64/op_mul_double.S */ +/* File: mips64/fbinopWide.S */ + /*: + * Generic 64-bit floating-point operation. + * + * For: add-double, sub-double, mul-double, div-double. + * form: f0, f0, f1 + */ + /* binop vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG_DOUBLE f0, a2 # f0 <- vBB + GET_VREG_DOUBLE f1, a3 # f1 <- vCC + mul.d f0, f0, f1 # f0 <- f0 op f1 + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_DOUBLE f0, a4 # vAA <- f0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_div_double: /* 0xae */ +/* File: mips64/op_div_double.S */ +/* File: mips64/fbinopWide.S */ + /*: + * Generic 64-bit floating-point operation. + * + * For: add-double, sub-double, mul-double, div-double. + * form: f0, f0, f1 + */ + /* binop vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG_DOUBLE f0, a2 # f0 <- vBB + GET_VREG_DOUBLE f1, a3 # f1 <- vCC + div.d f0, f0, f1 # f0 <- f0 op f1 + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_DOUBLE f0, a4 # vAA <- f0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_rem_double: /* 0xaf */ +/* File: mips64/op_rem_double.S */ + /* rem-double vAA, vBB, vCC */ + .extern fmod + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG_DOUBLE f12, a2 # f12 <- vBB + GET_VREG_DOUBLE f13, a3 # f13 <- vCC + jal fmod # f0 <- f12 op f13 + srl a4, rINST, 8 # a4 <- AA + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_DOUBLE f0, a4 # vAA <- f0 + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_add_int_2addr: /* 0xb0 */ +/* File: mips64/op_add_int_2addr.S */ +/* File: mips64/binop2addr.S */ + /* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vB (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, + * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, + * shl-int/2addr, shr-int/2addr, ushr-int/2addr + */ + /* binop/2addr vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG a0, a2 # a0 <- vA + GET_VREG a1, a3 # a1 <- vB + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + # optional op + addu a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_sub_int_2addr: /* 0xb1 */ +/* File: mips64/op_sub_int_2addr.S */ +/* File: mips64/binop2addr.S */ + /* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vB (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, + * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, + * shl-int/2addr, shr-int/2addr, ushr-int/2addr + */ + /* binop/2addr vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG a0, a2 # a0 <- vA + GET_VREG a1, a3 # a1 <- vB + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + # optional op + subu a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_int_2addr: /* 0xb2 */ +/* File: mips64/op_mul_int_2addr.S */ +/* File: mips64/binop2addr.S */ + /* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vB (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, + * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, + * shl-int/2addr, shr-int/2addr, ushr-int/2addr + */ + /* binop/2addr vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG a0, a2 # a0 <- vA + GET_VREG a1, a3 # a1 <- vB + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + # optional op + mul a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_div_int_2addr: /* 0xb3 */ +/* File: mips64/op_div_int_2addr.S */ +/* File: mips64/binop2addr.S */ + /* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vB (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, + * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, + * shl-int/2addr, shr-int/2addr, ushr-int/2addr + */ + /* binop/2addr vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG a0, a2 # a0 <- vA + GET_VREG a1, a3 # a1 <- vB + .if 1 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + # optional op + div a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_rem_int_2addr: /* 0xb4 */ +/* File: mips64/op_rem_int_2addr.S */ +/* File: mips64/binop2addr.S */ + /* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vB (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, + * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, + * shl-int/2addr, shr-int/2addr, ushr-int/2addr + */ + /* binop/2addr vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG a0, a2 # a0 <- vA + GET_VREG a1, a3 # a1 <- vB + .if 1 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + # optional op + mod a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_and_int_2addr: /* 0xb5 */ +/* File: mips64/op_and_int_2addr.S */ +/* File: mips64/binop2addr.S */ + /* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vB (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, + * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, + * shl-int/2addr, shr-int/2addr, ushr-int/2addr + */ + /* binop/2addr vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG a0, a2 # a0 <- vA + GET_VREG a1, a3 # a1 <- vB + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + # optional op + and a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_or_int_2addr: /* 0xb6 */ +/* File: mips64/op_or_int_2addr.S */ +/* File: mips64/binop2addr.S */ + /* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vB (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, + * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, + * shl-int/2addr, shr-int/2addr, ushr-int/2addr + */ + /* binop/2addr vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG a0, a2 # a0 <- vA + GET_VREG a1, a3 # a1 <- vB + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + # optional op + or a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_xor_int_2addr: /* 0xb7 */ +/* File: mips64/op_xor_int_2addr.S */ +/* File: mips64/binop2addr.S */ + /* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vB (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, + * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, + * shl-int/2addr, shr-int/2addr, ushr-int/2addr + */ + /* binop/2addr vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG a0, a2 # a0 <- vA + GET_VREG a1, a3 # a1 <- vB + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + # optional op + xor a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_shl_int_2addr: /* 0xb8 */ +/* File: mips64/op_shl_int_2addr.S */ +/* File: mips64/binop2addr.S */ + /* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vB (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, + * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, + * shl-int/2addr, shr-int/2addr, ushr-int/2addr + */ + /* binop/2addr vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG a0, a2 # a0 <- vA + GET_VREG a1, a3 # a1 <- vB + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + # optional op + sll a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_shr_int_2addr: /* 0xb9 */ +/* File: mips64/op_shr_int_2addr.S */ +/* File: mips64/binop2addr.S */ + /* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vB (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, + * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, + * shl-int/2addr, shr-int/2addr, ushr-int/2addr + */ + /* binop/2addr vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG a0, a2 # a0 <- vA + GET_VREG a1, a3 # a1 <- vB + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + # optional op + sra a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_ushr_int_2addr: /* 0xba */ +/* File: mips64/op_ushr_int_2addr.S */ +/* File: mips64/binop2addr.S */ + /* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vB (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, + * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, + * shl-int/2addr, shr-int/2addr, ushr-int/2addr + */ + /* binop/2addr vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG a0, a2 # a0 <- vA + GET_VREG a1, a3 # a1 <- vB + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + # optional op + srl a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_add_long_2addr: /* 0xbb */ +/* File: mips64/op_add_long_2addr.S */ +/* File: mips64/binopWide2addr.S */ + /* + * Generic 64-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vB (a1). Useful for integer division and modulus. Note that we + * *don't* check for (LONG_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-long/2addr, sub-long/2addr, mul-long/2addr, div-long/2addr, + * rem-long/2addr, and-long/2addr, or-long/2addr, xor-long/2addr, + * shl-long/2addr, shr-long/2addr, ushr-long/2addr + */ + /* binop/2addr vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG_WIDE a0, a2 # a0 <- vA + GET_VREG_WIDE a1, a3 # a1 <- vB + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + # optional op + daddu a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_sub_long_2addr: /* 0xbc */ +/* File: mips64/op_sub_long_2addr.S */ +/* File: mips64/binopWide2addr.S */ + /* + * Generic 64-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vB (a1). Useful for integer division and modulus. Note that we + * *don't* check for (LONG_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-long/2addr, sub-long/2addr, mul-long/2addr, div-long/2addr, + * rem-long/2addr, and-long/2addr, or-long/2addr, xor-long/2addr, + * shl-long/2addr, shr-long/2addr, ushr-long/2addr + */ + /* binop/2addr vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG_WIDE a0, a2 # a0 <- vA + GET_VREG_WIDE a1, a3 # a1 <- vB + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + # optional op + dsubu a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_long_2addr: /* 0xbd */ +/* File: mips64/op_mul_long_2addr.S */ +/* File: mips64/binopWide2addr.S */ + /* + * Generic 64-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vB (a1). Useful for integer division and modulus. Note that we + * *don't* check for (LONG_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-long/2addr, sub-long/2addr, mul-long/2addr, div-long/2addr, + * rem-long/2addr, and-long/2addr, or-long/2addr, xor-long/2addr, + * shl-long/2addr, shr-long/2addr, ushr-long/2addr + */ + /* binop/2addr vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG_WIDE a0, a2 # a0 <- vA + GET_VREG_WIDE a1, a3 # a1 <- vB + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + # optional op + dmul a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_div_long_2addr: /* 0xbe */ +/* File: mips64/op_div_long_2addr.S */ +/* File: mips64/binopWide2addr.S */ + /* + * Generic 64-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vB (a1). Useful for integer division and modulus. Note that we + * *don't* check for (LONG_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-long/2addr, sub-long/2addr, mul-long/2addr, div-long/2addr, + * rem-long/2addr, and-long/2addr, or-long/2addr, xor-long/2addr, + * shl-long/2addr, shr-long/2addr, ushr-long/2addr + */ + /* binop/2addr vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG_WIDE a0, a2 # a0 <- vA + GET_VREG_WIDE a1, a3 # a1 <- vB + .if 1 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + # optional op + ddiv a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_rem_long_2addr: /* 0xbf */ +/* File: mips64/op_rem_long_2addr.S */ +/* File: mips64/binopWide2addr.S */ + /* + * Generic 64-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vB (a1). Useful for integer division and modulus. Note that we + * *don't* check for (LONG_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-long/2addr, sub-long/2addr, mul-long/2addr, div-long/2addr, + * rem-long/2addr, and-long/2addr, or-long/2addr, xor-long/2addr, + * shl-long/2addr, shr-long/2addr, ushr-long/2addr + */ + /* binop/2addr vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG_WIDE a0, a2 # a0 <- vA + GET_VREG_WIDE a1, a3 # a1 <- vB + .if 1 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + # optional op + dmod a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_and_long_2addr: /* 0xc0 */ +/* File: mips64/op_and_long_2addr.S */ +/* File: mips64/binopWide2addr.S */ + /* + * Generic 64-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vB (a1). Useful for integer division and modulus. Note that we + * *don't* check for (LONG_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-long/2addr, sub-long/2addr, mul-long/2addr, div-long/2addr, + * rem-long/2addr, and-long/2addr, or-long/2addr, xor-long/2addr, + * shl-long/2addr, shr-long/2addr, ushr-long/2addr + */ + /* binop/2addr vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG_WIDE a0, a2 # a0 <- vA + GET_VREG_WIDE a1, a3 # a1 <- vB + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + # optional op + and a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_or_long_2addr: /* 0xc1 */ +/* File: mips64/op_or_long_2addr.S */ +/* File: mips64/binopWide2addr.S */ + /* + * Generic 64-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vB (a1). Useful for integer division and modulus. Note that we + * *don't* check for (LONG_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-long/2addr, sub-long/2addr, mul-long/2addr, div-long/2addr, + * rem-long/2addr, and-long/2addr, or-long/2addr, xor-long/2addr, + * shl-long/2addr, shr-long/2addr, ushr-long/2addr + */ + /* binop/2addr vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG_WIDE a0, a2 # a0 <- vA + GET_VREG_WIDE a1, a3 # a1 <- vB + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + # optional op + or a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_xor_long_2addr: /* 0xc2 */ +/* File: mips64/op_xor_long_2addr.S */ +/* File: mips64/binopWide2addr.S */ + /* + * Generic 64-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vB (a1). Useful for integer division and modulus. Note that we + * *don't* check for (LONG_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-long/2addr, sub-long/2addr, mul-long/2addr, div-long/2addr, + * rem-long/2addr, and-long/2addr, or-long/2addr, xor-long/2addr, + * shl-long/2addr, shr-long/2addr, ushr-long/2addr + */ + /* binop/2addr vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG_WIDE a0, a2 # a0 <- vA + GET_VREG_WIDE a1, a3 # a1 <- vB + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + # optional op + xor a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_shl_long_2addr: /* 0xc3 */ +/* File: mips64/op_shl_long_2addr.S */ +/* File: mips64/binopWide2addr.S */ + /* + * Generic 64-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vB (a1). Useful for integer division and modulus. Note that we + * *don't* check for (LONG_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-long/2addr, sub-long/2addr, mul-long/2addr, div-long/2addr, + * rem-long/2addr, and-long/2addr, or-long/2addr, xor-long/2addr, + * shl-long/2addr, shr-long/2addr, ushr-long/2addr + */ + /* binop/2addr vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG_WIDE a0, a2 # a0 <- vA + GET_VREG_WIDE a1, a3 # a1 <- vB + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + # optional op + dsll a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_shr_long_2addr: /* 0xc4 */ +/* File: mips64/op_shr_long_2addr.S */ +/* File: mips64/binopWide2addr.S */ + /* + * Generic 64-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vB (a1). Useful for integer division and modulus. Note that we + * *don't* check for (LONG_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-long/2addr, sub-long/2addr, mul-long/2addr, div-long/2addr, + * rem-long/2addr, and-long/2addr, or-long/2addr, xor-long/2addr, + * shl-long/2addr, shr-long/2addr, ushr-long/2addr + */ + /* binop/2addr vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG_WIDE a0, a2 # a0 <- vA + GET_VREG_WIDE a1, a3 # a1 <- vB + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + # optional op + dsra a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_ushr_long_2addr: /* 0xc5 */ +/* File: mips64/op_ushr_long_2addr.S */ +/* File: mips64/binopWide2addr.S */ + /* + * Generic 64-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vB (a1). Useful for integer division and modulus. Note that we + * *don't* check for (LONG_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-long/2addr, sub-long/2addr, mul-long/2addr, div-long/2addr, + * rem-long/2addr, and-long/2addr, or-long/2addr, xor-long/2addr, + * shl-long/2addr, shr-long/2addr, ushr-long/2addr + */ + /* binop/2addr vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG_WIDE a0, a2 # a0 <- vA + GET_VREG_WIDE a1, a3 # a1 <- vB + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + # optional op + dsrl a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_add_float_2addr: /* 0xc6 */ +/* File: mips64/op_add_float_2addr.S */ +/* File: mips64/fbinop2addr.S */ + /*: + * Generic 32-bit "/2addr" floating-point operation. + * + * For: add-float/2addr, sub-float/2addr, mul-float/2addr, div-float/2addr. + * form: f0, f0, f1 + */ + /* binop/2addr vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG_FLOAT f0, a2 # f0 <- vA + GET_VREG_FLOAT f1, a3 # f1 <- vB + add.s f0, f0, f1 # f0 <- f0 op f1 + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_FLOAT f0, a2 # vA <- f0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_sub_float_2addr: /* 0xc7 */ +/* File: mips64/op_sub_float_2addr.S */ +/* File: mips64/fbinop2addr.S */ + /*: + * Generic 32-bit "/2addr" floating-point operation. + * + * For: add-float/2addr, sub-float/2addr, mul-float/2addr, div-float/2addr. + * form: f0, f0, f1 + */ + /* binop/2addr vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG_FLOAT f0, a2 # f0 <- vA + GET_VREG_FLOAT f1, a3 # f1 <- vB + sub.s f0, f0, f1 # f0 <- f0 op f1 + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_FLOAT f0, a2 # vA <- f0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_float_2addr: /* 0xc8 */ +/* File: mips64/op_mul_float_2addr.S */ +/* File: mips64/fbinop2addr.S */ + /*: + * Generic 32-bit "/2addr" floating-point operation. + * + * For: add-float/2addr, sub-float/2addr, mul-float/2addr, div-float/2addr. + * form: f0, f0, f1 + */ + /* binop/2addr vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG_FLOAT f0, a2 # f0 <- vA + GET_VREG_FLOAT f1, a3 # f1 <- vB + mul.s f0, f0, f1 # f0 <- f0 op f1 + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_FLOAT f0, a2 # vA <- f0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_div_float_2addr: /* 0xc9 */ +/* File: mips64/op_div_float_2addr.S */ +/* File: mips64/fbinop2addr.S */ + /*: + * Generic 32-bit "/2addr" floating-point operation. + * + * For: add-float/2addr, sub-float/2addr, mul-float/2addr, div-float/2addr. + * form: f0, f0, f1 + */ + /* binop/2addr vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG_FLOAT f0, a2 # f0 <- vA + GET_VREG_FLOAT f1, a3 # f1 <- vB + div.s f0, f0, f1 # f0 <- f0 op f1 + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_FLOAT f0, a2 # vA <- f0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_rem_float_2addr: /* 0xca */ +/* File: mips64/op_rem_float_2addr.S */ + /* rem-float/2addr vA, vB */ + .extern fmodf + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG_FLOAT f12, a2 # f12 <- vA + GET_VREG_FLOAT f13, a3 # f13 <- vB + jal fmodf # f0 <- f12 op f13 + ext a2, rINST, 8, 4 # a2 <- A + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_FLOAT f0, a2 # vA <- f0 + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_add_double_2addr: /* 0xcb */ +/* File: mips64/op_add_double_2addr.S */ +/* File: mips64/fbinopWide2addr.S */ + /*: + * Generic 64-bit "/2addr" floating-point operation. + * + * For: add-double/2addr, sub-double/2addr, mul-double/2addr, div-double/2addr. + * form: f0, f0, f1 + */ + /* binop/2addr vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG_DOUBLE f0, a2 # f0 <- vA + GET_VREG_DOUBLE f1, a3 # f1 <- vB + add.d f0, f0, f1 # f0 <- f0 op f1 + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_DOUBLE f0, a2 # vA <- f0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_sub_double_2addr: /* 0xcc */ +/* File: mips64/op_sub_double_2addr.S */ +/* File: mips64/fbinopWide2addr.S */ + /*: + * Generic 64-bit "/2addr" floating-point operation. + * + * For: add-double/2addr, sub-double/2addr, mul-double/2addr, div-double/2addr. + * form: f0, f0, f1 + */ + /* binop/2addr vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG_DOUBLE f0, a2 # f0 <- vA + GET_VREG_DOUBLE f1, a3 # f1 <- vB + sub.d f0, f0, f1 # f0 <- f0 op f1 + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_DOUBLE f0, a2 # vA <- f0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_double_2addr: /* 0xcd */ +/* File: mips64/op_mul_double_2addr.S */ +/* File: mips64/fbinopWide2addr.S */ + /*: + * Generic 64-bit "/2addr" floating-point operation. + * + * For: add-double/2addr, sub-double/2addr, mul-double/2addr, div-double/2addr. + * form: f0, f0, f1 + */ + /* binop/2addr vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG_DOUBLE f0, a2 # f0 <- vA + GET_VREG_DOUBLE f1, a3 # f1 <- vB + mul.d f0, f0, f1 # f0 <- f0 op f1 + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_DOUBLE f0, a2 # vA <- f0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_div_double_2addr: /* 0xce */ +/* File: mips64/op_div_double_2addr.S */ +/* File: mips64/fbinopWide2addr.S */ + /*: + * Generic 64-bit "/2addr" floating-point operation. + * + * For: add-double/2addr, sub-double/2addr, mul-double/2addr, div-double/2addr. + * form: f0, f0, f1 + */ + /* binop/2addr vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG_DOUBLE f0, a2 # f0 <- vA + GET_VREG_DOUBLE f1, a3 # f1 <- vB + div.d f0, f0, f1 # f0 <- f0 op f1 + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_DOUBLE f0, a2 # vA <- f0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_rem_double_2addr: /* 0xcf */ +/* File: mips64/op_rem_double_2addr.S */ + /* rem-double/2addr vA, vB */ + .extern fmod + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG_DOUBLE f12, a2 # f12 <- vA + GET_VREG_DOUBLE f13, a3 # f13 <- vB + jal fmod # f0 <- f12 op f13 + ext a2, rINST, 8, 4 # a2 <- A + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_DOUBLE f0, a2 # vA <- f0 + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_add_int_lit16: /* 0xd0 */ +/* File: mips64/op_add_int_lit16.S */ +/* File: mips64/binopLit16.S */ + /* + * Generic 32-bit "lit16" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * CCCC (a1). Useful for integer division and modulus. + * + * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16, + * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16 + */ + /* binop/lit16 vA, vB, #+CCCC */ + lh a1, 2(rPC) # a1 <- sign-extended CCCC + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG a0, a3 # a0 <- vB + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + addu a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + + +/* ------------------------------ */ + .balign 128 +.L_op_rsub_int: /* 0xd1 */ +/* File: mips64/op_rsub_int.S */ +/* File: mips64/binopLit16.S */ + /* + * Generic 32-bit "lit16" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * CCCC (a1). Useful for integer division and modulus. + * + * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16, + * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16 + */ + /* binop/lit16 vA, vB, #+CCCC */ + lh a1, 2(rPC) # a1 <- sign-extended CCCC + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG a0, a3 # a0 <- vB + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + subu a0, a1, a0 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_int_lit16: /* 0xd2 */ +/* File: mips64/op_mul_int_lit16.S */ +/* File: mips64/binopLit16.S */ + /* + * Generic 32-bit "lit16" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * CCCC (a1). Useful for integer division and modulus. + * + * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16, + * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16 + */ + /* binop/lit16 vA, vB, #+CCCC */ + lh a1, 2(rPC) # a1 <- sign-extended CCCC + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG a0, a3 # a0 <- vB + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + mul a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + + +/* ------------------------------ */ + .balign 128 +.L_op_div_int_lit16: /* 0xd3 */ +/* File: mips64/op_div_int_lit16.S */ +/* File: mips64/binopLit16.S */ + /* + * Generic 32-bit "lit16" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * CCCC (a1). Useful for integer division and modulus. + * + * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16, + * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16 + */ + /* binop/lit16 vA, vB, #+CCCC */ + lh a1, 2(rPC) # a1 <- sign-extended CCCC + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG a0, a3 # a0 <- vB + .if 1 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + div a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + + +/* ------------------------------ */ + .balign 128 +.L_op_rem_int_lit16: /* 0xd4 */ +/* File: mips64/op_rem_int_lit16.S */ +/* File: mips64/binopLit16.S */ + /* + * Generic 32-bit "lit16" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * CCCC (a1). Useful for integer division and modulus. + * + * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16, + * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16 + */ + /* binop/lit16 vA, vB, #+CCCC */ + lh a1, 2(rPC) # a1 <- sign-extended CCCC + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG a0, a3 # a0 <- vB + .if 1 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + mod a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + + +/* ------------------------------ */ + .balign 128 +.L_op_and_int_lit16: /* 0xd5 */ +/* File: mips64/op_and_int_lit16.S */ +/* File: mips64/binopLit16.S */ + /* + * Generic 32-bit "lit16" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * CCCC (a1). Useful for integer division and modulus. + * + * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16, + * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16 + */ + /* binop/lit16 vA, vB, #+CCCC */ + lh a1, 2(rPC) # a1 <- sign-extended CCCC + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG a0, a3 # a0 <- vB + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + and a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + + +/* ------------------------------ */ + .balign 128 +.L_op_or_int_lit16: /* 0xd6 */ +/* File: mips64/op_or_int_lit16.S */ +/* File: mips64/binopLit16.S */ + /* + * Generic 32-bit "lit16" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * CCCC (a1). Useful for integer division and modulus. + * + * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16, + * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16 + */ + /* binop/lit16 vA, vB, #+CCCC */ + lh a1, 2(rPC) # a1 <- sign-extended CCCC + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG a0, a3 # a0 <- vB + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + or a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + + +/* ------------------------------ */ + .balign 128 +.L_op_xor_int_lit16: /* 0xd7 */ +/* File: mips64/op_xor_int_lit16.S */ +/* File: mips64/binopLit16.S */ + /* + * Generic 32-bit "lit16" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * CCCC (a1). Useful for integer division and modulus. + * + * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16, + * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16 + */ + /* binop/lit16 vA, vB, #+CCCC */ + lh a1, 2(rPC) # a1 <- sign-extended CCCC + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG a0, a3 # a0 <- vB + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + xor a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + + +/* ------------------------------ */ + .balign 128 +.L_op_add_int_lit8: /* 0xd8 */ +/* File: mips64/op_add_int_lit8.S */ +/* File: mips64/binopLit8.S */ + /* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * CC (a1). Useful for integer division and modulus. + * + * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8, + * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + /* binop/lit8 vAA, vBB, #+CC */ + lbu a3, 2(rPC) # a3 <- BB + lb a1, 3(rPC) # a1 <- sign-extended CC + srl a2, rINST, 8 # a2 <- AA + GET_VREG a0, a3 # a0 <- vBB + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + addu a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + + +/* ------------------------------ */ + .balign 128 +.L_op_rsub_int_lit8: /* 0xd9 */ +/* File: mips64/op_rsub_int_lit8.S */ +/* File: mips64/binopLit8.S */ + /* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * CC (a1). Useful for integer division and modulus. + * + * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8, + * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + /* binop/lit8 vAA, vBB, #+CC */ + lbu a3, 2(rPC) # a3 <- BB + lb a1, 3(rPC) # a1 <- sign-extended CC + srl a2, rINST, 8 # a2 <- AA + GET_VREG a0, a3 # a0 <- vBB + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + subu a0, a1, a0 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_int_lit8: /* 0xda */ +/* File: mips64/op_mul_int_lit8.S */ +/* File: mips64/binopLit8.S */ + /* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * CC (a1). Useful for integer division and modulus. + * + * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8, + * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + /* binop/lit8 vAA, vBB, #+CC */ + lbu a3, 2(rPC) # a3 <- BB + lb a1, 3(rPC) # a1 <- sign-extended CC + srl a2, rINST, 8 # a2 <- AA + GET_VREG a0, a3 # a0 <- vBB + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + mul a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + + +/* ------------------------------ */ + .balign 128 +.L_op_div_int_lit8: /* 0xdb */ +/* File: mips64/op_div_int_lit8.S */ +/* File: mips64/binopLit8.S */ + /* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * CC (a1). Useful for integer division and modulus. + * + * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8, + * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + /* binop/lit8 vAA, vBB, #+CC */ + lbu a3, 2(rPC) # a3 <- BB + lb a1, 3(rPC) # a1 <- sign-extended CC + srl a2, rINST, 8 # a2 <- AA + GET_VREG a0, a3 # a0 <- vBB + .if 1 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + div a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + + +/* ------------------------------ */ + .balign 128 +.L_op_rem_int_lit8: /* 0xdc */ +/* File: mips64/op_rem_int_lit8.S */ +/* File: mips64/binopLit8.S */ + /* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * CC (a1). Useful for integer division and modulus. + * + * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8, + * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + /* binop/lit8 vAA, vBB, #+CC */ + lbu a3, 2(rPC) # a3 <- BB + lb a1, 3(rPC) # a1 <- sign-extended CC + srl a2, rINST, 8 # a2 <- AA + GET_VREG a0, a3 # a0 <- vBB + .if 1 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + mod a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + + +/* ------------------------------ */ + .balign 128 +.L_op_and_int_lit8: /* 0xdd */ +/* File: mips64/op_and_int_lit8.S */ +/* File: mips64/binopLit8.S */ + /* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * CC (a1). Useful for integer division and modulus. + * + * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8, + * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + /* binop/lit8 vAA, vBB, #+CC */ + lbu a3, 2(rPC) # a3 <- BB + lb a1, 3(rPC) # a1 <- sign-extended CC + srl a2, rINST, 8 # a2 <- AA + GET_VREG a0, a3 # a0 <- vBB + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + and a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + + +/* ------------------------------ */ + .balign 128 +.L_op_or_int_lit8: /* 0xde */ +/* File: mips64/op_or_int_lit8.S */ +/* File: mips64/binopLit8.S */ + /* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * CC (a1). Useful for integer division and modulus. + * + * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8, + * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + /* binop/lit8 vAA, vBB, #+CC */ + lbu a3, 2(rPC) # a3 <- BB + lb a1, 3(rPC) # a1 <- sign-extended CC + srl a2, rINST, 8 # a2 <- AA + GET_VREG a0, a3 # a0 <- vBB + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + or a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + + +/* ------------------------------ */ + .balign 128 +.L_op_xor_int_lit8: /* 0xdf */ +/* File: mips64/op_xor_int_lit8.S */ +/* File: mips64/binopLit8.S */ + /* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * CC (a1). Useful for integer division and modulus. + * + * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8, + * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + /* binop/lit8 vAA, vBB, #+CC */ + lbu a3, 2(rPC) # a3 <- BB + lb a1, 3(rPC) # a1 <- sign-extended CC + srl a2, rINST, 8 # a2 <- AA + GET_VREG a0, a3 # a0 <- vBB + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + xor a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + + +/* ------------------------------ */ + .balign 128 +.L_op_shl_int_lit8: /* 0xe0 */ +/* File: mips64/op_shl_int_lit8.S */ +/* File: mips64/binopLit8.S */ + /* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * CC (a1). Useful for integer division and modulus. + * + * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8, + * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + /* binop/lit8 vAA, vBB, #+CC */ + lbu a3, 2(rPC) # a3 <- BB + lb a1, 3(rPC) # a1 <- sign-extended CC + srl a2, rINST, 8 # a2 <- AA + GET_VREG a0, a3 # a0 <- vBB + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + sll a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + + +/* ------------------------------ */ + .balign 128 +.L_op_shr_int_lit8: /* 0xe1 */ +/* File: mips64/op_shr_int_lit8.S */ +/* File: mips64/binopLit8.S */ + /* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * CC (a1). Useful for integer division and modulus. + * + * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8, + * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + /* binop/lit8 vAA, vBB, #+CC */ + lbu a3, 2(rPC) # a3 <- BB + lb a1, 3(rPC) # a1 <- sign-extended CC + srl a2, rINST, 8 # a2 <- AA + GET_VREG a0, a3 # a0 <- vBB + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + sra a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + + +/* ------------------------------ */ + .balign 128 +.L_op_ushr_int_lit8: /* 0xe2 */ +/* File: mips64/op_ushr_int_lit8.S */ +/* File: mips64/binopLit8.S */ + /* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * CC (a1). Useful for integer division and modulus. + * + * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8, + * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + /* binop/lit8 vAA, vBB, #+CC */ + lbu a3, 2(rPC) # a3 <- BB + lb a1, 3(rPC) # a1 <- sign-extended CC + srl a2, rINST, 8 # a2 <- AA + GET_VREG a0, a3 # a0 <- vBB + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + srl a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_quick: /* 0xe3 */ +/* File: mips64/op_iget_quick.S */ + /* For: iget-quick, iget-boolean-quick, iget-byte-quick, iget-char-quick, iget-short-quick */ + /* op vA, vB, offset//CCCC */ + srl a2, rINST, 12 # a2 <- B + lhu a1, 2(rPC) # a1 <- field byte offset + GET_VREG_U a3, a2 # a3 <- object we're operating on + ext a4, rINST, 8, 4 # a4 <- A + daddu a1, a1, a3 + beqz a3, common_errNullObject # object was null + lw a0, 0(a1) # a0 <- obj.field + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + SET_VREG a0, a4 # fp[A] <- a0 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_iget_wide_quick: /* 0xe4 */ +/* File: mips64/op_iget_wide_quick.S */ + /* iget-wide-quick vA, vB, offset//CCCC */ + srl a2, rINST, 12 # a2 <- B + lhu a4, 2(rPC) # a4 <- field byte offset + GET_VREG_U a3, a2 # a3 <- object we're operating on + ext a2, rINST, 8, 4 # a2 <- A + beqz a3, common_errNullObject # object was null + daddu a4, a3, a4 # create direct pointer + lw a0, 0(a4) + lw a1, 4(a4) + dinsu a0, a1, 32, 32 + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + SET_VREG_WIDE a0, a2 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_iget_object_quick: /* 0xe5 */ +/* File: mips64/op_iget_object_quick.S */ + /* For: iget-object-quick */ + /* op vA, vB, offset//CCCC */ + .extern artIGetObjectFromMterp + srl a2, rINST, 12 # a2 <- B + lhu a1, 2(rPC) # a1 <- field byte offset + EXPORT_PC + GET_VREG_U a0, a2 # a0 <- object we're operating on + jal artIGetObjectFromMterp # (obj, offset) + ld a3, THREAD_EXCEPTION_OFFSET(rSELF) + ext a2, rINST, 8, 4 # a2 <- A + PREFETCH_INST 2 + bnez a3, MterpPossibleException # bail out + SET_VREG_OBJECT v0, a2 # fp[A] <- v0 + ADVANCE 2 # advance rPC + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_iput_quick: /* 0xe6 */ +/* File: mips64/op_iput_quick.S */ + /* For: iput-quick, iput-boolean-quick, iput-byte-quick, iput-char-quick, iput-short-quick */ + /* op vA, vB, offset//CCCC */ + srl a2, rINST, 12 # a2 <- B + lhu a1, 2(rPC) # a1 <- field byte offset + GET_VREG_U a3, a2 # a3 <- fp[B], the object pointer + ext a2, rINST, 8, 4 # a2 <- A + beqz a3, common_errNullObject # object was null + GET_VREG a0, a2 # a0 <- fp[A] + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + daddu a1, a1, a3 + sw a0, 0(a1) # obj.field <- a0 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_iput_wide_quick: /* 0xe7 */ +/* File: mips64/op_iput_wide_quick.S */ + /* iput-wide-quick vA, vB, offset//CCCC */ + srl a2, rINST, 12 # a2 <- B + lhu a3, 2(rPC) # a3 <- field byte offset + GET_VREG_U a2, a2 # a2 <- fp[B], the object pointer + ext a0, rINST, 8, 4 # a0 <- A + beqz a2, common_errNullObject # object was null + GET_VREG_WIDE a0, a0 # a0 <- fp[A] + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + daddu a1, a2, a3 # create a direct pointer + sw a0, 0(a1) + dsrl32 a0, a0, 0 + sw a0, 4(a1) + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_iput_object_quick: /* 0xe8 */ +/* File: mips64/op_iput_object_quick.S */ + .extern MterpIputObjectQuick + EXPORT_PC + daddu a0, rFP, OFF_FP_SHADOWFRAME + move a1, rPC + move a2, rINST + jal MterpIputObjectQuick + beqzc v0, MterpException + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_virtual_quick: /* 0xe9 */ +/* File: mips64/op_invoke_virtual_quick.S */ +/* File: mips64/invoke.S */ + /* + * Generic invoke handler wrapper. + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + .extern MterpInvokeVirtualQuick + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rPC + move a3, rINST + jal MterpInvokeVirtualQuick + beqzc v0, MterpException + FETCH_ADVANCE_INST 3 + GET_INST_OPCODE v0 + GOTO_OPCODE v0 + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_virtual_range_quick: /* 0xea */ +/* File: mips64/op_invoke_virtual_range_quick.S */ +/* File: mips64/invoke.S */ + /* + * Generic invoke handler wrapper. + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + .extern MterpInvokeVirtualQuickRange + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rPC + move a3, rINST + jal MterpInvokeVirtualQuickRange + beqzc v0, MterpException + FETCH_ADVANCE_INST 3 + GET_INST_OPCODE v0 + GOTO_OPCODE v0 + + +/* ------------------------------ */ + .balign 128 +.L_op_iput_boolean_quick: /* 0xeb */ +/* File: mips64/op_iput_boolean_quick.S */ +/* File: mips64/op_iput_quick.S */ + /* For: iput-quick, iput-boolean-quick, iput-byte-quick, iput-char-quick, iput-short-quick */ + /* op vA, vB, offset//CCCC */ + srl a2, rINST, 12 # a2 <- B + lhu a1, 2(rPC) # a1 <- field byte offset + GET_VREG_U a3, a2 # a3 <- fp[B], the object pointer + ext a2, rINST, 8, 4 # a2 <- A + beqz a3, common_errNullObject # object was null + GET_VREG a0, a2 # a0 <- fp[A] + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + daddu a1, a1, a3 + sb a0, 0(a1) # obj.field <- a0 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_iput_byte_quick: /* 0xec */ +/* File: mips64/op_iput_byte_quick.S */ +/* File: mips64/op_iput_quick.S */ + /* For: iput-quick, iput-boolean-quick, iput-byte-quick, iput-char-quick, iput-short-quick */ + /* op vA, vB, offset//CCCC */ + srl a2, rINST, 12 # a2 <- B + lhu a1, 2(rPC) # a1 <- field byte offset + GET_VREG_U a3, a2 # a3 <- fp[B], the object pointer + ext a2, rINST, 8, 4 # a2 <- A + beqz a3, common_errNullObject # object was null + GET_VREG a0, a2 # a0 <- fp[A] + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + daddu a1, a1, a3 + sb a0, 0(a1) # obj.field <- a0 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_iput_char_quick: /* 0xed */ +/* File: mips64/op_iput_char_quick.S */ +/* File: mips64/op_iput_quick.S */ + /* For: iput-quick, iput-boolean-quick, iput-byte-quick, iput-char-quick, iput-short-quick */ + /* op vA, vB, offset//CCCC */ + srl a2, rINST, 12 # a2 <- B + lhu a1, 2(rPC) # a1 <- field byte offset + GET_VREG_U a3, a2 # a3 <- fp[B], the object pointer + ext a2, rINST, 8, 4 # a2 <- A + beqz a3, common_errNullObject # object was null + GET_VREG a0, a2 # a0 <- fp[A] + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + daddu a1, a1, a3 + sh a0, 0(a1) # obj.field <- a0 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_iput_short_quick: /* 0xee */ +/* File: mips64/op_iput_short_quick.S */ +/* File: mips64/op_iput_quick.S */ + /* For: iput-quick, iput-boolean-quick, iput-byte-quick, iput-char-quick, iput-short-quick */ + /* op vA, vB, offset//CCCC */ + srl a2, rINST, 12 # a2 <- B + lhu a1, 2(rPC) # a1 <- field byte offset + GET_VREG_U a3, a2 # a3 <- fp[B], the object pointer + ext a2, rINST, 8, 4 # a2 <- A + beqz a3, common_errNullObject # object was null + GET_VREG a0, a2 # a0 <- fp[A] + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + daddu a1, a1, a3 + sh a0, 0(a1) # obj.field <- a0 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_boolean_quick: /* 0xef */ +/* File: mips64/op_iget_boolean_quick.S */ +/* File: mips64/op_iget_quick.S */ + /* For: iget-quick, iget-boolean-quick, iget-byte-quick, iget-char-quick, iget-short-quick */ + /* op vA, vB, offset//CCCC */ + srl a2, rINST, 12 # a2 <- B + lhu a1, 2(rPC) # a1 <- field byte offset + GET_VREG_U a3, a2 # a3 <- object we're operating on + ext a4, rINST, 8, 4 # a4 <- A + daddu a1, a1, a3 + beqz a3, common_errNullObject # object was null + lbu a0, 0(a1) # a0 <- obj.field + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + SET_VREG a0, a4 # fp[A] <- a0 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_byte_quick: /* 0xf0 */ +/* File: mips64/op_iget_byte_quick.S */ +/* File: mips64/op_iget_quick.S */ + /* For: iget-quick, iget-boolean-quick, iget-byte-quick, iget-char-quick, iget-short-quick */ + /* op vA, vB, offset//CCCC */ + srl a2, rINST, 12 # a2 <- B + lhu a1, 2(rPC) # a1 <- field byte offset + GET_VREG_U a3, a2 # a3 <- object we're operating on + ext a4, rINST, 8, 4 # a4 <- A + daddu a1, a1, a3 + beqz a3, common_errNullObject # object was null + lb a0, 0(a1) # a0 <- obj.field + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + SET_VREG a0, a4 # fp[A] <- a0 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_char_quick: /* 0xf1 */ +/* File: mips64/op_iget_char_quick.S */ +/* File: mips64/op_iget_quick.S */ + /* For: iget-quick, iget-boolean-quick, iget-byte-quick, iget-char-quick, iget-short-quick */ + /* op vA, vB, offset//CCCC */ + srl a2, rINST, 12 # a2 <- B + lhu a1, 2(rPC) # a1 <- field byte offset + GET_VREG_U a3, a2 # a3 <- object we're operating on + ext a4, rINST, 8, 4 # a4 <- A + daddu a1, a1, a3 + beqz a3, common_errNullObject # object was null + lhu a0, 0(a1) # a0 <- obj.field + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + SET_VREG a0, a4 # fp[A] <- a0 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_short_quick: /* 0xf2 */ +/* File: mips64/op_iget_short_quick.S */ +/* File: mips64/op_iget_quick.S */ + /* For: iget-quick, iget-boolean-quick, iget-byte-quick, iget-char-quick, iget-short-quick */ + /* op vA, vB, offset//CCCC */ + srl a2, rINST, 12 # a2 <- B + lhu a1, 2(rPC) # a1 <- field byte offset + GET_VREG_U a3, a2 # a3 <- object we're operating on + ext a4, rINST, 8, 4 # a4 <- A + daddu a1, a1, a3 + beqz a3, common_errNullObject # object was null + lh a0, 0(a1) # a0 <- obj.field + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + SET_VREG a0, a4 # fp[A] <- a0 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_lambda: /* 0xf3 */ +/* Transfer stub to alternate interpreter */ + b MterpFallback + +/* ------------------------------ */ + .balign 128 +.L_op_unused_f4: /* 0xf4 */ +/* File: mips64/op_unused_f4.S */ +/* File: mips64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + b MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_capture_variable: /* 0xf5 */ +/* Transfer stub to alternate interpreter */ + b MterpFallback + +/* ------------------------------ */ + .balign 128 +.L_op_create_lambda: /* 0xf6 */ +/* Transfer stub to alternate interpreter */ + b MterpFallback + +/* ------------------------------ */ + .balign 128 +.L_op_liberate_variable: /* 0xf7 */ +/* Transfer stub to alternate interpreter */ + b MterpFallback + +/* ------------------------------ */ + .balign 128 +.L_op_box_lambda: /* 0xf8 */ +/* Transfer stub to alternate interpreter */ + b MterpFallback + +/* ------------------------------ */ + .balign 128 +.L_op_unbox_lambda: /* 0xf9 */ +/* Transfer stub to alternate interpreter */ + b MterpFallback + +/* ------------------------------ */ + .balign 128 +.L_op_unused_fa: /* 0xfa */ +/* File: mips64/op_unused_fa.S */ +/* File: mips64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + b MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_fb: /* 0xfb */ +/* File: mips64/op_unused_fb.S */ +/* File: mips64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + b MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_fc: /* 0xfc */ +/* File: mips64/op_unused_fc.S */ +/* File: mips64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + b MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_fd: /* 0xfd */ +/* File: mips64/op_unused_fd.S */ +/* File: mips64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + b MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_fe: /* 0xfe */ +/* File: mips64/op_unused_fe.S */ +/* File: mips64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + b MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_ff: /* 0xff */ +/* File: mips64/op_unused_ff.S */ +/* File: mips64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + b MterpFallback + + + .balign 128 + .size artMterpAsmInstructionStart, .-artMterpAsmInstructionStart + .global artMterpAsmInstructionEnd +artMterpAsmInstructionEnd: + +/* + * =========================================================================== + * Sister implementations + * =========================================================================== + */ + .global artMterpAsmSisterStart + .type artMterpAsmSisterStart, %function + .text + .balign 4 +artMterpAsmSisterStart: + +/* continuation for op_float_to_int */ +.Lop_float_to_int_trunc: + trunc.w.s f0, f0 + mfc1 t0, f0 +.Lop_float_to_int_done: + /* Can't include fcvtFooter.S after break */ + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG t0, a1 + GOTO_OPCODE v0 # jump to next instruction + +/* continuation for op_float_to_long */ +.Lop_float_to_long_trunc: + trunc.l.s f0, f0 + dmfc1 t0, f0 +.Lop_float_to_long_done: + /* Can't include fcvtFooter.S after break */ + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE t0, a1 + GOTO_OPCODE v0 # jump to next instruction + +/* continuation for op_double_to_int */ +.Lop_double_to_int_trunc: + trunc.w.d f0, f0 + mfc1 t0, f0 +.Lop_double_to_int_done: + /* Can't include fcvtFooter.S after break */ + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG t0, a1 + GOTO_OPCODE v0 # jump to next instruction + +/* continuation for op_double_to_long */ +.Lop_double_to_long_trunc: + trunc.l.d f0, f0 + dmfc1 t0, f0 +.Lop_double_to_long_done: + /* Can't include fcvtFooter.S after break */ + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE t0, a1 + GOTO_OPCODE v0 # jump to next instruction + + .size artMterpAsmSisterStart, .-artMterpAsmSisterStart + .global artMterpAsmSisterEnd +artMterpAsmSisterEnd: + + + .global artMterpAsmAltInstructionStart + .type artMterpAsmAltInstructionStart, %function + .text + +artMterpAsmAltInstructionStart = .L_ALT_op_nop +/* ------------------------------ */ + .balign 128 +.L_ALT_op_nop: /* 0x00 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (0 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move: /* 0x01 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (1 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_from16: /* 0x02 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (2 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_16: /* 0x03 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (3 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_wide: /* 0x04 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (4 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_wide_from16: /* 0x05 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (5 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_wide_16: /* 0x06 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (6 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_object: /* 0x07 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (7 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_object_from16: /* 0x08 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (8 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_object_16: /* 0x09 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (9 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_result: /* 0x0a */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (10 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_result_wide: /* 0x0b */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (11 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_result_object: /* 0x0c */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (12 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_exception: /* 0x0d */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (13 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_return_void: /* 0x0e */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (14 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_return: /* 0x0f */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (15 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_return_wide: /* 0x10 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (16 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_return_object: /* 0x11 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (17 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_4: /* 0x12 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (18 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_16: /* 0x13 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (19 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const: /* 0x14 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (20 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_high16: /* 0x15 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (21 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_wide_16: /* 0x16 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (22 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_wide_32: /* 0x17 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (23 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_wide: /* 0x18 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (24 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_wide_high16: /* 0x19 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (25 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_string: /* 0x1a */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (26 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_string_jumbo: /* 0x1b */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (27 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_class: /* 0x1c */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (28 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_monitor_enter: /* 0x1d */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (29 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_monitor_exit: /* 0x1e */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (30 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_check_cast: /* 0x1f */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (31 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_instance_of: /* 0x20 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (32 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_array_length: /* 0x21 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (33 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_new_instance: /* 0x22 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (34 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_new_array: /* 0x23 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (35 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_filled_new_array: /* 0x24 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (36 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_filled_new_array_range: /* 0x25 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (37 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_fill_array_data: /* 0x26 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (38 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_throw: /* 0x27 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (39 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_goto: /* 0x28 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (40 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_goto_16: /* 0x29 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (41 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_goto_32: /* 0x2a */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (42 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_packed_switch: /* 0x2b */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (43 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sparse_switch: /* 0x2c */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (44 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_cmpl_float: /* 0x2d */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (45 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_cmpg_float: /* 0x2e */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (46 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_cmpl_double: /* 0x2f */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (47 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_cmpg_double: /* 0x30 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (48 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_cmp_long: /* 0x31 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (49 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_eq: /* 0x32 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (50 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_ne: /* 0x33 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (51 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_lt: /* 0x34 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (52 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_ge: /* 0x35 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (53 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_gt: /* 0x36 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (54 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_le: /* 0x37 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (55 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_eqz: /* 0x38 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (56 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_nez: /* 0x39 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (57 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_ltz: /* 0x3a */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (58 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_gez: /* 0x3b */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (59 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_gtz: /* 0x3c */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (60 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_lez: /* 0x3d */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (61 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_3e: /* 0x3e */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (62 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_3f: /* 0x3f */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (63 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_40: /* 0x40 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (64 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_41: /* 0x41 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (65 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_42: /* 0x42 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (66 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_43: /* 0x43 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (67 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aget: /* 0x44 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (68 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aget_wide: /* 0x45 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (69 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aget_object: /* 0x46 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (70 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aget_boolean: /* 0x47 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (71 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aget_byte: /* 0x48 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (72 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aget_char: /* 0x49 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (73 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aget_short: /* 0x4a */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (74 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aput: /* 0x4b */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (75 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aput_wide: /* 0x4c */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (76 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aput_object: /* 0x4d */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (77 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aput_boolean: /* 0x4e */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (78 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aput_byte: /* 0x4f */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (79 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aput_char: /* 0x50 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (80 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aput_short: /* 0x51 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (81 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget: /* 0x52 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (82 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_wide: /* 0x53 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (83 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_object: /* 0x54 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (84 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_boolean: /* 0x55 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (85 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_byte: /* 0x56 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (86 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_char: /* 0x57 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (87 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_short: /* 0x58 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (88 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput: /* 0x59 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (89 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_wide: /* 0x5a */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (90 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_object: /* 0x5b */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (91 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_boolean: /* 0x5c */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (92 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_byte: /* 0x5d */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (93 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_char: /* 0x5e */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (94 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_short: /* 0x5f */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (95 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sget: /* 0x60 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (96 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sget_wide: /* 0x61 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (97 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sget_object: /* 0x62 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (98 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sget_boolean: /* 0x63 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (99 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sget_byte: /* 0x64 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (100 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sget_char: /* 0x65 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (101 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sget_short: /* 0x66 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (102 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sput: /* 0x67 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (103 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sput_wide: /* 0x68 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (104 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sput_object: /* 0x69 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (105 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sput_boolean: /* 0x6a */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (106 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sput_byte: /* 0x6b */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (107 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sput_char: /* 0x6c */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (108 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sput_short: /* 0x6d */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (109 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_virtual: /* 0x6e */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (110 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_super: /* 0x6f */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (111 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_direct: /* 0x70 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (112 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_static: /* 0x71 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (113 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_interface: /* 0x72 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (114 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_return_void_no_barrier: /* 0x73 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (115 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_virtual_range: /* 0x74 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (116 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_super_range: /* 0x75 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (117 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_direct_range: /* 0x76 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (118 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_static_range: /* 0x77 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (119 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_interface_range: /* 0x78 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (120 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_79: /* 0x79 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (121 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_7a: /* 0x7a */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (122 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_neg_int: /* 0x7b */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (123 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_not_int: /* 0x7c */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (124 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_neg_long: /* 0x7d */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (125 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_not_long: /* 0x7e */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (126 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_neg_float: /* 0x7f */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (127 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_neg_double: /* 0x80 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (128 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_int_to_long: /* 0x81 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (129 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_int_to_float: /* 0x82 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (130 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_int_to_double: /* 0x83 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (131 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_long_to_int: /* 0x84 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (132 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_long_to_float: /* 0x85 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (133 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_long_to_double: /* 0x86 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (134 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_float_to_int: /* 0x87 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (135 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_float_to_long: /* 0x88 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (136 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_float_to_double: /* 0x89 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (137 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_double_to_int: /* 0x8a */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (138 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_double_to_long: /* 0x8b */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (139 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_double_to_float: /* 0x8c */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (140 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_int_to_byte: /* 0x8d */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (141 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_int_to_char: /* 0x8e */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (142 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_int_to_short: /* 0x8f */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (143 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_int: /* 0x90 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (144 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sub_int: /* 0x91 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (145 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_int: /* 0x92 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (146 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_int: /* 0x93 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (147 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_int: /* 0x94 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (148 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_and_int: /* 0x95 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (149 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_or_int: /* 0x96 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (150 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_xor_int: /* 0x97 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (151 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shl_int: /* 0x98 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (152 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shr_int: /* 0x99 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (153 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_ushr_int: /* 0x9a */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (154 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_long: /* 0x9b */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (155 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sub_long: /* 0x9c */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (156 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_long: /* 0x9d */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (157 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_long: /* 0x9e */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (158 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_long: /* 0x9f */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (159 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_and_long: /* 0xa0 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (160 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_or_long: /* 0xa1 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (161 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_xor_long: /* 0xa2 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (162 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shl_long: /* 0xa3 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (163 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shr_long: /* 0xa4 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (164 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_ushr_long: /* 0xa5 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (165 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_float: /* 0xa6 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (166 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sub_float: /* 0xa7 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (167 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_float: /* 0xa8 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (168 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_float: /* 0xa9 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (169 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_float: /* 0xaa */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (170 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_double: /* 0xab */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (171 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sub_double: /* 0xac */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (172 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_double: /* 0xad */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (173 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_double: /* 0xae */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (174 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_double: /* 0xaf */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (175 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_int_2addr: /* 0xb0 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (176 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sub_int_2addr: /* 0xb1 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (177 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_int_2addr: /* 0xb2 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (178 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_int_2addr: /* 0xb3 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (179 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_int_2addr: /* 0xb4 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (180 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_and_int_2addr: /* 0xb5 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (181 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_or_int_2addr: /* 0xb6 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (182 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_xor_int_2addr: /* 0xb7 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (183 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shl_int_2addr: /* 0xb8 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (184 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shr_int_2addr: /* 0xb9 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (185 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_ushr_int_2addr: /* 0xba */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (186 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_long_2addr: /* 0xbb */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (187 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sub_long_2addr: /* 0xbc */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (188 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_long_2addr: /* 0xbd */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (189 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_long_2addr: /* 0xbe */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (190 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_long_2addr: /* 0xbf */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (191 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_and_long_2addr: /* 0xc0 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (192 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_or_long_2addr: /* 0xc1 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (193 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_xor_long_2addr: /* 0xc2 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (194 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shl_long_2addr: /* 0xc3 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (195 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shr_long_2addr: /* 0xc4 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (196 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_ushr_long_2addr: /* 0xc5 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (197 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_float_2addr: /* 0xc6 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (198 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sub_float_2addr: /* 0xc7 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (199 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_float_2addr: /* 0xc8 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (200 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_float_2addr: /* 0xc9 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (201 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_float_2addr: /* 0xca */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (202 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_double_2addr: /* 0xcb */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (203 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sub_double_2addr: /* 0xcc */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (204 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_double_2addr: /* 0xcd */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (205 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_double_2addr: /* 0xce */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (206 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_double_2addr: /* 0xcf */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (207 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_int_lit16: /* 0xd0 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (208 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rsub_int: /* 0xd1 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (209 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_int_lit16: /* 0xd2 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (210 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_int_lit16: /* 0xd3 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (211 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_int_lit16: /* 0xd4 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (212 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_and_int_lit16: /* 0xd5 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (213 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_or_int_lit16: /* 0xd6 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (214 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_xor_int_lit16: /* 0xd7 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (215 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_int_lit8: /* 0xd8 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (216 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rsub_int_lit8: /* 0xd9 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (217 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_int_lit8: /* 0xda */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (218 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_int_lit8: /* 0xdb */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (219 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_int_lit8: /* 0xdc */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (220 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_and_int_lit8: /* 0xdd */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (221 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_or_int_lit8: /* 0xde */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (222 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_xor_int_lit8: /* 0xdf */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (223 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shl_int_lit8: /* 0xe0 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (224 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shr_int_lit8: /* 0xe1 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (225 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_ushr_int_lit8: /* 0xe2 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (226 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_quick: /* 0xe3 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (227 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_wide_quick: /* 0xe4 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (228 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_object_quick: /* 0xe5 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (229 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_quick: /* 0xe6 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (230 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_wide_quick: /* 0xe7 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (231 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_object_quick: /* 0xe8 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (232 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_virtual_quick: /* 0xe9 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (233 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_virtual_range_quick: /* 0xea */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (234 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_boolean_quick: /* 0xeb */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (235 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_byte_quick: /* 0xec */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (236 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_char_quick: /* 0xed */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (237 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_short_quick: /* 0xee */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (238 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_boolean_quick: /* 0xef */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (239 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_byte_quick: /* 0xf0 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (240 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_char_quick: /* 0xf1 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (241 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_short_quick: /* 0xf2 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (242 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_lambda: /* 0xf3 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (243 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_f4: /* 0xf4 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (244 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_capture_variable: /* 0xf5 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (245 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_create_lambda: /* 0xf6 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (246 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_liberate_variable: /* 0xf7 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (247 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_box_lambda: /* 0xf8 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (248 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unbox_lambda: /* 0xf9 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (249 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_fa: /* 0xfa */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (250 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_fb: /* 0xfb */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (251 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_fc: /* 0xfc */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (252 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_fd: /* 0xfd */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (253 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_fe: /* 0xfe */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (254 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_ff: /* 0xff */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (255 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + + .balign 128 + .size artMterpAsmAltInstructionStart, .-artMterpAsmAltInstructionStart + .global artMterpAsmAltInstructionEnd +artMterpAsmAltInstructionEnd: +/* File: mips64/footer.S */ +/* + * We've detected a condition that will result in an exception, but the exception + * has not yet been thrown. Just bail out to the reference interpreter to deal with it. + * TUNING: for consistency, we may want to just go ahead and handle these here. + */ + + .extern MterpLogDivideByZeroException +common_errDivideByZero: + EXPORT_PC +#if MTERP_LOGGING + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + jal MterpLogDivideByZeroException +#endif + b MterpCommonFallback + + .extern MterpLogArrayIndexException +common_errArrayIndex: + EXPORT_PC +#if MTERP_LOGGING + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + jal MterpLogArrayIndexException +#endif + b MterpCommonFallback + + .extern MterpLogNullObjectException +common_errNullObject: + EXPORT_PC +#if MTERP_LOGGING + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + jal MterpLogNullObjectException +#endif + b MterpCommonFallback + +/* + * If we're here, something is out of the ordinary. If there is a pending + * exception, handle it. Otherwise, roll back and retry with the reference + * interpreter. + */ +MterpPossibleException: + ld a0, THREAD_EXCEPTION_OFFSET(rSELF) + beqzc a0, MterpFallback # If not, fall back to reference interpreter. + /* intentional fallthrough - handle pending exception. */ +/* + * On return from a runtime helper routine, we've found a pending exception. + * Can we handle it here - or need to bail out to caller? + * + */ + .extern MterpHandleException +MterpException: + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + jal MterpHandleException # (self, shadow_frame) + beqzc v0, MterpExceptionReturn # no local catch, back to caller. + ld a0, OFF_FP_CODE_ITEM(rFP) + lwu a1, OFF_FP_DEX_PC(rFP) + REFRESH_IBASE + daddu rPC, a0, CODEITEM_INSNS_OFFSET + dlsa rPC, a1, rPC, 1 # generate new dex_pc_ptr + sd rPC, OFF_FP_DEX_PC_PTR(rFP) + /* resume execution at catch block */ + FETCH_INST + GET_INST_OPCODE v0 + GOTO_OPCODE v0 + /* NOTE: no fallthrough */ + +/* + * Check for suspend check request. Assumes rINST already loaded, rPC advanced and + * still needs to get the opcode and branch to it, and flags are in ra. + */ + .extern MterpSuspendCheck +MterpCheckSuspendAndContinue: + REFRESH_IBASE + and ra, ra, (THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + bnez ra, check1 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction +check1: + EXPORT_PC + move a0, rSELF + jal MterpSuspendCheck # (self) + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + +/* + * Bail out to reference interpreter. + */ + .extern MterpLogFallback +MterpFallback: + EXPORT_PC +#if MTERP_LOGGING + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + jal MterpLogFallback +#endif +MterpCommonFallback: + li v0, 0 # signal retry with reference interpreter. + b MterpDone + +/* + * We pushed some registers on the stack in ExecuteMterpImpl, then saved + * SP and RA. Here we restore SP, restore the registers, and then restore + * RA to PC. + * + * On entry: + * uint32_t* rFP (should still be live, pointer to base of vregs) + */ +MterpExceptionReturn: + li v0, 1 # signal return to caller. + b MterpDone +/* + * Returned value is expected in a0 and if it's not 64-bit, the 32 most + * significant bits of a0 must be 0. + */ +MterpReturn: + ld a2, OFF_FP_RESULT_REGISTER(rFP) + lw ra, THREAD_FLAGS_OFFSET(rSELF) + sd a0, 0(a2) + move a0, rSELF + and ra, ra, (THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + beqzc ra, check2 + jal MterpSuspendCheck # (self) +check2: + li v0, 1 # signal return to caller. +MterpDone: + ld s5, STACK_OFFSET_S5(sp) + .cfi_restore 21 + ld s4, STACK_OFFSET_S4(sp) + .cfi_restore 20 + ld s3, STACK_OFFSET_S3(sp) + .cfi_restore 19 + ld s2, STACK_OFFSET_S2(sp) + .cfi_restore 18 + ld s1, STACK_OFFSET_S1(sp) + .cfi_restore 17 + ld s0, STACK_OFFSET_S0(sp) + .cfi_restore 16 + + ld ra, STACK_OFFSET_RA(sp) + .cfi_restore 31 + + ld t8, STACK_OFFSET_GP(sp) + .cpreturn + .cfi_restore 28 + + .set noreorder + jr ra + daddu sp, sp, STACK_SIZE + .cfi_adjust_cfa_offset -STACK_SIZE + + .cfi_endproc + .size ExecuteMterpImpl, .-ExecuteMterpImpl + diff --git a/runtime/interpreter/mterp/rebuild.sh b/runtime/interpreter/mterp/rebuild.sh index e3f043749f..ca3dcd9a13 100755 --- a/runtime/interpreter/mterp/rebuild.sh +++ b/runtime/interpreter/mterp/rebuild.sh @@ -20,5 +20,4 @@ # set -e -# for arch in arm x86 mips arm64 x86_64 mips64; do TARGET_ARCH_EXT=$arch make -f Makefile_mterp; done -for arch in arm x86 mips arm64 x86_64 ; do TARGET_ARCH_EXT=$arch make -f Makefile_mterp; done +for arch in arm x86 mips arm64 x86_64 mips64; do TARGET_ARCH_EXT=$arch make -f Makefile_mterp; done -- GitLab From 0f73bda41a849734587a8df6428402beb65c2bc0 Mon Sep 17 00:00:00 2001 From: David Brazdil Date: Wed, 2 Mar 2016 16:09:46 +0000 Subject: [PATCH 099/204] Improve Checker error messages Now prints the offending Checker line and variable values. Change-Id: Id4395083de6ddddbd69f66cb7ba38c943146799a --- tools/checker/common/logger.py | 46 +++++++++++++++++++++++----------- tools/checker/match/file.py | 17 +++++++------ tools/checker/match/line.py | 6 ++--- 3 files changed, 43 insertions(+), 26 deletions(-) diff --git a/tools/checker/common/logger.py b/tools/checker/common/logger.py index 28bb458da7..f13eaf6142 100644 --- a/tools/checker/common/logger.py +++ b/tools/checker/common/logger.py @@ -13,6 +13,7 @@ # limitations under the License. from __future__ import print_function +import collections import sys class Logger(object): @@ -21,7 +22,7 @@ class Logger(object): NoOutput, Error, Info = range(3) class Color(object): - Default, Blue, Gray, Purple, Red = range(5) + Default, Blue, Gray, Purple, Red, Green = range(6) @staticmethod def terminalCode(color, out=sys.stdout): @@ -35,6 +36,8 @@ class Logger(object): return '\033[95m' elif color == Logger.Color.Red: return '\033[91m' + elif color == Logger.Color.Green: + return '\033[32m' else: return '\033[0m' @@ -52,19 +55,34 @@ class Logger(object): out.flush() @staticmethod - def fail(msg, file=None, line=-1): - location = "" - if file: - location += file + ":" - if line > 0: - location += str(line) + ":" - if location: - location += " " - - Logger.log(location, Logger.Level.Error, color=Logger.Color.Gray, newLine=False, out=sys.stderr) + def fail(msg, file=None, line=-1, lineText=None, variables=None): Logger.log("error: ", Logger.Level.Error, color=Logger.Color.Red, newLine=False, out=sys.stderr) Logger.log(msg, Logger.Level.Error, out=sys.stderr) - sys.exit(msg) + + if lineText: + loc = "" + if file: + loc += file + ":" + if line > 0: + loc += str(line) + ":" + if loc: + loc += " " + Logger.log(loc, Logger.Level.Error, color=Logger.Color.Gray, newLine=False, out=sys.stderr) + Logger.log(lineText, Logger.Level.Error, out=sys.stderr) + + if variables: + longestName = 0 + for var in variables: + longestName = max(longestName, len(var)) + + for var in collections.OrderedDict(sorted(variables.items())): + padding = ' ' * (longestName - len(var)) + Logger.log(var, Logger.Level.Error, color=Logger.Color.Green, newLine=False, out=sys.stderr) + Logger.log(padding, Logger.Level.Error, newLine=False, out=sys.stderr) + Logger.log(" = ", Logger.Level.Error, newLine=False, out=sys.stderr) + Logger.log(variables[var], Logger.Level.Error, out=sys.stderr) + + sys.exit(1) @staticmethod def startTest(name): @@ -76,6 +94,6 @@ class Logger(object): Logger.log("PASS", color=Logger.Color.Blue) @staticmethod - def testFailed(msg, file=None, line=-1): + def testFailed(msg, assertion, variables): Logger.log("FAIL", color=Logger.Color.Red) - Logger.fail(msg, file, line) + Logger.fail(msg, assertion.fileName, assertion.lineNo, assertion.originalText, variables) diff --git a/tools/checker/match/file.py b/tools/checker/match/file.py index 3ded07482f..6ff19d5197 100644 --- a/tools/checker/match/file.py +++ b/tools/checker/match/file.py @@ -23,9 +23,10 @@ MatchScope = namedtuple("MatchScope", ["start", "end"]) MatchInfo = namedtuple("MatchInfo", ["scope", "variables"]) class MatchFailedException(Exception): - def __init__(self, assertion, lineNo): + def __init__(self, assertion, lineNo, variables): self.assertion = assertion self.lineNo = lineNo + self.variables = variables def splitIntoGroups(assertions): """ Breaks up a list of assertions, grouping instructions which should be @@ -58,7 +59,7 @@ def findMatchingLine(assertion, c1Pass, scope, variables, excludeLines=[]): newVariables = MatchLines(assertion, c1Pass.body[i], variables) if newVariables is not None: return MatchInfo(MatchScope(i, i), newVariables) - raise MatchFailedException(assertion, scope.start) + raise MatchFailedException(assertion, scope.start, variables) def matchDagGroup(assertions, c1Pass, scope, variables): """ Attempts to find matching `c1Pass` lines for a group of DAG assertions. @@ -92,12 +93,12 @@ def testNotGroup(assertions, c1Pass, scope, variables): for assertion in assertions: assert assertion.variant == TestAssertion.Variant.Not if MatchLines(assertion, line, variables) is not None: - raise MatchFailedException(assertion, i) + raise MatchFailedException(assertion, i, variables) def testEvalGroup(assertions, scope, variables): for assertion in assertions: if not EvaluateLine(assertion, variables): - raise MatchFailedException(assertion, scope.start) + raise MatchFailedException(assertion, scope.start, variables) def MatchTestCase(testCase, c1Pass): """ Runs a test case against a C1visualizer graph dump. @@ -181,8 +182,8 @@ def MatchFiles(checkerFile, c1File, targetArch, debuggableMode): except MatchFailedException as e: lineNo = c1Pass.startLineNo + e.lineNo if e.assertion.variant == TestAssertion.Variant.Not: - Logger.testFailed("NOT assertion matched line {}".format(lineNo), - e.assertion.fileName, e.assertion.lineNo) + msg = "NOT assertion matched line {}" else: - Logger.testFailed("Assertion could not be matched starting from line {}".format(lineNo), - e.assertion.fileName, e.assertion.lineNo) + msg = "Assertion could not be matched starting from line {}" + msg = msg.format(lineNo) + Logger.testFailed(msg, e.assertion, e.variables) diff --git a/tools/checker/match/line.py b/tools/checker/match/line.py index 08f001f660..ed48a5329f 100644 --- a/tools/checker/match/line.py +++ b/tools/checker/match/line.py @@ -35,15 +35,13 @@ def getVariable(name, variables, pos): if name in variables: return variables[name] else: - Logger.testFailed("Missing definition of variable \"{}\"".format(name), - pos.fileName, pos.lineNo) + Logger.testFailed("Missing definition of variable \"{}\"".format(name), pos, variables) def setVariable(name, value, variables, pos): if name not in variables: return variables.copyWith(name, value) else: - Logger.testFailed("Multiple definitions of variable \"{}\"".format(name), - pos.fileName, pos.lineNo) + Logger.testFailed("Multiple definitions of variable \"{}\"".format(name), pos, variables) def matchWords(checkerWord, stringWord, variables, pos): """ Attempts to match a list of TestExpressions against a string. -- GitLab From 35122443e5f8606cc5a660ac32745a06aefb341b Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Wed, 2 Mar 2016 12:05:30 +0000 Subject: [PATCH 100/204] Revert "Revert "Use the interpreter as a heartbeat for the JIT."" Bug: 27398183 Bug: 23128949 Bug: 26846185 This reverts commit a96917a6983a5abbe973255a3846fda549fb1657. Change-Id: I5c4f0d87d3293a6a7ab56a33396670704b66a347 --- runtime/interpreter/interpreter.cc | 4 +- runtime/jit/jit.cc | 4 + runtime/jit/jit.h | 4 + runtime/jit/jit_code_cache.cc | 215 +++++++++++++++++------------ runtime/jit/jit_code_cache.h | 17 ++- runtime/jit/jit_instrumentation.cc | 13 +- runtime/jit/profiling_info.h | 15 +- 7 files changed, 174 insertions(+), 98 deletions(-) diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc index bfb1f9de06..b7e5b301ad 100644 --- a/runtime/interpreter/interpreter.cc +++ b/runtime/interpreter/interpreter.cc @@ -295,9 +295,7 @@ static inline JValue Execute(Thread* self, const DexFile::CodeItem* code_item, } jit::Jit* jit = Runtime::Current()->GetJit(); - if (UNLIKELY(jit != nullptr && - jit->JitAtFirstUse() && - jit->GetCodeCache()->ContainsMethod(method))) { + if (jit != nullptr && jit->CanInvokeCompiledCode(method)) { JValue result; // Pop the shadow frame before calling into compiled code. diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc index 3e66ce20eb..91b006a3ea 100644 --- a/runtime/jit/jit.cc +++ b/runtime/jit/jit.cc @@ -213,6 +213,10 @@ bool Jit::JitAtFirstUse() { return false; } +bool Jit::CanInvokeCompiledCode(ArtMethod* method) { + return code_cache_->ContainsPc(method->GetEntryPointFromQuickCompiledCode()); +} + Jit::~Jit() { DCHECK(!save_profiling_info_ || !ProfileSaver::IsStarted()); if (dump_info_on_shutdown_) { diff --git a/runtime/jit/jit.h b/runtime/jit/jit.h index 109ca3dbd1..3f54192d9f 100644 --- a/runtime/jit/jit.h +++ b/runtime/jit/jit.h @@ -84,8 +84,12 @@ class Jit { // into the specified class linker to the jit debug interface, void DumpTypeInfoForLoadedTypes(ClassLinker* linker); + // Return whether we should try to JIT compiled code as soon as an ArtMethod is invoked. bool JitAtFirstUse(); + // Return whether we can invoke JIT code for `method`. + bool CanInvokeCompiledCode(ArtMethod* method); + // If an OSR compiled version is available for `method`, // and `dex_pc + dex_pc_offset` is an entry point of that compiled // version, this method will jump to the compiled code, let it run, diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index 8858b486f9..e8a7189d57 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -123,7 +123,7 @@ JitCodeCache::JitCodeCache(MemMap* code_map, current_capacity_(initial_code_capacity + initial_data_capacity), code_end_(initial_code_capacity), data_end_(initial_data_capacity), - has_done_full_collection_(false), + last_collection_increased_code_cache_(false), last_update_time_ns_(0), garbage_collect_code_(garbage_collect_code), used_memory_for_data_(0), @@ -546,34 +546,20 @@ void JitCodeCache::MarkCompiledCodeOnThreadStacks(Thread* self) { } } -void JitCodeCache::RemoveUnusedCode(Thread* self) { - // Clear the osr map, chances are most of the code in it is now dead. - { - MutexLock mu(self, lock_); - osr_code_map_.clear(); - } - - // Run a checkpoint on all threads to mark the JIT compiled code they are running. - MarkCompiledCodeOnThreadStacks(self); - - // Iterate over all compiled code and remove entries that are not marked and not - // the entrypoint of their corresponding ArtMethod. - { - MutexLock mu(self, lock_); - ScopedCodeCacheWrite scc(code_map_.get()); - for (auto it = method_code_map_.begin(); it != method_code_map_.end();) { - const void* code_ptr = it->first; - ArtMethod* method = it->second; - uintptr_t allocation = FromCodeToAllocation(code_ptr); - const OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromCodePointer(code_ptr); - if ((method->GetEntryPointFromQuickCompiledCode() != method_header->GetEntryPoint()) && - !GetLiveBitmap()->Test(allocation)) { - FreeCode(code_ptr, method); - it = method_code_map_.erase(it); - } else { - ++it; - } - } +bool JitCodeCache::ShouldDoFullCollection() { + if (current_capacity_ == max_capacity_) { + // Always do a full collection when the code cache is full. + return true; + } else if (current_capacity_ < kReservedCapacity) { + // Always do partial collection when the code cache size is below the reserved + // capacity. + return false; + } else if (last_collection_increased_code_cache_) { + // This time do a full collection. + return true; + } else { + // This time do a partial collection. + return false; } } @@ -599,21 +585,10 @@ void JitCodeCache::GarbageCollectCache(Thread* self) { } } - // Check if we want to do a full collection. - bool do_full_collection = true; + bool do_full_collection = false; { MutexLock mu(self, lock_); - if (current_capacity_ == max_capacity_) { - // Always do a full collection when the code cache is full. - do_full_collection = true; - } else if (current_capacity_ < kReservedCapacity) { - // Do a partial collection until we hit the reserved capacity limit. - do_full_collection = false; - } else if (has_done_full_collection_) { - // Do a partial collection if we have done a full collection in the last - // collection round. - do_full_collection = false; - } + do_full_collection = ShouldDoFullCollection(); } if (!kIsDebugBuild || VLOG_IS_ON(jit)) { @@ -624,45 +599,91 @@ void JitCodeCache::GarbageCollectCache(Thread* self) { << ", data=" << PrettySize(DataCacheSize()); } - if (do_full_collection) { - DoFullCollection(self); - } else { - RemoveUnusedCode(self); + DoCollection(self, /* collect_profiling_info */ do_full_collection); + + if (!kIsDebugBuild || VLOG_IS_ON(jit)) { + LOG(INFO) << "After code cache collection, code=" + << PrettySize(CodeCacheSize()) + << ", data=" << PrettySize(DataCacheSize()); } { MutexLock mu(self, lock_); - if (!do_full_collection) { - has_done_full_collection_ = false; - IncreaseCodeCacheCapacity(); + + // Increase the code cache only when we do partial collections. + // TODO: base this strategy on how full the code cache is? + if (do_full_collection) { + last_collection_increased_code_cache_ = false; } else { - has_done_full_collection_ = true; + last_collection_increased_code_cache_ = true; + IncreaseCodeCacheCapacity(); + } + + bool next_collection_will_be_full = ShouldDoFullCollection(); + + // Start polling the liveness of compiled code to prepare for the next full collection. + // We avoid doing this if exit stubs are installed to not mess with the instrumentation. + // TODO(ngeoffray): Clean up instrumentation and code cache interactions. + if (!Runtime::Current()->GetInstrumentation()->AreExitStubsInstalled() && + next_collection_will_be_full) { + // Save the entry point of methods we have compiled, and update the entry + // point of those methods to the interpreter. If the method is invoked, the + // interpreter will update its entry point to the compiled code and call it. + for (ProfilingInfo* info : profiling_infos_) { + const void* entry_point = info->GetMethod()->GetEntryPointFromQuickCompiledCode(); + if (ContainsPc(entry_point)) { + info->SetSavedEntryPoint(entry_point); + info->GetMethod()->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge()); + } + } + + DCHECK(CheckLiveCompiledCodeHasProfilingInfo()); } live_bitmap_.reset(nullptr); NotifyCollectionDone(self); } +} - if (!kIsDebugBuild || VLOG_IS_ON(jit)) { - LOG(INFO) << "After code cache collection, code=" - << PrettySize(CodeCacheSize()) - << ", data=" << PrettySize(DataCacheSize()); +void JitCodeCache::RemoveUnusedAndUnmarkedCode(Thread* self) { + MutexLock mu(self, lock_); + ScopedCodeCacheWrite scc(code_map_.get()); + // Iterate over all compiled code and remove entries that are not marked and not + // the entrypoint of their corresponding ArtMethod. + for (auto it = method_code_map_.begin(); it != method_code_map_.end();) { + const void* code_ptr = it->first; + ArtMethod* method = it->second; + uintptr_t allocation = FromCodeToAllocation(code_ptr); + const OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromCodePointer(code_ptr); + const void* entrypoint = method->GetEntryPointFromQuickCompiledCode(); + if ((entrypoint == method_header->GetEntryPoint()) || GetLiveBitmap()->Test(allocation)) { + ++it; + } else { + if (entrypoint == GetQuickToInterpreterBridge()) { + method->ClearCounter(); + } + FreeCode(code_ptr, method); + it = method_code_map_.erase(it); + } } } -void JitCodeCache::DoFullCollection(Thread* self) { - instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); +void JitCodeCache::DoCollection(Thread* self, bool collect_profiling_info) { { MutexLock mu(self, lock_); - // Walk over all compiled methods and set the entry points of these - // methods to interpreter. - for (auto& it : method_code_map_) { - instrumentation->UpdateMethodsCode(it.second, GetQuickToInterpreterBridge()); - } - - // Clear the profiling info of methods that are not being compiled. - for (ProfilingInfo* info : profiling_infos_) { - if (!info->IsMethodBeingCompiled()) { - info->GetMethod()->SetProfilingInfo(nullptr); + if (collect_profiling_info) { + // Clear the profiling info of methods that do not have compiled code as entrypoint. + // Also remove the saved entry point from the ProfilingInfo objects. + for (ProfilingInfo* info : profiling_infos_) { + const void* ptr = info->GetMethod()->GetEntryPointFromQuickCompiledCode(); + if (!ContainsPc(ptr) && !info->IsMethodBeingCompiled()) { + info->GetMethod()->SetProfilingInfo(nullptr); + } + info->SetSavedEntryPoint(nullptr); + } + } else if (kIsDebugBuild) { + // Sanity check that the profiling infos do not have a dangling entry point. + for (ProfilingInfo* info : profiling_infos_) { + DCHECK(info->GetSavedEntryPoint() == nullptr); } } @@ -674,41 +695,50 @@ void JitCodeCache::DoFullCollection(Thread* self) { // Run a checkpoint on all threads to mark the JIT compiled code they are running. MarkCompiledCodeOnThreadStacks(self); - { - MutexLock mu(self, lock_); - // Free unused compiled code, and restore the entry point of used compiled code. - { - ScopedCodeCacheWrite scc(code_map_.get()); - for (auto it = method_code_map_.begin(); it != method_code_map_.end();) { - const void* code_ptr = it->first; - ArtMethod* method = it->second; - uintptr_t allocation = FromCodeToAllocation(code_ptr); - const OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromCodePointer(code_ptr); - if (GetLiveBitmap()->Test(allocation)) { - instrumentation->UpdateMethodsCode(method, method_header->GetEntryPoint()); - ++it; - } else { - method->ClearCounter(); - DCHECK_NE(method->GetEntryPointFromQuickCompiledCode(), method_header->GetEntryPoint()); - FreeCode(code_ptr, method); - it = method_code_map_.erase(it); - } - } - } + // Remove compiled code that is not the entrypoint of their method and not in the call + // stack. + RemoveUnusedAndUnmarkedCode(self); - // Free all profiling infos of methods that were not being compiled. + if (collect_profiling_info) { + MutexLock mu(self, lock_); + // Free all profiling infos of methods not compiled nor being compiled. auto profiling_kept_end = std::remove_if(profiling_infos_.begin(), profiling_infos_.end(), [this] (ProfilingInfo* info) NO_THREAD_SAFETY_ANALYSIS { - if (info->GetMethod()->GetProfilingInfo(sizeof(void*)) == nullptr) { + const void* ptr = info->GetMethod()->GetEntryPointFromQuickCompiledCode(); + if (ContainsPc(ptr) && info->GetMethod()->GetProfilingInfo(sizeof(void*)) == nullptr) { + // Make sure compiled methods have a ProfilingInfo object. It is needed for + // code cache collection. + info->GetMethod()->SetProfilingInfo(info); + } else if (info->GetMethod()->GetProfilingInfo(sizeof(void*)) != info) { + // No need for this ProfilingInfo object anymore. FreeData(reinterpret_cast(info)); return true; } return false; }); profiling_infos_.erase(profiling_kept_end, profiling_infos_.end()); + DCHECK(CheckLiveCompiledCodeHasProfilingInfo()); } } +bool JitCodeCache::CheckLiveCompiledCodeHasProfilingInfo() { + // Check that methods we have compiled do have a ProfilingInfo object. We would + // have memory leaks of compiled code otherwise. + for (const auto& it : method_code_map_) { + ArtMethod* method = it.second; + if (method->GetProfilingInfo(sizeof(void*)) == nullptr) { + const void* code_ptr = it.first; + const OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromCodePointer(code_ptr); + if (method_header->GetEntryPoint() == method->GetEntryPointFromQuickCompiledCode()) { + // If the code is not dead, then we have a problem. Note that this can even + // happen just after a collection, as mutator threads are running in parallel + // and could deoptimize an existing compiled code. + return false; + } + } + } + return true; +} OatQuickMethodHeader* JitCodeCache::LookupMethodHeader(uintptr_t pc, ArtMethod* method) { static_assert(kRuntimeISA != kThumb2, "kThumb2 cannot be a runtime ISA"); @@ -849,6 +879,13 @@ size_t JitCodeCache::GetMemorySizeOfCodePointer(const void* ptr) { void JitCodeCache::InvalidateCompiledCodeFor(ArtMethod* method, const OatQuickMethodHeader* header) { + ProfilingInfo* profiling_info = method->GetProfilingInfo(sizeof(void*)); + if ((profiling_info != nullptr) && + (profiling_info->GetSavedEntryPoint() == header->GetEntryPoint())) { + // Prevent future uses of the compiled code. + profiling_info->SetSavedEntryPoint(nullptr); + } + if (method->GetEntryPointFromQuickCompiledCode() == header->GetEntryPoint()) { // The entrypoint is the one to invalidate, so we just update // it to the interpreter entry point and clear the counter to get the method diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h index 4574edfb46..7b33b928e6 100644 --- a/runtime/jit/jit_code_cache.h +++ b/runtime/jit/jit_code_cache.h @@ -124,6 +124,11 @@ class JitCodeCache { return live_bitmap_.get(); } + // Return whether we should do a full collection given the current state of the cache. + bool ShouldDoFullCollection() + REQUIRES(lock_) + SHARED_REQUIRES(Locks::mutator_lock_); + // Perform a collection on the code cache. void GarbageCollectCache(Thread* self) REQUIRES(!lock_) @@ -235,11 +240,11 @@ class JitCodeCache { // Set the footprint limit of the code cache. void SetFootprintLimit(size_t new_footprint) REQUIRES(lock_); - void DoFullCollection(Thread* self) + void DoCollection(Thread* self, bool collect_profiling_info) REQUIRES(!lock_) SHARED_REQUIRES(Locks::mutator_lock_); - void RemoveUnusedCode(Thread* self) + void RemoveUnusedAndUnmarkedCode(Thread* self) REQUIRES(!lock_) SHARED_REQUIRES(Locks::mutator_lock_); @@ -247,6 +252,10 @@ class JitCodeCache { REQUIRES(!lock_) SHARED_REQUIRES(Locks::mutator_lock_); + bool CheckLiveCompiledCodeHasProfilingInfo() + REQUIRES(lock_) + SHARED_REQUIRES(Locks::mutator_lock_); + // Lock for guarding allocations, collections, and the method_code_map_. Mutex lock_; // Condition to wait on during collection. @@ -282,8 +291,8 @@ class JitCodeCache { // The current footprint in bytes of the data portion of the code cache. size_t data_end_ GUARDED_BY(lock_); - // Whether a full collection has already been done on the current capacity. - bool has_done_full_collection_ GUARDED_BY(lock_); + // Whether the last collection round increased the code cache. + bool last_collection_increased_code_cache_ GUARDED_BY(lock_); // Last time the the code_cache was updated. // It is atomic to avoid locking when reading it. diff --git a/runtime/jit/jit_instrumentation.cc b/runtime/jit/jit_instrumentation.cc index 46c362ac62..d751e5aae9 100644 --- a/runtime/jit/jit_instrumentation.cc +++ b/runtime/jit/jit_instrumentation.cc @@ -187,7 +187,18 @@ void JitInstrumentationListener::MethodEntered(Thread* thread, return; } - instrumentation_cache_->AddSamples(thread, method, 1); + ProfilingInfo* profiling_info = method->GetProfilingInfo(sizeof(void*)); + // Update the entrypoint if the ProfilingInfo has one. The interpreter will call it + // instead of interpreting the method. + // We avoid doing this if exit stubs are installed to not mess with the instrumentation. + // TODO(ngeoffray): Clean up instrumentation and code cache interactions. + if ((profiling_info != nullptr) && + (profiling_info->GetSavedEntryPoint() != nullptr) && + !Runtime::Current()->GetInstrumentation()->AreExitStubsInstalled()) { + method->SetEntryPointFromQuickCompiledCode(profiling_info->GetSavedEntryPoint()); + } else { + instrumentation_cache_->AddSamples(thread, method, 1); + } } void JitInstrumentationListener::Branch(Thread* thread, diff --git a/runtime/jit/profiling_info.h b/runtime/jit/profiling_info.h index ab7237376b..d54f3dfc11 100644 --- a/runtime/jit/profiling_info.h +++ b/runtime/jit/profiling_info.h @@ -126,11 +126,20 @@ class ProfilingInfo { is_method_being_compiled_ = value; } + void SetSavedEntryPoint(const void* entry_point) { + saved_entry_point_ = entry_point; + } + + const void* GetSavedEntryPoint() const { + return saved_entry_point_; + } + private: ProfilingInfo(ArtMethod* method, const std::vector& entries) : number_of_inline_caches_(entries.size()), method_(method), - is_method_being_compiled_(false) { + is_method_being_compiled_(false), + saved_entry_point_(nullptr) { memset(&cache_, 0, number_of_inline_caches_ * sizeof(InlineCache)); for (size_t i = 0; i < number_of_inline_caches_; ++i) { cache_[i].dex_pc_ = entries[i]; @@ -148,6 +157,10 @@ class ProfilingInfo { // TODO: Make the JIT code cache lock global. bool is_method_being_compiled_; + // Entry point of the corresponding ArtMethod, while the JIT code cache + // is poking for the liveness of compiled code. + const void* saved_entry_point_; + // Dynamically allocated array of size `number_of_inline_caches_`. InlineCache cache_[0]; -- GitLab From 2f9fcc999fab4ba6cd86c30e664325b47b9618e5 Mon Sep 17 00:00:00 2001 From: Aart Bik Date: Tue, 1 Mar 2016 15:16:54 -0800 Subject: [PATCH 101/204] Simplified intrinsic macro mechanism. Rationale: Reduces boiler-plate code in all intrinsics code generators. Also, the newly introduced "unreachable" macro provides a static verifier that we do not have unreachable and thus redundant code in the generators. In fact, this change exposes that the MIPS32 and MIPS64 rotation intrinsics (IntegerRotateRight, LongRotateRight, IntegerRotateLeft, LongRotateLeft) are unreachable, since they are handled as HIR constructs for all architectures. Thus the code can be removed. Change-Id: I0309799a0db580232137ded72bb8a7bbd45440a8 --- compiler/optimizing/intrinsics.h | 40 +++ compiler/optimizing/intrinsics_arm.cc | 72 ++--- compiler/optimizing/intrinsics_arm64.cc | 50 +--- compiler/optimizing/intrinsics_list.h | 2 +- compiler/optimizing/intrinsics_mips.cc | 346 +++++------------------ compiler/optimizing/intrinsics_mips64.cc | 218 +++----------- compiler/optimizing/intrinsics_x86.cc | 46 +-- compiler/optimizing/intrinsics_x86_64.cc | 33 +-- 8 files changed, 198 insertions(+), 609 deletions(-) diff --git a/compiler/optimizing/intrinsics.h b/compiler/optimizing/intrinsics.h index 2ab50bb436..0cec5ccfd3 100644 --- a/compiler/optimizing/intrinsics.h +++ b/compiler/optimizing/intrinsics.h @@ -193,6 +193,46 @@ class SystemArrayCopyOptimizations : public IntrinsicOptimizations { #undef INTRISIC_OPTIMIZATION +// +// Macros for use in the intrinsics code generators. +// + +// Defines an unimplemented intrinsic: that is, a method call that is recognized as an +// intrinsic to exploit e.g. no side-effects or exceptions, but otherwise not handled +// by this architecture-specific intrinsics code generator. Eventually it is implemented +// as a true method call. +#define UNIMPLEMENTED_INTRINSIC(Arch, Name) \ +void IntrinsicLocationsBuilder ## Arch::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) { \ +} \ +void IntrinsicCodeGenerator ## Arch::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) { \ +} + +// Defines a list of unreached intrinsics: that is, method calls that are recognized as +// an intrinsic, and then always converted into HIR instructions before they reach any +// architecture-specific intrinsics code generator. +#define UNREACHABLE_INTRINSIC(Arch, Name) \ +void IntrinsicLocationsBuilder ## Arch::Visit ## Name(HInvoke* invoke) { \ + LOG(FATAL) << "Unreachable: intrinsic " << invoke->GetIntrinsic() \ + << " should have been converted to HIR"; \ +} \ +void IntrinsicCodeGenerator ## Arch::Visit ## Name(HInvoke* invoke) { \ + LOG(FATAL) << "Unreachable: intrinsic " << invoke->GetIntrinsic() \ + << " should have been converted to HIR"; \ +} +#define UNREACHABLE_INTRINSICS(Arch) \ +UNREACHABLE_INTRINSIC(Arch, FloatFloatToIntBits) \ +UNREACHABLE_INTRINSIC(Arch, DoubleDoubleToLongBits) \ +UNREACHABLE_INTRINSIC(Arch, FloatIsNaN) \ +UNREACHABLE_INTRINSIC(Arch, DoubleIsNaN) \ +UNREACHABLE_INTRINSIC(Arch, IntegerRotateLeft) \ +UNREACHABLE_INTRINSIC(Arch, LongRotateLeft) \ +UNREACHABLE_INTRINSIC(Arch, IntegerRotateRight) \ +UNREACHABLE_INTRINSIC(Arch, LongRotateRight) \ +UNREACHABLE_INTRINSIC(Arch, IntegerCompare) \ +UNREACHABLE_INTRINSIC(Arch, LongCompare) \ +UNREACHABLE_INTRINSIC(Arch, IntegerSignum) \ +UNREACHABLE_INTRINSIC(Arch, LongSignum) + } // namespace art #endif // ART_COMPILER_OPTIMIZING_INTRINSICS_H_ diff --git a/compiler/optimizing/intrinsics_arm.cc b/compiler/optimizing/intrinsics_arm.cc index 276085ef30..69c970852d 100644 --- a/compiler/optimizing/intrinsics_arm.cc +++ b/compiler/optimizing/intrinsics_arm.cc @@ -1979,54 +1979,30 @@ void IntrinsicCodeGeneratorARM::VisitStringGetCharsNoCheck(HInvoke* invoke) { __ Bind(&done); } -// Unimplemented intrinsics. - -#define UNIMPLEMENTED_INTRINSIC(Name) \ -void IntrinsicLocationsBuilderARM::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) { \ -} \ -void IntrinsicCodeGeneratorARM::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) { \ -} - -UNIMPLEMENTED_INTRINSIC(IntegerBitCount) -UNIMPLEMENTED_INTRINSIC(LongBitCount) -UNIMPLEMENTED_INTRINSIC(MathMinDoubleDouble) -UNIMPLEMENTED_INTRINSIC(MathMinFloatFloat) -UNIMPLEMENTED_INTRINSIC(MathMaxDoubleDouble) -UNIMPLEMENTED_INTRINSIC(MathMaxFloatFloat) -UNIMPLEMENTED_INTRINSIC(MathMinLongLong) -UNIMPLEMENTED_INTRINSIC(MathMaxLongLong) -UNIMPLEMENTED_INTRINSIC(MathCeil) // Could be done by changing rounding mode, maybe? -UNIMPLEMENTED_INTRINSIC(MathFloor) // Could be done by changing rounding mode, maybe? -UNIMPLEMENTED_INTRINSIC(MathRint) -UNIMPLEMENTED_INTRINSIC(MathRoundDouble) // Could be done by changing rounding mode, maybe? -UNIMPLEMENTED_INTRINSIC(MathRoundFloat) // Could be done by changing rounding mode, maybe? -UNIMPLEMENTED_INTRINSIC(UnsafeCASLong) // High register pressure. -UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar) -UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent) - -UNIMPLEMENTED_INTRINSIC(FloatIsInfinite) -UNIMPLEMENTED_INTRINSIC(DoubleIsInfinite) - -UNIMPLEMENTED_INTRINSIC(IntegerHighestOneBit) -UNIMPLEMENTED_INTRINSIC(LongHighestOneBit) -UNIMPLEMENTED_INTRINSIC(IntegerLowestOneBit) -UNIMPLEMENTED_INTRINSIC(LongLowestOneBit) - -// Handled as HIR instructions. -UNIMPLEMENTED_INTRINSIC(FloatFloatToIntBits) -UNIMPLEMENTED_INTRINSIC(DoubleDoubleToLongBits) -UNIMPLEMENTED_INTRINSIC(FloatIsNaN) -UNIMPLEMENTED_INTRINSIC(DoubleIsNaN) -UNIMPLEMENTED_INTRINSIC(IntegerRotateLeft) -UNIMPLEMENTED_INTRINSIC(LongRotateLeft) -UNIMPLEMENTED_INTRINSIC(IntegerRotateRight) -UNIMPLEMENTED_INTRINSIC(LongRotateRight) -UNIMPLEMENTED_INTRINSIC(IntegerCompare) -UNIMPLEMENTED_INTRINSIC(LongCompare) -UNIMPLEMENTED_INTRINSIC(IntegerSignum) -UNIMPLEMENTED_INTRINSIC(LongSignum) - -#undef UNIMPLEMENTED_INTRINSIC +UNIMPLEMENTED_INTRINSIC(ARM, IntegerBitCount) +UNIMPLEMENTED_INTRINSIC(ARM, LongBitCount) +UNIMPLEMENTED_INTRINSIC(ARM, MathMinDoubleDouble) +UNIMPLEMENTED_INTRINSIC(ARM, MathMinFloatFloat) +UNIMPLEMENTED_INTRINSIC(ARM, MathMaxDoubleDouble) +UNIMPLEMENTED_INTRINSIC(ARM, MathMaxFloatFloat) +UNIMPLEMENTED_INTRINSIC(ARM, MathMinLongLong) +UNIMPLEMENTED_INTRINSIC(ARM, MathMaxLongLong) +UNIMPLEMENTED_INTRINSIC(ARM, MathCeil) // Could be done by changing rounding mode, maybe? +UNIMPLEMENTED_INTRINSIC(ARM, MathFloor) // Could be done by changing rounding mode, maybe? +UNIMPLEMENTED_INTRINSIC(ARM, MathRint) +UNIMPLEMENTED_INTRINSIC(ARM, MathRoundDouble) // Could be done by changing rounding mode, maybe? +UNIMPLEMENTED_INTRINSIC(ARM, MathRoundFloat) // Could be done by changing rounding mode, maybe? +UNIMPLEMENTED_INTRINSIC(ARM, UnsafeCASLong) // High register pressure. +UNIMPLEMENTED_INTRINSIC(ARM, SystemArrayCopyChar) +UNIMPLEMENTED_INTRINSIC(ARM, ReferenceGetReferent) +UNIMPLEMENTED_INTRINSIC(ARM, FloatIsInfinite) +UNIMPLEMENTED_INTRINSIC(ARM, DoubleIsInfinite) +UNIMPLEMENTED_INTRINSIC(ARM, IntegerHighestOneBit) +UNIMPLEMENTED_INTRINSIC(ARM, LongHighestOneBit) +UNIMPLEMENTED_INTRINSIC(ARM, IntegerLowestOneBit) +UNIMPLEMENTED_INTRINSIC(ARM, LongLowestOneBit) + +UNREACHABLE_INTRINSICS(ARM) #undef __ diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc index 9f9d8c493f..7a4a6ef266 100644 --- a/compiler/optimizing/intrinsics_arm64.cc +++ b/compiler/optimizing/intrinsics_arm64.cc @@ -1672,43 +1672,19 @@ void IntrinsicCodeGeneratorARM64::VisitStringGetCharsNoCheck(HInvoke* invoke) { __ Bind(&done); } -// Unimplemented intrinsics. - -#define UNIMPLEMENTED_INTRINSIC(Name) \ -void IntrinsicLocationsBuilderARM64::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) { \ -} \ -void IntrinsicCodeGeneratorARM64::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) { \ -} - -UNIMPLEMENTED_INTRINSIC(IntegerBitCount) -UNIMPLEMENTED_INTRINSIC(LongBitCount) -UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar) -UNIMPLEMENTED_INTRINSIC(SystemArrayCopy) -UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent) - -UNIMPLEMENTED_INTRINSIC(FloatIsInfinite) -UNIMPLEMENTED_INTRINSIC(DoubleIsInfinite) - -UNIMPLEMENTED_INTRINSIC(IntegerHighestOneBit) -UNIMPLEMENTED_INTRINSIC(LongHighestOneBit) -UNIMPLEMENTED_INTRINSIC(IntegerLowestOneBit) -UNIMPLEMENTED_INTRINSIC(LongLowestOneBit) - -// Handled as HIR instructions. -UNIMPLEMENTED_INTRINSIC(FloatFloatToIntBits) -UNIMPLEMENTED_INTRINSIC(DoubleDoubleToLongBits) -UNIMPLEMENTED_INTRINSIC(FloatIsNaN) -UNIMPLEMENTED_INTRINSIC(DoubleIsNaN) -UNIMPLEMENTED_INTRINSIC(IntegerRotateLeft) -UNIMPLEMENTED_INTRINSIC(LongRotateLeft) -UNIMPLEMENTED_INTRINSIC(IntegerRotateRight) -UNIMPLEMENTED_INTRINSIC(LongRotateRight) -UNIMPLEMENTED_INTRINSIC(IntegerCompare) -UNIMPLEMENTED_INTRINSIC(LongCompare) -UNIMPLEMENTED_INTRINSIC(IntegerSignum) -UNIMPLEMENTED_INTRINSIC(LongSignum) - -#undef UNIMPLEMENTED_INTRINSIC +UNIMPLEMENTED_INTRINSIC(ARM64, IntegerBitCount) +UNIMPLEMENTED_INTRINSIC(ARM64, LongBitCount) +UNIMPLEMENTED_INTRINSIC(ARM64, SystemArrayCopyChar) +UNIMPLEMENTED_INTRINSIC(ARM64, SystemArrayCopy) +UNIMPLEMENTED_INTRINSIC(ARM64, ReferenceGetReferent) +UNIMPLEMENTED_INTRINSIC(ARM64, FloatIsInfinite) +UNIMPLEMENTED_INTRINSIC(ARM64, DoubleIsInfinite) +UNIMPLEMENTED_INTRINSIC(ARM64, IntegerHighestOneBit) +UNIMPLEMENTED_INTRINSIC(ARM64, LongHighestOneBit) +UNIMPLEMENTED_INTRINSIC(ARM64, IntegerLowestOneBit) +UNIMPLEMENTED_INTRINSIC(ARM64, LongLowestOneBit) + +UNREACHABLE_INTRINSICS(ARM64) #undef __ diff --git a/compiler/optimizing/intrinsics_list.h b/compiler/optimizing/intrinsics_list.h index e1aea924cf..b8933e1684 100644 --- a/compiler/optimizing/intrinsics_list.h +++ b/compiler/optimizing/intrinsics_list.h @@ -19,7 +19,7 @@ // All intrinsics supported by the optimizing compiler. Format is name, then whether it is expected // to be a HInvokeStaticOrDirect node (compared to HInvokeVirtual), then whether it requires an -// environment. +// environment, may have side effects, or may throw exceptions. #define INTRINSICS_LIST(V) \ V(DoubleDoubleToRawLongBits, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc index a737d8100a..5a35dd57a4 100644 --- a/compiler/optimizing/intrinsics_mips.cc +++ b/compiler/optimizing/intrinsics_mips.cc @@ -607,202 +607,6 @@ void IntrinsicCodeGeneratorMIPS::VisitLongNumberOfTrailingZeros(HInvoke* invoke) GetAssembler()); } -enum RotationDirection { - kRotateRight, - kRotateLeft, -}; - -static void GenRotate(HInvoke* invoke, - Primitive::Type type, - bool isR2OrNewer, - RotationDirection direction, - MipsAssembler* assembler) { - DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong); - - LocationSummary* locations = invoke->GetLocations(); - if (invoke->InputAt(1)->IsIntConstant()) { - int32_t shift = static_cast(invoke->InputAt(1)->AsIntConstant()->GetValue()); - if (type == Primitive::kPrimInt) { - Register in = locations->InAt(0).AsRegister(); - Register out = locations->Out().AsRegister(); - - shift &= 0x1f; - if (direction == kRotateLeft) { - shift = (32 - shift) & 0x1F; - } - - if (isR2OrNewer) { - if ((shift != 0) || (out != in)) { - __ Rotr(out, in, shift); - } - } else { - if (shift == 0) { - if (out != in) { - __ Move(out, in); - } - } else { - __ Srl(AT, in, shift); - __ Sll(out, in, 32 - shift); - __ Or(out, out, AT); - } - } - } else { // Primitive::kPrimLong - Register in_lo = locations->InAt(0).AsRegisterPairLow(); - Register in_hi = locations->InAt(0).AsRegisterPairHigh(); - Register out_lo = locations->Out().AsRegisterPairLow(); - Register out_hi = locations->Out().AsRegisterPairHigh(); - - shift &= 0x3f; - if (direction == kRotateLeft) { - shift = (64 - shift) & 0x3F; - } - - if (shift == 0) { - __ Move(out_lo, in_lo); - __ Move(out_hi, in_hi); - } else if (shift == 32) { - __ Move(out_lo, in_hi); - __ Move(out_hi, in_lo); - } else if (shift < 32) { - __ Srl(AT, in_lo, shift); - __ Sll(out_lo, in_hi, 32 - shift); - __ Or(out_lo, out_lo, AT); - __ Srl(AT, in_hi, shift); - __ Sll(out_hi, in_lo, 32 - shift); - __ Or(out_hi, out_hi, AT); - } else { - __ Sll(AT, in_lo, 64 - shift); - __ Srl(out_lo, in_hi, shift - 32); - __ Or(out_lo, out_lo, AT); - __ Sll(AT, in_hi, 64 - shift); - __ Srl(out_hi, in_lo, shift - 32); - __ Or(out_hi, out_hi, AT); - } - } - } else { // !invoke->InputAt(1)->IsIntConstant() - Register shamt = locations->InAt(1).AsRegister(); - if (type == Primitive::kPrimInt) { - Register in = locations->InAt(0).AsRegister(); - Register out = locations->Out().AsRegister(); - - if (isR2OrNewer) { - if (direction == kRotateRight) { - __ Rotrv(out, in, shamt); - } else { - // negu tmp, shamt - __ Subu(TMP, ZERO, shamt); - __ Rotrv(out, in, TMP); - } - } else { - if (direction == kRotateRight) { - __ Srlv(AT, in, shamt); - __ Subu(TMP, ZERO, shamt); - __ Sllv(out, in, TMP); - __ Or(out, out, AT); - } else { - __ Sllv(AT, in, shamt); - __ Subu(TMP, ZERO, shamt); - __ Srlv(out, in, TMP); - __ Or(out, out, AT); - } - } - } else { // Primitive::kPrimLong - Register in_lo = locations->InAt(0).AsRegisterPairLow(); - Register in_hi = locations->InAt(0).AsRegisterPairHigh(); - Register out_lo = locations->Out().AsRegisterPairLow(); - Register out_hi = locations->Out().AsRegisterPairHigh(); - - MipsLabel done; - - if (direction == kRotateRight) { - __ Nor(TMP, ZERO, shamt); - __ Srlv(AT, in_lo, shamt); - __ Sll(out_lo, in_hi, 1); - __ Sllv(out_lo, out_lo, TMP); - __ Or(out_lo, out_lo, AT); - __ Srlv(AT, in_hi, shamt); - __ Sll(out_hi, in_lo, 1); - __ Sllv(out_hi, out_hi, TMP); - __ Or(out_hi, out_hi, AT); - } else { - __ Nor(TMP, ZERO, shamt); - __ Sllv(AT, in_lo, shamt); - __ Srl(out_lo, in_hi, 1); - __ Srlv(out_lo, out_lo, TMP); - __ Or(out_lo, out_lo, AT); - __ Sllv(AT, in_hi, shamt); - __ Srl(out_hi, in_lo, 1); - __ Srlv(out_hi, out_hi, TMP); - __ Or(out_hi, out_hi, AT); - } - - __ Andi(TMP, shamt, 32); - __ Beqz(TMP, &done); - __ Move(TMP, out_hi); - __ Move(out_hi, out_lo); - __ Move(out_lo, TMP); - - __ Bind(&done); - } - } -} - -// int java.lang.Integer.rotateRight(int i, int distance) -void IntrinsicLocationsBuilderMIPS::VisitIntegerRotateRight(HInvoke* invoke) { - LocationSummary* locations = new (arena_) LocationSummary(invoke, - LocationSummary::kNoCall, - kIntrinsified); - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RegisterOrConstant(invoke->InputAt(1))); - locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); -} - -void IntrinsicCodeGeneratorMIPS::VisitIntegerRotateRight(HInvoke* invoke) { - GenRotate(invoke, Primitive::kPrimInt, IsR2OrNewer(), kRotateRight, GetAssembler()); -} - -// long java.lang.Long.rotateRight(long i, int distance) -void IntrinsicLocationsBuilderMIPS::VisitLongRotateRight(HInvoke* invoke) { - LocationSummary* locations = new (arena_) LocationSummary(invoke, - LocationSummary::kNoCall, - kIntrinsified); - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RegisterOrConstant(invoke->InputAt(1))); - locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); -} - -void IntrinsicCodeGeneratorMIPS::VisitLongRotateRight(HInvoke* invoke) { - GenRotate(invoke, Primitive::kPrimLong, IsR2OrNewer(), kRotateRight, GetAssembler()); -} - -// int java.lang.Integer.rotateLeft(int i, int distance) -void IntrinsicLocationsBuilderMIPS::VisitIntegerRotateLeft(HInvoke* invoke) { - LocationSummary* locations = new (arena_) LocationSummary(invoke, - LocationSummary::kNoCall, - kIntrinsified); - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RegisterOrConstant(invoke->InputAt(1))); - locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); -} - -void IntrinsicCodeGeneratorMIPS::VisitIntegerRotateLeft(HInvoke* invoke) { - GenRotate(invoke, Primitive::kPrimInt, IsR2OrNewer(), kRotateLeft, GetAssembler()); -} - -// long java.lang.Long.rotateLeft(long i, int distance) -void IntrinsicLocationsBuilderMIPS::VisitLongRotateLeft(HInvoke* invoke) { - LocationSummary* locations = new (arena_) LocationSummary(invoke, - LocationSummary::kNoCall, - kIntrinsified); - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RegisterOrConstant(invoke->InputAt(1))); - locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); -} - -void IntrinsicCodeGeneratorMIPS::VisitLongRotateLeft(HInvoke* invoke) { - GenRotate(invoke, Primitive::kPrimLong, IsR2OrNewer(), kRotateLeft, GetAssembler()); -} - // int java.lang.Integer.reverse(int) void IntrinsicLocationsBuilderMIPS::VisitIntegerReverse(HInvoke* invoke) { CreateIntToIntLocations(arena_, invoke); @@ -1698,90 +1502,72 @@ void IntrinsicCodeGeneratorMIPS::VisitStringEquals(HInvoke* invoke) { __ Bind(&end); } -// Unimplemented intrinsics. - -#define UNIMPLEMENTED_INTRINSIC(Name) \ -void IntrinsicLocationsBuilderMIPS::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) { \ -} \ -void IntrinsicCodeGeneratorMIPS::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) { \ -} - -UNIMPLEMENTED_INTRINSIC(IntegerBitCount) -UNIMPLEMENTED_INTRINSIC(LongBitCount) - -UNIMPLEMENTED_INTRINSIC(MathCeil) -UNIMPLEMENTED_INTRINSIC(MathFloor) -UNIMPLEMENTED_INTRINSIC(MathRint) -UNIMPLEMENTED_INTRINSIC(MathRoundDouble) -UNIMPLEMENTED_INTRINSIC(MathRoundFloat) -UNIMPLEMENTED_INTRINSIC(ThreadCurrentThread) -UNIMPLEMENTED_INTRINSIC(UnsafeGet) -UNIMPLEMENTED_INTRINSIC(UnsafeGetVolatile) -UNIMPLEMENTED_INTRINSIC(UnsafeGetLong) -UNIMPLEMENTED_INTRINSIC(UnsafeGetLongVolatile) -UNIMPLEMENTED_INTRINSIC(UnsafeGetObject) -UNIMPLEMENTED_INTRINSIC(UnsafeGetObjectVolatile) -UNIMPLEMENTED_INTRINSIC(UnsafePut) -UNIMPLEMENTED_INTRINSIC(UnsafePutOrdered) -UNIMPLEMENTED_INTRINSIC(UnsafePutVolatile) -UNIMPLEMENTED_INTRINSIC(UnsafePutObject) -UNIMPLEMENTED_INTRINSIC(UnsafePutObjectOrdered) -UNIMPLEMENTED_INTRINSIC(UnsafePutObjectVolatile) -UNIMPLEMENTED_INTRINSIC(UnsafePutLong) -UNIMPLEMENTED_INTRINSIC(UnsafePutLongOrdered) -UNIMPLEMENTED_INTRINSIC(UnsafePutLongVolatile) -UNIMPLEMENTED_INTRINSIC(UnsafeCASInt) -UNIMPLEMENTED_INTRINSIC(UnsafeCASLong) -UNIMPLEMENTED_INTRINSIC(UnsafeCASObject) -UNIMPLEMENTED_INTRINSIC(StringCompareTo) -UNIMPLEMENTED_INTRINSIC(StringIndexOf) -UNIMPLEMENTED_INTRINSIC(StringIndexOfAfter) -UNIMPLEMENTED_INTRINSIC(StringNewStringFromBytes) -UNIMPLEMENTED_INTRINSIC(StringNewStringFromChars) -UNIMPLEMENTED_INTRINSIC(StringNewStringFromString) - -UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent) -UNIMPLEMENTED_INTRINSIC(StringGetCharsNoCheck) -UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar) -UNIMPLEMENTED_INTRINSIC(SystemArrayCopy) - -UNIMPLEMENTED_INTRINSIC(MathCos) -UNIMPLEMENTED_INTRINSIC(MathSin) -UNIMPLEMENTED_INTRINSIC(MathAcos) -UNIMPLEMENTED_INTRINSIC(MathAsin) -UNIMPLEMENTED_INTRINSIC(MathAtan) -UNIMPLEMENTED_INTRINSIC(MathAtan2) -UNIMPLEMENTED_INTRINSIC(MathCbrt) -UNIMPLEMENTED_INTRINSIC(MathCosh) -UNIMPLEMENTED_INTRINSIC(MathExp) -UNIMPLEMENTED_INTRINSIC(MathExpm1) -UNIMPLEMENTED_INTRINSIC(MathHypot) -UNIMPLEMENTED_INTRINSIC(MathLog) -UNIMPLEMENTED_INTRINSIC(MathLog10) -UNIMPLEMENTED_INTRINSIC(MathNextAfter) -UNIMPLEMENTED_INTRINSIC(MathSinh) -UNIMPLEMENTED_INTRINSIC(MathTan) -UNIMPLEMENTED_INTRINSIC(MathTanh) - -UNIMPLEMENTED_INTRINSIC(FloatIsInfinite) -UNIMPLEMENTED_INTRINSIC(DoubleIsInfinite) - -UNIMPLEMENTED_INTRINSIC(IntegerHighestOneBit) -UNIMPLEMENTED_INTRINSIC(LongHighestOneBit) -UNIMPLEMENTED_INTRINSIC(IntegerLowestOneBit) -UNIMPLEMENTED_INTRINSIC(LongLowestOneBit) - -// Handled as HIR instructions. -UNIMPLEMENTED_INTRINSIC(FloatFloatToIntBits) -UNIMPLEMENTED_INTRINSIC(DoubleDoubleToLongBits) -UNIMPLEMENTED_INTRINSIC(FloatIsNaN) -UNIMPLEMENTED_INTRINSIC(DoubleIsNaN) -UNIMPLEMENTED_INTRINSIC(IntegerCompare) -UNIMPLEMENTED_INTRINSIC(LongCompare) -UNIMPLEMENTED_INTRINSIC(IntegerSignum) -UNIMPLEMENTED_INTRINSIC(LongSignum) - -#undef UNIMPLEMENTED_INTRINSIC +UNIMPLEMENTED_INTRINSIC(MIPS, IntegerBitCount) +UNIMPLEMENTED_INTRINSIC(MIPS, LongBitCount) + +UNIMPLEMENTED_INTRINSIC(MIPS, MathCeil) +UNIMPLEMENTED_INTRINSIC(MIPS, MathFloor) +UNIMPLEMENTED_INTRINSIC(MIPS, MathRint) +UNIMPLEMENTED_INTRINSIC(MIPS, MathRoundDouble) +UNIMPLEMENTED_INTRINSIC(MIPS, MathRoundFloat) +UNIMPLEMENTED_INTRINSIC(MIPS, ThreadCurrentThread) +UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGet) +UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetVolatile) +UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetLong) +UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetLongVolatile) +UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetObject) +UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetObjectVolatile) +UNIMPLEMENTED_INTRINSIC(MIPS, UnsafePut) +UNIMPLEMENTED_INTRINSIC(MIPS, UnsafePutOrdered) +UNIMPLEMENTED_INTRINSIC(MIPS, UnsafePutVolatile) +UNIMPLEMENTED_INTRINSIC(MIPS, UnsafePutObject) +UNIMPLEMENTED_INTRINSIC(MIPS, UnsafePutObjectOrdered) +UNIMPLEMENTED_INTRINSIC(MIPS, UnsafePutObjectVolatile) +UNIMPLEMENTED_INTRINSIC(MIPS, UnsafePutLong) +UNIMPLEMENTED_INTRINSIC(MIPS, UnsafePutLongOrdered) +UNIMPLEMENTED_INTRINSIC(MIPS, UnsafePutLongVolatile) +UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeCASInt) +UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeCASLong) +UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeCASObject) +UNIMPLEMENTED_INTRINSIC(MIPS, StringCompareTo) +UNIMPLEMENTED_INTRINSIC(MIPS, StringIndexOf) +UNIMPLEMENTED_INTRINSIC(MIPS, StringIndexOfAfter) +UNIMPLEMENTED_INTRINSIC(MIPS, StringNewStringFromBytes) +UNIMPLEMENTED_INTRINSIC(MIPS, StringNewStringFromChars) +UNIMPLEMENTED_INTRINSIC(MIPS, StringNewStringFromString) + +UNIMPLEMENTED_INTRINSIC(MIPS, ReferenceGetReferent) +UNIMPLEMENTED_INTRINSIC(MIPS, StringGetCharsNoCheck) +UNIMPLEMENTED_INTRINSIC(MIPS, SystemArrayCopyChar) +UNIMPLEMENTED_INTRINSIC(MIPS, SystemArrayCopy) + +UNIMPLEMENTED_INTRINSIC(MIPS, MathCos) +UNIMPLEMENTED_INTRINSIC(MIPS, MathSin) +UNIMPLEMENTED_INTRINSIC(MIPS, MathAcos) +UNIMPLEMENTED_INTRINSIC(MIPS, MathAsin) +UNIMPLEMENTED_INTRINSIC(MIPS, MathAtan) +UNIMPLEMENTED_INTRINSIC(MIPS, MathAtan2) +UNIMPLEMENTED_INTRINSIC(MIPS, MathCbrt) +UNIMPLEMENTED_INTRINSIC(MIPS, MathCosh) +UNIMPLEMENTED_INTRINSIC(MIPS, MathExp) +UNIMPLEMENTED_INTRINSIC(MIPS, MathExpm1) +UNIMPLEMENTED_INTRINSIC(MIPS, MathHypot) +UNIMPLEMENTED_INTRINSIC(MIPS, MathLog) +UNIMPLEMENTED_INTRINSIC(MIPS, MathLog10) +UNIMPLEMENTED_INTRINSIC(MIPS, MathNextAfter) +UNIMPLEMENTED_INTRINSIC(MIPS, MathSinh) +UNIMPLEMENTED_INTRINSIC(MIPS, MathTan) +UNIMPLEMENTED_INTRINSIC(MIPS, MathTanh) + +UNIMPLEMENTED_INTRINSIC(MIPS, FloatIsInfinite) +UNIMPLEMENTED_INTRINSIC(MIPS, DoubleIsInfinite) + +UNIMPLEMENTED_INTRINSIC(MIPS, IntegerHighestOneBit) +UNIMPLEMENTED_INTRINSIC(MIPS, LongHighestOneBit) +UNIMPLEMENTED_INTRINSIC(MIPS, IntegerLowestOneBit) +UNIMPLEMENTED_INTRINSIC(MIPS, LongLowestOneBit) + +UNREACHABLE_INTRINSICS(MIPS) #undef __ diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc index a7a2560043..45611f0ac7 100644 --- a/compiler/optimizing/intrinsics_mips64.cc +++ b/compiler/optimizing/intrinsics_mips64.cc @@ -340,130 +340,6 @@ void IntrinsicCodeGeneratorMIPS64::VisitLongNumberOfTrailingZeros(HInvoke* invok GenNumberOfTrailingZeroes(invoke->GetLocations(), /* is64bit */ true, GetAssembler()); } -static void GenRotateRight(HInvoke* invoke, - Primitive::Type type, - Mips64Assembler* assembler) { - DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong); - - LocationSummary* locations = invoke->GetLocations(); - GpuRegister in = locations->InAt(0).AsRegister(); - GpuRegister out = locations->Out().AsRegister(); - - if (invoke->InputAt(1)->IsIntConstant()) { - uint32_t shift = static_cast(invoke->InputAt(1)->AsIntConstant()->GetValue()); - if (type == Primitive::kPrimInt) { - shift &= 0x1f; - __ Rotr(out, in, shift); - } else { - shift &= 0x3f; - if (shift < 32) { - __ Drotr(out, in, shift); - } else { - shift &= 0x1f; - __ Drotr32(out, in, shift); - } - } - } else { - GpuRegister shamt = locations->InAt(1).AsRegister(); - if (type == Primitive::kPrimInt) { - __ Rotrv(out, in, shamt); - } else { - __ Drotrv(out, in, shamt); - } - } -} - -// int java.lang.Integer.rotateRight(int i, int distance) -void IntrinsicLocationsBuilderMIPS64::VisitIntegerRotateRight(HInvoke* invoke) { - LocationSummary* locations = new (arena_) LocationSummary(invoke, - LocationSummary::kNoCall, - kIntrinsified); - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RegisterOrConstant(invoke->InputAt(1))); - locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); -} - -void IntrinsicCodeGeneratorMIPS64::VisitIntegerRotateRight(HInvoke* invoke) { - GenRotateRight(invoke, Primitive::kPrimInt, GetAssembler()); -} - -// long java.lang.Long.rotateRight(long i, int distance) -void IntrinsicLocationsBuilderMIPS64::VisitLongRotateRight(HInvoke* invoke) { - LocationSummary* locations = new (arena_) LocationSummary(invoke, - LocationSummary::kNoCall, - kIntrinsified); - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RegisterOrConstant(invoke->InputAt(1))); - locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); -} - -void IntrinsicCodeGeneratorMIPS64::VisitLongRotateRight(HInvoke* invoke) { - GenRotateRight(invoke, Primitive::kPrimLong, GetAssembler()); -} - -static void GenRotateLeft(HInvoke* invoke, - Primitive::Type type, - Mips64Assembler* assembler) { - DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong); - - LocationSummary* locations = invoke->GetLocations(); - GpuRegister in = locations->InAt(0).AsRegister(); - GpuRegister out = locations->Out().AsRegister(); - - if (invoke->InputAt(1)->IsIntConstant()) { - int32_t shift = -static_cast(invoke->InputAt(1)->AsIntConstant()->GetValue()); - if (type == Primitive::kPrimInt) { - shift &= 0x1f; - __ Rotr(out, in, shift); - } else { - shift &= 0x3f; - if (shift < 32) { - __ Drotr(out, in, shift); - } else { - shift &= 0x1f; - __ Drotr32(out, in, shift); - } - } - } else { - GpuRegister shamt = locations->InAt(1).AsRegister(); - if (type == Primitive::kPrimInt) { - __ Subu(TMP, ZERO, shamt); - __ Rotrv(out, in, TMP); - } else { - __ Dsubu(TMP, ZERO, shamt); - __ Drotrv(out, in, TMP); - } - } -} - -// int java.lang.Integer.rotateLeft(int i, int distance) -void IntrinsicLocationsBuilderMIPS64::VisitIntegerRotateLeft(HInvoke* invoke) { - LocationSummary* locations = new (arena_) LocationSummary(invoke, - LocationSummary::kNoCall, - kIntrinsified); - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RegisterOrConstant(invoke->InputAt(1))); - locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); -} - -void IntrinsicCodeGeneratorMIPS64::VisitIntegerRotateLeft(HInvoke* invoke) { - GenRotateLeft(invoke, Primitive::kPrimInt, GetAssembler()); -} - -// long java.lang.Long.rotateLeft(long i, int distance) -void IntrinsicLocationsBuilderMIPS64::VisitLongRotateLeft(HInvoke* invoke) { - LocationSummary* locations = new (arena_) LocationSummary(invoke, - LocationSummary::kNoCall, - kIntrinsified); - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RegisterOrConstant(invoke->InputAt(1))); - locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); -} - -void IntrinsicCodeGeneratorMIPS64::VisitLongRotateLeft(HInvoke* invoke) { - GenRotateLeft(invoke, Primitive::kPrimLong, GetAssembler()); -} - static void GenReverse(LocationSummary* locations, Primitive::Type type, Mips64Assembler* assembler) { @@ -1785,62 +1661,44 @@ void IntrinsicCodeGeneratorMIPS64::VisitStringNewStringFromString(HInvoke* invok __ Bind(slow_path->GetExitLabel()); } -// Unimplemented intrinsics. - -#define UNIMPLEMENTED_INTRINSIC(Name) \ -void IntrinsicLocationsBuilderMIPS64::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) { \ -} \ -void IntrinsicCodeGeneratorMIPS64::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) { \ -} - -UNIMPLEMENTED_INTRINSIC(IntegerBitCount) -UNIMPLEMENTED_INTRINSIC(LongBitCount) - -UNIMPLEMENTED_INTRINSIC(MathRoundDouble) -UNIMPLEMENTED_INTRINSIC(MathRoundFloat) - -UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent) -UNIMPLEMENTED_INTRINSIC(StringGetCharsNoCheck) -UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar) -UNIMPLEMENTED_INTRINSIC(SystemArrayCopy) - -UNIMPLEMENTED_INTRINSIC(MathCos) -UNIMPLEMENTED_INTRINSIC(MathSin) -UNIMPLEMENTED_INTRINSIC(MathAcos) -UNIMPLEMENTED_INTRINSIC(MathAsin) -UNIMPLEMENTED_INTRINSIC(MathAtan) -UNIMPLEMENTED_INTRINSIC(MathAtan2) -UNIMPLEMENTED_INTRINSIC(MathCbrt) -UNIMPLEMENTED_INTRINSIC(MathCosh) -UNIMPLEMENTED_INTRINSIC(MathExp) -UNIMPLEMENTED_INTRINSIC(MathExpm1) -UNIMPLEMENTED_INTRINSIC(MathHypot) -UNIMPLEMENTED_INTRINSIC(MathLog) -UNIMPLEMENTED_INTRINSIC(MathLog10) -UNIMPLEMENTED_INTRINSIC(MathNextAfter) -UNIMPLEMENTED_INTRINSIC(MathSinh) -UNIMPLEMENTED_INTRINSIC(MathTan) -UNIMPLEMENTED_INTRINSIC(MathTanh) - -UNIMPLEMENTED_INTRINSIC(FloatIsInfinite) -UNIMPLEMENTED_INTRINSIC(DoubleIsInfinite) - -UNIMPLEMENTED_INTRINSIC(IntegerHighestOneBit) -UNIMPLEMENTED_INTRINSIC(LongHighestOneBit) -UNIMPLEMENTED_INTRINSIC(IntegerLowestOneBit) -UNIMPLEMENTED_INTRINSIC(LongLowestOneBit) - -// Handled as HIR instructions. -UNIMPLEMENTED_INTRINSIC(FloatFloatToIntBits) -UNIMPLEMENTED_INTRINSIC(DoubleDoubleToLongBits) -UNIMPLEMENTED_INTRINSIC(FloatIsNaN) -UNIMPLEMENTED_INTRINSIC(DoubleIsNaN) -UNIMPLEMENTED_INTRINSIC(IntegerCompare) -UNIMPLEMENTED_INTRINSIC(LongCompare) -UNIMPLEMENTED_INTRINSIC(IntegerSignum) -UNIMPLEMENTED_INTRINSIC(LongSignum) - -#undef UNIMPLEMENTED_INTRINSIC +UNIMPLEMENTED_INTRINSIC(MIPS64, IntegerBitCount) +UNIMPLEMENTED_INTRINSIC(MIPS64, LongBitCount) + +UNIMPLEMENTED_INTRINSIC(MIPS64, MathRoundDouble) +UNIMPLEMENTED_INTRINSIC(MIPS64, MathRoundFloat) + +UNIMPLEMENTED_INTRINSIC(MIPS64, ReferenceGetReferent) +UNIMPLEMENTED_INTRINSIC(MIPS64, StringGetCharsNoCheck) +UNIMPLEMENTED_INTRINSIC(MIPS64, SystemArrayCopyChar) +UNIMPLEMENTED_INTRINSIC(MIPS64, SystemArrayCopy) + +UNIMPLEMENTED_INTRINSIC(MIPS64, MathCos) +UNIMPLEMENTED_INTRINSIC(MIPS64, MathSin) +UNIMPLEMENTED_INTRINSIC(MIPS64, MathAcos) +UNIMPLEMENTED_INTRINSIC(MIPS64, MathAsin) +UNIMPLEMENTED_INTRINSIC(MIPS64, MathAtan) +UNIMPLEMENTED_INTRINSIC(MIPS64, MathAtan2) +UNIMPLEMENTED_INTRINSIC(MIPS64, MathCbrt) +UNIMPLEMENTED_INTRINSIC(MIPS64, MathCosh) +UNIMPLEMENTED_INTRINSIC(MIPS64, MathExp) +UNIMPLEMENTED_INTRINSIC(MIPS64, MathExpm1) +UNIMPLEMENTED_INTRINSIC(MIPS64, MathHypot) +UNIMPLEMENTED_INTRINSIC(MIPS64, MathLog) +UNIMPLEMENTED_INTRINSIC(MIPS64, MathLog10) +UNIMPLEMENTED_INTRINSIC(MIPS64, MathNextAfter) +UNIMPLEMENTED_INTRINSIC(MIPS64, MathSinh) +UNIMPLEMENTED_INTRINSIC(MIPS64, MathTan) +UNIMPLEMENTED_INTRINSIC(MIPS64, MathTanh) + +UNIMPLEMENTED_INTRINSIC(MIPS64, FloatIsInfinite) +UNIMPLEMENTED_INTRINSIC(MIPS64, DoubleIsInfinite) + +UNIMPLEMENTED_INTRINSIC(MIPS64, IntegerHighestOneBit) +UNIMPLEMENTED_INTRINSIC(MIPS64, LongHighestOneBit) +UNIMPLEMENTED_INTRINSIC(MIPS64, IntegerLowestOneBit) +UNIMPLEMENTED_INTRINSIC(MIPS64, LongLowestOneBit) + +UNREACHABLE_INTRINSICS(MIPS64) #undef __ diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc index bfa4e374f7..9a2dc4182d 100644 --- a/compiler/optimizing/intrinsics_x86.cc +++ b/compiler/optimizing/intrinsics_x86.cc @@ -2627,41 +2627,17 @@ void IntrinsicCodeGeneratorX86::VisitLongNumberOfTrailingZeros(HInvoke* invoke) GenTrailingZeros(GetAssembler(), codegen_, invoke, /* is_long */ true); } -// Unimplemented intrinsics. - -#define UNIMPLEMENTED_INTRINSIC(Name) \ -void IntrinsicLocationsBuilderX86::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) { \ -} \ -void IntrinsicCodeGeneratorX86::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) { \ -} - -UNIMPLEMENTED_INTRINSIC(MathRoundDouble) -UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent) -UNIMPLEMENTED_INTRINSIC(SystemArrayCopy) - -UNIMPLEMENTED_INTRINSIC(FloatIsInfinite) -UNIMPLEMENTED_INTRINSIC(DoubleIsInfinite) - -UNIMPLEMENTED_INTRINSIC(IntegerHighestOneBit) -UNIMPLEMENTED_INTRINSIC(LongHighestOneBit) -UNIMPLEMENTED_INTRINSIC(IntegerLowestOneBit) -UNIMPLEMENTED_INTRINSIC(LongLowestOneBit) - -// Handled as HIR instructions. -UNIMPLEMENTED_INTRINSIC(FloatFloatToIntBits) -UNIMPLEMENTED_INTRINSIC(DoubleDoubleToLongBits) -UNIMPLEMENTED_INTRINSIC(FloatIsNaN) -UNIMPLEMENTED_INTRINSIC(DoubleIsNaN) -UNIMPLEMENTED_INTRINSIC(IntegerRotateLeft) -UNIMPLEMENTED_INTRINSIC(LongRotateLeft) -UNIMPLEMENTED_INTRINSIC(IntegerRotateRight) -UNIMPLEMENTED_INTRINSIC(LongRotateRight) -UNIMPLEMENTED_INTRINSIC(IntegerCompare) -UNIMPLEMENTED_INTRINSIC(LongCompare) -UNIMPLEMENTED_INTRINSIC(IntegerSignum) -UNIMPLEMENTED_INTRINSIC(LongSignum) - -#undef UNIMPLEMENTED_INTRINSIC +UNIMPLEMENTED_INTRINSIC(X86, MathRoundDouble) +UNIMPLEMENTED_INTRINSIC(X86, ReferenceGetReferent) +UNIMPLEMENTED_INTRINSIC(X86, SystemArrayCopy) +UNIMPLEMENTED_INTRINSIC(X86, FloatIsInfinite) +UNIMPLEMENTED_INTRINSIC(X86, DoubleIsInfinite) +UNIMPLEMENTED_INTRINSIC(X86, IntegerHighestOneBit) +UNIMPLEMENTED_INTRINSIC(X86, LongHighestOneBit) +UNIMPLEMENTED_INTRINSIC(X86, IntegerLowestOneBit) +UNIMPLEMENTED_INTRINSIC(X86, LongLowestOneBit) + +UNREACHABLE_INTRINSICS(X86) #undef __ diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc index 15c399712d..75204b4b49 100644 --- a/compiler/optimizing/intrinsics_x86_64.cc +++ b/compiler/optimizing/intrinsics_x86_64.cc @@ -2711,34 +2711,11 @@ void IntrinsicCodeGeneratorX86_64::VisitLongNumberOfTrailingZeros(HInvoke* invok GenTrailingZeros(GetAssembler(), codegen_, invoke, /* is_long */ true); } -// Unimplemented intrinsics. - -#define UNIMPLEMENTED_INTRINSIC(Name) \ -void IntrinsicLocationsBuilderX86_64::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) { \ -} \ -void IntrinsicCodeGeneratorX86_64::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) { \ -} - -UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent) - -UNIMPLEMENTED_INTRINSIC(FloatIsInfinite) -UNIMPLEMENTED_INTRINSIC(DoubleIsInfinite) - -// Handled as HIR instructions. -UNIMPLEMENTED_INTRINSIC(FloatFloatToIntBits) -UNIMPLEMENTED_INTRINSIC(DoubleDoubleToLongBits) -UNIMPLEMENTED_INTRINSIC(FloatIsNaN) -UNIMPLEMENTED_INTRINSIC(DoubleIsNaN) -UNIMPLEMENTED_INTRINSIC(IntegerRotateLeft) -UNIMPLEMENTED_INTRINSIC(LongRotateLeft) -UNIMPLEMENTED_INTRINSIC(IntegerRotateRight) -UNIMPLEMENTED_INTRINSIC(LongRotateRight) -UNIMPLEMENTED_INTRINSIC(IntegerCompare) -UNIMPLEMENTED_INTRINSIC(LongCompare) -UNIMPLEMENTED_INTRINSIC(IntegerSignum) -UNIMPLEMENTED_INTRINSIC(LongSignum) - -#undef UNIMPLEMENTED_INTRINSIC +UNIMPLEMENTED_INTRINSIC(X86_64, ReferenceGetReferent) +UNIMPLEMENTED_INTRINSIC(X86_64, FloatIsInfinite) +UNIMPLEMENTED_INTRINSIC(X86_64, DoubleIsInfinite) + +UNREACHABLE_INTRINSICS(X86_64) #undef __ -- GitLab From 511e41b4ec2d378a5c434598ebd0b3d6136fff9d Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Wed, 2 Mar 2016 17:09:35 +0000 Subject: [PATCH 102/204] Clear inline caches if a ProfilingInfo gets revived. This avoids stalled class references. Bug: 27398183 Bug: 23128949 Bug: 26846185 Change-Id: I9539215241708e26fef887e02201ce1feabc2d1a --- runtime/jit/jit_code_cache.cc | 12 ++++++++++-- runtime/jit/profiling_info.h | 6 +++++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index e8a7189d57..e041a42eeb 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -705,9 +705,17 @@ void JitCodeCache::DoCollection(Thread* self, bool collect_profiling_info) { auto profiling_kept_end = std::remove_if(profiling_infos_.begin(), profiling_infos_.end(), [this] (ProfilingInfo* info) NO_THREAD_SAFETY_ANALYSIS { const void* ptr = info->GetMethod()->GetEntryPointFromQuickCompiledCode(); + // We have previously cleared the ProfilingInfo pointer in the ArtMethod in the hope + // that the compiled code would not get revived. As mutator threads run concurrently, + // they may have revived the compiled code, and now we are in the situation where + // a method has compiled code but no ProfilingInfo. + // We make sure compiled methods have a ProfilingInfo object. It is needed for + // code cache collection. if (ContainsPc(ptr) && info->GetMethod()->GetProfilingInfo(sizeof(void*)) == nullptr) { - // Make sure compiled methods have a ProfilingInfo object. It is needed for - // code cache collection. + // We clear the inline caches as classes in it might be stalled. + info->ClearInlineCaches(); + // Do a fence to make sure the clearing is seen before attaching to the method. + QuasiAtomic::ThreadFenceRelease(); info->GetMethod()->SetProfilingInfo(info); } else if (info->GetMethod()->GetProfilingInfo(sizeof(void*)) != info) { // No need for this ProfilingInfo object anymore. diff --git a/runtime/jit/profiling_info.h b/runtime/jit/profiling_info.h index d54f3dfc11..a8c056c7c9 100644 --- a/runtime/jit/profiling_info.h +++ b/runtime/jit/profiling_info.h @@ -134,13 +134,17 @@ class ProfilingInfo { return saved_entry_point_; } + void ClearInlineCaches() { + memset(&cache_, 0, number_of_inline_caches_ * sizeof(InlineCache)); + } + private: ProfilingInfo(ArtMethod* method, const std::vector& entries) : number_of_inline_caches_(entries.size()), method_(method), is_method_being_compiled_(false), saved_entry_point_(nullptr) { - memset(&cache_, 0, number_of_inline_caches_ * sizeof(InlineCache)); + ClearInlineCaches(); for (size_t i = 0; i < number_of_inline_caches_; ++i) { cache_[i].dex_pc_ = entries[i]; } -- GitLab From 5b83050affa6a3b1d3863c0b903f9d48fe4aefb2 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Wed, 2 Mar 2016 10:30:23 -0800 Subject: [PATCH 103/204] Fix potential linear alloc memory leak Previously, if we created a linear alloc for a class loader but never created the class table, the linear alloc would never get freed since it would have no corresponding ClassLoaderData. Fixes valgrind-test-art-host-gtest-oat_test Bug: 27384882 Change-Id: Ic8f35b58c3117127a39521b6b9d25ef12c72040c --- runtime/class_linker.cc | 36 ++++++++++++++++++++++-------------- runtime/class_linker.h | 11 +++++++++-- runtime/stack.cc | 2 +- 3 files changed, 32 insertions(+), 17 deletions(-) diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index b5e6532b6e..5f26b5d7b5 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -2855,8 +2855,9 @@ LinearAlloc* ClassLinker::GetOrCreateAllocatorForClassLoader(mirror::ClassLoader WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); LinearAlloc* allocator = class_loader->GetAllocator(); if (allocator == nullptr) { - allocator = Runtime::Current()->CreateLinearAlloc(); - class_loader->SetAllocator(allocator); + RegisterClassLoader(class_loader); + allocator = class_loader->GetAllocator(); + CHECK(allocator != nullptr); } return allocator; } @@ -4817,24 +4818,31 @@ void ClassLinker::FixupTemporaryDeclaringClass(mirror::Class* temp_class, Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(new_class); } +void ClassLinker::RegisterClassLoader(mirror::ClassLoader* class_loader) { + CHECK(class_loader->GetAllocator() == nullptr); + CHECK(class_loader->GetClassTable() == nullptr); + Thread* const self = Thread::Current(); + ClassLoaderData data; + data.weak_root = self->GetJniEnv()->vm->AddWeakGlobalRef(self, class_loader); + // Create and set the class table. + data.class_table = new ClassTable; + class_loader->SetClassTable(data.class_table); + // Create and set the linear allocator. + data.allocator = Runtime::Current()->CreateLinearAlloc(); + class_loader->SetAllocator(data.allocator); + // Add to the list so that we know to free the data later. + class_loaders_.push_back(data); +} + ClassTable* ClassLinker::InsertClassTableForClassLoader(mirror::ClassLoader* class_loader) { if (class_loader == nullptr) { return &boot_class_table_; } ClassTable* class_table = class_loader->GetClassTable(); if (class_table == nullptr) { - class_table = new ClassTable; - Thread* const self = Thread::Current(); - ClassLoaderData data; - data.weak_root = self->GetJniEnv()->vm->AddWeakGlobalRef(self, class_loader); - data.class_table = class_table; - // Don't already have a class table, add it to the class loader. - CHECK(class_loader->GetClassTable() == nullptr); - class_loader->SetClassTable(data.class_table); - // Should have been set when we registered the dex file. - data.allocator = class_loader->GetAllocator(); - CHECK(data.allocator != nullptr); - class_loaders_.push_back(data); + RegisterClassLoader(class_loader); + class_table = class_loader->GetClassTable(); + DCHECK(class_table != nullptr); } return class_table; } diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 729617ddab..0a75b273e5 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -576,12 +576,12 @@ class ClassLinker { // Unlike GetOrCreateAllocatorForClassLoader, GetAllocatorForClassLoader asserts that the // allocator for this class loader is already created. - static LinearAlloc* GetAllocatorForClassLoader(mirror::ClassLoader* class_loader) + LinearAlloc* GetAllocatorForClassLoader(mirror::ClassLoader* class_loader) SHARED_REQUIRES(Locks::mutator_lock_); // Return the linear alloc for a class loader if it is already allocated, otherwise allocate and // set it. TODO: Consider using a lock other than classlinker_classes_lock_. - static LinearAlloc* GetOrCreateAllocatorForClassLoader(mirror::ClassLoader* class_loader) + LinearAlloc* GetOrCreateAllocatorForClassLoader(mirror::ClassLoader* class_loader) REQUIRES(!Locks::classlinker_classes_lock_) SHARED_REQUIRES(Locks::mutator_lock_); @@ -980,9 +980,16 @@ class ClassLinker { mirror::Class* LookupClassFromBootImage(const char* descriptor) SHARED_REQUIRES(Locks::mutator_lock_); + // Register a class loader and create its class table and allocator. Should not be called if + // these are already created. + void RegisterClassLoader(mirror::ClassLoader* class_loader) + SHARED_REQUIRES(Locks::mutator_lock_) + REQUIRES(Locks::classlinker_classes_lock_); + // Returns null if not found. ClassTable* ClassTableForClassLoader(mirror::ClassLoader* class_loader) SHARED_REQUIRES(Locks::mutator_lock_, Locks::classlinker_classes_lock_); + // Insert a new class table if not found. ClassTable* InsertClassTableForClassLoader(mirror::ClassLoader* class_loader) SHARED_REQUIRES(Locks::mutator_lock_) diff --git a/runtime/stack.cc b/runtime/stack.cc index b1f1ed61b4..ee5da8e150 100644 --- a/runtime/stack.cc +++ b/runtime/stack.cc @@ -739,7 +739,7 @@ void StackVisitor::SanityCheckFrame() const { // Check class linker linear allocs. mirror::Class* klass = method->GetDeclaringClass(); LinearAlloc* const class_linear_alloc = (klass != nullptr) - ? ClassLinker::GetAllocatorForClassLoader(klass->GetClassLoader()) + ? runtime->GetClassLinker()->GetAllocatorForClassLoader(klass->GetClassLoader()) : linear_alloc; if (!class_linear_alloc->Contains(method)) { // Check image space. -- GitLab From 6cba74b8c89ac8738212032313635050bb1535a1 Mon Sep 17 00:00:00 2001 From: Hiroshi Yamauchi Date: Wed, 2 Mar 2016 12:12:54 -0800 Subject: [PATCH 104/204] Disable 577-profile-foreign-dex temporarily. Until a fix arrives. Bug: 27454772 Change-Id: Ieec219792c507ad85561a4188ec873d2f18e4604 --- test/Android.run-test.mk | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index 19b535858f..d674614fc8 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -224,11 +224,13 @@ ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES), # Disable 097-duplicate-method while investigation (broken by latest Jack release, b/27358065) # Disable 137-cfi (b/27391690). # Disable 536-checker-needs-access-check and 537-checker-inline-and-unverified (b/27425061) +# Disable 577-profile-foreign-dex (b/27454772). TEST_ART_BROKEN_ALL_TARGET_TESTS := \ 097-duplicate-method \ 137-cfi \ 536-checker-needs-access-check \ 537-checker-inline-and-unverified \ + 577-profile-foreign-dex \ ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \ $(COMPILER_TYPES), $(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \ -- GitLab From 40f1f0a70fe94efdd5135fc3efa349d7edfbe45d Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Wed, 2 Mar 2016 08:51:21 -0800 Subject: [PATCH 105/204] Revert "Disable test after libunwind change." This reverts commit 18047e4cd06387958d315d4de6d9c1753a08ee9e. Moving the in-process lzma disable to libunwind allows to unwind not-zipped debug-info, again. Bug: 27391690 Change-Id: I8c24dd0e73bc9d57ce83c6f5a8272d8252e5b809 --- test/Android.run-test.mk | 1 - 1 file changed, 1 deletion(-) diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index 19b535858f..c538e375cd 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -226,7 +226,6 @@ ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES), # Disable 536-checker-needs-access-check and 537-checker-inline-and-unverified (b/27425061) TEST_ART_BROKEN_ALL_TARGET_TESTS := \ 097-duplicate-method \ - 137-cfi \ 536-checker-needs-access-check \ 537-checker-inline-and-unverified \ -- GitLab From 145c31ab1ed46777105141ec10f2e40a665f810d Mon Sep 17 00:00:00 2001 From: Mingyao Yang Date: Tue, 1 Mar 2016 14:51:52 -0800 Subject: [PATCH 106/204] change image in run-jdwp-tests.sh Change-Id: I305fdd5b8b33c446529fe99f872af4fb8685bd7c --- tools/run-jdwp-tests.sh | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tools/run-jdwp-tests.sh b/tools/run-jdwp-tests.sh index e4af9fa0d7..8422e20823 100755 --- a/tools/run-jdwp-tests.sh +++ b/tools/run-jdwp-tests.sh @@ -51,7 +51,7 @@ vm_command="--vm-command=$art" image_compiler_option="" debug="no" verbose="no" -image="-Ximage:/data/art-test/core-jit.art" +image="-Ximage:/data/art-test/core-optimizing-pic.art" vm_args="" # By default, we run the whole JDWP test suite. test="org.apache.harmony.jpda.tests.share.AllTests" @@ -70,9 +70,6 @@ while true; do device_dir="" # Vogar knows which VM to use on host. vm_command="" - # We only compile the image on the host. Note that not providing this option - # for target testing puts us below the adb command limit for vogar. - image_compiler_option="--vm-arg -Ximage-compiler-option --vm-arg --debuggable" shift elif [[ $1 == -Ximage:* ]]; then image="$1" -- GitLab From 0b8b4a609120b90081d898dbf3c26f68fe80de96 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Wed, 2 Mar 2016 12:52:37 -0800 Subject: [PATCH 107/204] Delete alloc tracking map outside of critical section There can be lock order violations otherwise due to runtime shutdown lock that may get acquired in the condition variable destructor. Change-Id: I23cb2dfe241f5cc6c42bf6766e89042cf06069b6 --- runtime/gc/allocation_record.cc | 5 ++++- runtime/gc/heap.cc | 4 ++++ runtime/gc/heap.h | 4 ++++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/runtime/gc/allocation_record.cc b/runtime/gc/allocation_record.cc index 4de5388d8c..4672483308 100644 --- a/runtime/gc/allocation_record.cc +++ b/runtime/gc/allocation_record.cc @@ -245,6 +245,9 @@ void AllocRecordObjectMap::SetAllocTrackingEnabled(bool enable) { heap->SetAllocTrackingEnabled(true); } } else { + // Delete outside of the critical section to avoid possible lock violations like the runtime + // shutdown lock. + std::unique_ptr map; { MutexLock mu(self, *Locks::alloc_tracker_lock_); if (!heap->IsAllocTrackingEnabled()) { @@ -252,7 +255,7 @@ void AllocRecordObjectMap::SetAllocTrackingEnabled(bool enable) { } heap->SetAllocTrackingEnabled(false); LOG(INFO) << "Disabling alloc tracker"; - heap->SetAllocationRecords(nullptr); + map = heap->ReleaseAllocationRecords(); } // If an allocation comes in before we uninstrument, we will safely drop it on the floor. Runtime::Current()->GetInstrumentation()->UninstrumentQuickAllocEntryPoints(); diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index 1b4cbeccda..bebff0fd6b 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -3947,6 +3947,10 @@ void Heap::SetAllocationRecords(AllocRecordObjectMap* records) { allocation_records_.reset(records); } +std::unique_ptr Heap::ReleaseAllocationRecords() { + return std::move(allocation_records_); +} + void Heap::VisitAllocationRecords(RootVisitor* visitor) const { if (IsAllocTrackingEnabled()) { MutexLock mu(Thread::Current(), *Locks::alloc_tracker_lock_); diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index 46dce04325..889069d8ae 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -766,6 +766,10 @@ class Heap { return allocation_records_.get(); } + // Release ownership of the allocation records. + std::unique_ptr ReleaseAllocationRecords() + REQUIRES(Locks::alloc_tracker_lock_); + void SetAllocationRecords(AllocRecordObjectMap* records) REQUIRES(Locks::alloc_tracker_lock_); -- GitLab From 700347e9bc6c2ed29046e0c13122a5ad57d2fc51 Mon Sep 17 00:00:00 2001 From: Mingyao Yang Date: Wed, 2 Mar 2016 14:59:32 -0800 Subject: [PATCH 108/204] Add a PassScope for PrepareForRegisterAllocation This pass does transform the graph so make it part of cfg-dumping. Change-Id: I42e361382c85c97b974faad8bb0fcf2cb0750355 --- compiler/optimizing/optimizing_compiler.cc | 6 +++++- compiler/optimizing/prepare_for_register_allocation.h | 3 +++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index 13d6d620f8..c1b4d2403d 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -483,7 +483,11 @@ NO_INLINE // Avoid increasing caller's frame size by large stack-allocated obje static void AllocateRegisters(HGraph* graph, CodeGenerator* codegen, PassObserver* pass_observer) { - PrepareForRegisterAllocation(graph).Run(); + { + PassScope scope(PrepareForRegisterAllocation::kPrepareForRegisterAllocationPassName, + pass_observer); + PrepareForRegisterAllocation(graph).Run(); + } SsaLivenessAnalysis liveness(graph, codegen); { PassScope scope(SsaLivenessAnalysis::kLivenessPassName, pass_observer); diff --git a/compiler/optimizing/prepare_for_register_allocation.h b/compiler/optimizing/prepare_for_register_allocation.h index c8b8b0dcfa..c90724c251 100644 --- a/compiler/optimizing/prepare_for_register_allocation.h +++ b/compiler/optimizing/prepare_for_register_allocation.h @@ -32,6 +32,9 @@ class PrepareForRegisterAllocation : public HGraphDelegateVisitor { void Run(); + static constexpr const char* kPrepareForRegisterAllocationPassName = + "prepare_for_register_allocation"; + private: void VisitNullCheck(HNullCheck* check) OVERRIDE; void VisitDivZeroCheck(HDivZeroCheck* check) OVERRIDE; -- GitLab From eeed32cd6b9d6949dd1e0ebbea6ea897e58bb2b8 Mon Sep 17 00:00:00 2001 From: Roland Levillain Date: Thu, 3 Mar 2016 10:43:21 +0000 Subject: [PATCH 109/204] Ignore 145-alloc-tracking-stress run-test failures with CC. This run-test sometimes fails with a timeout on ART Builbot's x86-64 concurrent collector configuration. Disable it to make the build turn green again, while we investigate the failure. Bug: 27467554 Change-Id: I0bac307ec85f197afb8763ca41a6ee50567c4d5f --- test/Android.run-test.mk | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index 67efccd041..e05d4f54c5 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -551,6 +551,7 @@ TEST_ART_BROKEN_DEFAULT_READ_BARRIER_RUN_TESTS := \ $(TEST_ART_BROKEN_INTERPRETER_RUN_TESTS) # Tests that should fail in the read barrier configuration with the Optimizing compiler (AOT). +# 145: Test sometimes times out in read barrier configuration (b/27467554). # 484: Baker's fast path based read barrier compiler instrumentation generates code containing # more parallel moves on x86, thus some Checker assertions may fail. # 527: On ARM64, the read barrier instrumentation does not support the HArm64IntermediateAddress @@ -558,6 +559,7 @@ TEST_ART_BROKEN_DEFAULT_READ_BARRIER_RUN_TESTS := \ # 537: Expects an array copy to be intrinsified on x86-64, but calling-on-slowpath intrinsics are # not yet handled in the read barrier configuration. TEST_ART_BROKEN_OPTIMIZING_READ_BARRIER_RUN_TESTS := \ + 145-alloc-tracking-stress \ 484-checker-register-hints \ 527-checker-array-access-split \ 537-checker-arraycopy -- GitLab From 7ba9966b76bbf818513018fa1da72c89330fe384 Mon Sep 17 00:00:00 2001 From: Serguei Katkov Date: Wed, 2 Mar 2016 16:25:36 +0600 Subject: [PATCH 110/204] ART: cleanup exit_block_ in graph if exit block is removed If we remove the exit block from the graph (for example method contains infinite loop) we should also clean the field exit_block_ in graph. At least inliner expects it. Change-Id: Icda668da2233cdd6cd673635a1949f5ed34cf270 Signed-off-by: Serguei Katkov --- compiler/optimizing/nodes.cc | 5 +++- test/579-inline-infinite/expected.txt | 0 test/579-inline-infinite/info.txt | 2 ++ test/579-inline-infinite/src/Main.java | 38 ++++++++++++++++++++++++++ 4 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 test/579-inline-infinite/expected.txt create mode 100644 test/579-inline-infinite/info.txt create mode 100644 test/579-inline-infinite/src/Main.java diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index 0e0b83e4b4..77ded29b49 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -127,6 +127,9 @@ void HGraph::RemoveDeadBlocks(const ArenaBitVector& visited) { // Remove the block from the list of blocks, so that further analyses // never see it. blocks_[i] = nullptr; + if (block->IsExitBlock()) { + SetExitBlock(nullptr); + } } } } @@ -1870,7 +1873,7 @@ void HGraph::DeleteDeadEmptyBlock(HBasicBlock* block) { DCHECK(block->GetPhis().IsEmpty()); if (block->IsExitBlock()) { - exit_block_ = nullptr; + SetExitBlock(nullptr); } RemoveElement(reverse_post_order_, block); diff --git a/test/579-inline-infinite/expected.txt b/test/579-inline-infinite/expected.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/579-inline-infinite/info.txt b/test/579-inline-infinite/info.txt new file mode 100644 index 0000000000..6fb917c222 --- /dev/null +++ b/test/579-inline-infinite/info.txt @@ -0,0 +1,2 @@ +Regression test for optimizing. +Inlining of method with infinite loop cause a crash. diff --git a/test/579-inline-infinite/src/Main.java b/test/579-inline-infinite/src/Main.java new file mode 100644 index 0000000000..f214ed4ffd --- /dev/null +++ b/test/579-inline-infinite/src/Main.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +class Infinite implements Runnable { + public int field; + + private final void $noinline$infinite() { + while(true) { + field++; + } + } + + public void run() { + $noinline$infinite(); + } +} + +public class Main { + public static void main(String[] args) { + Thread thr = new Thread(new Infinite()); + thr.setDaemon(true); + thr.start(); + // This is a compiler test, so just finish. + } +} -- GitLab From 49924c970536bc570b84e3bf0d525fa9f56debde Mon Sep 17 00:00:00 2001 From: "xueliang.zhong" Date: Thu, 3 Mar 2016 10:52:51 +0000 Subject: [PATCH 111/204] Integer.bitCount and Long.bitCount intrinsics for ARM64 Change-Id: If6180acc90239e52e5d33901b65e194d1ca7e248 --- compiler/optimizing/intrinsics_arm64.cc | 37 +++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc index 7a4a6ef266..2e1198c515 100644 --- a/compiler/optimizing/intrinsics_arm64.cc +++ b/compiler/optimizing/intrinsics_arm64.cc @@ -46,6 +46,7 @@ using helpers::RegisterFrom; using helpers::SRegisterFrom; using helpers::WRegisterFrom; using helpers::XRegisterFrom; +using helpers::InputRegisterAt; namespace { @@ -367,6 +368,40 @@ void IntrinsicCodeGeneratorARM64::VisitLongReverse(HInvoke* invoke) { GenReverse(invoke->GetLocations(), Primitive::kPrimLong, GetVIXLAssembler()); } +static void GenBitCount(HInvoke* instr, bool is_long, vixl::MacroAssembler* masm) { + DCHECK(instr->GetType() == Primitive::kPrimInt); + DCHECK((is_long && instr->InputAt(0)->GetType() == Primitive::kPrimLong) || + (!is_long && instr->InputAt(0)->GetType() == Primitive::kPrimInt)); + + Location out = instr->GetLocations()->Out(); + UseScratchRegisterScope temps(masm); + + Register src = InputRegisterAt(instr, 0); + FPRegister fpr = is_long ? temps.AcquireD() : temps.AcquireS(); + Register dst = is_long ? XRegisterFrom(out) : WRegisterFrom(out); + + __ Fmov(fpr, src); + __ Cnt (fpr.V8B(), fpr.V8B()); + __ Addv(fpr.B(), fpr.V8B()); + __ Fmov(dst, fpr); +} + +void IntrinsicLocationsBuilderARM64::VisitLongBitCount(HInvoke* invoke) { + CreateIntToIntLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorARM64::VisitLongBitCount(HInvoke* invoke) { + GenBitCount(invoke, /* is_long */ true, GetVIXLAssembler()); +} + +void IntrinsicLocationsBuilderARM64::VisitIntegerBitCount(HInvoke* invoke) { + CreateIntToIntLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorARM64::VisitIntegerBitCount(HInvoke* invoke) { + GenBitCount(invoke, /* is_long */ false, GetVIXLAssembler()); +} + static void CreateFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) { LocationSummary* locations = new (arena) LocationSummary(invoke, LocationSummary::kNoCall, @@ -1672,8 +1707,6 @@ void IntrinsicCodeGeneratorARM64::VisitStringGetCharsNoCheck(HInvoke* invoke) { __ Bind(&done); } -UNIMPLEMENTED_INTRINSIC(ARM64, IntegerBitCount) -UNIMPLEMENTED_INTRINSIC(ARM64, LongBitCount) UNIMPLEMENTED_INTRINSIC(ARM64, SystemArrayCopyChar) UNIMPLEMENTED_INTRINSIC(ARM64, SystemArrayCopy) UNIMPLEMENTED_INTRINSIC(ARM64, ReferenceGetReferent) -- GitLab From bcd94c8ea9bde4e075c25fbdfb3a2ef6858eed7b Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Thu, 3 Mar 2016 13:23:33 +0000 Subject: [PATCH 112/204] Refine statistics around the JIT. - Better namings. - Now also time the code cache collection time. - Random cleanups. bug:23128949 bug:27445008 bug:27442890 Change-Id: I1dd52544bea678af868e7c47907f7a0fc9a146c3 --- compiler/jit/jit_compiler.cc | 4 +- compiler/jit/jit_compiler.h | 16 +--- runtime/jit/jit.cc | 10 +-- runtime/jit/jit_code_cache.cc | 144 +++++++++++++++++++--------------- runtime/jit/jit_code_cache.h | 29 +++---- 5 files changed, 104 insertions(+), 99 deletions(-) diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc index 79a6d38fc6..6ff1e2e95e 100644 --- a/compiler/jit/jit_compiler.cc +++ b/compiler/jit/jit_compiler.cc @@ -85,7 +85,7 @@ NO_RETURN static void Usage(const char* fmt, ...) { exit(EXIT_FAILURE); } -JitCompiler::JitCompiler() : total_time_(0) { +JitCompiler::JitCompiler() { compiler_options_.reset(new CompilerOptions( CompilerOptions::kDefaultCompilerFilter, CompilerOptions::kDefaultHugeMethodThreshold, @@ -195,7 +195,6 @@ JitCompiler::~JitCompiler() { bool JitCompiler::CompileMethod(Thread* self, ArtMethod* method, bool osr) { DCHECK(!method->IsProxyMethod()); TimingLogger logger("JIT compiler timing logger", true, VLOG_IS_ON(jit)); - const uint64_t start_time = NanoTime(); StackHandleScope<2> hs(self); self->AssertNoPendingException(); Runtime* runtime = Runtime::Current(); @@ -236,7 +235,6 @@ bool JitCompiler::CompileMethod(Thread* self, ArtMethod* method, bool osr) { runtime->GetJitArenaPool()->TrimMaps(); } - total_time_ += NanoTime() - start_time; runtime->GetJit()->AddTimingLogger(logger); return success; } diff --git a/compiler/jit/jit_compiler.h b/compiler/jit/jit_compiler.h index 5294d0ee35..bc134fe27b 100644 --- a/compiler/jit/jit_compiler.h +++ b/compiler/jit/jit_compiler.h @@ -18,13 +18,10 @@ #define ART_COMPILER_JIT_JIT_COMPILER_H_ #include "base/mutex.h" -#include "compiler_callbacks.h" #include "compiled_method.h" -#include "dex/verification_results.h" #include "dex/quick/dex_file_to_method_inliner_map.h" #include "driver/compiler_driver.h" #include "driver/compiler_options.h" -#include "oat_file.h" namespace art { @@ -37,23 +34,19 @@ class JitCompiler { public: static JitCompiler* Create(); virtual ~JitCompiler(); + + // Compilation entrypoint. Returns whether the compilation succeeded. bool CompileMethod(Thread* self, ArtMethod* method, bool osr) SHARED_REQUIRES(Locks::mutator_lock_); - CompilerCallbacks* GetCompilerCallbacks() const; - size_t GetTotalCompileTime() const { - return total_time_; - } + CompilerOptions* GetCompilerOptions() const { return compiler_options_.get(); } private: - uint64_t total_time_; std::unique_ptr compiler_options_; std::unique_ptr cumulative_logger_; - std::unique_ptr verification_results_; std::unique_ptr method_inliner_map_; - std::unique_ptr callbacks_; std::unique_ptr compiler_driver_; std::unique_ptr instruction_set_features_; std::unique_ptr perf_file_; @@ -62,8 +55,7 @@ class JitCompiler { // This is in the compiler since the runtime doesn't have access to the compiled method // structures. - bool AddToCodeCache(ArtMethod* method, - const CompiledMethod* compiled_method) + bool AddToCodeCache(ArtMethod* method, const CompiledMethod* compiled_method) SHARED_REQUIRES(Locks::mutator_lock_); DISALLOW_COPY_AND_ASSIGN(JitCompiler); diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc index 91b006a3ea..a3b99e3dda 100644 --- a/runtime/jit/jit.cc +++ b/runtime/jit/jit.cc @@ -59,12 +59,7 @@ JitOptions* JitOptions::CreateFromRuntimeArguments(const RuntimeArgumentMap& opt } void Jit::DumpInfo(std::ostream& os) { - os << "JIT code cache size=" << PrettySize(code_cache_->CodeCacheSize()) << "\n" - << "JIT data cache size=" << PrettySize(code_cache_->DataCacheSize()) << "\n" - << "JIT current capacity=" << PrettySize(code_cache_->GetCurrentCapacity()) << "\n" - << "JIT number of compiled code=" << code_cache_->NumberOfCompiledCode() << "\n" - << "JIT total number of compilations=" << code_cache_->NumberOfCompilations() << "\n" - << "JIT total number of osr compilations=" << code_cache_->NumberOfOsrCompilations() << "\n"; + code_cache_->Dump(os); cumulative_timings_.Dump(os); } @@ -97,7 +92,7 @@ Jit* Jit::Create(JitOptions* options, std::string* error_msg) { return nullptr; } jit->save_profiling_info_ = options->GetSaveProfilingInfo(); - LOG(INFO) << "JIT created with initial_capacity=" + VLOG(jit) << "JIT created with initial_capacity=" << PrettySize(options->GetCodeCacheInitialCapacity()) << ", max_capacity=" << PrettySize(options->GetCodeCacheMaxCapacity()) << ", compile_threshold=" << options->GetCompileThreshold() @@ -174,7 +169,6 @@ bool Jit::CompileMethod(ArtMethod* method, Thread* self, bool osr) { // of that proxy method, as the compiler does not expect a proxy method. ArtMethod* method_to_compile = method->GetInterfaceMethodIfProxy(sizeof(void*)); if (!code_cache_->NotifyCompilationOf(method_to_compile, self, osr)) { - VLOG(jit) << "JIT not compiling " << PrettyMethod(method) << " due to code cache"; return false; } bool success = jit_compile_method_(jit_compiler_handle_, method_to_compile, self, osr); diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index e0380bd85c..8c69bc8104 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -24,6 +24,7 @@ #include "debugger_interface.h" #include "entrypoints/runtime_asm_entrypoints.h" #include "gc/accounting/bitmap-inl.h" +#include "jit/jit.h" #include "jit/profiling_info.h" #include "linear_alloc.h" #include "mem_map.h" @@ -129,7 +130,9 @@ JitCodeCache::JitCodeCache(MemMap* code_map, used_memory_for_data_(0), used_memory_for_code_(0), number_of_compilations_(0), - number_of_osr_compilations_(0) { + number_of_osr_compilations_(0), + number_of_deoptimizations_(0), + number_of_collections_(0) { DCHECK_GE(max_capacity, initial_code_capacity + initial_data_capacity); code_mspace_ = create_mspace_with_base(code_map_->Begin(), code_end_, false /*locked*/); @@ -363,16 +366,6 @@ uint8_t* JitCodeCache::CommitCodeInternal(Thread* self, return reinterpret_cast(method_header); } -size_t JitCodeCache::NumberOfCompilations() { - MutexLock mu(Thread::Current(), lock_); - return number_of_compilations_; -} - -size_t JitCodeCache::NumberOfOsrCompilations() { - MutexLock mu(Thread::Current(), lock_); - return number_of_osr_compilations_; -} - size_t JitCodeCache::CodeCacheSize() { MutexLock mu(Thread::Current(), lock_); return CodeCacheSizeLocked(); @@ -391,11 +384,6 @@ size_t JitCodeCache::DataCacheSizeLocked() { return used_memory_for_data_; } -size_t JitCodeCache::NumberOfCompiledCode() { - MutexLock mu(Thread::Current(), lock_); - return method_code_map_.size(); -} - void JitCodeCache::ClearData(Thread* self, void* data) { MutexLock mu(self, lock_); FreeData(reinterpret_cast(data)); @@ -577,6 +565,7 @@ void JitCodeCache::GarbageCollectCache(Thread* self) { if (WaitForPotentialCollectionToComplete(self)) { return; } else { + number_of_collections_++; live_bitmap_.reset(CodeCacheBitmap::Create( "code-cache-bitmap", reinterpret_cast(code_map_->Begin()), @@ -585,63 +574,69 @@ void JitCodeCache::GarbageCollectCache(Thread* self) { } } - bool do_full_collection = false; + TimingLogger logger("JIT code cache timing logger", true, VLOG_IS_ON(jit)); { - MutexLock mu(self, lock_); - do_full_collection = ShouldDoFullCollection(); - } + TimingLogger::ScopedTiming st("Code cache collection", &logger); - if (!kIsDebugBuild || VLOG_IS_ON(jit)) { - LOG(INFO) << "Do " - << (do_full_collection ? "full" : "partial") - << " code cache collection, code=" - << PrettySize(CodeCacheSize()) - << ", data=" << PrettySize(DataCacheSize()); - } - - DoCollection(self, /* collect_profiling_info */ do_full_collection); + bool do_full_collection = false; + { + MutexLock mu(self, lock_); + do_full_collection = ShouldDoFullCollection(); + } - if (!kIsDebugBuild || VLOG_IS_ON(jit)) { - LOG(INFO) << "After code cache collection, code=" - << PrettySize(CodeCacheSize()) - << ", data=" << PrettySize(DataCacheSize()); - } + if (!kIsDebugBuild || VLOG_IS_ON(jit)) { + LOG(INFO) << "Do " + << (do_full_collection ? "full" : "partial") + << " code cache collection, code=" + << PrettySize(CodeCacheSize()) + << ", data=" << PrettySize(DataCacheSize()); + } - { - MutexLock mu(self, lock_); + DoCollection(self, /* collect_profiling_info */ do_full_collection); - // Increase the code cache only when we do partial collections. - // TODO: base this strategy on how full the code cache is? - if (do_full_collection) { - last_collection_increased_code_cache_ = false; - } else { - last_collection_increased_code_cache_ = true; - IncreaseCodeCacheCapacity(); + if (!kIsDebugBuild || VLOG_IS_ON(jit)) { + LOG(INFO) << "After code cache collection, code=" + << PrettySize(CodeCacheSize()) + << ", data=" << PrettySize(DataCacheSize()); } - bool next_collection_will_be_full = ShouldDoFullCollection(); + { + MutexLock mu(self, lock_); + + // Increase the code cache only when we do partial collections. + // TODO: base this strategy on how full the code cache is? + if (do_full_collection) { + last_collection_increased_code_cache_ = false; + } else { + last_collection_increased_code_cache_ = true; + IncreaseCodeCacheCapacity(); + } - // Start polling the liveness of compiled code to prepare for the next full collection. - // We avoid doing this if exit stubs are installed to not mess with the instrumentation. - // TODO(ngeoffray): Clean up instrumentation and code cache interactions. - if (!Runtime::Current()->GetInstrumentation()->AreExitStubsInstalled() && - next_collection_will_be_full) { - // Save the entry point of methods we have compiled, and update the entry - // point of those methods to the interpreter. If the method is invoked, the - // interpreter will update its entry point to the compiled code and call it. - for (ProfilingInfo* info : profiling_infos_) { - const void* entry_point = info->GetMethod()->GetEntryPointFromQuickCompiledCode(); - if (ContainsPc(entry_point)) { - info->SetSavedEntryPoint(entry_point); - info->GetMethod()->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge()); + bool next_collection_will_be_full = ShouldDoFullCollection(); + + // Start polling the liveness of compiled code to prepare for the next full collection. + // We avoid doing this if exit stubs are installed to not mess with the instrumentation. + // TODO(ngeoffray): Clean up instrumentation and code cache interactions. + if (!Runtime::Current()->GetInstrumentation()->AreExitStubsInstalled() && + next_collection_will_be_full) { + // Save the entry point of methods we have compiled, and update the entry + // point of those methods to the interpreter. If the method is invoked, the + // interpreter will update its entry point to the compiled code and call it. + for (ProfilingInfo* info : profiling_infos_) { + const void* entry_point = info->GetMethod()->GetEntryPointFromQuickCompiledCode(); + if (ContainsPc(entry_point)) { + info->SetSavedEntryPoint(entry_point); + info->GetMethod()->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge()); + } } - } - DCHECK(CheckLiveCompiledCodeHasProfilingInfo()); + DCHECK(CheckLiveCompiledCodeHasProfilingInfo()); + } + live_bitmap_.reset(nullptr); + NotifyCollectionDone(self); } - live_bitmap_.reset(nullptr); - NotifyCollectionDone(self); } + Runtime::Current()->GetJit()->AddTimingLogger(logger); } void JitCodeCache::RemoveUnusedAndUnmarkedCode(Thread* self) { @@ -874,17 +869,27 @@ uint64_t JitCodeCache::GetLastUpdateTimeNs() const { bool JitCodeCache::NotifyCompilationOf(ArtMethod* method, Thread* self, bool osr) { if (!osr && ContainsPc(method->GetEntryPointFromQuickCompiledCode())) { + VLOG(jit) << PrettyMethod(method) << " is already compiled"; return false; } MutexLock mu(self, lock_); if (osr && (osr_code_map_.find(method) != osr_code_map_.end())) { + VLOG(jit) << PrettyMethod(method) << " is already osr compiled"; return false; } + ProfilingInfo* info = method->GetProfilingInfo(sizeof(void*)); - if (info == nullptr || info->IsMethodBeingCompiled()) { + if (info == nullptr) { + VLOG(jit) << PrettyMethod(method) << " needs a ProfilingInfo to be compiled"; + return false; + } + + if (info->IsMethodBeingCompiled()) { + VLOG(jit) << PrettyMethod(method) << " is already being compiled"; return false; } + info->SetIsMethodBeingCompiled(true); return true; } @@ -924,6 +929,8 @@ void JitCodeCache::InvalidateCompiledCodeFor(ArtMethod* method, osr_code_map_.erase(it); } } + MutexLock mu(Thread::Current(), lock_); + number_of_deoptimizations_++; } uint8_t* JitCodeCache::AllocateCode(size_t code_size) { @@ -953,5 +960,18 @@ void JitCodeCache::FreeData(uint8_t* data) { mspace_free(data_mspace_, data); } +void JitCodeCache::Dump(std::ostream& os) { + MutexLock mu(Thread::Current(), lock_); + os << "Current JIT code cache size: " << PrettySize(used_memory_for_code_) << "\n" + << "Current JIT data cache size: " << PrettySize(used_memory_for_data_) << "\n" + << "Current JIT capacity: " << PrettySize(current_capacity_) << "\n" + << "Current number of JIT code cache entries: " << method_code_map_.size() << "\n" + << "Total number of JIT compilations: " << number_of_compilations_ << "\n" + << "Total number of JIT compilations for on stack replacement: " + << number_of_osr_compilations_ << "\n" + << "Total number of deoptimizations: " << number_of_deoptimizations_ << "\n" + << "Total number of JIT code cache collections: " << number_of_collections_ << std::endl; +} + } // namespace jit } // namespace art diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h index aa1b139078..2a41a70dcf 100644 --- a/runtime/jit/jit_code_cache.h +++ b/runtime/jit/jit_code_cache.h @@ -67,14 +67,6 @@ class JitCodeCache { // Number of bytes allocated in the data cache. size_t DataCacheSize() REQUIRES(!lock_); - // Number of compiled code in the code cache. Note that this is not the number - // of methods that got JIT compiled, as we might have collected some. - size_t NumberOfCompiledCode() REQUIRES(!lock_); - - // Number of compilations done throughout the lifetime of the JIT. - size_t NumberOfCompilations() REQUIRES(!lock_); - size_t NumberOfOsrCompilations() REQUIRES(!lock_); - bool NotifyCompilationOf(ArtMethod* method, Thread* self, bool osr) SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!lock_); @@ -185,6 +177,8 @@ class JitCodeCache { REQUIRES(!lock_) SHARED_REQUIRES(Locks::mutator_lock_); + void Dump(std::ostream& os) REQUIRES(!lock_); + private: // Take ownership of maps. JitCodeCache(MemMap* code_map, @@ -256,6 +250,11 @@ class JitCodeCache { REQUIRES(lock_) SHARED_REQUIRES(Locks::mutator_lock_); + void FreeCode(uint8_t* code) REQUIRES(lock_); + uint8_t* AllocateCode(size_t code_size) REQUIRES(lock_); + void FreeData(uint8_t* data) REQUIRES(lock_); + uint8_t* AllocateData(size_t data_size) REQUIRES(lock_); + // Lock for guarding allocations, collections, and the method_code_map_. Mutex lock_; // Condition to wait on during collection. @@ -307,19 +306,21 @@ class JitCodeCache { // The size in bytes of used memory for the code portion of the code cache. size_t used_memory_for_code_ GUARDED_BY(lock_); - void FreeCode(uint8_t* code) REQUIRES(lock_); - uint8_t* AllocateCode(size_t code_size) REQUIRES(lock_); - void FreeData(uint8_t* data) REQUIRES(lock_); - uint8_t* AllocateData(size_t data_size) REQUIRES(lock_); - // Number of compilations done throughout the lifetime of the JIT. size_t number_of_compilations_ GUARDED_BY(lock_); + + // Number of compilations for on-stack-replacement done throughout the lifetime of the JIT. size_t number_of_osr_compilations_ GUARDED_BY(lock_); + // Number of deoptimizations done throughout the lifetime of the JIT. + size_t number_of_deoptimizations_ GUARDED_BY(lock_); + + // Number of code cache collections done throughout the lifetime of the JIT. + size_t number_of_collections_ GUARDED_BY(lock_); + DISALLOW_IMPLICIT_CONSTRUCTORS(JitCodeCache); }; - } // namespace jit } // namespace art -- GitLab From d204ba5ac9c5488880d85dc198e7b6aefea2f0bb Mon Sep 17 00:00:00 2001 From: Alex Light Date: Tue, 1 Mar 2016 14:33:51 -0800 Subject: [PATCH 113/204] Move some default-methods tests to Java from Smali. Move all smali tests for default method behavior in non-source-incompatible contexts to java. Also move some of the simpler tests for source and binary incompatibilities into java as well when possible. Bug: 27310767 Change-Id: I753196f19849494825953c1bf06f15b7132f459b --- test/048-reflect-v8/build | 4 - test/563-checker-invoke-super/build | 4 - test/960-default-smali/build | 29 +-- test/960-default-smali/smali/A.smali | 38 --- test/960-default-smali/smali/Attendant.smali | 53 ---- test/960-default-smali/smali/B.smali | 38 --- test/960-default-smali/smali/C.smali | 37 --- test/960-default-smali/smali/D.smali | 38 --- test/960-default-smali/smali/E.smali | 38 --- test/960-default-smali/smali/Extension.smali | 30 --- test/960-default-smali/smali/F.smali | 47 ---- test/960-default-smali/smali/G.smali | 37 --- test/960-default-smali/smali/Greeter.smali | 40 --- test/960-default-smali/smali/Greeter2.smali | 39 --- test/960-default-smali/smali/Greeter3.smali | 40 --- test/960-default-smali/smali/H.smali | 28 --- test/960-default-smali/smali/I.smali | 28 --- test/960-default-smali/smali/J.smali | 29 --- test/960-default-smali/src/A.java | 20 ++ test/960-default-smali/src/Attendant.java | 24 ++ test/960-default-smali/src/B.java | 20 ++ test/960-default-smali/src/C.java | 20 ++ test/960-default-smali/src/D.java | 20 ++ test/960-default-smali/src/E.java | 20 ++ test/960-default-smali/src/Extension.java | 20 ++ test/960-default-smali/src/F.java | 23 ++ test/960-default-smali/src/G.java | 20 ++ test/960-default-smali/src/Greeter.java | 21 ++ test/960-default-smali/src/Greeter2.java | 20 ++ test/960-default-smali/src/Greeter3.java | 21 ++ test/960-default-smali/src/H.java | 16 ++ test/960-default-smali/src/I.java | 16 ++ test/960-default-smali/src/J.java | 16 ++ .../{smali => src}/classes.xml | 0 .../build | 33 +-- .../{generate_smali.py => generate_java.py} | 176 ++++---------- test/962-iface-static/build | 25 +- test/962-iface-static/smali/Displayer.smali | 45 ---- test/962-iface-static/smali/Main.smali | 40 --- test/962-iface-static/smali/iface.smali | 43 ---- test/962-iface-static/src/Displayer.java | 23 ++ test/962-iface-static/src/Iface.java | 21 ++ test/962-iface-static/src/Main.java | 20 ++ test/963-default-range-smali/build | 25 +- test/963-default-range-smali/smali/A.smali | 29 --- test/963-default-range-smali/smali/Main.smali | 77 ------ .../963-default-range-smali/smali/iface.smali | 40 --- test/963-default-range-smali/src/A.java | 16 ++ test/963-default-range-smali/src/Iface.java | 29 +++ test/963-default-range-smali/src/Main.java | 41 ++++ test/964-default-iface-init-generated/build | 31 +-- .../smali/Displayer.smali | 45 ---- .../src/Displayer.java | 24 ++ .../{generate_smali.py => generate_java.py} | 212 ++++------------ test/965-default-verify/build | 33 +-- test/965-default-verify/smali/Iface.smali | 40 --- test/965-default-verify/smali/Main.smali | 179 -------------- test/965-default-verify/smali/Statics.smali | 30 --- test/965-default-verify/src/Iface.java | 23 ++ test/965-default-verify/src/Main.java | 61 +++++ .../{build-src => src}/Statics.java | 2 +- test/965-default-verify/src2/Statics.java | 20 ++ test/966-default-conflict/build | 21 +- test/966-default-conflict/smali/Iface.smali | 39 --- test/966-default-conflict/smali/Iface2.smali | 31 --- test/966-default-conflict/smali/Main.smali | 227 ----------------- test/966-default-conflict/src/Iface.java | 23 ++ .../{build-src => src}/Iface2.java | 0 test/966-default-conflict/src/Main.java | 71 ++++++ test/966-default-conflict/src2/Iface2.java | 20 ++ test/967-default-ame/build | 22 +- test/967-default-ame/smali/Iface.smali | 39 --- test/967-default-ame/smali/Iface2.smali | 27 --- test/967-default-ame/smali/Iface3.smali | 26 -- test/967-default-ame/smali/Main.smali | 228 ------------------ test/967-default-ame/src/Iface.java | 23 ++ .../{build-src => src}/Iface2.java | 0 .../{build-src => src}/Iface3.java | 0 test/967-default-ame/src/Main.java | 71 ++++++ test/967-default-ame/src2/Iface.java | 23 ++ test/967-default-ame/src2/Iface2.java | 18 ++ test/967-default-ame/src2/Iface3.java | 18 ++ test/969-iface-super/build | 29 +-- test/969-iface-super/expected.txt | 26 +- test/969-iface-super/smali/A.smali | 28 --- test/969-iface-super/smali/B.smali | 28 --- test/969-iface-super/smali/C.smali | 41 ---- test/969-iface-super/smali/D.smali | 41 ---- test/969-iface-super/smali/E.smali | 41 ---- test/969-iface-super/smali/F.smali | 40 --- test/969-iface-super/smali/G.smali | 53 ---- test/969-iface-super/smali/H.smali | 66 ----- test/969-iface-super/smali/iface.smali | 30 --- test/969-iface-super/smali/iface2.smali | 36 --- test/969-iface-super/smali/iface3.smali | 22 -- test/969-iface-super/src/A.java | 16 ++ test/969-iface-super/src/B.java | 16 ++ test/969-iface-super/src/C.java | 20 ++ test/969-iface-super/src/D.java | 20 ++ test/969-iface-super/src/E.java | 20 ++ test/969-iface-super/src/F.java | 20 ++ test/969-iface-super/src/G.java | 23 ++ test/969-iface-super/src/H.java | 26 ++ test/969-iface-super/src/Iface.java | 20 ++ test/969-iface-super/src/Iface2.java | 20 ++ test/969-iface-super/src/Iface3.java | 16 ++ .../{smali => src}/classes.xml | 20 +- ...te_smali_main.py => generate_java_main.py} | 131 +++------- 108 files changed, 1277 insertions(+), 2776 deletions(-) delete mode 100644 test/960-default-smali/smali/A.smali delete mode 100644 test/960-default-smali/smali/Attendant.smali delete mode 100644 test/960-default-smali/smali/B.smali delete mode 100644 test/960-default-smali/smali/C.smali delete mode 100644 test/960-default-smali/smali/D.smali delete mode 100644 test/960-default-smali/smali/E.smali delete mode 100644 test/960-default-smali/smali/Extension.smali delete mode 100644 test/960-default-smali/smali/F.smali delete mode 100644 test/960-default-smali/smali/G.smali delete mode 100644 test/960-default-smali/smali/Greeter.smali delete mode 100644 test/960-default-smali/smali/Greeter2.smali delete mode 100644 test/960-default-smali/smali/Greeter3.smali delete mode 100644 test/960-default-smali/smali/H.smali delete mode 100644 test/960-default-smali/smali/I.smali delete mode 100644 test/960-default-smali/smali/J.smali create mode 100644 test/960-default-smali/src/A.java create mode 100644 test/960-default-smali/src/Attendant.java create mode 100644 test/960-default-smali/src/B.java create mode 100644 test/960-default-smali/src/C.java create mode 100644 test/960-default-smali/src/D.java create mode 100644 test/960-default-smali/src/E.java create mode 100644 test/960-default-smali/src/Extension.java create mode 100644 test/960-default-smali/src/F.java create mode 100644 test/960-default-smali/src/G.java create mode 100644 test/960-default-smali/src/Greeter.java create mode 100644 test/960-default-smali/src/Greeter2.java create mode 100644 test/960-default-smali/src/Greeter3.java create mode 100644 test/960-default-smali/src/H.java create mode 100644 test/960-default-smali/src/I.java create mode 100644 test/960-default-smali/src/J.java rename test/960-default-smali/{smali => src}/classes.xml (100%) rename test/961-default-iface-resolution-generated/util-src/{generate_smali.py => generate_java.py} (71%) delete mode 100644 test/962-iface-static/smali/Displayer.smali delete mode 100644 test/962-iface-static/smali/Main.smali delete mode 100644 test/962-iface-static/smali/iface.smali create mode 100644 test/962-iface-static/src/Displayer.java create mode 100644 test/962-iface-static/src/Iface.java create mode 100644 test/962-iface-static/src/Main.java delete mode 100644 test/963-default-range-smali/smali/A.smali delete mode 100644 test/963-default-range-smali/smali/Main.smali delete mode 100644 test/963-default-range-smali/smali/iface.smali create mode 100644 test/963-default-range-smali/src/A.java create mode 100644 test/963-default-range-smali/src/Iface.java create mode 100644 test/963-default-range-smali/src/Main.java delete mode 100644 test/964-default-iface-init-generated/smali/Displayer.smali create mode 100644 test/964-default-iface-init-generated/src/Displayer.java rename test/964-default-iface-init-generated/util-src/{generate_smali.py => generate_java.py} (68%) delete mode 100644 test/965-default-verify/smali/Iface.smali delete mode 100644 test/965-default-verify/smali/Main.smali delete mode 100644 test/965-default-verify/smali/Statics.smali create mode 100644 test/965-default-verify/src/Iface.java create mode 100644 test/965-default-verify/src/Main.java rename test/965-default-verify/{build-src => src}/Statics.java (94%) create mode 100644 test/965-default-verify/src2/Statics.java delete mode 100644 test/966-default-conflict/smali/Iface.smali delete mode 100644 test/966-default-conflict/smali/Iface2.smali delete mode 100644 test/966-default-conflict/smali/Main.smali create mode 100644 test/966-default-conflict/src/Iface.java rename test/966-default-conflict/{build-src => src}/Iface2.java (100%) create mode 100644 test/966-default-conflict/src/Main.java create mode 100644 test/966-default-conflict/src2/Iface2.java delete mode 100644 test/967-default-ame/smali/Iface.smali delete mode 100644 test/967-default-ame/smali/Iface2.smali delete mode 100644 test/967-default-ame/smali/Iface3.smali delete mode 100644 test/967-default-ame/smali/Main.smali create mode 100644 test/967-default-ame/src/Iface.java rename test/967-default-ame/{build-src => src}/Iface2.java (100%) rename test/967-default-ame/{build-src => src}/Iface3.java (100%) create mode 100644 test/967-default-ame/src/Main.java create mode 100644 test/967-default-ame/src2/Iface.java create mode 100644 test/967-default-ame/src2/Iface2.java create mode 100644 test/967-default-ame/src2/Iface3.java delete mode 100644 test/969-iface-super/smali/A.smali delete mode 100644 test/969-iface-super/smali/B.smali delete mode 100644 test/969-iface-super/smali/C.smali delete mode 100644 test/969-iface-super/smali/D.smali delete mode 100644 test/969-iface-super/smali/E.smali delete mode 100644 test/969-iface-super/smali/F.smali delete mode 100644 test/969-iface-super/smali/G.smali delete mode 100644 test/969-iface-super/smali/H.smali delete mode 100644 test/969-iface-super/smali/iface.smali delete mode 100644 test/969-iface-super/smali/iface2.smali delete mode 100644 test/969-iface-super/smali/iface3.smali create mode 100644 test/969-iface-super/src/A.java create mode 100644 test/969-iface-super/src/B.java create mode 100644 test/969-iface-super/src/C.java create mode 100644 test/969-iface-super/src/D.java create mode 100644 test/969-iface-super/src/E.java create mode 100644 test/969-iface-super/src/F.java create mode 100644 test/969-iface-super/src/G.java create mode 100644 test/969-iface-super/src/H.java create mode 100644 test/969-iface-super/src/Iface.java create mode 100644 test/969-iface-super/src/Iface2.java create mode 100644 test/969-iface-super/src/Iface3.java rename test/969-iface-super/{smali => src}/classes.xml (85%) rename test/utils/python/{generate_smali_main.py => generate_java_main.py} (67%) diff --git a/test/048-reflect-v8/build b/test/048-reflect-v8/build index 4ea1838465..3552b5c46c 100644 --- a/test/048-reflect-v8/build +++ b/test/048-reflect-v8/build @@ -20,9 +20,5 @@ set -e # Hard-wired use of experimental jack. # TODO: fix this temporary work-around for lambdas, see b/19467889 export USE_JACK=true -export JACK_SERVER=false -export JACK_REPOSITORY="${ANDROID_BUILD_TOP}/prebuilts/sdk/tools/jacks" -# e.g. /foo/bar/jack-3.10.ALPHA.jar -> 3.10.ALPHA -export JACK_VERSION="$(find "$JACK_REPOSITORY" -name '*ALPHA*' | sed 's/.*jack-//g' | sed 's/[.]jar//g')" ./default-build "$@" --experimental default-methods diff --git a/test/563-checker-invoke-super/build b/test/563-checker-invoke-super/build index e06193ba78..32f84ef5ab 100755 --- a/test/563-checker-invoke-super/build +++ b/test/563-checker-invoke-super/build @@ -20,9 +20,5 @@ set -e # Hard-wired use of experimental jack. # TODO: fix this temporary work-around for lambdas, see b/19467889 export USE_JACK=true -export JACK_SERVER=false -export JACK_REPOSITORY="${ANDROID_BUILD_TOP}/prebuilts/sdk/tools/jacks" -# e.g. /foo/bar/jack-3.10.ALPHA.jar -> 3.10.ALPHA -export JACK_VERSION="$(find "$JACK_REPOSITORY" -name '*ALPHA*' | sed 's/.*jack-//g' | sed 's/[.]jar//g')" ./default-build "$@" --experimental default-methods diff --git a/test/960-default-smali/build b/test/960-default-smali/build index b72afcdf18..e8f4ed084a 100755 --- a/test/960-default-smali/build +++ b/test/960-default-smali/build @@ -17,27 +17,14 @@ # make us exit on a failure set -e -# Generate the smali Main.smali file or fail -${ANDROID_BUILD_TOP}/art/test/utils/python/generate_smali_main.py ./smali - -# Should we compile with Java source code. By default we will use Smali. -USES_JAVA_SOURCE="false" -if [[ $@ == *"--jvm"* ]]; then - USES_JAVA_SOURCE="true" -elif [[ "$USE_JACK" == "true" ]]; then - if $JACK -D jack.java.source.version=1.8 >& /dev/null; then - USES_JAVA_SOURCE="true" - else - echo "WARNING: Cannot use jack because it does not support JLS 1.8. Falling back to smali" >&2 - fi +if [[ $@ != *"--jvm"* ]]; then + # Don't do anything with jvm + # Hard-wired use of experimental jack. + # TODO: fix this temporary work-around for default-methods, see b/19467889 + export USE_JACK=true fi -if [[ "$USES_JAVA_SOURCE" == "true" ]]; then - # We are compiling java code, create it. - mkdir -p src - ${ANDROID_BUILD_TOP}/art/tools/extract-embedded-java ./smali ./src - # Ignore the smali directory. - EXTRA_ARGS="--no-smali" -fi +# Generate the Main.java file or fail +${ANDROID_BUILD_TOP}/art/test/utils/python/generate_java_main.py ./src -./default-build "$@" "$EXTRA_ARGS" --experimental default-methods +./default-build "$@" --experimental default-methods diff --git a/test/960-default-smali/smali/A.smali b/test/960-default-smali/smali/A.smali deleted file mode 100644 index e755612fbe..0000000000 --- a/test/960-default-smali/smali/A.smali +++ /dev/null @@ -1,38 +0,0 @@ -# /* -# * Copyright 2015 The Android Open Source Project -# * -# * Licensed under the Apache License, Version 2.0 (the "License"); -# * you may not use this file except in compliance with the License. -# * You may obtain a copy of the License at -# * -# * http://www.apache.org/licenses/LICENSE-2.0 -# * -# * Unless required by applicable law or agreed to in writing, software -# * distributed under the License is distributed on an "AS IS" BASIS, -# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# * See the License for the specific language governing permissions and -# * limitations under the License. -# */ - -.class public LA; -.super Ljava/lang/Object; -.implements LGreeter; - -# class A implements Greeter { -# public String SayHi() { -# return "Hi "; -# } -# } - -.method public constructor ()V - .registers 1 - invoke-direct {p0}, Ljava/lang/Object;->()V - return-void -.end method - -.method public SayHi()Ljava/lang/String; - .registers 1 - - const-string v0, "Hi " - return-object v0 -.end method diff --git a/test/960-default-smali/smali/Attendant.smali b/test/960-default-smali/smali/Attendant.smali deleted file mode 100644 index ab63aeefcb..0000000000 --- a/test/960-default-smali/smali/Attendant.smali +++ /dev/null @@ -1,53 +0,0 @@ -# /* -# * Copyright 2015 The Android Open Source Project -# * -# * Licensed under the Apache License, Version 2.0 (the "License"); -# * you may not use this file except in compliance with the License. -# * You may obtain a copy of the License at -# * -# * http://www.apache.org/licenses/LICENSE-2.0 -# * -# * Unless required by applicable law or agreed to in writing, software -# * distributed under the License is distributed on an "AS IS" BASIS, -# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# * See the License for the specific language governing permissions and -# * limitations under the License. -# */ - -.class public abstract interface LAttendant; -.super Ljava/lang/Object; - -# public interface Attendant { -# public default String SayHi() { -# return "welcome to " + GetPlace(); -# } -# public default String SayHiTwice() { -# return SayHi() + SayHi(); -# } -# -# public String GetPlace(); -# } - -.method public SayHi()Ljava/lang/String; - .locals 2 - const-string v0, "welcome to " - invoke-interface {p0}, LAttendant;->GetPlace()Ljava/lang/String; - move-result-object v1 - invoke-virtual {v0, v1}, Ljava/lang/String;->concat(Ljava/lang/String;)Ljava/lang/String; - move-result-object v0 - return-object v0 -.end method - -.method public SayHiTwice()Ljava/lang/String; - .locals 2 - invoke-interface {p0}, LAttendant;->SayHi()Ljava/lang/String; - move-result-object v0 - invoke-interface {p0}, LAttendant;->SayHi()Ljava/lang/String; - move-result-object v1 - invoke-virtual {v0, v1}, Ljava/lang/String;->concat(Ljava/lang/String;)Ljava/lang/String; - move-result-object v0 - return-object v0 -.end method - -.method public abstract GetPlace()Ljava/lang/String; -.end method diff --git a/test/960-default-smali/smali/B.smali b/test/960-default-smali/smali/B.smali deleted file mode 100644 index d847dd12ff..0000000000 --- a/test/960-default-smali/smali/B.smali +++ /dev/null @@ -1,38 +0,0 @@ -# /* -# * Copyright 2015 The Android Open Source Project -# * -# * Licensed under the Apache License, Version 2.0 (the "License"); -# * you may not use this file except in compliance with the License. -# * You may obtain a copy of the License at -# * -# * http://www.apache.org/licenses/LICENSE-2.0 -# * -# * Unless required by applicable law or agreed to in writing, software -# * distributed under the License is distributed on an "AS IS" BASIS, -# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# * See the License for the specific language governing permissions and -# * limitations under the License. -# */ - -.class public LB; -.super Ljava/lang/Object; -.implements LGreeter2; - -# class B implements Greeter2 { -# public String SayHi() { -# return "Hello "; -# } -# } - -.method public constructor ()V - .registers 1 - invoke-direct {p0}, Ljava/lang/Object;->()V - return-void -.end method - -.method public SayHi()Ljava/lang/String; - .registers 1 - - const-string v0, "Hello " - return-object v0 -.end method diff --git a/test/960-default-smali/smali/C.smali b/test/960-default-smali/smali/C.smali deleted file mode 100644 index 08a8508be1..0000000000 --- a/test/960-default-smali/smali/C.smali +++ /dev/null @@ -1,37 +0,0 @@ -# /* -# * Copyright 2015 The Android Open Source Project -# * -# * Licensed under the Apache License, Version 2.0 (the "License"); -# * you may not use this file except in compliance with the License. -# * You may obtain a copy of the License at -# * -# * http://www.apache.org/licenses/LICENSE-2.0 -# * -# * Unless required by applicable law or agreed to in writing, software -# * distributed under the License is distributed on an "AS IS" BASIS, -# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# * See the License for the specific language governing permissions and -# * limitations under the License. -# */ - -.class public LC; -.super LA; - -# class C extends A { -# public String SayHiTwice() { -# return "You don't control me"; -# } -# } - -.method public constructor ()V - .registers 1 - invoke-direct {p0}, LA;->()V - return-void -.end method - -.method public SayHiTwice()Ljava/lang/String; - .registers 1 - - const-string v0, "You don't control me" - return-object v0 -.end method diff --git a/test/960-default-smali/smali/D.smali b/test/960-default-smali/smali/D.smali deleted file mode 100644 index 32f3b7ec8b..0000000000 --- a/test/960-default-smali/smali/D.smali +++ /dev/null @@ -1,38 +0,0 @@ -# /* -# * Copyright 2015 The Android Open Source Project -# * -# * Licensed under the Apache License, Version 2.0 (the "License"); -# * you may not use this file except in compliance with the License. -# * You may obtain a copy of the License at -# * -# * http://www.apache.org/licenses/LICENSE-2.0 -# * -# * Unless required by applicable law or agreed to in writing, software -# * distributed under the License is distributed on an "AS IS" BASIS, -# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# * See the License for the specific language governing permissions and -# * limitations under the License. -# */ - -.class public LD; -.super Ljava/lang/Object; -.implements LGreeter3; - -# class D implements Greeter3 { -# public String GetName() { -# return "Alex "; -# } -# } - -.method public constructor ()V - .registers 1 - invoke-direct {p0}, Ljava/lang/Object;->()V - return-void -.end method - -.method public GetName()Ljava/lang/String; - .registers 1 - - const-string v0, "Alex " - return-object v0 -.end method diff --git a/test/960-default-smali/smali/E.smali b/test/960-default-smali/smali/E.smali deleted file mode 100644 index bae6250414..0000000000 --- a/test/960-default-smali/smali/E.smali +++ /dev/null @@ -1,38 +0,0 @@ -# /* -# * Copyright 2015 The Android Open Source Project -# * -# * Licensed under the Apache License, Version 2.0 (the "License"); -# * you may not use this file except in compliance with the License. -# * You may obtain a copy of the License at -# * -# * http://www.apache.org/licenses/LICENSE-2.0 -# * -# * Unless required by applicable law or agreed to in writing, software -# * distributed under the License is distributed on an "AS IS" BASIS, -# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# * See the License for the specific language governing permissions and -# * limitations under the License. -# */ - -.class public LE; -.super LA; -.implements LGreeter2; - -# class E extends A implements Greeter2 { -# public String SayHi() { -# return "Hi2 "; -# } -# } - -.method public constructor ()V - .registers 1 - invoke-direct {p0}, LA;->()V - return-void -.end method - -.method public SayHi()Ljava/lang/String; - .registers 1 - - const-string v0, "Hi2 " - return-object v0 -.end method diff --git a/test/960-default-smali/smali/Extension.smali b/test/960-default-smali/smali/Extension.smali deleted file mode 100644 index 60ffa26ec6..0000000000 --- a/test/960-default-smali/smali/Extension.smali +++ /dev/null @@ -1,30 +0,0 @@ -# /* -# * Copyright 2015 The Android Open Source Project -# * -# * Licensed under the Apache License, Version 2.0 (the "License"); -# * you may not use this file except in compliance with the License. -# * You may obtain a copy of the License at -# * -# * http://www.apache.org/licenses/LICENSE-2.0 -# * -# * Unless required by applicable law or agreed to in writing, software -# * distributed under the License is distributed on an "AS IS" BASIS, -# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# * See the License for the specific language governing permissions and -# * limitations under the License. -# */ - -.class public abstract interface LExtension; -.super Ljava/lang/Object; - -# public interface Extension { -# public default String SayHi() { -# return "welcome "; -# } -# } - -.method public SayHi()Ljava/lang/String; - .locals 1 - const-string v0, "welcome " - return-object v0 -.end method diff --git a/test/960-default-smali/smali/F.smali b/test/960-default-smali/smali/F.smali deleted file mode 100644 index 3eaa089e1f..0000000000 --- a/test/960-default-smali/smali/F.smali +++ /dev/null @@ -1,47 +0,0 @@ -# /* -# * Copyright 2015 The Android Open Source Project -# * -# * Licensed under the Apache License, Version 2.0 (the "License"); -# * you may not use this file except in compliance with the License. -# * You may obtain a copy of the License at -# * -# * http://www.apache.org/licenses/LICENSE-2.0 -# * -# * Unless required by applicable law or agreed to in writing, software -# * distributed under the License is distributed on an "AS IS" BASIS, -# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# * See the License for the specific language governing permissions and -# * limitations under the License. -# */ - -.class public LF; -.super LA; -.implements LAttendant; - -# class F extends A implements Attendant { -# public String GetPlace() { -# return "android"; -# } -# public String SayHiTwice() { -# return "We can override both interfaces"; -# } -# } - -.method public constructor ()V - .registers 1 - invoke-direct {p0}, Ljava/lang/Object;->()V - return-void -.end method - -.method public SayHiTwice()Ljava/lang/String; - .registers 1 - - const-string v0, "We can override both interfaces" - return-object v0 -.end method - -.method public GetPlace()Ljava/lang/String; - .registers 1 - const-string v0, "android" - return-object v0 -.end method diff --git a/test/960-default-smali/smali/G.smali b/test/960-default-smali/smali/G.smali deleted file mode 100644 index 446f2a4c64..0000000000 --- a/test/960-default-smali/smali/G.smali +++ /dev/null @@ -1,37 +0,0 @@ -# /* -# * Copyright 2015 The Android Open Source Project -# * -# * Licensed under the Apache License, Version 2.0 (the "License"); -# * you may not use this file except in compliance with the License. -# * You may obtain a copy of the License at -# * -# * http://www.apache.org/licenses/LICENSE-2.0 -# * -# * Unless required by applicable law or agreed to in writing, software -# * distributed under the License is distributed on an "AS IS" BASIS, -# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# * See the License for the specific language governing permissions and -# * limitations under the License. -# */ - -.class public LG; -.super Ljava/lang/Object; -.implements LAttendant; - -# class G implements Attendant { -# public String GetPlace() { -# return "android"; -# } -# } - -.method public constructor ()V - .registers 1 - invoke-direct {p0}, Ljava/lang/Object;->()V - return-void -.end method - -.method public GetPlace()Ljava/lang/String; - .registers 1 - const-string v0, "android" - return-object v0 -.end method diff --git a/test/960-default-smali/smali/Greeter.smali b/test/960-default-smali/smali/Greeter.smali deleted file mode 100644 index 28530ffc6f..0000000000 --- a/test/960-default-smali/smali/Greeter.smali +++ /dev/null @@ -1,40 +0,0 @@ -# /* -# * Copyright 2015 The Android Open Source Project -# * -# * Licensed under the Apache License, Version 2.0 (the "License"); -# * you may not use this file except in compliance with the License. -# * You may obtain a copy of the License at -# * -# * http://www.apache.org/licenses/LICENSE-2.0 -# * -# * Unless required by applicable law or agreed to in writing, software -# * distributed under the License is distributed on an "AS IS" BASIS, -# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# * See the License for the specific language governing permissions and -# * limitations under the License. -# */ - -.class public abstract interface LGreeter; -.super Ljava/lang/Object; - -# public interface Greeter { -# public String SayHi(); -# -# public default String SayHiTwice() { -# return SayHi() + SayHi(); -# } -# } - -.method public abstract SayHi()Ljava/lang/String; -.end method - -.method public SayHiTwice()Ljava/lang/String; - .locals 2 - invoke-interface {p0}, LGreeter;->SayHi()Ljava/lang/String; - move-result-object v0 - invoke-interface {p0}, LGreeter;->SayHi()Ljava/lang/String; - move-result-object v1 - invoke-virtual {v0, v1}, Ljava/lang/String;->concat(Ljava/lang/String;)Ljava/lang/String; - move-result-object v0 - return-object v0 -.end method diff --git a/test/960-default-smali/smali/Greeter2.smali b/test/960-default-smali/smali/Greeter2.smali deleted file mode 100644 index ace1798bab..0000000000 --- a/test/960-default-smali/smali/Greeter2.smali +++ /dev/null @@ -1,39 +0,0 @@ -# /* -# * Copyright 2015 The Android Open Source Project -# * -# * Licensed under the Apache License, Version 2.0 (the "License"); -# * you may not use this file except in compliance with the License. -# * You may obtain a copy of the License at -# * -# * http://www.apache.org/licenses/LICENSE-2.0 -# * -# * Unless required by applicable law or agreed to in writing, software -# * distributed under the License is distributed on an "AS IS" BASIS, -# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# * See the License for the specific language governing permissions and -# * limitations under the License. -# */ - -.class public abstract interface LGreeter2; -.super Ljava/lang/Object; -.implements LGreeter; - -# public interface Greeter2 extends Greeter { -# public default String SayHiTwice() { -# return "I say " + SayHi() + SayHi(); -# } -# } - -.method public SayHiTwice()Ljava/lang/String; - .locals 3 - const-string v0, "I say " - invoke-interface {p0}, LGreeter;->SayHi()Ljava/lang/String; - move-result-object v1 - invoke-virtual {v0, v1}, Ljava/lang/String;->concat(Ljava/lang/String;)Ljava/lang/String; - move-result-object v0 - invoke-interface {p0}, LGreeter;->SayHi()Ljava/lang/String; - move-result-object v1 - invoke-virtual {v0, v1}, Ljava/lang/String;->concat(Ljava/lang/String;)Ljava/lang/String; - move-result-object v0 - return-object v0 -.end method diff --git a/test/960-default-smali/smali/Greeter3.smali b/test/960-default-smali/smali/Greeter3.smali deleted file mode 100644 index 31fc2e79ff..0000000000 --- a/test/960-default-smali/smali/Greeter3.smali +++ /dev/null @@ -1,40 +0,0 @@ -# /* -# * Copyright 2015 The Android Open Source Project -# * -# * Licensed under the Apache License, Version 2.0 (the "License"); -# * you may not use this file except in compliance with the License. -# * You may obtain a copy of the License at -# * -# * http://www.apache.org/licenses/LICENSE-2.0 -# * -# * Unless required by applicable law or agreed to in writing, software -# * distributed under the License is distributed on an "AS IS" BASIS, -# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# * See the License for the specific language governing permissions and -# * limitations under the License. -# */ - -.class public abstract interface LGreeter3; -.super Ljava/lang/Object; -.implements LGreeter; - -# public interface Greeter3 extends Greeter { -# public String GetName(); -# -# public default String SayHi() { -# return "Hello " + GetName(); -# } -# } - -.method public abstract GetName()Ljava/lang/String; -.end method - -.method public SayHi()Ljava/lang/String; - .locals 2 - const-string v0, "Hello " - invoke-interface {p0}, LGreeter3;->GetName()Ljava/lang/String; - move-result-object v1 - invoke-virtual {v0, v1}, Ljava/lang/String;->concat(Ljava/lang/String;)Ljava/lang/String; - move-result-object v0 - return-object v0 -.end method diff --git a/test/960-default-smali/smali/H.smali b/test/960-default-smali/smali/H.smali deleted file mode 100644 index 82065ea49d..0000000000 --- a/test/960-default-smali/smali/H.smali +++ /dev/null @@ -1,28 +0,0 @@ -# /* -# * Copyright 2015 The Android Open Source Project -# * -# * Licensed under the Apache License, Version 2.0 (the "License"); -# * you may not use this file except in compliance with the License. -# * You may obtain a copy of the License at -# * -# * http://www.apache.org/licenses/LICENSE-2.0 -# * -# * Unless required by applicable law or agreed to in writing, software -# * distributed under the License is distributed on an "AS IS" BASIS, -# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# * See the License for the specific language governing permissions and -# * limitations under the License. -# */ - -.class public LH; -.super Ljava/lang/Object; -.implements LExtension; - -# class H implements Extension { -# } - -.method public constructor ()V - .registers 1 - invoke-direct {p0}, Ljava/lang/Object;->()V - return-void -.end method diff --git a/test/960-default-smali/smali/I.smali b/test/960-default-smali/smali/I.smali deleted file mode 100644 index 72fb58afe4..0000000000 --- a/test/960-default-smali/smali/I.smali +++ /dev/null @@ -1,28 +0,0 @@ -# /* -# * Copyright 2015 The Android Open Source Project -# * -# * Licensed under the Apache License, Version 2.0 (the "License"); -# * you may not use this file except in compliance with the License. -# * You may obtain a copy of the License at -# * -# * http://www.apache.org/licenses/LICENSE-2.0 -# * -# * Unless required by applicable law or agreed to in writing, software -# * distributed under the License is distributed on an "AS IS" BASIS, -# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# * See the License for the specific language governing permissions and -# * limitations under the License. -# */ - -.class public LI; -.super LA; -.implements LGreeter2; - -# class I extends A implements Greeter2 { -# } - -.method public constructor ()V - .registers 1 - invoke-direct {p0}, Ljava/lang/Object;->()V - return-void -.end method diff --git a/test/960-default-smali/smali/J.smali b/test/960-default-smali/smali/J.smali deleted file mode 100644 index 93f3d6231c..0000000000 --- a/test/960-default-smali/smali/J.smali +++ /dev/null @@ -1,29 +0,0 @@ -# /* -# * Copyright 2015 The Android Open Source Project -# * -# * Licensed under the Apache License, Version 2.0 (the "License"); -# * you may not use this file except in compliance with the License. -# * You may obtain a copy of the License at -# * -# * http://www.apache.org/licenses/LICENSE-2.0 -# * -# * Unless required by applicable law or agreed to in writing, software -# * distributed under the License is distributed on an "AS IS" BASIS, -# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# * See the License for the specific language governing permissions and -# * limitations under the License. -# */ - -.class public LJ; -.super LA; - -# class J extends A { -# } - - -.method public constructor ()V - .registers 1 - invoke-direct {p0}, LA;->()V - return-void -.end method - diff --git a/test/960-default-smali/src/A.java b/test/960-default-smali/src/A.java new file mode 100644 index 0000000000..7664a263f1 --- /dev/null +++ b/test/960-default-smali/src/A.java @@ -0,0 +1,20 @@ +/* + * Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +class A implements Greeter { + public String SayHi() { + return "Hi "; + } +} diff --git a/test/960-default-smali/src/Attendant.java b/test/960-default-smali/src/Attendant.java new file mode 100644 index 0000000000..9f9a58a402 --- /dev/null +++ b/test/960-default-smali/src/Attendant.java @@ -0,0 +1,24 @@ +/* + * Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +public interface Attendant { + public default String SayHi() { + return "welcome to " + GetPlace(); + } + public default String SayHiTwice() { + return SayHi() + SayHi(); + } + public String GetPlace(); +} diff --git a/test/960-default-smali/src/B.java b/test/960-default-smali/src/B.java new file mode 100644 index 0000000000..18aaadea4c --- /dev/null +++ b/test/960-default-smali/src/B.java @@ -0,0 +1,20 @@ +/* + * Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +class B implements Greeter2 { + public String SayHi() { + return "Hello "; + } +} diff --git a/test/960-default-smali/src/C.java b/test/960-default-smali/src/C.java new file mode 100644 index 0000000000..f0bc185f95 --- /dev/null +++ b/test/960-default-smali/src/C.java @@ -0,0 +1,20 @@ +/* + * Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +class C extends A { + public String SayHiTwice() { + return "You don't control me"; + } +} diff --git a/test/960-default-smali/src/D.java b/test/960-default-smali/src/D.java new file mode 100644 index 0000000000..b1697cd865 --- /dev/null +++ b/test/960-default-smali/src/D.java @@ -0,0 +1,20 @@ +/* + * Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +class D implements Greeter3 { + public String GetName() { + return "Alex "; + } +} diff --git a/test/960-default-smali/src/E.java b/test/960-default-smali/src/E.java new file mode 100644 index 0000000000..477cb6727c --- /dev/null +++ b/test/960-default-smali/src/E.java @@ -0,0 +1,20 @@ +/* + * Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +class E extends A implements Greeter2 { + public String SayHi() { + return "Hi2 "; + } +} diff --git a/test/960-default-smali/src/Extension.java b/test/960-default-smali/src/Extension.java new file mode 100644 index 0000000000..89617ddab6 --- /dev/null +++ b/test/960-default-smali/src/Extension.java @@ -0,0 +1,20 @@ +/* + * Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +public interface Extension { + public default String SayHi() { + return "welcome "; + } +} diff --git a/test/960-default-smali/src/F.java b/test/960-default-smali/src/F.java new file mode 100644 index 0000000000..0282de7793 --- /dev/null +++ b/test/960-default-smali/src/F.java @@ -0,0 +1,23 @@ +/* + * Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +class F extends A implements Attendant { + public String GetPlace() { + return "android"; + } + public String SayHiTwice() { + return "We can override both interfaces"; + } +} diff --git a/test/960-default-smali/src/G.java b/test/960-default-smali/src/G.java new file mode 100644 index 0000000000..86a140aa43 --- /dev/null +++ b/test/960-default-smali/src/G.java @@ -0,0 +1,20 @@ +/* + * Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +class G implements Attendant { + public String GetPlace() { + return "android"; + } +} diff --git a/test/960-default-smali/src/Greeter.java b/test/960-default-smali/src/Greeter.java new file mode 100644 index 0000000000..cee2283acd --- /dev/null +++ b/test/960-default-smali/src/Greeter.java @@ -0,0 +1,21 @@ +/* + * Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +public interface Greeter { + public String SayHi(); + public default String SayHiTwice() { + return SayHi() + SayHi(); + } +} diff --git a/test/960-default-smali/src/Greeter2.java b/test/960-default-smali/src/Greeter2.java new file mode 100644 index 0000000000..07f6c53841 --- /dev/null +++ b/test/960-default-smali/src/Greeter2.java @@ -0,0 +1,20 @@ +/* + * Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +public interface Greeter2 extends Greeter { + public default String SayHiTwice() { + return "I say " + SayHi() + SayHi(); + } +} diff --git a/test/960-default-smali/src/Greeter3.java b/test/960-default-smali/src/Greeter3.java new file mode 100644 index 0000000000..bbb7171a36 --- /dev/null +++ b/test/960-default-smali/src/Greeter3.java @@ -0,0 +1,21 @@ +/* + * Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +public interface Greeter3 extends Greeter { + public String GetName(); + public default String SayHi() { + return "Hello " + GetName(); + } +} diff --git a/test/960-default-smali/src/H.java b/test/960-default-smali/src/H.java new file mode 100644 index 0000000000..d87a6db8f4 --- /dev/null +++ b/test/960-default-smali/src/H.java @@ -0,0 +1,16 @@ +/* + * Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +class H implements Extension { } diff --git a/test/960-default-smali/src/I.java b/test/960-default-smali/src/I.java new file mode 100644 index 0000000000..8d6779cd27 --- /dev/null +++ b/test/960-default-smali/src/I.java @@ -0,0 +1,16 @@ +/* + * Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +class I extends A implements Greeter2 { } diff --git a/test/960-default-smali/src/J.java b/test/960-default-smali/src/J.java new file mode 100644 index 0000000000..a365e406c6 --- /dev/null +++ b/test/960-default-smali/src/J.java @@ -0,0 +1,16 @@ +/* + * Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +class J extends A { } diff --git a/test/960-default-smali/smali/classes.xml b/test/960-default-smali/src/classes.xml similarity index 100% rename from test/960-default-smali/smali/classes.xml rename to test/960-default-smali/src/classes.xml diff --git a/test/961-default-iface-resolution-generated/build b/test/961-default-iface-resolution-generated/build index 005f76c2dc..ccebbe4ac9 100755 --- a/test/961-default-iface-resolution-generated/build +++ b/test/961-default-iface-resolution-generated/build @@ -26,32 +26,19 @@ restore_ulimit() { } trap 'restore_ulimit' ERR -mkdir -p ./smali - -# Generate the smali files and expected.txt or fail -./util-src/generate_smali.py ./smali ./expected.txt - -# Should we compile with Java source code. By default we will use Smali. -USES_JAVA_SOURCE="false" -if [[ $@ == *"--jvm"* ]]; then - USES_JAVA_SOURCE="true" -elif [[ $USE_JACK == "true" ]]; then - if "$JACK" -D jack.java.source.version=1.8 >& /dev/null; then - USES_JAVA_SOURCE="true" - else - echo "WARNING: Cannot use jack because it does not support JLS 1.8. Falling back to smali" >&2 - fi +if [[ $@ != *"--jvm"* ]]; then + # Don't do anything with jvm + # Hard-wired use of experimental jack. + # TODO: fix this temporary work-around for default-methods, see b/19467889 + export USE_JACK=true fi -if [[ "$USES_JAVA_SOURCE" == "true" ]]; then - # We are compiling java code, create it. - mkdir -p src - ${ANDROID_BUILD_TOP}/art/tools/extract-embedded-java ./smali ./src - # Ignore the smali directory. - EXTRA_ARGS="--no-smali" -fi +mkdir -p ./src + +# Generate the smali files and expected.txt or fail +./util-src/generate_java.py ./src ./expected.txt -./default-build "$@" "$EXTRA_ARGS" --experimental default-methods +./default-build "$@" --experimental default-methods # Reset the ulimit back to its initial value restore_ulimit diff --git a/test/961-default-iface-resolution-generated/util-src/generate_smali.py b/test/961-default-iface-resolution-generated/util-src/generate_java.py similarity index 71% rename from test/961-default-iface-resolution-generated/util-src/generate_smali.py rename to test/961-default-iface-resolution-generated/util-src/generate_java.py index 921a096dd3..a205cd6ce0 100755 --- a/test/961-default-iface-resolution-generated/util-src/generate_smali.py +++ b/test/961-default-iface-resolution-generated/util-src/generate_java.py @@ -15,7 +15,7 @@ # limitations under the License. """ -Generate Smali test files for test 961. +Generate Java test files for test 961. """ import os @@ -43,48 +43,27 @@ import string # every possible interface tree up to 5 layers deep. MAX_IFACE_DEPTH = 5 -class MainClass(mixins.DumpMixin, mixins.Named, mixins.SmaliFileMixin): +class MainClass(mixins.DumpMixin, mixins.Named, mixins.JavaFileMixin): """ - A Main.smali file containing the Main class and the main function. It will run + A Main.java file containing the Main class and the main function. It will run all the test functions we have. """ MAIN_CLASS_TEMPLATE = """{copyright} - -.class public LMain; -.super Ljava/lang/Object; - -# class Main {{ - -.method public constructor ()V - .registers 1 - invoke-direct {{p0}}, Ljava/lang/Object;->()V - return-void -.end method - +class Main {{ {test_groups} - {main_func} - -# }} +}} """ MAIN_FUNCTION_TEMPLATE = """ -# public static void main(String[] args) {{ -.method public static main([Ljava/lang/String;)V - .locals 2 - sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream; - + public static void main(String[] args) {{ {test_group_invoke} - - return-void -.end method -# }} + }} """ TEST_GROUP_INVOKE_TEMPLATE = """ -# {test_name}(); - invoke-static {{}}, {test_name}()V + {test_name}(); """ def __init__(self): @@ -114,7 +93,7 @@ class MainClass(mixins.DumpMixin, mixins.Named, mixins.SmaliFileMixin): def __str__(self): """ - Print the MainClass smali code. + Print the MainClass java code. """ all_tests = sorted(self.tests) test_invoke = "" @@ -125,7 +104,7 @@ class MainClass(mixins.DumpMixin, mixins.Named, mixins.SmaliFileMixin): test_invoke += self.TEST_GROUP_INVOKE_TEMPLATE.format(test_name=t.get_name()) main_func = self.MAIN_FUNCTION_TEMPLATE.format(test_group_invoke=test_invoke) - return self.MAIN_CLASS_TEMPLATE.format(copyright = get_copyright("smali"), + return self.MAIN_CLASS_TEMPLATE.format(copyright = get_copyright("java"), test_groups = test_groups, main_func = main_func) @@ -136,49 +115,18 @@ class Func(mixins.Named, mixins.NameComparableMixin): """ TEST_FUNCTION_TEMPLATE = """ -# public static void {fname}() {{ -# try {{ -# {farg} v = new {farg}(); -# System.out.printf("%s calls default method on %s\\n", -# v.CalledClassName(), -# v.CalledInterfaceName()); -# return; -# }} catch (Error e) {{ -# e.printStackTrace(System.out); -# return; -# }} -# }} -.method public static {fname}()V - .locals 7 - :call_{fname}_try_start - new-instance v6, L{farg}; - invoke-direct {{v6}}, L{farg};->()V - - const/4 v0, 2 - new-array v1,v0, [Ljava/lang/Object; - const/4 v0, 0 - invoke-virtual {{v6}}, L{farg};->CalledClassName()Ljava/lang/String; - move-result-object v4 - aput-object v4,v1,v0 - - sget-object v2, Ljava/lang/System;->out:Ljava/io/PrintStream; - const-string v3, "%s calls default method on %s\\n" - - invoke-virtual {{v6}}, L{farg};->CalledInterfaceName()Ljava/lang/String; - move-result-object v4 - const/4 v0, 1 - aput-object v4, v1, v0 - - invoke-virtual {{v2,v3,v1}}, Ljava/io/PrintStream;->printf(Ljava/lang/String;[Ljava/lang/Object;)Ljava/io/PrintStream; - return-void - :call_{fname}_try_end - .catch Ljava/lang/Error; {{:call_{fname}_try_start .. :call_{fname}_try_end}} :error_{fname}_start - :error_{fname}_start - move-exception v3 - sget-object v2, Ljava/lang/System;->out:Ljava/io/PrintStream; - invoke-virtual {{v3,v2}}, Ljava/lang/Error;->printStackTrace(Ljava/io/PrintStream;)V - return-void -.end method + public static void {fname}() {{ + try {{ + {farg} v = new {farg}(); + System.out.printf("%s calls default method on %s\\n", + v.CalledClassName(), + v.CalledInterfaceName()); + return; + }} catch (Error e) {{ + e.printStackTrace(System.out); + return; + }} + }} """ def __init__(self, farg): @@ -202,38 +150,21 @@ class Func(mixins.Named, mixins.NameComparableMixin): def __str__(self): """ - Print the smali code of this function. + Print the java code of this function. """ return self.TEST_FUNCTION_TEMPLATE.format(fname=self.get_name(), farg=self.farg.get_name()) -class TestClass(mixins.DumpMixin, mixins.Named, mixins.NameComparableMixin, mixins.SmaliFileMixin): +class TestClass(mixins.DumpMixin, mixins.Named, mixins.NameComparableMixin, mixins.JavaFileMixin): """ A class that will be instantiated to test default method resolution order. """ TEST_CLASS_TEMPLATE = """{copyright} - -.class public L{class_name}; -.super Ljava/lang/Object; -.implements L{iface_name}; - -# public class {class_name} implements {iface_name} {{ -# public String CalledClassName() {{ -# return "{tree}"; -# }} -# }} - -.method public constructor ()V - .registers 1 - invoke-direct {{p0}}, Ljava/lang/Object;->()V - return-void -.end method - -.method public CalledClassName()Ljava/lang/String; - .locals 1 - const-string v0, "{tree}" - return-object v0 -.end method +public class {class_name} implements {iface_name} {{ + public String CalledClassName() {{ + return "{tree}"; + }} +}} """ def __init__(self, iface): @@ -276,46 +207,30 @@ class TestClass(mixins.DumpMixin, mixins.Named, mixins.NameComparableMixin, mixi def __str__(self): """ - Print the smali code of this class. + Print the java code of this class. """ - return self.TEST_CLASS_TEMPLATE.format(copyright = get_copyright('smali'), + return self.TEST_CLASS_TEMPLATE.format(copyright = get_copyright('java'), iface_name = self.iface.get_name(), tree = self.get_tree(), class_name = self.class_name) -class TestInterface(mixins.DumpMixin, mixins.Named, mixins.NameComparableMixin, mixins.SmaliFileMixin): +class TestInterface(mixins.DumpMixin, mixins.Named, mixins.NameComparableMixin, mixins.JavaFileMixin): """ An interface that will be used to test default method resolution order. """ TEST_INTERFACE_TEMPLATE = """{copyright} -.class public abstract interface L{class_name}; -.super Ljava/lang/Object; -{implements_spec} - -# public interface {class_name} {extends} {ifaces} {{ -# public String CalledClassName(); -.method public abstract CalledClassName()Ljava/lang/String; -.end method +public interface {class_name} {extends} {ifaces} {{ + public String CalledClassName(); {funcs} - -# }} +}} """ DEFAULT_FUNC_TEMPLATE = """ -# public default String CalledInterfaceName() {{ -# return "{tree}"; -# }} -.method public CalledInterfaceName()Ljava/lang/String; - .locals 1 - const-string v0, "{tree}" - return-object v0 -.end method -""" - - IMPLEMENTS_TEMPLATE = """ -.implements L{iface_name}; + public default String CalledInterfaceName() {{ + return "{tree}"; + }} """ def __init__(self, ifaces, default): @@ -357,12 +272,10 @@ class TestInterface(mixins.DumpMixin, mixins.Named, mixins.NameComparableMixin, def __str__(self): """ - Print the smali code of this interface. + Print the java code of this interface. """ - s_ifaces = " " j_ifaces = " " for i in self.ifaces: - s_ifaces += self.IMPLEMENTS_TEMPLATE.format(iface_name = i.get_name()) j_ifaces += " {},".format(i.get_name()) j_ifaces = j_ifaces[0:-1] if self.default: @@ -371,8 +284,7 @@ class TestInterface(mixins.DumpMixin, mixins.Named, mixins.NameComparableMixin, class_name = self.class_name) else: funcs = "" - return self.TEST_INTERFACE_TEMPLATE.format(copyright = get_copyright('smali'), - implements_spec = s_ifaces, + return self.TEST_INTERFACE_TEMPLATE.format(copyright = get_copyright('java'), extends = "extends" if len(self.ifaces) else "", ifaces = j_ifaces, funcs = funcs, @@ -451,16 +363,16 @@ def create_all_test_files(): return mc, classes def main(argv): - smali_dir = Path(argv[1]) - if not smali_dir.exists() or not smali_dir.is_dir(): - print("{} is not a valid smali dir".format(smali_dir), file=sys.stderr) + java_dir = Path(argv[1]) + if not java_dir.exists() or not java_dir.is_dir(): + print("{} is not a valid java dir".format(java_dir), file=sys.stderr) sys.exit(1) expected_txt = Path(argv[2]) mainclass, all_files = create_all_test_files() with expected_txt.open('w') as out: print(mainclass.get_expected(), file=out) for f in all_files: - f.dump(smali_dir) + f.dump(java_dir) if __name__ == '__main__': main(sys.argv) diff --git a/test/962-iface-static/build b/test/962-iface-static/build index e17272f769..0dd8573f54 100755 --- a/test/962-iface-static/build +++ b/test/962-iface-static/build @@ -17,24 +17,11 @@ # make us exit on a failure set -e -# Should we compile with Java source code. By default we will use Smali. -USES_JAVA_SOURCE="false" -if [[ $@ == *"--jvm"* ]]; then - USES_JAVA_SOURCE="true" -elif [[ "$USE_JACK" == "true" ]]; then - if $JACK -D jack.java.source.version=1.8 2>/dev/null; then - USES_JAVA_SOURCE="true" - else - echo "WARNING: Cannot use jack because it does not support JLS 1.8. Falling back to smali" >&2 - fi +if [[ $@ != *"--jvm"* ]]; then + # Don't do anything with jvm + # Hard-wired use of experimental jack. + # TODO: fix this temporary work-around for default-methods, see b/19467889 + export USE_JACK=true fi -if [[ "$USES_JAVA_SOURCE" == "true" ]]; then - # We are compiling java code, create it. - mkdir -p src - ${ANDROID_BUILD_TOP}/art/tools/extract-embedded-java ./smali ./src - # Ignore the smali directory. - EXTRA_ARGS="--no-smali" -fi - -./default-build "$@" "$EXTRA_ARGS" --experimental default-methods +./default-build "$@" --experimental default-methods diff --git a/test/962-iface-static/smali/Displayer.smali b/test/962-iface-static/smali/Displayer.smali deleted file mode 100644 index ed4c013d3b..0000000000 --- a/test/962-iface-static/smali/Displayer.smali +++ /dev/null @@ -1,45 +0,0 @@ -# /* -# * Copyright (C) 2015 The Android Open Source Project -# * -# * Licensed under the Apache License, Version 2.0 (the "License"); -# * you may not use this file except in compliance with the License. -# * You may obtain a copy of the License at -# * -# * http://www.apache.org/licenses/LICENSE-2.0 -# * -# * Unless required by applicable law or agreed to in writing, software -# * distributed under the License is distributed on an "AS IS" BASIS, -# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# * See the License for the specific language governing permissions and -# * limitations under the License. -# */ -# -# public class Displayer { -# static { -# System.out.println("init"); -# } -# -# public Displayer() { -# System.out.println("constructor"); -# } -# } - -.class public LDisplayer; -.super Ljava/lang/Object; - -.method static constructor ()V - .locals 3 - sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream; - const-string v0, "init" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - return-void -.end method - -.method public constructor ()V - .locals 2 - invoke-direct {p0}, Ljava/lang/Object;->()V - sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream; - const-string v0, "constructor" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - return-void -.end method diff --git a/test/962-iface-static/smali/Main.smali b/test/962-iface-static/smali/Main.smali deleted file mode 100644 index 72fa5e0e6e..0000000000 --- a/test/962-iface-static/smali/Main.smali +++ /dev/null @@ -1,40 +0,0 @@ -# /* -# * Copyright (C) 2015 The Android Open Source Project -# * -# * Licensed under the Apache License, Version 2.0 (the "License"); -# * you may not use this file except in compliance with the License. -# * You may obtain a copy of the License at -# * -# * http://www.apache.org/licenses/LICENSE-2.0 -# * -# * Unless required by applicable law or agreed to in writing, software -# * distributed under the License is distributed on an "AS IS" BASIS, -# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# * See the License for the specific language governing permissions and -# * limitations under the License. -# */ -# -# class Main { -# public static void main(String[] args) { -# System.out.println(iface.SayHi()); -# } -# } -.class public LMain; -.super Ljava/lang/Object; - -.method public constructor ()V - .registers 1 - invoke-direct {p0}, Ljava/lang/Object;->()V - return-void -.end method - -.method public static main([Ljava/lang/String;)V - .locals 2 - sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream; - - invoke-static {}, Liface;->SayHi()Ljava/lang/String; - move-result-object v0 - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - return-void -.end method diff --git a/test/962-iface-static/smali/iface.smali b/test/962-iface-static/smali/iface.smali deleted file mode 100644 index 5b9c03ec46..0000000000 --- a/test/962-iface-static/smali/iface.smali +++ /dev/null @@ -1,43 +0,0 @@ -# /* -# * Copyright (C) 2015 The Android Open Source Project -# * -# * Licensed under the Apache License, Version 2.0 (the "License"); -# * you may not use this file except in compliance with the License. -# * You may obtain a copy of the License at -# * -# * http://www.apache.org/licenses/LICENSE-2.0 -# * -# * Unless required by applicable law or agreed to in writing, software -# * distributed under the License is distributed on an "AS IS" BASIS, -# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# * See the License for the specific language governing permissions and -# * limitations under the License. -# */ -# -# public interface iface { -# public static final Displayer f = new Displayer(); -# -# public static String SayHi() { -# return "Hello"; -# } -# } - -.class public abstract interface Liface; -.super Ljava/lang/Object; - -.field public final static f:LDisplayer; - -.method static constructor ()V - .locals 3 - new-instance v1, LDisplayer; - invoke-direct {v1}, LDisplayer;->()V - sput-object v1, Liface;->f:LDisplayer; - return-void -.end method - -.method public static SayHi()Ljava/lang/String; - .locals 1 - const-string v0, "Hello" - return-object v0 -.end method - diff --git a/test/962-iface-static/src/Displayer.java b/test/962-iface-static/src/Displayer.java new file mode 100644 index 0000000000..5b28b3f86d --- /dev/null +++ b/test/962-iface-static/src/Displayer.java @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +public class Displayer { + static { + System.out.println("init"); + } + public Displayer() { + System.out.println("constructor"); + } +} diff --git a/test/962-iface-static/src/Iface.java b/test/962-iface-static/src/Iface.java new file mode 100644 index 0000000000..82c7808a31 --- /dev/null +++ b/test/962-iface-static/src/Iface.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +public interface Iface { + public static final Displayer f = new Displayer(); + public static String SayHi() { + return "Hello"; + } +} diff --git a/test/962-iface-static/src/Main.java b/test/962-iface-static/src/Main.java new file mode 100644 index 0000000000..7cb8eb7f7b --- /dev/null +++ b/test/962-iface-static/src/Main.java @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +class Main { + public static void main(String[] args) { + System.out.println(Iface.SayHi()); + } +} diff --git a/test/963-default-range-smali/build b/test/963-default-range-smali/build index e17272f769..0dd8573f54 100755 --- a/test/963-default-range-smali/build +++ b/test/963-default-range-smali/build @@ -17,24 +17,11 @@ # make us exit on a failure set -e -# Should we compile with Java source code. By default we will use Smali. -USES_JAVA_SOURCE="false" -if [[ $@ == *"--jvm"* ]]; then - USES_JAVA_SOURCE="true" -elif [[ "$USE_JACK" == "true" ]]; then - if $JACK -D jack.java.source.version=1.8 2>/dev/null; then - USES_JAVA_SOURCE="true" - else - echo "WARNING: Cannot use jack because it does not support JLS 1.8. Falling back to smali" >&2 - fi +if [[ $@ != *"--jvm"* ]]; then + # Don't do anything with jvm + # Hard-wired use of experimental jack. + # TODO: fix this temporary work-around for default-methods, see b/19467889 + export USE_JACK=true fi -if [[ "$USES_JAVA_SOURCE" == "true" ]]; then - # We are compiling java code, create it. - mkdir -p src - ${ANDROID_BUILD_TOP}/art/tools/extract-embedded-java ./smali ./src - # Ignore the smali directory. - EXTRA_ARGS="--no-smali" -fi - -./default-build "$@" "$EXTRA_ARGS" --experimental default-methods +./default-build "$@" --experimental default-methods diff --git a/test/963-default-range-smali/smali/A.smali b/test/963-default-range-smali/smali/A.smali deleted file mode 100644 index b3d91dd76b..0000000000 --- a/test/963-default-range-smali/smali/A.smali +++ /dev/null @@ -1,29 +0,0 @@ -# /* -# * Copyright 2015 The Android Open Source Project -# * -# * Licensed under the Apache License, Version 2.0 (the "License"); -# * you may not use this file except in compliance with the License. -# * You may obtain a copy of the License at -# * -# * http://www.apache.org/licenses/LICENSE-2.0 -# * -# * Unless required by applicable law or agreed to in writing, software -# * distributed under the License is distributed on an "AS IS" BASIS, -# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# * See the License for the specific language governing permissions and -# * limitations under the License. -# */ - -.class public LA; -.super Ljava/lang/Object; -.implements Liface; - -# class A implements iface { -# } - -.method public constructor ()V - .registers 1 - invoke-direct {p0}, Ljava/lang/Object;->()V - return-void -.end method - diff --git a/test/963-default-range-smali/smali/Main.smali b/test/963-default-range-smali/smali/Main.smali deleted file mode 100644 index 400fba72d9..0000000000 --- a/test/963-default-range-smali/smali/Main.smali +++ /dev/null @@ -1,77 +0,0 @@ -# /* -# * Copyright (C) 2015 The Android Open Source Project -# * -# * Licensed under the Apache License, Version 2.0 (the "License"); -# * you may not use this file except in compliance with the License. -# * You may obtain a copy of the License at -# * -# * http://www.apache.org/licenses/LICENSE-2.0 -# * -# * Unless required by applicable law or agreed to in writing, software -# * distributed under the License is distributed on an "AS IS" BASIS, -# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# * See the License for the specific language governing permissions and -# * limitations under the License. -# */ -# -# class Main { -# public static void main(String[] args) { -# A a = new A(); -# System.out.println(a.SayHi("a string 0", -# "a string 1", -# "a string 2", -# "a string 3", -# "a string 4", -# "a string 5", -# "a string 6", -# "a string 7", -# "a string 8", -# "a string 9")); -# iface b = (iface)a; -# System.out.println(b.SayHi("a string 0", -# "a string 1", -# "a string 2", -# "a string 3", -# "a string 4", -# "a string 5", -# "a string 6", -# "a string 7", -# "a string 8", -# "a string 9")); -# } -# } -.class public LMain; -.super Ljava/lang/Object; - -.method public constructor ()V - .registers 1 - invoke-direct {p0}, Ljava/lang/Object;->()V - return-void -.end method - -.method public static main([Ljava/lang/String;)V - .locals 15 - sget-object v12, Ljava/lang/System;->out:Ljava/io/PrintStream; - - new-instance v1, LA; - invoke-direct {v1}, LA;->()V - const-string v2, "a string 0" - const-string v3, "a string 1" - const-string v4, "a string 2" - const-string v5, "a string 3" - const-string v6, "a string 4" - const-string v7, "a string 5" - const-string v8, "a string 6" - const-string v9, "a string 7" - const-string v10, "a string 8" - const-string v11, "a string 9" - invoke-virtual/range {v1 .. v11}, LA;->SayHi(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; - move-result-object v0 - invoke-virtual {v12,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - invoke-interface/range {v1 .. v11}, Liface;->SayHi(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; - move-result-object v0 - invoke-virtual {v12,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - return-void -.end method diff --git a/test/963-default-range-smali/smali/iface.smali b/test/963-default-range-smali/smali/iface.smali deleted file mode 100644 index c2c3ce69a7..0000000000 --- a/test/963-default-range-smali/smali/iface.smali +++ /dev/null @@ -1,40 +0,0 @@ -# /* -# * Copyright (C) 2015 The Android Open Source Project -# * -# * Licensed under the Apache License, Version 2.0 (the "License"); -# * you may not use this file except in compliance with the License. -# * You may obtain a copy of the License at -# * -# * http://www.apache.org/licenses/LICENSE-2.0 -# * -# * Unless required by applicable law or agreed to in writing, software -# * distributed under the License is distributed on an "AS IS" BASIS, -# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# * See the License for the specific language governing permissions and -# * limitations under the License. -# */ -# -# public interface iface { -# public default String SayHi(String n1, -# String n2, -# String n3, -# String n4, -# String n5, -# String n6, -# String n7, -# String n8, -# String n9, -# String n0) { -# return "Hello"; -# } -# } - -.class public abstract interface Liface; -.super Ljava/lang/Object; - -.method public SayHi(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; - .locals 1 - const-string v0, "Hello" - return-object v0 -.end method - diff --git a/test/963-default-range-smali/src/A.java b/test/963-default-range-smali/src/A.java new file mode 100644 index 0000000000..617eccba49 --- /dev/null +++ b/test/963-default-range-smali/src/A.java @@ -0,0 +1,16 @@ +/* + * Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +class A implements Iface { } diff --git a/test/963-default-range-smali/src/Iface.java b/test/963-default-range-smali/src/Iface.java new file mode 100644 index 0000000000..7556209b54 --- /dev/null +++ b/test/963-default-range-smali/src/Iface.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +public interface Iface { + public default String SayHi(String n1, + String n2, + String n3, + String n4, + String n5, + String n6, + String n7, + String n8, + String n9, + String n0) { + return "Hello"; + } +} diff --git a/test/963-default-range-smali/src/Main.java b/test/963-default-range-smali/src/Main.java new file mode 100644 index 0000000000..841842dc12 --- /dev/null +++ b/test/963-default-range-smali/src/Main.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +class Main { + public static void main(String[] args) { + A a = new A(); + System.out.println(a.SayHi("a string 0", + "a string 1", + "a string 2", + "a string 3", + "a string 4", + "a string 5", + "a string 6", + "a string 7", + "a string 8", + "a string 9")); + Iface b = a; + System.out.println(b.SayHi("a string 0", + "a string 1", + "a string 2", + "a string 3", + "a string 4", + "a string 5", + "a string 6", + "a string 7", + "a string 8", + "a string 9")); + } +} diff --git a/test/964-default-iface-init-generated/build b/test/964-default-iface-init-generated/build index 0780da14e2..ccebbe4ac9 100755 --- a/test/964-default-iface-init-generated/build +++ b/test/964-default-iface-init-generated/build @@ -26,30 +26,19 @@ restore_ulimit() { } trap 'restore_ulimit' ERR -# Generate the smali files and expected.txt or fail -./util-src/generate_smali.py ./smali ./expected.txt - -# Should we compile with Java source code. By default we will use Smali. -USES_JAVA_SOURCE="false" -if [[ $@ == *"--jvm"* ]]; then - USES_JAVA_SOURCE="true" -elif [[ "$USE_JACK" == "true" ]]; then - if $JACK -D jack.java.source.version=1.8 2>/dev/null; then - USES_JAVA_SOURCE="true" - else - echo "WARNING: Cannot use jack because it does not support JLS 1.8. Falling back to smali" >&2 - fi +if [[ $@ != *"--jvm"* ]]; then + # Don't do anything with jvm + # Hard-wired use of experimental jack. + # TODO: fix this temporary work-around for default-methods, see b/19467889 + export USE_JACK=true fi -if [[ "$USES_JAVA_SOURCE" == "true" ]]; then - # We are compiling java code, create it. - mkdir -p src - ${ANDROID_BUILD_TOP}/art/tools/extract-embedded-java ./smali ./src - # Ignore the smali directory. - EXTRA_ARGS="--no-smali" -fi +mkdir -p ./src + +# Generate the smali files and expected.txt or fail +./util-src/generate_java.py ./src ./expected.txt -./default-build "$@" "$EXTRA_ARGS" --experimental default-methods +./default-build "$@" --experimental default-methods # Reset the ulimit back to its initial value restore_ulimit diff --git a/test/964-default-iface-init-generated/smali/Displayer.smali b/test/964-default-iface-init-generated/smali/Displayer.smali deleted file mode 100644 index 91280a8a42..0000000000 --- a/test/964-default-iface-init-generated/smali/Displayer.smali +++ /dev/null @@ -1,45 +0,0 @@ -# /* -# * Copyright (C) 2015 The Android Open Source Project -# * -# * Licensed under the Apache License, Version 2.0 (the "License"); -# * you may not use this file except in compliance with the License. -# * You may obtain a copy of the License at -# * -# * http://www.apache.org/licenses/LICENSE-2.0 -# * -# * Unless required by applicable law or agreed to in writing, software -# * distributed under the License is distributed on an "AS IS" BASIS, -# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# * See the License for the specific language governing permissions and -# * limitations under the License. -# */ -# -# // This class is b/c java does not allow static {} blocks in interfaces. -# public class Displayer { -# public Displayer(String type) { -# System.out.println("initialization of " + type); -# } -# public void touch() { -# return; -# } -# } - -.class public LDisplayer; -.super Ljava/lang/Object; - -.method public constructor (Ljava/lang/String;)V - .locals 2 - invoke-direct {p0}, Ljava/lang/Object;->()V - const-string v0, "initialization of " - invoke-virtual {v0, p1}, Ljava/lang/String;->concat(Ljava/lang/String;)Ljava/lang/String; - move-result-object v0 - sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream; - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - return-void -.end method - -.method public touch()V - .locals 0 - return-void -.end method - diff --git a/test/964-default-iface-init-generated/src/Displayer.java b/test/964-default-iface-init-generated/src/Displayer.java new file mode 100644 index 0000000000..4be0ab2732 --- /dev/null +++ b/test/964-default-iface-init-generated/src/Displayer.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +// This class is b/c java does not allow static {} blocks in interfaces. +public class Displayer { + public Displayer(String type) { + System.out.println("initialization of " + type); + } + public void touch() { + return; + } +} diff --git a/test/964-default-iface-init-generated/util-src/generate_smali.py b/test/964-default-iface-init-generated/util-src/generate_java.py similarity index 68% rename from test/964-default-iface-init-generated/util-src/generate_smali.py rename to test/964-default-iface-init-generated/util-src/generate_java.py index c0ba157109..b2df49f70e 100755 --- a/test/964-default-iface-init-generated/util-src/generate_smali.py +++ b/test/964-default-iface-init-generated/util-src/generate_java.py @@ -15,7 +15,7 @@ # limitations under the License. """ -Generate Smali test files for test 964. +Generate java test files for test 964. """ import os @@ -40,47 +40,27 @@ import string # The max depth the tree can have. MAX_IFACE_DEPTH = 3 -class MainClass(mixins.DumpMixin, mixins.Named, mixins.SmaliFileMixin): +class MainClass(mixins.DumpMixin, mixins.Named, mixins.JavaFileMixin): """ - A Main.smali file containing the Main class and the main function. It will run + A Main.java file containing the Main class and the main function. It will run all the test functions we have. """ MAIN_CLASS_TEMPLATE = """{copyright} - -.class public LMain; -.super Ljava/lang/Object; - -# class Main {{ - -.method public constructor ()V - .registers 1 - invoke-direct {{p0}}, Ljava/lang/Object;->()V - return-void -.end method - +class Main {{ {test_groups} - {main_func} - -# }} +}} """ MAIN_FUNCTION_TEMPLATE = """ -# public static void main(String[] args) {{ -.method public static main([Ljava/lang/String;)V - .locals 2 - + public static void main(String[] args) {{ {test_group_invoke} - - return-void -.end method -# }} + }} """ TEST_GROUP_INVOKE_TEMPLATE = """ -# {test_name}(); - invoke-static {{}}, {test_name}()V + {test_name}(); """ def __init__(self): @@ -110,7 +90,7 @@ class MainClass(mixins.DumpMixin, mixins.Named, mixins.SmaliFileMixin): def __str__(self): """ - Print the smali code for this test. + Print the java code for this test. """ all_tests = sorted(self.tests) test_invoke = "" @@ -121,7 +101,7 @@ class MainClass(mixins.DumpMixin, mixins.Named, mixins.SmaliFileMixin): test_invoke += self.TEST_GROUP_INVOKE_TEMPLATE.format(test_name=t.get_name()) main_func = self.MAIN_FUNCTION_TEMPLATE.format(test_group_invoke=test_invoke) - return self.MAIN_CLASS_TEMPLATE.format(copyright = get_copyright('smali'), + return self.MAIN_CLASS_TEMPLATE.format(copyright = get_copyright('java'), test_groups = test_groups, main_func = main_func) @@ -132,46 +112,19 @@ class Func(mixins.Named, mixins.NameComparableMixin): """ TEST_FUNCTION_TEMPLATE = """ -# public static void {fname}() {{ -# try {{ -# System.out.println("About to initialize {tree}"); -# {farg} v = new {farg}(); -# System.out.println("Initialized {tree}"); -# v.touchAll(); -# System.out.println("All of {tree} hierarchy initialized"); -# return; -# }} catch (Error e) {{ -# e.printStackTrace(System.out); -# return; -# }} -# }} -.method public static {fname}()V - .locals 7 - :call_{fname}_try_start - sget-object v2, Ljava/lang/System;->out:Ljava/io/PrintStream; - const-string v3, "About to initialize {tree}" - invoke-virtual {{v2, v3}}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - new-instance v6, L{farg}; - invoke-direct {{v6}}, L{farg};->()V - - const-string v3, "Initialized {tree}" - invoke-virtual {{v2, v3}}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - invoke-virtual {{v6}}, L{farg};->touchAll()V - - const-string v3, "All of {tree} hierarchy initialized" - invoke-virtual {{v2, v3}}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - return-void - :call_{fname}_try_end - .catch Ljava/lang/Error; {{:call_{fname}_try_start .. :call_{fname}_try_end}} :error_{fname}_start - :error_{fname}_start - move-exception v3 - sget-object v2, Ljava/lang/System;->out:Ljava/io/PrintStream; - invoke-virtual {{v3,v2}}, Ljava/lang/Error;->printStackTrace(Ljava/io/PrintStream;)V - return-void -.end method + public static void {fname}() {{ + try {{ + System.out.println("About to initialize {tree}"); + {farg} v = new {farg}(); + System.out.println("Initialized {tree}"); + v.touchAll(); + System.out.println("All of {tree} hierarchy initialized"); + return; + }} catch (Error e) {{ + e.printStackTrace(System.out); + return; + }} + }} """ OUTPUT_FORMAT = """ @@ -190,7 +143,7 @@ All of {tree} hierarchy initialized def __str__(self): """ - Print the smali code for this test function. + Print the java code for this test function. """ return self.TEST_FUNCTION_TEMPLATE.format(fname=self.get_name(), farg=self.farg.get_name(), @@ -211,57 +164,26 @@ All of {tree} hierarchy initialized initialize_output = self.farg.get_initialize_output().strip(), touch_output = self.farg.get_touch_output().strip()) -class TestClass(mixins.DumpMixin, mixins.Named, mixins.NameComparableMixin, mixins.SmaliFileMixin): +class TestClass(mixins.DumpMixin, mixins.Named, mixins.NameComparableMixin, mixins.JavaFileMixin): """ A class that will be instantiated to test interface initialization order. """ TEST_CLASS_TEMPLATE = """{copyright} - -.class public L{class_name}; -.super Ljava/lang/Object; -{implements_spec} - -# public class {class_name} implements {ifaces} {{ -# -# public {class_name}() {{ -# }} -.method public constructor ()V - .locals 2 - invoke-direct {{p0}}, Ljava/lang/Object;->()V - return-void -.end method - -# public void marker() {{ -# return; -# }} -.method public marker()V - .locals 0 - return-void -.end method - -# public void touchAll() {{ -.method public touchAll()V - .locals 2 - sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream; - {touch_calls} - return-void -.end method -# }} -# }} -""" - - IMPLEMENTS_TEMPLATE = """ -.implements L{iface_name}; +public class {class_name} implements {ifaces} {{ + public void marker() {{ + return; + }} + + public void touchAll() {{ +{touch_calls} + }} +}} """ TOUCH_CALL_TEMPLATE = """ -# System.out.println("{class_name} touching {iface_name}"); -# {iface_name}.field.touch(); - const-string v1, "{class_name} touching {iface_name}" - invoke-virtual {{v0, v1}}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - sget-object v1, L{iface_name};->field:LDisplayer; - invoke-virtual {{v1}}, LDisplayer;->touch()V + System.out.println("{class_name} touching {iface_name}"); + {iface_name}.field.touch(); """ TOUCH_OUTPUT_TEMPLATE = """ @@ -306,63 +228,32 @@ class TestClass(mixins.DumpMixin, mixins.Named, mixins.NameComparableMixin, mixi def __str__(self): """ - Print the smali code for this class. + Print the java code for this class. """ - s_ifaces = '\n'.join(map(lambda a: self.IMPLEMENTS_TEMPLATE.format(iface_name = a.get_name()), - self.ifaces)) j_ifaces = ', '.join(map(lambda a: a.get_name(), self.ifaces)) touches = '\n'.join(map(lambda a: self.TOUCH_CALL_TEMPLATE.format(class_name = self.class_name, iface_name = a.get_name()), self.get_all_interfaces())) - return self.TEST_CLASS_TEMPLATE.format(copyright = get_copyright('smali'), - implements_spec = s_ifaces, + return self.TEST_CLASS_TEMPLATE.format(copyright = get_copyright('java'), ifaces = j_ifaces, class_name = self.class_name, touch_calls = touches) -class TestInterface(mixins.DumpMixin, mixins.Named, mixins.NameComparableMixin, mixins.SmaliFileMixin): +class TestInterface(mixins.DumpMixin, mixins.Named, mixins.NameComparableMixin, mixins.JavaFileMixin): """ An interface that will be used to test default method resolution order. """ TEST_INTERFACE_TEMPLATE = """{copyright} -.class public abstract interface L{class_name}; -.super Ljava/lang/Object; -{implements_spec} - -# public interface {class_name} {extends} {ifaces} {{ -# public static final Displayer field = new Displayer("{tree}"); -.field public final static field:LDisplayer; - -.method static constructor ()V - .locals 3 - const-string v2, "{tree}" - new-instance v1, LDisplayer; - invoke-direct {{v1, v2}}, LDisplayer;->(Ljava/lang/String;)V - sput-object v1, L{class_name};->field:LDisplayer; - return-void -.end method - -# public void marker(); -.method public abstract marker()V -.end method - +public interface {class_name} {extends} {ifaces} {{ + public static final Displayer field = new Displayer("{tree}"); + public void marker(); {funcs} - -# }} +}} """ DEFAULT_FUNC_TEMPLATE = """ -# public default void {class_name}_DEFAULT_FUNC() {{ -# return; -# }} -.method public {class_name}_DEFAULT_FUNC()V - .locals 0 - return-void -.end method -""" - IMPLEMENTS_TEMPLATE = """ -.implements L{iface_name}; + public default void {class_name}_DEFAULT_FUNC() {{ return; }} """ OUTPUT_TEMPLATE = "initialization of {tree}" @@ -429,17 +320,14 @@ class TestInterface(mixins.DumpMixin, mixins.Named, mixins.NameComparableMixin, def __str__(self): """ - Print the smali code for this interface. + Print the java code for this interface. """ - s_ifaces = '\n'.join(map(lambda a: self.IMPLEMENTS_TEMPLATE.format(iface_name = a.get_name()), - self.ifaces)) j_ifaces = ', '.join(map(lambda a: a.get_name(), self.ifaces)) if self.default: funcs = self.DEFAULT_FUNC_TEMPLATE.format(class_name = self.class_name) else: funcs = "" - return self.TEST_INTERFACE_TEMPLATE.format(copyright = get_copyright('smali'), - implements_spec = s_ifaces, + return self.TEST_INTERFACE_TEMPLATE.format(copyright = get_copyright('java'), extends = "extends" if len(self.ifaces) else "", ifaces = j_ifaces, funcs = funcs, @@ -516,16 +404,16 @@ def create_all_test_files(): return mc, classes def main(argv): - smali_dir = Path(argv[1]) - if not smali_dir.exists() or not smali_dir.is_dir(): - print("{} is not a valid smali dir".format(smali_dir), file=sys.stderr) + java_dir = Path(argv[1]) + if not java_dir.exists() or not java_dir.is_dir(): + print("{} is not a valid java dir".format(java_dir), file=sys.stderr) sys.exit(1) expected_txt = Path(argv[2]) mainclass, all_files = create_all_test_files() with expected_txt.open('w') as out: print(mainclass.get_expected(), file=out) for f in all_files: - f.dump(smali_dir) + f.dump(java_dir) if __name__ == '__main__': main(sys.argv) diff --git a/test/965-default-verify/build b/test/965-default-verify/build index 5ba54380df..0dd8573f54 100755 --- a/test/965-default-verify/build +++ b/test/965-default-verify/build @@ -17,32 +17,11 @@ # make us exit on a failure set -e -# Should we compile with Java source code. By default we will use Smali. -USES_JAVA_SOURCE="false" -if [[ $@ == *"--jvm"* ]]; then - USES_JAVA_SOURCE="true" -elif [[ "$USE_JACK" == "true" ]]; then - if $JACK -D jack.java.source.version=1.8 2>/dev/null; then - USES_JAVA_SOURCE="true" - else - echo "WARNING: Cannot use jack because it does not support JLS 1.8. Falling back to smali" >&2 - fi +if [[ $@ != *"--jvm"* ]]; then + # Don't do anything with jvm + # Hard-wired use of experimental jack. + # TODO: fix this temporary work-around for default-methods, see b/19467889 + export USE_JACK=true fi -if [[ "$USES_JAVA_SOURCE" == "true" ]]; then - # We are compiling Java code, create it. - mkdir -p src - mkdir -p src2 - ${ANDROID_BUILD_TOP}/art/tools/extract-embedded-java ./smali ./src - # Move build-src to src and the src copies to src2. This is needed because of - # how our default build script works and we wanted the java and smali code - # to be the same in the smali files. - for f in `find ./build-src -type f -name "*.java" | xargs -i basename \{\}`; do - mv ./src/$f ./src2/$f - mv ./build-src/$f ./src/$f - done - # Ignore the smali directory. - EXTRA_ARGS="--no-smali" -fi - -./default-build "$@" "$EXTRA_ARGS" --experimental default-methods +./default-build "$@" --experimental default-methods diff --git a/test/965-default-verify/smali/Iface.smali b/test/965-default-verify/smali/Iface.smali deleted file mode 100644 index 74799a6cf3..0000000000 --- a/test/965-default-verify/smali/Iface.smali +++ /dev/null @@ -1,40 +0,0 @@ -# /* -# * Copyright (C) 2015 The Android Open Source Project -# * -# * Licensed under the Apache License, Version 2.0 (the "License"); -# * you may not use this file except in compliance with the License. -# * You may obtain a copy of the License at -# * -# * http://www.apache.org/licenses/LICENSE-2.0 -# * -# * Unless required by applicable law or agreed to in writing, software -# * distributed under the License is distributed on an "AS IS" BASIS, -# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# * See the License for the specific language governing permissions and -# * limitations under the License. -# */ -# -# public interface Iface { -# public default String sayHi() { -# return "Hello"; -# } -# -# public default void verificationSoftFail() { -# Statics.nonexistantFunction(); -# } -# } - -.class public abstract interface LIface; -.super Ljava/lang/Object; - -.method public sayHi()Ljava/lang/String; - .locals 1 - const-string v0, "Hello" - return-object v0 -.end method - -.method public verificationSoftFail()V - .locals 1 - invoke-static {}, LStatics;->nonexistantFunction()V - return-void -.end method diff --git a/test/965-default-verify/smali/Main.smali b/test/965-default-verify/smali/Main.smali deleted file mode 100644 index 8e9070692d..0000000000 --- a/test/965-default-verify/smali/Main.smali +++ /dev/null @@ -1,179 +0,0 @@ -# /* -# * Copyright (C) 2015 The Android Open Source Project -# * -# * Licensed under the Apache License, Version 2.0 (the "License"); -# * you may not use this file except in compliance with the License. -# * You may obtain a copy of the License at -# * -# * http://www.apache.org/licenses/LICENSE-2.0 -# * -# * Unless required by applicable law or agreed to in writing, software -# * distributed under the License is distributed on an "AS IS" BASIS, -# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# * See the License for the specific language governing permissions and -# * limitations under the License. -# */ -# -# class Main implements Iface { -# public static void main(String[] args) { -# System.out.println("Create Main instance"); -# Main m = new Main(); -# System.out.println("Calling functions on concrete Main"); -# callMain(m); -# System.out.println("Calling functions on interface Iface"); -# callIface(m); -# } -# -# public static void callMain(Main m) { -# System.out.println("Calling verifiable function on Main"); -# System.out.println(m.sayHi()); -# System.out.println("Calling unverifiable function on Main"); -# try { -# m.verificationSoftFail(); -# System.out.println("Unexpected no error Thrown on Main"); -# } catch (NoSuchMethodError e) { -# System.out.println("Expected NSME Thrown on Main"); -# } catch (Throwable e) { -# System.out.println("Unexpected Error Thrown on Main"); -# e.printStackTrace(System.out); -# } -# System.out.println("Calling verifiable function on Main"); -# System.out.println(m.sayHi()); -# return; -# } -# -# public static void callIface(Iface m) { -# System.out.println("Calling verifiable function on Iface"); -# System.out.println(m.sayHi()); -# System.out.println("Calling unverifiable function on Iface"); -# try { -# m.verificationSoftFail(); -# System.out.println("Unexpected no error Thrown on Iface"); -# } catch (NoSuchMethodError e) { -# System.out.println("Expected NSME Thrown on Iface"); -# } catch (Throwable e) { -# System.out.println("Unexpected Error Thrown on Iface"); -# e.printStackTrace(System.out); -# } -# System.out.println("Calling verifiable function on Iface"); -# System.out.println(m.sayHi()); -# return; -# } -# } - -.class public LMain; -.super Ljava/lang/Object; -.implements LIface; - -.method public constructor ()V - .registers 1 - invoke-direct {p0}, Ljava/lang/Object;->()V - return-void -.end method - -.method public static main([Ljava/lang/String;)V - .locals 3 - sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream; - - const-string v0, "Create Main instance" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - new-instance v2, LMain; - invoke-direct {v2}, LMain;->()V - - const-string v0, "Calling functions on concrete Main" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - invoke-static {v2}, LMain;->callMain(LMain;)V - - const-string v0, "Calling functions on interface Iface" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - invoke-static {v2}, LMain;->callIface(LIface;)V - - return-void -.end method - -.method public static callIface(LIface;)V - .locals 3 - sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream; - const-string v0, "Calling verifiable function on Iface" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - invoke-interface {p0}, LIface;->sayHi()Ljava/lang/String; - move-result-object v0 - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - const-string v0, "Calling unverifiable function on Iface" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - :try_start - invoke-interface {p0}, LIface;->verificationSoftFail()V - - const-string v0, "Unexpected no error Thrown on Iface" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - goto :error_end - :try_end - .catch Ljava/lang/NoSuchMethodError; {:try_start .. :try_end} :NSME_error_start - .catch Ljava/lang/Throwable; {:try_start .. :try_end} :other_error_start - :NSME_error_start - const-string v0, "Expected NSME Thrown on Iface" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - goto :error_end - :other_error_start - move-exception v2 - const-string v0, "Unexpected Error Thrown on Iface" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - invoke-virtual {v2,v1}, Ljava/lang/Throwable;->printStackTrace(Ljava/io/PrintStream;)V - goto :error_end - :error_end - const-string v0, "Calling verifiable function on Iface" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - invoke-interface {p0}, LIface;->sayHi()Ljava/lang/String; - move-result-object v0 - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - return-void -.end method - -.method public static callMain(LMain;)V - .locals 3 - sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream; - const-string v0, "Calling verifiable function on Main" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - invoke-virtual {p0}, LMain;->sayHi()Ljava/lang/String; - move-result-object v0 - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - const-string v0, "Calling unverifiable function on Main" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - :try_start - invoke-virtual {p0}, LMain;->verificationSoftFail()V - - const-string v0, "Unexpected no error Thrown on Main" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - goto :error_end - :try_end - .catch Ljava/lang/NoSuchMethodError; {:try_start .. :try_end} :NSME_error_start - .catch Ljava/lang/Throwable; {:try_start .. :try_end} :other_error_start - :NSME_error_start - const-string v0, "Expected NSME Thrown on Main" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - goto :error_end - :other_error_start - move-exception v2 - const-string v0, "Unexpected Error Thrown on Main" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - invoke-virtual {v2,v1}, Ljava/lang/Throwable;->printStackTrace(Ljava/io/PrintStream;)V - goto :error_end - :error_end - const-string v0, "Calling verifiable function on Main" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - invoke-virtual {p0}, LMain;->sayHi()Ljava/lang/String; - move-result-object v0 - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - return-void -.end method diff --git a/test/965-default-verify/smali/Statics.smali b/test/965-default-verify/smali/Statics.smali deleted file mode 100644 index 1e8cac034a..0000000000 --- a/test/965-default-verify/smali/Statics.smali +++ /dev/null @@ -1,30 +0,0 @@ -# /* -# * Copyright (C) 2015 The Android Open Source Project -# * -# * Licensed under the Apache License, Version 2.0 (the "License"); -# * you may not use this file except in compliance with the License. -# * You may obtain a copy of the License at -# * -# * http://www.apache.org/licenses/LICENSE-2.0 -# * -# * Unless required by applicable law or agreed to in writing, software -# * distributed under the License is distributed on an "AS IS" BASIS, -# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# * See the License for the specific language governing permissions and -# * limitations under the License. -# */ -# -# class Statics { -# // public static void nonexistantFunction() { -# // System.out.println("I don't exist"); -# // } -# } -# -.class public LStatics; -.super Ljava/lang/Object; - -.method public constructor ()V - .registers 1 - invoke-direct {p0}, Ljava/lang/Object;->()V - return-void -.end method diff --git a/test/965-default-verify/src/Iface.java b/test/965-default-verify/src/Iface.java new file mode 100644 index 0000000000..180fba2833 --- /dev/null +++ b/test/965-default-verify/src/Iface.java @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +public interface Iface { + public default String sayHi() { + return "Hello"; + } + public default void verificationSoftFail() { + Statics.nonexistantFunction(); + } +} diff --git a/test/965-default-verify/src/Main.java b/test/965-default-verify/src/Main.java new file mode 100644 index 0000000000..6374cb5aa0 --- /dev/null +++ b/test/965-default-verify/src/Main.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +class Main implements Iface { + public static void main(String[] args) { + System.out.println("Create Main instance"); + Main m = new Main(); + System.out.println("Calling functions on concrete Main"); + callMain(m); + System.out.println("Calling functions on interface Iface"); + callIface(m); + } + + public static void callMain(Main m) { + System.out.println("Calling verifiable function on Main"); + System.out.println(m.sayHi()); + System.out.println("Calling unverifiable function on Main"); + try { + m.verificationSoftFail(); + System.out.println("Unexpected no error Thrown on Main"); + } catch (NoSuchMethodError e) { + System.out.println("Expected NSME Thrown on Main"); + } catch (Throwable e) { + System.out.println("Unexpected Error Thrown on Main"); + e.printStackTrace(System.out); + } + System.out.println("Calling verifiable function on Main"); + System.out.println(m.sayHi()); + return; + } + + public static void callIface(Iface m) { + System.out.println("Calling verifiable function on Iface"); + System.out.println(m.sayHi()); + System.out.println("Calling unverifiable function on Iface"); + try { + m.verificationSoftFail(); + System.out.println("Unexpected no error Thrown on Iface"); + } catch (NoSuchMethodError e) { + System.out.println("Expected NSME Thrown on Iface"); + } catch (Throwable e) { + System.out.println("Unexpected Error Thrown on Iface"); + e.printStackTrace(System.out); + } + System.out.println("Calling verifiable function on Iface"); + System.out.println(m.sayHi()); + return; + } +} diff --git a/test/965-default-verify/build-src/Statics.java b/test/965-default-verify/src/Statics.java similarity index 94% rename from test/965-default-verify/build-src/Statics.java rename to test/965-default-verify/src/Statics.java index 300aeecca7..2e17ba4174 100644 --- a/test/965-default-verify/build-src/Statics.java +++ b/test/965-default-verify/src/Statics.java @@ -16,7 +16,7 @@ class Statics { public static void nonexistantFunction() { - System.out.println("I don't exist"); + System.out.println("I don't exist"); } } diff --git a/test/965-default-verify/src2/Statics.java b/test/965-default-verify/src2/Statics.java new file mode 100644 index 0000000000..7899ca9c5e --- /dev/null +++ b/test/965-default-verify/src2/Statics.java @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +class Statics { + // public static void nonexistantFunction() { + // System.out.println("I don't exist"); + // } +} diff --git a/test/966-default-conflict/build b/test/966-default-conflict/build index e66e8409c6..0dd8573f54 100755 --- a/test/966-default-conflict/build +++ b/test/966-default-conflict/build @@ -17,18 +17,11 @@ # make us exit on a failure set -e -# TODO: Support running with jack. - -if [[ $@ == *"--jvm"* ]]; then - # Build the Java files if we are running a --jvm test - mkdir -p src - mkdir -p classes - ${ANDROID_BUILD_TOP}/art/tools/extract-embedded-java ./smali ./src - # Build with the non-conflicting version - ${JAVAC} -implicit:none -d classes src/Iface.java build-src/Iface2.java src/Main.java - rm classes/Iface2.class - # Build with the conflicting version - ${JAVAC} -implicit:none -cp classes -d classes src/Iface2.java -else - ./default-build "$@" --experimental default-methods +if [[ $@ != *"--jvm"* ]]; then + # Don't do anything with jvm + # Hard-wired use of experimental jack. + # TODO: fix this temporary work-around for default-methods, see b/19467889 + export USE_JACK=true fi + +./default-build "$@" --experimental default-methods diff --git a/test/966-default-conflict/smali/Iface.smali b/test/966-default-conflict/smali/Iface.smali deleted file mode 100644 index e996b3a4f4..0000000000 --- a/test/966-default-conflict/smali/Iface.smali +++ /dev/null @@ -1,39 +0,0 @@ -# /* -# * Copyright (C) 2015 The Android Open Source Project -# * -# * Licensed under the Apache License, Version 2.0 (the "License"); -# * you may not use this file except in compliance with the License. -# * You may obtain a copy of the License at -# * -# * http://www.apache.org/licenses/LICENSE-2.0 -# * -# * Unless required by applicable law or agreed to in writing, software -# * distributed under the License is distributed on an "AS IS" BASIS, -# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# * See the License for the specific language governing permissions and -# * limitations under the License. -# */ -# -# public interface Iface { -# public default String sayHi() { -# return "Hi"; -# } -# public default String charge() { -# return "CHARGE"; -# } -# } - -.class public abstract interface LIface; -.super Ljava/lang/Object; - -.method public sayHi()Ljava/lang/String; - .locals 1 - const-string v0, "Hi" - return-object v0 -.end method - -.method public charge()Ljava/lang/String; - .locals 1 - const-string v0, "CHARGE" - return-object v0 -.end method diff --git a/test/966-default-conflict/smali/Iface2.smali b/test/966-default-conflict/smali/Iface2.smali deleted file mode 100644 index 82fa547dea..0000000000 --- a/test/966-default-conflict/smali/Iface2.smali +++ /dev/null @@ -1,31 +0,0 @@ -# /* -# * Copyright (C) 2015 The Android Open Source Project -# * -# * Licensed under the Apache License, Version 2.0 (the "License"); -# * you may not use this file except in compliance with the License. -# * You may obtain a copy of the License at -# * -# * http://www.apache.org/licenses/LICENSE-2.0 -# * -# * Unless required by applicable law or agreed to in writing, software -# * distributed under the License is distributed on an "AS IS" BASIS, -# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# * See the License for the specific language governing permissions and -# * limitations under the License. -# */ -# -# public interface Iface2 { -# public default String sayHi() { -# return "hello"; -# } -# } - -.class public abstract interface LIface2; -.super Ljava/lang/Object; - -.method public sayHi()Ljava/lang/String; - .locals 1 - const-string v0, "hello" - return-object v0 -.end method - diff --git a/test/966-default-conflict/smali/Main.smali b/test/966-default-conflict/smali/Main.smali deleted file mode 100644 index ce974d8135..0000000000 --- a/test/966-default-conflict/smali/Main.smali +++ /dev/null @@ -1,227 +0,0 @@ -# /* -# * Copyright (C) 2015 The Android Open Source Project -# * -# * Licensed under the Apache License, Version 2.0 (the "License"); -# * you may not use this file except in compliance with the License. -# * You may obtain a copy of the License at -# * -# * http://www.apache.org/licenses/LICENSE-2.0 -# * -# * Unless required by applicable law or agreed to in writing, software -# * distributed under the License is distributed on an "AS IS" BASIS, -# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# * See the License for the specific language governing permissions and -# * limitations under the License. -# */ -# -# class Main implements Iface, Iface2 { -# public static void main(String[] args) { -# System.out.println("Create Main instance"); -# Main m = new Main(); -# System.out.println("Calling functions on concrete Main"); -# callMain(m); -# System.out.println("Calling functions on interface Iface"); -# callIface(m); -# System.out.println("Calling functions on interface Iface2"); -# callIface2(m); -# } -# -# public static void callMain(Main m) { -# System.out.println("Calling non-conflicting function on Main"); -# System.out.println(m.charge()); -# System.out.println("Calling conflicting function on Main"); -# try { -# System.out.println(m.sayHi()); -# System.out.println("Unexpected no error Thrown on Main"); -# } catch (AbstractMethodError e) { -# System.out.println("Unexpected AME Thrown on Main"); -# } catch (IncompatibleClassChangeError e) { -# System.out.println("Expected ICCE Thrown on Main"); -# } -# System.out.println("Calling non-conflicting function on Main"); -# System.out.println(m.charge()); -# return; -# } -# -# public static void callIface(Iface m) { -# System.out.println("Calling non-conflicting function on Iface"); -# System.out.println(m.charge()); -# System.out.println("Calling conflicting function on Iface"); -# try { -# System.out.println(m.sayHi()); -# System.out.println("Unexpected no error Thrown on Iface"); -# } catch (AbstractMethodError e) { -# System.out.println("Unexpected AME Thrown on Iface"); -# } catch (IncompatibleClassChangeError e) { -# System.out.println("Expected ICCE Thrown on Iface"); -# } -# System.out.println("Calling non-conflicting function on Iface"); -# System.out.println(m.charge()); -# return; -# } -# -# public static void callIface2(Iface2 m) { -# System.out.println("Calling conflicting function on Iface2"); -# try { -# System.out.println(m.sayHi()); -# System.out.println("Unexpected no error Thrown on Iface2"); -# } catch (AbstractMethodError e) { -# System.out.println("Unexpected AME Thrown on Iface2"); -# } catch (IncompatibleClassChangeError e) { -# System.out.println("Expected ICCE Thrown on Iface2"); -# } -# return; -# } -# } - -.class public LMain; -.super Ljava/lang/Object; -.implements LIface; -.implements LIface2; - -.method public constructor ()V - .registers 1 - invoke-direct {p0}, Ljava/lang/Object;->()V - return-void -.end method - -.method public static main([Ljava/lang/String;)V - .locals 3 - sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream; - - const-string v0, "Create Main instance" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - new-instance v2, LMain; - invoke-direct {v2}, LMain;->()V - - const-string v0, "Calling functions on concrete Main" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - invoke-static {v2}, LMain;->callMain(LMain;)V - - const-string v0, "Calling functions on interface Iface" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - invoke-static {v2}, LMain;->callIface(LIface;)V - - const-string v0, "Calling functions on interface Iface2" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - invoke-static {v2}, LMain;->callIface2(LIface2;)V - - return-void -.end method - -.method public static callIface(LIface;)V - .locals 2 - sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream; - const-string v0, "Calling non-conflicting function on Iface" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - invoke-interface {p0}, LIface;->charge()Ljava/lang/String; - move-result-object v0 - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - const-string v0, "Calling conflicting function on Iface" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - :try_start - invoke-interface {p0}, LIface;->sayHi()Ljava/lang/String; - move-result-object v0 - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - const-string v0, "Unexpected no error Thrown on Iface" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - goto :error_end - :try_end - .catch Ljava/lang/AbstractMethodError; {:try_start .. :try_end} :AME_error_start - .catch Ljava/lang/IncompatibleClassChangeError; {:try_start .. :try_end} :ICCE_error_start - :AME_error_start - const-string v0, "Unexpected AME Thrown on Iface" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - goto :error_end - :ICCE_error_start - const-string v0, "Expected ICCE Thrown on Iface" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - goto :error_end - :error_end - const-string v0, "Calling non-conflicting function on Iface" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - invoke-interface {p0}, LIface;->charge()Ljava/lang/String; - move-result-object v0 - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - return-void -.end method - -.method public static callIface2(LIface2;)V - .locals 2 - sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream; - const-string v0, "Calling conflicting function on Iface2" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - :try_start - invoke-interface {p0}, LIface2;->sayHi()Ljava/lang/String; - move-result-object v0 - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - const-string v0, "Unexpected no error Thrown on Iface2" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - goto :error_end - :try_end - .catch Ljava/lang/AbstractMethodError; {:try_start .. :try_end} :AME_error_start - .catch Ljava/lang/IncompatibleClassChangeError; {:try_start .. :try_end} :ICCE_error_start - :AME_error_start - const-string v0, "Unexpected AME Thrown on Iface2" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - goto :error_end - :ICCE_error_start - const-string v0, "Expected ICCE Thrown on Iface2" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - goto :error_end - :error_end - - return-void -.end method - -.method public static callMain(LMain;)V - .locals 2 - sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream; - const-string v0, "Calling non-conflicting function on Main" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - invoke-virtual {p0}, LMain;->charge()Ljava/lang/String; - move-result-object v0 - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - const-string v0, "Calling conflicting function on Main" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - :try_start - invoke-virtual {p0}, LMain;->sayHi()Ljava/lang/String; - move-result-object v0 - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - const-string v0, "Unexpected no error Thrown on Main" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - goto :error_end - :try_end - .catch Ljava/lang/AbstractMethodError; {:try_start .. :try_end} :AME_error_start - .catch Ljava/lang/IncompatibleClassChangeError; {:try_start .. :try_end} :ICCE_error_start - :AME_error_start - const-string v0, "Unexpected AME Thrown on Main" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - goto :error_end - :ICCE_error_start - const-string v0, "Expected ICCE Thrown on Main" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - goto :error_end - :error_end - const-string v0, "Calling non-conflicting function on Main" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - invoke-virtual {p0}, LMain;->charge()Ljava/lang/String; - move-result-object v0 - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - return-void -.end method diff --git a/test/966-default-conflict/src/Iface.java b/test/966-default-conflict/src/Iface.java new file mode 100644 index 0000000000..2131ed878d --- /dev/null +++ b/test/966-default-conflict/src/Iface.java @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +public interface Iface { + public default String sayHi() { + return "Hi"; + } + public default String charge() { + return "CHARGE"; + } +} diff --git a/test/966-default-conflict/build-src/Iface2.java b/test/966-default-conflict/src/Iface2.java similarity index 100% rename from test/966-default-conflict/build-src/Iface2.java rename to test/966-default-conflict/src/Iface2.java diff --git a/test/966-default-conflict/src/Main.java b/test/966-default-conflict/src/Main.java new file mode 100644 index 0000000000..ce8cb47209 --- /dev/null +++ b/test/966-default-conflict/src/Main.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +class Main implements Iface, Iface2 { + public static void main(String[] args) { + System.out.println("Create Main instance"); + Main m = new Main(); + System.out.println("Calling functions on concrete Main"); + callMain(m); + System.out.println("Calling functions on interface Iface"); + callIface(m); + System.out.println("Calling functions on interface Iface2"); + callIface2(m); + } + public static void callMain(Main m) { + System.out.println("Calling non-conflicting function on Main"); + System.out.println(m.charge()); + System.out.println("Calling conflicting function on Main"); + try { + System.out.println(m.sayHi()); + System.out.println("Unexpected no error Thrown on Main"); + } catch (AbstractMethodError e) { + System.out.println("Unexpected AME Thrown on Main"); + } catch (IncompatibleClassChangeError e) { + System.out.println("Expected ICCE Thrown on Main"); + } + System.out.println("Calling non-conflicting function on Main"); + System.out.println(m.charge()); + return; + } + public static void callIface(Iface m) { + System.out.println("Calling non-conflicting function on Iface"); + System.out.println(m.charge()); + System.out.println("Calling conflicting function on Iface"); + try { + System.out.println(m.sayHi()); + System.out.println("Unexpected no error Thrown on Iface"); + } catch (AbstractMethodError e) { + System.out.println("Unexpected AME Thrown on Iface"); + } catch (IncompatibleClassChangeError e) { + System.out.println("Expected ICCE Thrown on Iface"); + } + System.out.println("Calling non-conflicting function on Iface"); + System.out.println(m.charge()); + return; + } + public static void callIface2(Iface2 m) { + System.out.println("Calling conflicting function on Iface2"); + try { + System.out.println(m.sayHi()); + System.out.println("Unexpected no error Thrown on Iface2"); + } catch (AbstractMethodError e) { + System.out.println("Unexpected AME Thrown on Iface2"); + } catch (IncompatibleClassChangeError e) { + System.out.println("Expected ICCE Thrown on Iface2"); + } + return; + } +} diff --git a/test/966-default-conflict/src2/Iface2.java b/test/966-default-conflict/src2/Iface2.java new file mode 100644 index 0000000000..d29033cd93 --- /dev/null +++ b/test/966-default-conflict/src2/Iface2.java @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +public interface Iface2 { + public default String sayHi() { + return "hello"; + } +} diff --git a/test/967-default-ame/build b/test/967-default-ame/build index 53001a9ad2..0dd8573f54 100755 --- a/test/967-default-ame/build +++ b/test/967-default-ame/build @@ -17,19 +17,11 @@ # make us exit on a failure set -e -# TODO: Support running with jack. - -if [[ $@ == *"--jvm"* ]]; then - # Build the Java files if we are running a --jvm test - mkdir -p src - mkdir -p classes - ${ANDROID_BUILD_TOP}/art/tools/extract-embedded-java ./smali ./src - # Build with the non-conflicting version - ${JAVAC} -implicit:none -d classes src/Iface.java build-src/Iface2.java build-src/Iface3.java src/Main.java - rm classes/Iface2.class - rm classes/Iface3.class - # Build with the conflicting version - ${JAVAC} -implicit:none -cp classes -d classes src/Iface2.java src/Iface3.java -else - ./default-build "$@" --experimental default-methods +if [[ $@ != *"--jvm"* ]]; then + # Don't do anything with jvm + # Hard-wired use of experimental jack. + # TODO: fix this temporary work-around for default-methods, see b/19467889 + export USE_JACK=true fi + +./default-build "$@" --experimental default-methods diff --git a/test/967-default-ame/smali/Iface.smali b/test/967-default-ame/smali/Iface.smali deleted file mode 100644 index e996b3a4f4..0000000000 --- a/test/967-default-ame/smali/Iface.smali +++ /dev/null @@ -1,39 +0,0 @@ -# /* -# * Copyright (C) 2015 The Android Open Source Project -# * -# * Licensed under the Apache License, Version 2.0 (the "License"); -# * you may not use this file except in compliance with the License. -# * You may obtain a copy of the License at -# * -# * http://www.apache.org/licenses/LICENSE-2.0 -# * -# * Unless required by applicable law or agreed to in writing, software -# * distributed under the License is distributed on an "AS IS" BASIS, -# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# * See the License for the specific language governing permissions and -# * limitations under the License. -# */ -# -# public interface Iface { -# public default String sayHi() { -# return "Hi"; -# } -# public default String charge() { -# return "CHARGE"; -# } -# } - -.class public abstract interface LIface; -.super Ljava/lang/Object; - -.method public sayHi()Ljava/lang/String; - .locals 1 - const-string v0, "Hi" - return-object v0 -.end method - -.method public charge()Ljava/lang/String; - .locals 1 - const-string v0, "CHARGE" - return-object v0 -.end method diff --git a/test/967-default-ame/smali/Iface2.smali b/test/967-default-ame/smali/Iface2.smali deleted file mode 100644 index a21a8ddbc7..0000000000 --- a/test/967-default-ame/smali/Iface2.smali +++ /dev/null @@ -1,27 +0,0 @@ -# /* -# * Copyright (C) 2015 The Android Open Source Project -# * -# * Licensed under the Apache License, Version 2.0 (the "License"); -# * you may not use this file except in compliance with the License. -# * You may obtain a copy of the License at -# * -# * http://www.apache.org/licenses/LICENSE-2.0 -# * -# * Unless required by applicable law or agreed to in writing, software -# * distributed under the License is distributed on an "AS IS" BASIS, -# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# * See the License for the specific language governing permissions and -# * limitations under the License. -# */ -# -# public interface Iface2 extends Iface { -# public String sayHi(); -# } - -.class public abstract interface LIface2; -.super Ljava/lang/Object; -.implements LIface; - -.method public abstract sayHi()Ljava/lang/String; -.end method - diff --git a/test/967-default-ame/smali/Iface3.smali b/test/967-default-ame/smali/Iface3.smali deleted file mode 100644 index 874e96d069..0000000000 --- a/test/967-default-ame/smali/Iface3.smali +++ /dev/null @@ -1,26 +0,0 @@ -# /* -# * Copyright (C) 2015 The Android Open Source Project -# * -# * Licensed under the Apache License, Version 2.0 (the "License"); -# * you may not use this file except in compliance with the License. -# * You may obtain a copy of the License at -# * -# * http://www.apache.org/licenses/LICENSE-2.0 -# * -# * Unless required by applicable law or agreed to in writing, software -# * distributed under the License is distributed on an "AS IS" BASIS, -# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# * See the License for the specific language governing permissions and -# * limitations under the License. -# */ -# -# public interface Iface3 { -# public String charge(); -# } - -.class public abstract interface LIface3; -.super Ljava/lang/Object; - -.method public abstract charge()Ljava/lang/String; -.end method - diff --git a/test/967-default-ame/smali/Main.smali b/test/967-default-ame/smali/Main.smali deleted file mode 100644 index e4d63cfa24..0000000000 --- a/test/967-default-ame/smali/Main.smali +++ /dev/null @@ -1,228 +0,0 @@ -# /* -# * Copyright (C) 2015 The Android Open Source Project -# * -# * Licensed under the Apache License, Version 2.0 (the "License"); -# * you may not use this file except in compliance with the License. -# * You may obtain a copy of the License at -# * -# * http://www.apache.org/licenses/LICENSE-2.0 -# * -# * Unless required by applicable law or agreed to in writing, software -# * distributed under the License is distributed on an "AS IS" BASIS, -# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# * See the License for the specific language governing permissions and -# * limitations under the License. -# */ -# -# class Main implements Iface, Iface2, Iface3 { -# public static void main(String[] args) { -# System.out.println("Create Main instance"); -# Main m = new Main(); -# System.out.println("Calling functions on concrete Main"); -# callMain(m); -# System.out.println("Calling functions on interface Iface"); -# callIface(m); -# System.out.println("Calling functions on interface Iface2"); -# callIface2(m); -# } -# -# public static void callMain(Main m) { -# System.out.println("Calling non-abstract function on Main"); -# System.out.println(m.charge()); -# System.out.println("Calling abstract function on Main"); -# try { -# System.out.println(m.sayHi()); -# System.out.println("Unexpected no error Thrown on Main"); -# } catch (AbstractMethodError e) { -# System.out.println("Expected AME Thrown on Main"); -# } catch (IncompatibleClassChangeError e) { -# System.out.println("Unexpected ICCE Thrown on Main"); -# } -# System.out.println("Calling non-abstract function on Main"); -# System.out.println(m.charge()); -# return; -# } -# -# public static void callIface(Iface m) { -# System.out.println("Calling non-abstract function on Iface"); -# System.out.println(m.charge()); -# System.out.println("Calling abstract function on Iface"); -# try { -# System.out.println(m.sayHi()); -# System.out.println("Unexpected no error Thrown on Iface"); -# } catch (AbstractMethodError e) { -# System.out.println("Expected AME Thrown on Iface"); -# } catch (IncompatibleClassChangeError e) { -# System.out.println("Unexpected ICCE Thrown on Iface"); -# } -# System.out.println("Calling non-abstract function on Iface"); -# System.out.println(m.charge()); -# return; -# } -# -# public static void callIface2(Iface2 m) { -# System.out.println("Calling abstract function on Iface2"); -# try { -# System.out.println(m.sayHi()); -# System.out.println("Unexpected no error Thrown on Iface2"); -# } catch (AbstractMethodError e) { -# System.out.println("Expected AME Thrown on Iface2"); -# } catch (IncompatibleClassChangeError e) { -# System.out.println("Unexpected ICCE Thrown on Iface2"); -# } -# return; -# } -# } - -.class public LMain; -.super Ljava/lang/Object; -.implements LIface; -.implements LIface2; -.implements LIface3; - -.method public constructor ()V - .registers 1 - invoke-direct {p0}, Ljava/lang/Object;->()V - return-void -.end method - -.method public static main([Ljava/lang/String;)V - .locals 3 - sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream; - - const-string v0, "Create Main instance" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - new-instance v2, LMain; - invoke-direct {v2}, LMain;->()V - - const-string v0, "Calling functions on concrete Main" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - invoke-static {v2}, LMain;->callMain(LMain;)V - - const-string v0, "Calling functions on interface Iface" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - invoke-static {v2}, LMain;->callIface(LIface;)V - - const-string v0, "Calling functions on interface Iface2" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - invoke-static {v2}, LMain;->callIface2(LIface2;)V - - return-void -.end method - -.method public static callIface(LIface;)V - .locals 2 - sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream; - const-string v0, "Calling non-abstract function on Iface" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - invoke-interface {p0}, LIface;->charge()Ljava/lang/String; - move-result-object v0 - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - const-string v0, "Calling abstract function on Iface" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - :try_start - invoke-interface {p0}, LIface;->sayHi()Ljava/lang/String; - move-result-object v0 - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - const-string v0, "Unexpected no error Thrown on Iface" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - goto :error_end - :try_end - .catch Ljava/lang/AbstractMethodError; {:try_start .. :try_end} :AME_error_start - .catch Ljava/lang/IncompatibleClassChangeError; {:try_start .. :try_end} :ICCE_error_start - :AME_error_start - const-string v0, "Expected AME Thrown on Iface" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - goto :error_end - :ICCE_error_start - const-string v0, "Unexpected ICCE Thrown on Iface" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - goto :error_end - :error_end - const-string v0, "Calling non-abstract function on Iface" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - invoke-interface {p0}, LIface;->charge()Ljava/lang/String; - move-result-object v0 - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - return-void -.end method - -.method public static callIface2(LIface2;)V - .locals 2 - sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream; - const-string v0, "Calling abstract function on Iface2" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - :try_start - invoke-interface {p0}, LIface2;->sayHi()Ljava/lang/String; - move-result-object v0 - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - const-string v0, "Unexpected no error Thrown on Iface2" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - goto :error_end - :try_end - .catch Ljava/lang/AbstractMethodError; {:try_start .. :try_end} :AME_error_start - .catch Ljava/lang/IncompatibleClassChangeError; {:try_start .. :try_end} :ICCE_error_start - :AME_error_start - const-string v0, "Expected AME Thrown on Iface2" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - goto :error_end - :ICCE_error_start - const-string v0, "Unexpected ICCE Thrown on Iface2" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - goto :error_end - :error_end - - return-void -.end method - -.method public static callMain(LMain;)V - .locals 2 - sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream; - const-string v0, "Calling non-abstract function on Main" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - invoke-virtual {p0}, LMain;->charge()Ljava/lang/String; - move-result-object v0 - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - const-string v0, "Calling abstract function on Main" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - :try_start - invoke-virtual {p0}, LMain;->sayHi()Ljava/lang/String; - move-result-object v0 - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - const-string v0, "Unexpected no error Thrown on Main" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - goto :error_end - :try_end - .catch Ljava/lang/AbstractMethodError; {:try_start .. :try_end} :AME_error_start - .catch Ljava/lang/IncompatibleClassChangeError; {:try_start .. :try_end} :ICCE_error_start - :AME_error_start - const-string v0, "Expected AME Thrown on Main" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - goto :error_end - :ICCE_error_start - const-string v0, "Unexpected ICCE Thrown on Main" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - goto :error_end - :error_end - const-string v0, "Calling non-abstract function on Main" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - invoke-virtual {p0}, LMain;->charge()Ljava/lang/String; - move-result-object v0 - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - return-void -.end method diff --git a/test/967-default-ame/src/Iface.java b/test/967-default-ame/src/Iface.java new file mode 100644 index 0000000000..2131ed878d --- /dev/null +++ b/test/967-default-ame/src/Iface.java @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +public interface Iface { + public default String sayHi() { + return "Hi"; + } + public default String charge() { + return "CHARGE"; + } +} diff --git a/test/967-default-ame/build-src/Iface2.java b/test/967-default-ame/src/Iface2.java similarity index 100% rename from test/967-default-ame/build-src/Iface2.java rename to test/967-default-ame/src/Iface2.java diff --git a/test/967-default-ame/build-src/Iface3.java b/test/967-default-ame/src/Iface3.java similarity index 100% rename from test/967-default-ame/build-src/Iface3.java rename to test/967-default-ame/src/Iface3.java diff --git a/test/967-default-ame/src/Main.java b/test/967-default-ame/src/Main.java new file mode 100644 index 0000000000..3e48062aba --- /dev/null +++ b/test/967-default-ame/src/Main.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +class Main implements Iface, Iface2, Iface3 { + public static void main(String[] args) { + System.out.println("Create Main instance"); + Main m = new Main(); + System.out.println("Calling functions on concrete Main"); + callMain(m); + System.out.println("Calling functions on interface Iface"); + callIface(m); + System.out.println("Calling functions on interface Iface2"); + callIface2(m); + } + public static void callMain(Main m) { + System.out.println("Calling non-abstract function on Main"); + System.out.println(m.charge()); + System.out.println("Calling abstract function on Main"); + try { + System.out.println(m.sayHi()); + System.out.println("Unexpected no error Thrown on Main"); + } catch (AbstractMethodError e) { + System.out.println("Expected AME Thrown on Main"); + } catch (IncompatibleClassChangeError e) { + System.out.println("Unexpected ICCE Thrown on Main"); + } + System.out.println("Calling non-abstract function on Main"); + System.out.println(m.charge()); + return; + } + public static void callIface(Iface m) { + System.out.println("Calling non-abstract function on Iface"); + System.out.println(m.charge()); + System.out.println("Calling abstract function on Iface"); + try { + System.out.println(m.sayHi()); + System.out.println("Unexpected no error Thrown on Iface"); + } catch (AbstractMethodError e) { + System.out.println("Expected AME Thrown on Iface"); + } catch (IncompatibleClassChangeError e) { + System.out.println("Unexpected ICCE Thrown on Iface"); + } + System.out.println("Calling non-abstract function on Iface"); + System.out.println(m.charge()); + return; + } + public static void callIface2(Iface2 m) { + System.out.println("Calling abstract function on Iface2"); + try { + System.out.println(m.sayHi()); + System.out.println("Unexpected no error Thrown on Iface2"); + } catch (AbstractMethodError e) { + System.out.println("Expected AME Thrown on Iface2"); + } catch (IncompatibleClassChangeError e) { + System.out.println("Unexpected ICCE Thrown on Iface2"); + } + return; + } +} diff --git a/test/967-default-ame/src2/Iface.java b/test/967-default-ame/src2/Iface.java new file mode 100644 index 0000000000..2131ed878d --- /dev/null +++ b/test/967-default-ame/src2/Iface.java @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +public interface Iface { + public default String sayHi() { + return "Hi"; + } + public default String charge() { + return "CHARGE"; + } +} diff --git a/test/967-default-ame/src2/Iface2.java b/test/967-default-ame/src2/Iface2.java new file mode 100644 index 0000000000..0e4fb5f2aa --- /dev/null +++ b/test/967-default-ame/src2/Iface2.java @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +public interface Iface2 extends Iface { + public String sayHi(); +} diff --git a/test/967-default-ame/src2/Iface3.java b/test/967-default-ame/src2/Iface3.java new file mode 100644 index 0000000000..70fc33ba93 --- /dev/null +++ b/test/967-default-ame/src2/Iface3.java @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +public interface Iface3 { + public String charge(); +} diff --git a/test/969-iface-super/build b/test/969-iface-super/build index b1ef320d05..e8f4ed084a 100755 --- a/test/969-iface-super/build +++ b/test/969-iface-super/build @@ -17,27 +17,14 @@ # make us exit on a failure set -e -# Should we compile with Java source code. By default we will use Smali. -USES_JAVA_SOURCE="false" -if [[ $@ == *"--jvm"* ]]; then - USES_JAVA_SOURCE="true" -elif [[ "$USE_JACK" == "true" ]]; then - if $JACK -D jack.java.source.version=1.8 2>/dev/null; then - USES_JAVA_SOURCE="true" - else - echo "WARNING: Cannot use jack because it does not support JLS 1.8. Falling back to smali" >&2 - fi +if [[ $@ != *"--jvm"* ]]; then + # Don't do anything with jvm + # Hard-wired use of experimental jack. + # TODO: fix this temporary work-around for default-methods, see b/19467889 + export USE_JACK=true fi -# Generate the smali Main.smali file or fail -${ANDROID_BUILD_TOP}/art/test/utils/python/generate_smali_main.py ./smali +# Generate the Main.java file or fail +${ANDROID_BUILD_TOP}/art/test/utils/python/generate_java_main.py ./src -if [[ "$USES_JAVA_SOURCE" == "true" ]]; then - # We are compiling java code, create it. - mkdir -p src - ${ANDROID_BUILD_TOP}/art/tools/extract-embedded-java ./smali ./src - # Ignore the smali directory. - EXTRA_ARGS="--no-smali" -fi - -./default-build "$@" "$EXTRA_ARGS" --experimental default-methods +./default-build "$@" --experimental default-methods diff --git a/test/969-iface-super/expected.txt b/test/969-iface-super/expected.txt index f310d10e12..f7a63d6b08 100644 --- a/test/969-iface-super/expected.txt +++ b/test/969-iface-super/expected.txt @@ -1,39 +1,39 @@ Testing for type A A-virtual A.SayHi()='Hello ' -A-interface iface.SayHi()='Hello ' +A-interface Iface.SayHi()='Hello ' End testing for type A Testing for type B B-virtual B.SayHi()='Hello Hello ' -B-interface iface.SayHi()='Hello Hello ' -B-interface iface2.SayHi()='Hello Hello ' +B-interface Iface.SayHi()='Hello Hello ' +B-interface Iface2.SayHi()='Hello Hello ' End testing for type B Testing for type C C-virtual C.SayHi()='Hello and welcome ' -C-interface iface.SayHi()='Hello and welcome ' +C-interface Iface.SayHi()='Hello and welcome ' End testing for type C Testing for type D D-virtual D.SayHi()='Hello Hello and welcome ' -D-interface iface.SayHi()='Hello Hello and welcome ' -D-interface iface2.SayHi()='Hello Hello and welcome ' +D-interface Iface.SayHi()='Hello Hello and welcome ' +D-interface Iface2.SayHi()='Hello Hello and welcome ' End testing for type D Testing for type E E-virtual E.SayHi()='Hello there!' -E-interface iface.SayHi()='Hello there!' -E-interface iface3.SayHi()='Hello there!' +E-interface Iface.SayHi()='Hello there!' +E-interface Iface3.SayHi()='Hello there!' End testing for type E Testing for type F F-virtual E.SayHi()='Hello there!' F-virtual F.SayHi()='Hello there!' -F-interface iface.SayHi()='Hello there!' -F-interface iface3.SayHi()='Hello there!' +F-interface Iface.SayHi()='Hello there!' +F-interface Iface3.SayHi()='Hello there!' F-virtual F.SaySurprisedHi()='Hello there!!' End testing for type F Testing for type G G-virtual E.SayHi()='Hello there!?' G-virtual F.SayHi()='Hello there!?' G-virtual G.SayHi()='Hello there!?' -G-interface iface.SayHi()='Hello there!?' -G-interface iface3.SayHi()='Hello there!?' +G-interface Iface.SayHi()='Hello there!?' +G-interface Iface3.SayHi()='Hello there!?' G-virtual F.SaySurprisedHi()='Hello there!!' G-virtual G.SaySurprisedHi()='Hello there!!' G-virtual G.SayVerySurprisedHi()='Hello there!!!' @@ -42,6 +42,6 @@ Testing for type H H-virtual H.SayConfusedHi()='Hello ?!' H-virtual A.SayHi()='Hello ?' H-virtual H.SayHi()='Hello ?' -H-interface iface.SayHi()='Hello ?' +H-interface Iface.SayHi()='Hello ?' H-virtual H.SaySurprisedHi()='Hello !' End testing for type H diff --git a/test/969-iface-super/smali/A.smali b/test/969-iface-super/smali/A.smali deleted file mode 100644 index e7760a1062..0000000000 --- a/test/969-iface-super/smali/A.smali +++ /dev/null @@ -1,28 +0,0 @@ -# /* -# * Copyright (C) 2015 The Android Open Source Project -# * -# * Licensed under the Apache License, Version 2.0 (the "License"); -# * you may not use this file except in compliance with the License. -# * You may obtain a copy of the License at -# * -# * http://www.apache.org/licenses/LICENSE-2.0 -# * -# * Unless required by applicable law or agreed to in writing, software -# * distributed under the License is distributed on an "AS IS" BASIS, -# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# * See the License for the specific language governing permissions and -# * limitations under the License. -# */ -# -# public class A implements iface { -# } - -.class public LA; -.super Ljava/lang/Object; -.implements Liface; - -.method public constructor ()V - .registers 1 - invoke-direct {p0}, Ljava/lang/Object;->()V - return-void -.end method diff --git a/test/969-iface-super/smali/B.smali b/test/969-iface-super/smali/B.smali deleted file mode 100644 index e529d0534f..0000000000 --- a/test/969-iface-super/smali/B.smali +++ /dev/null @@ -1,28 +0,0 @@ -# /* -# * Copyright (C) 2015 The Android Open Source Project -# * -# * Licensed under the Apache License, Version 2.0 (the "License"); -# * you may not use this file except in compliance with the License. -# * You may obtain a copy of the License at -# * -# * http://www.apache.org/licenses/LICENSE-2.0 -# * -# * Unless required by applicable law or agreed to in writing, software -# * distributed under the License is distributed on an "AS IS" BASIS, -# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# * See the License for the specific language governing permissions and -# * limitations under the License. -# */ -# -# public class B implements iface2 { -# } - -.class public LB; -.super Ljava/lang/Object; -.implements Liface2; - -.method public constructor ()V - .registers 1 - invoke-direct {p0}, Ljava/lang/Object;->()V - return-void -.end method diff --git a/test/969-iface-super/smali/C.smali b/test/969-iface-super/smali/C.smali deleted file mode 100644 index 6fbb0c4b0e..0000000000 --- a/test/969-iface-super/smali/C.smali +++ /dev/null @@ -1,41 +0,0 @@ -# /* -# * Copyright (C) 2015 The Android Open Source Project -# * -# * Licensed under the Apache License, Version 2.0 (the "License"); -# * you may not use this file except in compliance with the License. -# * You may obtain a copy of the License at -# * -# * http://www.apache.org/licenses/LICENSE-2.0 -# * -# * Unless required by applicable law or agreed to in writing, software -# * distributed under the License is distributed on an "AS IS" BASIS, -# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# * See the License for the specific language governing permissions and -# * limitations under the License. -# */ -# -# public class C implements iface { -# public String SayHi() { -# return iface.super.SayHi() + " and welcome "; -# } -# } - -.class public LC; -.super Ljava/lang/Object; -.implements Liface; - -.method public constructor ()V - .registers 1 - invoke-direct {p0}, Ljava/lang/Object;->()V - return-void -.end method - -.method public SayHi()Ljava/lang/String; - .locals 2 - invoke-super {p0}, Liface;->SayHi()Ljava/lang/String; - move-result-object v0 - const-string v1, " and welcome " - invoke-virtual {v0, v1}, Ljava/lang/String;->concat(Ljava/lang/String;)Ljava/lang/String; - move-result-object v0 - return-object v0 -.end method diff --git a/test/969-iface-super/smali/D.smali b/test/969-iface-super/smali/D.smali deleted file mode 100644 index ecd4629584..0000000000 --- a/test/969-iface-super/smali/D.smali +++ /dev/null @@ -1,41 +0,0 @@ -# /* -# * Copyright (C) 2015 The Android Open Source Project -# * -# * Licensed under the Apache License, Version 2.0 (the "License"); -# * you may not use this file except in compliance with the License. -# * You may obtain a copy of the License at -# * -# * http://www.apache.org/licenses/LICENSE-2.0 -# * -# * Unless required by applicable law or agreed to in writing, software -# * distributed under the License is distributed on an "AS IS" BASIS, -# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# * See the License for the specific language governing permissions and -# * limitations under the License. -# */ -# -# public class D implements iface2 { -# public String SayHi() { -# return iface2.super.SayHi() + " and welcome "; -# } -# } - -.class public LD; -.super Ljava/lang/Object; -.implements Liface2; - -.method public constructor ()V - .registers 1 - invoke-direct {p0}, Ljava/lang/Object;->()V - return-void -.end method - -.method public SayHi()Ljava/lang/String; - .locals 2 - invoke-super {p0}, Liface2;->SayHi()Ljava/lang/String; - move-result-object v0 - const-string v1, " and welcome " - invoke-virtual {v0, v1}, Ljava/lang/String;->concat(Ljava/lang/String;)Ljava/lang/String; - move-result-object v0 - return-object v0 -.end method diff --git a/test/969-iface-super/smali/E.smali b/test/969-iface-super/smali/E.smali deleted file mode 100644 index 558aaea5d6..0000000000 --- a/test/969-iface-super/smali/E.smali +++ /dev/null @@ -1,41 +0,0 @@ -# /* -# * Copyright (C) 2015 The Android Open Source Project -# * -# * Licensed under the Apache License, Version 2.0 (the "License"); -# * you may not use this file except in compliance with the License. -# * You may obtain a copy of the License at -# * -# * http://www.apache.org/licenses/LICENSE-2.0 -# * -# * Unless required by applicable law or agreed to in writing, software -# * distributed under the License is distributed on an "AS IS" BASIS, -# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# * See the License for the specific language governing permissions and -# * limitations under the License. -# */ -# -# public class E implements iface3 { -# public String SayHi() { -# return iface3.super.SayHi() + " there!"; -# } -# } - -.class public LE; -.super Ljava/lang/Object; -.implements Liface3; - -.method public constructor ()V - .registers 1 - invoke-direct {p0}, Ljava/lang/Object;->()V - return-void -.end method - -.method public SayHi()Ljava/lang/String; - .locals 2 - invoke-super {p0}, Liface3;->SayHi()Ljava/lang/String; - move-result-object v0 - const-string v1, " there!" - invoke-virtual {v0, v1}, Ljava/lang/String;->concat(Ljava/lang/String;)Ljava/lang/String; - move-result-object v0 - return-object v0 -.end method diff --git a/test/969-iface-super/smali/F.smali b/test/969-iface-super/smali/F.smali deleted file mode 100644 index c402d5cdc9..0000000000 --- a/test/969-iface-super/smali/F.smali +++ /dev/null @@ -1,40 +0,0 @@ -# /* -# * Copyright (C) 2015 The Android Open Source Project -# * -# * Licensed under the Apache License, Version 2.0 (the "License"); -# * you may not use this file except in compliance with the License. -# * You may obtain a copy of the License at -# * -# * http://www.apache.org/licenses/LICENSE-2.0 -# * -# * Unless required by applicable law or agreed to in writing, software -# * distributed under the License is distributed on an "AS IS" BASIS, -# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# * See the License for the specific language governing permissions and -# * limitations under the License. -# */ -# -# public class F extends E { -# public String SaySurprisedHi() { -# return super.SayHi() + "!"; -# } -# } - -.class public LF; -.super LE; - -.method public constructor ()V - .registers 1 - invoke-direct {p0}, LE;->()V - return-void -.end method - -.method public SaySurprisedHi()Ljava/lang/String; - .registers 2 - invoke-super {p0}, LE;->SayHi()Ljava/lang/String; - move-result-object v0 - const-string v1, "!" - invoke-virtual {v0, v1}, Ljava/lang/String;->concat(Ljava/lang/String;)Ljava/lang/String; - move-result-object v0 - return-object v0 -.end method diff --git a/test/969-iface-super/smali/G.smali b/test/969-iface-super/smali/G.smali deleted file mode 100644 index 45705e6d86..0000000000 --- a/test/969-iface-super/smali/G.smali +++ /dev/null @@ -1,53 +0,0 @@ -# /* -# * Copyright (C) 2015 The Android Open Source Project -# * -# * Licensed under the Apache License, Version 2.0 (the "License"); -# * you may not use this file except in compliance with the License. -# * You may obtain a copy of the License at -# * -# * http://www.apache.org/licenses/LICENSE-2.0 -# * -# * Unless required by applicable law or agreed to in writing, software -# * distributed under the License is distributed on an "AS IS" BASIS, -# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# * See the License for the specific language governing permissions and -# * limitations under the License. -# */ -# -# public class G extends F { -# public String SayHi() { -# return super.SayHi() + "?"; -# } -# public String SayVerySurprisedHi() { -# return super.SaySurprisedHi() + "!"; -# } -# } - -.class public LG; -.super LF; - -.method public constructor ()V - .registers 1 - invoke-direct {p0}, LF;->()V - return-void -.end method - -.method public SayHi()Ljava/lang/String; - .locals 2 - invoke-super {p0}, LF;->SayHi()Ljava/lang/String; - move-result-object v0 - const-string v1, "?" - invoke-virtual {v0, v1}, Ljava/lang/String;->concat(Ljava/lang/String;)Ljava/lang/String; - move-result-object v0 - return-object v0 -.end method - -.method public SayVerySurprisedHi()Ljava/lang/String; - .locals 2 - invoke-super {p0}, LF;->SaySurprisedHi()Ljava/lang/String; - move-result-object v0 - const-string v1, "!" - invoke-virtual {v0, v1}, Ljava/lang/String;->concat(Ljava/lang/String;)Ljava/lang/String; - move-result-object v0 - return-object v0 -.end method diff --git a/test/969-iface-super/smali/H.smali b/test/969-iface-super/smali/H.smali deleted file mode 100644 index 12f246b8c3..0000000000 --- a/test/969-iface-super/smali/H.smali +++ /dev/null @@ -1,66 +0,0 @@ -# /* -# * Copyright (C) 2015 The Android Open Source Project -# * -# * Licensed under the Apache License, Version 2.0 (the "License"); -# * you may not use this file except in compliance with the License. -# * You may obtain a copy of the License at -# * -# * http://www.apache.org/licenses/LICENSE-2.0 -# * -# * Unless required by applicable law or agreed to in writing, software -# * distributed under the License is distributed on an "AS IS" BASIS, -# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# * See the License for the specific language governing permissions and -# * limitations under the License. -# */ -# -# public class H extends A { -# public String SayHi() { -# return super.SayHi() + "?"; -# } -# public String SaySurprisedHi() { -# return super.SayHi() + "!"; -# } -# public String SayConfusedHi() { -# return SayHi() + "!"; -# } -# } - -.class public LH; -.super LA; - -.method public constructor ()V - .registers 1 - invoke-direct {p0}, LA;->()V - return-void -.end method - -.method public SayHi()Ljava/lang/String; - .locals 2 - invoke-super {p0}, LA;->SayHi()Ljava/lang/String; - move-result-object v0 - const-string v1, "?" - invoke-virtual {v0, v1}, Ljava/lang/String;->concat(Ljava/lang/String;)Ljava/lang/String; - move-result-object v0 - return-object v0 -.end method - -.method public SaySurprisedHi()Ljava/lang/String; - .locals 2 - invoke-super {p0}, LA;->SayHi()Ljava/lang/String; - move-result-object v0 - const-string v1, "!" - invoke-virtual {v0, v1}, Ljava/lang/String;->concat(Ljava/lang/String;)Ljava/lang/String; - move-result-object v0 - return-object v0 -.end method - -.method public SayConfusedHi()Ljava/lang/String; - .locals 2 - invoke-virtual {p0}, LH;->SayHi()Ljava/lang/String; - move-result-object v0 - const-string v1, "!" - invoke-virtual {v0, v1}, Ljava/lang/String;->concat(Ljava/lang/String;)Ljava/lang/String; - move-result-object v0 - return-object v0 -.end method diff --git a/test/969-iface-super/smali/iface.smali b/test/969-iface-super/smali/iface.smali deleted file mode 100644 index 08bb93dd0c..0000000000 --- a/test/969-iface-super/smali/iface.smali +++ /dev/null @@ -1,30 +0,0 @@ -# /* -# * Copyright (C) 2015 The Android Open Source Project -# * -# * Licensed under the Apache License, Version 2.0 (the "License"); -# * you may not use this file except in compliance with the License. -# * You may obtain a copy of the License at -# * -# * http://www.apache.org/licenses/LICENSE-2.0 -# * -# * Unless required by applicable law or agreed to in writing, software -# * distributed under the License is distributed on an "AS IS" BASIS, -# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# * See the License for the specific language governing permissions and -# * limitations under the License. -# */ -# -# public interface iface { -# public default String SayHi() { -# return "Hello "; -# } -# } - -.class public abstract interface Liface; -.super Ljava/lang/Object; - -.method public SayHi()Ljava/lang/String; - .locals 1 - const-string v0, "Hello " - return-object v0 -.end method diff --git a/test/969-iface-super/smali/iface2.smali b/test/969-iface-super/smali/iface2.smali deleted file mode 100644 index ce6f86432d..0000000000 --- a/test/969-iface-super/smali/iface2.smali +++ /dev/null @@ -1,36 +0,0 @@ -# /* -# * Copyright (C) 2015 The Android Open Source Project -# * -# * Licensed under the Apache License, Version 2.0 (the "License"); -# * you may not use this file except in compliance with the License. -# * You may obtain a copy of the License at -# * -# * http://www.apache.org/licenses/LICENSE-2.0 -# * -# * Unless required by applicable law or agreed to in writing, software -# * distributed under the License is distributed on an "AS IS" BASIS, -# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# * See the License for the specific language governing permissions and -# * limitations under the License. -# */ -# -# public interface iface2 extends iface { -# public default String SayHi() { -# return iface.super.SayHi() + iface.super.SayHi(); -# } -# } - -.class public abstract interface Liface2; -.super Ljava/lang/Object; -.implements Liface; - -.method public SayHi()Ljava/lang/String; - .locals 2 - invoke-super {p0}, Liface;->SayHi()Ljava/lang/String; - move-result-object v0 - invoke-super {p0}, Liface;->SayHi()Ljava/lang/String; - move-result-object v1 - invoke-virtual {v0, v1}, Ljava/lang/String;->concat(Ljava/lang/String;)Ljava/lang/String; - move-result-object v0 - return-object v0 -.end method diff --git a/test/969-iface-super/smali/iface3.smali b/test/969-iface-super/smali/iface3.smali deleted file mode 100644 index bf200364ec..0000000000 --- a/test/969-iface-super/smali/iface3.smali +++ /dev/null @@ -1,22 +0,0 @@ -# /* -# * Copyright (C) 2015 The Android Open Source Project -# * -# * Licensed under the Apache License, Version 2.0 (the "License"); -# * you may not use this file except in compliance with the License. -# * You may obtain a copy of the License at -# * -# * http://www.apache.org/licenses/LICENSE-2.0 -# * -# * Unless required by applicable law or agreed to in writing, software -# * distributed under the License is distributed on an "AS IS" BASIS, -# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# * See the License for the specific language governing permissions and -# * limitations under the License. -# */ -# -# public interface iface3 extends iface { -# } - -.class public abstract interface Liface3; -.super Ljava/lang/Object; -.implements Liface; diff --git a/test/969-iface-super/src/A.java b/test/969-iface-super/src/A.java new file mode 100644 index 0000000000..47db14ba84 --- /dev/null +++ b/test/969-iface-super/src/A.java @@ -0,0 +1,16 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +public class A implements Iface { } diff --git a/test/969-iface-super/src/B.java b/test/969-iface-super/src/B.java new file mode 100644 index 0000000000..70f63a237a --- /dev/null +++ b/test/969-iface-super/src/B.java @@ -0,0 +1,16 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +public class B implements Iface2 { } diff --git a/test/969-iface-super/src/C.java b/test/969-iface-super/src/C.java new file mode 100644 index 0000000000..0fa0b9280b --- /dev/null +++ b/test/969-iface-super/src/C.java @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +public class C implements Iface { + public String SayHi() { + return Iface.super.SayHi() + " and welcome "; + } +} diff --git a/test/969-iface-super/src/D.java b/test/969-iface-super/src/D.java new file mode 100644 index 0000000000..8a607c3adf --- /dev/null +++ b/test/969-iface-super/src/D.java @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +public class D implements Iface2 { + public String SayHi() { + return Iface2.super.SayHi() + " and welcome "; + } +} diff --git a/test/969-iface-super/src/E.java b/test/969-iface-super/src/E.java new file mode 100644 index 0000000000..d5942b22b6 --- /dev/null +++ b/test/969-iface-super/src/E.java @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +public class E implements Iface3 { + public String SayHi() { + return Iface3.super.SayHi() + " there!"; + } +} diff --git a/test/969-iface-super/src/F.java b/test/969-iface-super/src/F.java new file mode 100644 index 0000000000..610bcb158a --- /dev/null +++ b/test/969-iface-super/src/F.java @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +public class F extends E { + public String SaySurprisedHi() { + return super.SayHi() + "!"; + } +} diff --git a/test/969-iface-super/src/G.java b/test/969-iface-super/src/G.java new file mode 100644 index 0000000000..edaf3a9b11 --- /dev/null +++ b/test/969-iface-super/src/G.java @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +public class G extends F { + public String SayHi() { + return super.SayHi() + "?"; + } + public String SayVerySurprisedHi() { + return super.SaySurprisedHi() + "!"; + } +} diff --git a/test/969-iface-super/src/H.java b/test/969-iface-super/src/H.java new file mode 100644 index 0000000000..744bda6f82 --- /dev/null +++ b/test/969-iface-super/src/H.java @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +public class H extends A { + public String SayHi() { + return super.SayHi() + "?"; + } + public String SaySurprisedHi() { + return super.SayHi() + "!"; + } + public String SayConfusedHi() { + return SayHi() + "!"; + } +} diff --git a/test/969-iface-super/src/Iface.java b/test/969-iface-super/src/Iface.java new file mode 100644 index 0000000000..ece5e592de --- /dev/null +++ b/test/969-iface-super/src/Iface.java @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +public interface Iface { + public default String SayHi() { + return "Hello "; + } +} diff --git a/test/969-iface-super/src/Iface2.java b/test/969-iface-super/src/Iface2.java new file mode 100644 index 0000000000..d74ee6ddf5 --- /dev/null +++ b/test/969-iface-super/src/Iface2.java @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +public interface Iface2 extends Iface { + public default String SayHi() { + return Iface.super.SayHi() + Iface.super.SayHi(); + } +} diff --git a/test/969-iface-super/src/Iface3.java b/test/969-iface-super/src/Iface3.java new file mode 100644 index 0000000000..10b010cb3b --- /dev/null +++ b/test/969-iface-super/src/Iface3.java @@ -0,0 +1,16 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +public interface Iface3 extends Iface { } diff --git a/test/969-iface-super/smali/classes.xml b/test/969-iface-super/src/classes.xml similarity index 85% rename from test/969-iface-super/smali/classes.xml rename to test/969-iface-super/src/classes.xml index 4d205bd606..4c3dae4fa0 100644 --- a/test/969-iface-super/smali/classes.xml +++ b/test/969-iface-super/src/classes.xml @@ -18,35 +18,35 @@ - iface + Iface - iface2 + Iface2 - iface + Iface - iface2 + Iface2 - iface3 + Iface3 @@ -75,23 +75,23 @@ - + SayHi - + - iface + Iface - + - iface + Iface diff --git a/test/utils/python/generate_smali_main.py b/test/utils/python/generate_java_main.py similarity index 67% rename from test/utils/python/generate_smali_main.py rename to test/utils/python/generate_java_main.py index d796d313c6..f66d0dd372 100755 --- a/test/utils/python/generate_smali_main.py +++ b/test/utils/python/generate_java_main.py @@ -15,7 +15,7 @@ # limitations under the License. """ -Generate Smali Main file from a classes.xml file. +Generate Java Main file from a classes.xml file. """ import os @@ -38,48 +38,27 @@ import itertools import functools import xml.etree.ElementTree as ET -class MainClass(mixins.DumpMixin, mixins.Named, mixins.SmaliFileMixin): +class MainClass(mixins.DumpMixin, mixins.Named, mixins.JavaFileMixin): """ A mainclass and main method for this test. """ MAIN_CLASS_TEMPLATE = """{copyright} -.class public LMain; -.super Ljava/lang/Object; - -# class Main {{ - -.method public constructor ()V - .registers 1 - invoke-direct {{p0}}, Ljava/lang/Object;->()V - return-void -.end method - +class Main {{ {test_groups} - {test_funcs} - {main_func} - -# }} +}} """ MAIN_FUNCTION_TEMPLATE = """ -# public static void main(String[] args) {{ -.method public static main([Ljava/lang/String;)V - .locals 2 - sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream; - + public static void main(String[] args) {{ {test_group_invoke} - - return-void -.end method -# }} + }} """ TEST_GROUP_INVOKE_TEMPLATE = """ -# {test_name}(); - invoke-static {{}}, {test_name}()V + {test_name}(); """ def __init__(self): @@ -123,7 +102,7 @@ class MainClass(mixins.DumpMixin, mixins.Named, mixins.SmaliFileMixin): funcs = "" for f in self.global_funcs: funcs += str(f) - return self.MAIN_CLASS_TEMPLATE.format(copyright = get_copyright('smali'), + return self.MAIN_CLASS_TEMPLATE.format(copyright = get_copyright('java'), test_groups=test_groups, main_func=main_func, test_funcs=funcs) @@ -135,33 +114,19 @@ class InstanceTest(mixins.Named, mixins.NameComparableMixin): """ INSTANCE_TEST_TEMPLATE = """ -# public static void {test_name}() {{ -# System.out.println("Testing for type {ty}"); -# String s = "{ty}"; -# {ty} v = new {ty}(); -.method public static {test_name}()V - .locals 3 - sget-object v2, Ljava/lang/System;->out:Ljava/io/PrintStream; - const-string v0, "Testing for type {ty}" - invoke-virtual {{v2,v0}}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - const-string v0, "{ty}" - new-instance v1, L{ty}; - invoke-direct {{v1}}, L{ty};->()V + public static void {test_name}() {{ + System.out.println("Testing for type {ty}"); + String s = "{ty}"; + {ty} v = new {ty}(); {invokes} - const-string v0, "End testing for type {ty}" - invoke-virtual {{v2,v0}}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - return-void -.end method -# System.out.println("End testing for type {ty}"); -# }} + System.out.println("End testing for type {ty}"); + }} """ TEST_INVOKE_TEMPLATE = """ -# {fname}(s, v); - invoke-static {{v0, v1}}, {fname}(Ljava/lang/String;L{farg};)V + {fname}(s, v); """ def __init__(self, main, ty): @@ -188,7 +153,7 @@ class InstanceTest(mixins.Named, mixins.NameComparableMixin): def __str__(self): """ - Returns the smali code for this function + Returns the java code for this function """ func_invokes = "" for f in sorted(self.funcs, key=lambda a: (a.func, a.farg)): @@ -204,47 +169,15 @@ class Func(mixins.Named, mixins.NameComparableMixin): """ TEST_FUNCTION_TEMPLATE = """ -# public static void {fname}(String s, {farg} v) {{ -# try {{ -# System.out.printf("%s-{invoke_type:<9} {farg:>9}.{callfunc}()='%s'\\n", s, v.{callfunc}()); -# return; -# }} catch (Error e) {{ -# System.out.printf("%s-{invoke_type} on {farg}: {callfunc}() threw exception!\\n", s); -# e.printStackTrace(System.out); -# }} -# }} -.method public static {fname}(Ljava/lang/String;L{farg};)V - .locals 7 - :call_{fname}_try_start - const/4 v0, 2 - new-array v1,v0, [Ljava/lang/Object; - const/4 v0, 0 - aput-object p0,v1,v0 - - sget-object v2, Ljava/lang/System;->out:Ljava/io/PrintStream; - const-string v3, "%s-{invoke_type:<9} {farg:>9}.{callfunc}()='%s'\\n" - - invoke-{invoke_type} {{p1}}, L{farg};->{callfunc}()Ljava/lang/String; - move-result-object v4 - const/4 v0, 1 - aput-object v4, v1, v0 - - invoke-virtual {{v2,v3,v1}}, Ljava/io/PrintStream;->printf(Ljava/lang/String;[Ljava/lang/Object;)Ljava/io/PrintStream; - return-void - :call_{fname}_try_end - .catch Ljava/lang/Error; {{:call_{fname}_try_start .. :call_{fname}_try_end}} :error_{fname}_start - :error_{fname}_start - move-exception v3 - const/4 v0, 1 - new-array v1,v0, [Ljava/lang/Object; - const/4 v0, 0 - aput-object p0, v1, v0 - sget-object v2, Ljava/lang/System;->out:Ljava/io/PrintStream; - const-string v4, "%s-{invoke_type} on {farg}: {callfunc}() threw exception!\\n" - invoke-virtual {{v2,v4,v1}}, Ljava/io/PrintStream;->printf(Ljava/lang/String;[Ljava/lang/Object;)Ljava/io/PrintStream; - invoke-virtual {{v3,v2}}, Ljava/lang/Error;->printStackTrace(Ljava/io/PrintStream;)V - return-void -.end method + public static void {fname}(String s, {farg} v) {{ + try {{ + System.out.printf("%s-{invoke_type:<9} {farg:>9}.{callfunc}()='%s'\\n", s, v.{callfunc}()); + return; + }} catch (Error e) {{ + System.out.printf("%s-{invoke_type} on {farg}: {callfunc}() threw exception!\\n", s); + e.printStackTrace(System.out); + }} + }} """ def __init__(self, func, farg, invoke): @@ -263,7 +196,7 @@ class Func(mixins.Named, mixins.NameComparableMixin): def __str__(self): """ - Get the smali code for this test function + Get the java code for this test function """ return self.TEST_FUNCTION_TEMPLATE.format(fname=self.get_name(), farg=self.farg, @@ -307,7 +240,7 @@ def flatten_interface_methods(dat, i): def make_main_class(dat): """ - Creates a Main.smali file that runs all the tests + Creates a Main.java file that runs all the tests """ m = MainClass() for c in dat.classes.values(): @@ -365,12 +298,12 @@ def parse_xml(xml): return TestData(classes, ifaces) def main(argv): - smali_dir = Path(argv[1]) - if not smali_dir.exists() or not smali_dir.is_dir(): - print("{} is not a valid smali dir".format(smali_dir), file=sys.stderr) + java_dir = Path(argv[1]) + if not java_dir.exists() or not java_dir.is_dir(): + print("{} is not a valid java dir".format(java_dir), file=sys.stderr) sys.exit(1) - class_data = parse_xml((smali_dir / "classes.xml").open().read()) - make_main_class(class_data).dump(smali_dir) + class_data = parse_xml((java_dir / "classes.xml").open().read()) + make_main_class(class_data).dump(java_dir) if __name__ == '__main__': main(sys.argv) -- GitLab From 2714fe681fa9850bcbe3e2d3c3c72a5c77ca26b8 Mon Sep 17 00:00:00 2001 From: Chris Larsen Date: Thu, 11 Feb 2016 14:23:53 -0800 Subject: [PATCH 114/204] MIPS32: Implement isInfinite intrinsics. - boolean java.lang.Float.isInfinite(float) - boolean java.lang.Double.isInfinite(double) Change-Id: I17dc2380ec864fd7612025ed400e29dd115ccab4 --- compiler/optimizing/intrinsics_mips.cc | 65 +++++++++++++++- test/082-inline-execute/src/Main.java | 104 +++++++++++++++++++++++++ 2 files changed, 166 insertions(+), 3 deletions(-) diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc index 5a35dd57a4..78b400dfef 100644 --- a/compiler/optimizing/intrinsics_mips.cc +++ b/compiler/optimizing/intrinsics_mips.cc @@ -1502,6 +1502,66 @@ void IntrinsicCodeGeneratorMIPS::VisitStringEquals(HInvoke* invoke) { __ Bind(&end); } +static void GenIsInfinite(LocationSummary* locations, + const Primitive::Type type, + const bool isR6, + MipsAssembler* assembler) { + FRegister in = locations->InAt(0).AsFpuRegister(); + Register out = locations->Out().AsRegister(); + + DCHECK(type == Primitive::kPrimFloat || type == Primitive::kPrimDouble); + + if (isR6) { + if (type == Primitive::kPrimDouble) { + __ ClassD(FTMP, in); + } else { + __ ClassS(FTMP, in); + } + __ Mfc1(out, FTMP); + __ Andi(out, out, kPositiveInfinity | kNegativeInfinity); + __ Sltu(out, ZERO, out); + } else { + // If one, or more, of the exponent bits is zero, then the number can't be infinite. + if (type == Primitive::kPrimDouble) { + __ MoveFromFpuHigh(TMP, in); + __ LoadConst32(AT, 0x7FF00000); + } else { + __ Mfc1(TMP, in); + __ LoadConst32(AT, 0x7F800000); + } + __ Xor(TMP, TMP, AT); + + __ Sll(TMP, TMP, 1); + + if (type == Primitive::kPrimDouble) { + __ Mfc1(AT, in); + __ Or(TMP, TMP, AT); + } + // If any of the significand bits are one, then the number is not infinite. + __ Sltiu(out, TMP, 1); + } +} + +// boolean java.lang.Float.isInfinite(float) +void IntrinsicLocationsBuilderMIPS::VisitFloatIsInfinite(HInvoke* invoke) { + CreateFPToIntLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitFloatIsInfinite(HInvoke* invoke) { + GenIsInfinite(invoke->GetLocations(), Primitive::kPrimFloat, IsR6(), GetAssembler()); +} + +// boolean java.lang.Double.isInfinite(double) +void IntrinsicLocationsBuilderMIPS::VisitDoubleIsInfinite(HInvoke* invoke) { + CreateFPToIntLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitDoubleIsInfinite(HInvoke* invoke) { + GenIsInfinite(invoke->GetLocations(), Primitive::kPrimDouble, IsR6(), GetAssembler()); +} + +// Unimplemented intrinsics. + UNIMPLEMENTED_INTRINSIC(MIPS, IntegerBitCount) UNIMPLEMENTED_INTRINSIC(MIPS, LongBitCount) @@ -1559,9 +1619,6 @@ UNIMPLEMENTED_INTRINSIC(MIPS, MathSinh) UNIMPLEMENTED_INTRINSIC(MIPS, MathTan) UNIMPLEMENTED_INTRINSIC(MIPS, MathTanh) -UNIMPLEMENTED_INTRINSIC(MIPS, FloatIsInfinite) -UNIMPLEMENTED_INTRINSIC(MIPS, DoubleIsInfinite) - UNIMPLEMENTED_INTRINSIC(MIPS, IntegerHighestOneBit) UNIMPLEMENTED_INTRINSIC(MIPS, LongHighestOneBit) UNIMPLEMENTED_INTRINSIC(MIPS, IntegerLowestOneBit) @@ -1569,6 +1626,8 @@ UNIMPLEMENTED_INTRINSIC(MIPS, LongLowestOneBit) UNREACHABLE_INTRINSICS(MIPS) +#undef UNIMPLEMENTED_INTRINSIC + #undef __ } // namespace mips diff --git a/test/082-inline-execute/src/Main.java b/test/082-inline-execute/src/Main.java index 93a9005fe0..9aaed9d589 100644 --- a/test/082-inline-execute/src/Main.java +++ b/test/082-inline-execute/src/Main.java @@ -40,6 +40,10 @@ public class Main { test_Math_rint(); test_Math_round_D(); test_Math_round_F(); + test_Math_isNaN_D(); + test_Math_isNaN_F(); + test_Math_isInfinite_D(); + test_Math_isInfinite_F(); test_Short_reverseBytes(); test_Integer_reverseBytes(); test_Long_reverseBytes(); @@ -836,6 +840,106 @@ public class Main { Assert.assertEquals(Math.round(Float.NEGATIVE_INFINITY), Integer.MIN_VALUE); } + public static void test_Math_isNaN_D() { + // Quiet NaN. + Assert.assertTrue(Double.isNaN(Double.longBitsToDouble(0x7FF4000000000000l))); + Assert.assertTrue(Double.isNaN(Double.longBitsToDouble(0xFFF4000000000000l))); + // Signaling NaN. + Assert.assertTrue(Double.isNaN(Double.longBitsToDouble(0x7FF8000000000000l))); + Assert.assertTrue(Double.isNaN(Double.longBitsToDouble(0xFFF8000000000000l))); + // Distinct from +/- infinity. + Assert.assertFalse(Double.isNaN(Double.longBitsToDouble(0x7FF0000000000000l))); + Assert.assertFalse(Double.isNaN(Double.longBitsToDouble(0xFFF0000000000000l))); + // Distinct from normal numbers. + Assert.assertFalse(Double.isNaN(Double.longBitsToDouble(0x7FE0000000000000l))); + Assert.assertFalse(Double.isNaN(Double.longBitsToDouble(0xFFE0000000000000l))); + Assert.assertFalse(Double.isNaN(Double.longBitsToDouble(0x0010000000000000l))); + Assert.assertFalse(Double.isNaN(Double.longBitsToDouble(0x8010000000000000l))); + // Distinct from +/- zero. + Assert.assertFalse(Double.isNaN(Double.longBitsToDouble(0x0000000000000000l))); + Assert.assertFalse(Double.isNaN(Double.longBitsToDouble(0x8000000000000000l))); + // Distinct from subnormal numbers. + Assert.assertFalse(Double.isNaN(Double.longBitsToDouble(0x0008000000000000l))); + Assert.assertFalse(Double.isNaN(Double.longBitsToDouble(0x8008000000000000l))); + Assert.assertFalse(Double.isNaN(Double.longBitsToDouble(0x0000000000000001l))); + Assert.assertFalse(Double.isNaN(Double.longBitsToDouble(0x8000000000000001l))); + } + + public static void test_Math_isNaN_F() { + // Quiet NaN. + Assert.assertTrue(Float.isNaN(Float.intBitsToFloat(0x7FA00000))); + Assert.assertTrue(Float.isNaN(Float.intBitsToFloat(0xFFA00000))); + // Signaling NaN. + Assert.assertTrue(Float.isNaN(Float.intBitsToFloat(0x7FC00000))); + Assert.assertTrue(Float.isNaN(Float.intBitsToFloat(0xFFC00000))); + // Distinct from +/- infinity. + Assert.assertFalse(Float.isNaN(Float.intBitsToFloat(0x7F800000))); + Assert.assertFalse(Float.isNaN(Float.intBitsToFloat(0xFF800000))); + // Distinct from normal numbers. + Assert.assertFalse(Float.isNaN(Float.intBitsToFloat(0x7F000000))); + Assert.assertFalse(Float.isNaN(Float.intBitsToFloat(0xFF000000))); + Assert.assertFalse(Float.isNaN(Float.intBitsToFloat(0x00800000))); + Assert.assertFalse(Float.isNaN(Float.intBitsToFloat(0x80800000))); + // Distinct from +/- zero. + Assert.assertFalse(Float.isNaN(Float.intBitsToFloat(0x00000000))); + Assert.assertFalse(Float.isNaN(Float.intBitsToFloat(0x80000000))); + // Distinct from subnormal numbers. + Assert.assertFalse(Float.isNaN(Float.intBitsToFloat(0x00400000))); + Assert.assertFalse(Float.isNaN(Float.intBitsToFloat(0x80400000))); + Assert.assertFalse(Float.isNaN(Float.intBitsToFloat(0x00000001))); + Assert.assertFalse(Float.isNaN(Float.intBitsToFloat(0x80000001))); + } + + public static void test_Math_isInfinite_D() { + // Distinct from Quiet NaN. + Assert.assertFalse(Double.isInfinite(Double.longBitsToDouble(0x7FF4000000000000l))); + Assert.assertFalse(Double.isInfinite(Double.longBitsToDouble(0xFFF4000000000000l))); + // Distinct from Signaling NaN. + Assert.assertFalse(Double.isInfinite(Double.longBitsToDouble(0x7FF8000000000000l))); + Assert.assertFalse(Double.isInfinite(Double.longBitsToDouble(0xFFF8000000000000l))); + // +/- infinity. + Assert.assertTrue(Double.isInfinite(Double.longBitsToDouble(0x7FF0000000000000l))); + Assert.assertTrue(Double.isInfinite(Double.longBitsToDouble(0xFFF0000000000000l))); + // Distinct from normal numbers. + Assert.assertFalse(Double.isInfinite(Double.longBitsToDouble(0x7FE0000000000000l))); + Assert.assertFalse(Double.isInfinite(Double.longBitsToDouble(0xFFE0000000000000l))); + Assert.assertFalse(Double.isInfinite(Double.longBitsToDouble(0x0010000000000000l))); + Assert.assertFalse(Double.isInfinite(Double.longBitsToDouble(0x8010000000000000l))); + // Distinct from +/- zero. + Assert.assertFalse(Double.isInfinite(Double.longBitsToDouble(0x0000000000000000l))); + Assert.assertFalse(Double.isInfinite(Double.longBitsToDouble(0x8000000000000000l))); + // Distinct from subnormal numbers. + Assert.assertFalse(Double.isInfinite(Double.longBitsToDouble(0x0008000000000000l))); + Assert.assertFalse(Double.isInfinite(Double.longBitsToDouble(0x8008000000000000l))); + Assert.assertFalse(Double.isInfinite(Double.longBitsToDouble(0x0000000000000001l))); + Assert.assertFalse(Double.isInfinite(Double.longBitsToDouble(0x8000000000000001l))); + } + + public static void test_Math_isInfinite_F() { + // Distinct from Quiet NaN. + Assert.assertFalse(Float.isInfinite(Float.intBitsToFloat(0x7FA00000))); + Assert.assertFalse(Float.isInfinite(Float.intBitsToFloat(0xFFA00000))); + // Distinct from Signaling NaN. + Assert.assertFalse(Float.isInfinite(Float.intBitsToFloat(0x7FC00000))); + Assert.assertFalse(Float.isInfinite(Float.intBitsToFloat(0xFFC00000))); + // +/- infinity. + Assert.assertTrue(Float.isInfinite(Float.intBitsToFloat(0x7F800000))); + Assert.assertTrue(Float.isInfinite(Float.intBitsToFloat(0xFF800000))); + // Distinct from normal numbers. + Assert.assertFalse(Float.isInfinite(Float.intBitsToFloat(0x7F000000))); + Assert.assertFalse(Float.isInfinite(Float.intBitsToFloat(0xFF000000))); + Assert.assertFalse(Float.isInfinite(Float.intBitsToFloat(0x00800000))); + Assert.assertFalse(Float.isInfinite(Float.intBitsToFloat(0x80800000))); + // Distinct from +/- zero. + Assert.assertFalse(Float.isInfinite(Float.intBitsToFloat(0x00000000))); + Assert.assertFalse(Float.isInfinite(Float.intBitsToFloat(0x80000000))); + // Distinct from subnormal numbers. + Assert.assertFalse(Float.isInfinite(Float.intBitsToFloat(0x00400000))); + Assert.assertFalse(Float.isInfinite(Float.intBitsToFloat(0x80400000))); + Assert.assertFalse(Float.isInfinite(Float.intBitsToFloat(0x00000001))); + Assert.assertFalse(Float.isInfinite(Float.intBitsToFloat(0x80000001))); + } + public static void test_StrictMath_abs_I() { StrictMath.abs(-1); Assert.assertEquals(StrictMath.abs(0), 0); -- GitLab From 7211aa62b2d000ad0e1e1a49bc30b962c2c069cd Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Wed, 2 Mar 2016 19:23:17 -0800 Subject: [PATCH 115/204] Add systrace to VerifyClass Useful to see if verifier is preempting anything. (cherry picked from commit 7f459f5859e5f01c92c3711e7d63c062f510b416) Change-Id: I594cbdc5c2f8934fc6c4fc7ee47fc4ccfc3c627d --- runtime/verifier/method_verifier.cc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 0c6060e4e8..f71ebfe8b7 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -16,6 +16,9 @@ #include "method_verifier-inl.h" +#define ATRACE_TAG ATRACE_TAG_DALVIK +#include + #include #include "art_field-inl.h" @@ -283,6 +286,7 @@ MethodVerifier::FailureKind MethodVerifier::VerifyClass(Thread* self, // empty class, probably a marker interface return kNoFailure; } + ATRACE_BEGIN("VerifyClass"); ClassDataItemIterator it(*dex_file, class_data); while (it.HasNextStaticField() || it.HasNextInstanceField()) { it.Next(); @@ -317,6 +321,8 @@ MethodVerifier::FailureKind MethodVerifier::VerifyClass(Thread* self, data1.Merge(data2); + ATRACE_END(); + if (data1.kind == kNoFailure) { return kNoFailure; } else { -- GitLab From 9a3be989d0aee1a6998e33813c7f70906d27f89a Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Thu, 3 Mar 2016 23:14:49 +0000 Subject: [PATCH 116/204] ProfilingInfo roots should be visited by the declaring class. There seems to be an implicit assumption that only classes can visit native roots (ie kVisitNativeRoots in Class::VisitReferences). However, some places like Dbg::VisitRoots and AllocRecordObjectMap::VisitRoots visit ArtMethod::VisitRoot directly, and that breaks the assumptions. bug:27435111 Change-Id: I5b476e614ba820394635d946cb562bf872a50e7e --- runtime/art_method-inl.h | 6 ------ runtime/mirror/class-inl.h | 7 +++++++ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h index ebe89bbbd2..12d6d8f015 100644 --- a/runtime/art_method-inl.h +++ b/runtime/art_method-inl.h @@ -463,12 +463,6 @@ void ArtMethod::VisitRoots(RootVisitorType& visitor, size_t pointer_size) { interface_method->VisitRoots(visitor, pointer_size); } visitor.VisitRoot(declaring_class_.AddressWithoutBarrier()); - if (!IsNative()) { - ProfilingInfo* profiling_info = GetProfilingInfo(pointer_size); - if (profiling_info != nullptr) { - profiling_info->VisitRoots(visitor); - } - } } } diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h index 19584edf7f..cbcb4b964b 100644 --- a/runtime/mirror/class-inl.h +++ b/runtime/mirror/class-inl.h @@ -29,6 +29,7 @@ #include "dex_cache.h" #include "dex_file.h" #include "gc/heap-inl.h" +#include "jit/profiling_info.h" #include "iftable.h" #include "object_array-inl.h" #include "read_barrier-inl.h" @@ -939,6 +940,12 @@ void mirror::Class::VisitNativeRoots(Visitor& visitor, size_t pointer_size) { } for (ArtMethod& method : GetMethods(pointer_size)) { method.VisitRoots(visitor, pointer_size); + if (!method.IsNative()) { + ProfilingInfo* profiling_info = method.GetProfilingInfo(pointer_size); + if (profiling_info != nullptr) { + profiling_info->VisitRoots(visitor); + } + } } } -- GitLab From 9ea02c4f01b079bf6a5f49c62cb59fa5791cbc60 Mon Sep 17 00:00:00 2001 From: Hiroshi Yamauchi Date: Thu, 3 Mar 2016 15:09:27 -0800 Subject: [PATCH 117/204] Add ScopedGCCriticalSection to Trace::StopTracing. This attempts to fix a deadlock in 545-tracing-and-jit with the read barrier config. I think the cause is the same as what CL 174096 describes and it missed this one in Trace::StopTracing. Bug: 26429931 Bug: 12687968 Change-Id: I02ca45e2667c9f6afc27d73919d6c31d204cc621 --- runtime/trace.cc | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/runtime/trace.cc b/runtime/trace.cc index 99b2296b60..6b826414bd 100644 --- a/runtime/trace.cc +++ b/runtime/trace.cc @@ -389,9 +389,10 @@ void Trace::StopTracing(bool finish_tracing, bool flush_file) { bool stop_alloc_counting = false; Runtime* const runtime = Runtime::Current(); Trace* the_trace = nullptr; + Thread* const self = Thread::Current(); pthread_t sampling_pthread = 0U; { - MutexLock mu(Thread::Current(), *Locks::trace_lock_); + MutexLock mu(self, *Locks::trace_lock_); if (the_trace_ == nullptr) { LOG(ERROR) << "Trace stop requested, but no trace currently running"; } else { @@ -409,6 +410,9 @@ void Trace::StopTracing(bool finish_tracing, bool flush_file) { } { + gc::ScopedGCCriticalSection gcs(self, + gc::kGcCauseInstrumentation, + gc::kCollectorTypeInstrumentation); ScopedSuspendAll ssa(__FUNCTION__); if (the_trace != nullptr) { stop_alloc_counting = (the_trace->flags_ & Trace::kTraceCountAllocs) != 0; @@ -417,7 +421,7 @@ void Trace::StopTracing(bool finish_tracing, bool flush_file) { } if (the_trace->trace_mode_ == TraceMode::kSampling) { - MutexLock mu(Thread::Current(), *Locks::thread_list_lock_); + MutexLock mu(self, *Locks::thread_list_lock_); runtime->GetThreadList()->ForEach(ClearThreadStackTraceAndClockBase, nullptr); } else { runtime->GetInstrumentation()->DisableMethodTracing(kTracerInstrumentationKey); -- GitLab From edc16451dc62df5487a49f62aec36f9c1df77d30 Mon Sep 17 00:00:00 2001 From: Chris Larsen Date: Fri, 12 Feb 2016 17:59:00 -0800 Subject: [PATCH 118/204] MIPS32: Implement bitCount intrinsics. - int java.lang.Integer.bitCount(int) - int java.lang.Long.bitCount(long) Change-Id: Ibde089675064aa052cfcd9f53aa36c25a4cbfca9 --- compiler/optimizing/intrinsics_mips.cc | 139 ++++++++++++++++++++++++- 1 file changed, 136 insertions(+), 3 deletions(-) diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc index 78b400dfef..655fbb18f4 100644 --- a/compiler/optimizing/intrinsics_mips.cc +++ b/compiler/optimizing/intrinsics_mips.cc @@ -643,6 +643,142 @@ static void CreateFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) { locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); } +static void GenBitCount(LocationSummary* locations, + Primitive::Type type, + bool isR6, + MipsAssembler* assembler) { + DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong); + + Register out = locations->Out().AsRegister(); + + // https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel + // + // A generalization of the best bit counting method to integers of + // bit-widths up to 128 (parameterized by type T) is this: + // + // v = v - ((v >> 1) & (T)~(T)0/3); // temp + // v = (v & (T)~(T)0/15*3) + ((v >> 2) & (T)~(T)0/15*3); // temp + // v = (v + (v >> 4)) & (T)~(T)0/255*15; // temp + // c = (T)(v * ((T)~(T)0/255)) >> (sizeof(T) - 1) * BITS_PER_BYTE; // count + // + // For comparison, for 32-bit quantities, this algorithm can be executed + // using 20 MIPS instructions (the calls to LoadConst32() generate two + // machine instructions each for the values being used in this algorithm). + // A(n unrolled) loop-based algorithm required 25 instructions. + // + // For 64-bit quantities, this algorithm gets executed twice, (once + // for in_lo, and again for in_hi), but saves a few instructions + // because the mask values only have to be loaded once. Using this + // algorithm the count for a 64-bit operand can be performed in 33 + // instructions compared to a loop-based algorithm which required 47 + // instructions. + + if (type == Primitive::kPrimInt) { + Register in = locations->InAt(0).AsRegister(); + + __ Srl(TMP, in, 1); + __ LoadConst32(AT, 0x55555555); + __ And(TMP, TMP, AT); + __ Subu(TMP, in, TMP); + __ LoadConst32(AT, 0x33333333); + __ And(out, TMP, AT); + __ Srl(TMP, TMP, 2); + __ And(TMP, TMP, AT); + __ Addu(TMP, out, TMP); + __ Srl(out, TMP, 4); + __ Addu(out, out, TMP); + __ LoadConst32(AT, 0x0F0F0F0F); + __ And(out, out, AT); + __ LoadConst32(TMP, 0x01010101); + if (isR6) { + __ MulR6(out, out, TMP); + } else { + __ MulR2(out, out, TMP); + } + __ Srl(out, out, 24); + } else if (type == Primitive::kPrimLong) { + Register in_lo = locations->InAt(0).AsRegisterPairLow(); + Register in_hi = locations->InAt(0).AsRegisterPairHigh(); + Register tmp_hi = locations->GetTemp(0).AsRegister(); + Register out_hi = locations->GetTemp(1).AsRegister(); + Register tmp_lo = TMP; + Register out_lo = out; + + __ Srl(tmp_lo, in_lo, 1); + __ Srl(tmp_hi, in_hi, 1); + + __ LoadConst32(AT, 0x55555555); + + __ And(tmp_lo, tmp_lo, AT); + __ Subu(tmp_lo, in_lo, tmp_lo); + + __ And(tmp_hi, tmp_hi, AT); + __ Subu(tmp_hi, in_hi, tmp_hi); + + __ LoadConst32(AT, 0x33333333); + + __ And(out_lo, tmp_lo, AT); + __ Srl(tmp_lo, tmp_lo, 2); + __ And(tmp_lo, tmp_lo, AT); + __ Addu(tmp_lo, out_lo, tmp_lo); + __ Srl(out_lo, tmp_lo, 4); + __ Addu(out_lo, out_lo, tmp_lo); + + __ And(out_hi, tmp_hi, AT); + __ Srl(tmp_hi, tmp_hi, 2); + __ And(tmp_hi, tmp_hi, AT); + __ Addu(tmp_hi, out_hi, tmp_hi); + __ Srl(out_hi, tmp_hi, 4); + __ Addu(out_hi, out_hi, tmp_hi); + + __ LoadConst32(AT, 0x0F0F0F0F); + + __ And(out_lo, out_lo, AT); + __ And(out_hi, out_hi, AT); + + __ LoadConst32(AT, 0x01010101); + + if (isR6) { + __ MulR6(out_lo, out_lo, AT); + + __ MulR6(out_hi, out_hi, AT); + } else { + __ MulR2(out_lo, out_lo, AT); + + __ MulR2(out_hi, out_hi, AT); + } + + __ Srl(out_lo, out_lo, 24); + __ Srl(out_hi, out_hi, 24); + + __ Addu(out, out_hi, out_lo); + } +} + +// int java.lang.Integer.bitCount(int) +void IntrinsicLocationsBuilderMIPS::VisitIntegerBitCount(HInvoke* invoke) { + CreateIntToIntLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitIntegerBitCount(HInvoke* invoke) { + GenBitCount(invoke->GetLocations(), Primitive::kPrimInt, IsR6(), GetAssembler()); +} + +// int java.lang.Long.bitCount(int) +void IntrinsicLocationsBuilderMIPS::VisitLongBitCount(HInvoke* invoke) { + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kNoCall, + kIntrinsified); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister()); + locations->AddTemp(Location::RequiresRegister()); + locations->AddTemp(Location::RequiresRegister()); +} + +void IntrinsicCodeGeneratorMIPS::VisitLongBitCount(HInvoke* invoke) { + GenBitCount(invoke->GetLocations(), Primitive::kPrimLong, IsR6(), GetAssembler()); +} + static void MathAbsFP(LocationSummary* locations, bool is64bit, MipsAssembler* assembler) { FRegister in = locations->InAt(0).AsFpuRegister(); FRegister out = locations->Out().AsFpuRegister(); @@ -1562,9 +1698,6 @@ void IntrinsicCodeGeneratorMIPS::VisitDoubleIsInfinite(HInvoke* invoke) { // Unimplemented intrinsics. -UNIMPLEMENTED_INTRINSIC(MIPS, IntegerBitCount) -UNIMPLEMENTED_INTRINSIC(MIPS, LongBitCount) - UNIMPLEMENTED_INTRINSIC(MIPS, MathCeil) UNIMPLEMENTED_INTRINSIC(MIPS, MathFloor) UNIMPLEMENTED_INTRINSIC(MIPS, MathRint) -- GitLab From bbb2ebedfa5bf66513beb83f17f21fe6ac83fd0e Mon Sep 17 00:00:00 2001 From: Chris Larsen Date: Wed, 17 Feb 2016 17:44:58 -0800 Subject: [PATCH 119/204] MIPS32: Improve TrailingZeros Change-Id: I1269ad5c58e1e3f39f3c2a04a3ed6dc1e44ff2fc --- compiler/optimizing/intrinsics_mips.cc | 67 +++++++------------------- 1 file changed, 17 insertions(+), 50 deletions(-) diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc index 655fbb18f4..0622610ecf 100644 --- a/compiler/optimizing/intrinsics_mips.cc +++ b/compiler/optimizing/intrinsics_mips.cc @@ -490,7 +490,6 @@ void IntrinsicCodeGeneratorMIPS::VisitLongNumberOfLeadingZeros(HInvoke* invoke) static void GenNumberOfTrailingZeroes(LocationSummary* locations, bool is64bit, bool isR6, - bool isR2OrNewer, MipsAssembler* assembler) { Register out = locations->Out().AsRegister(); Register in_lo; @@ -503,7 +502,7 @@ static void GenNumberOfTrailingZeroes(LocationSummary* locations, // If in_lo is zero then count the number of trailing zeroes in in_hi; // otherwise count the number of trailing zeroes in in_lo. - // AT = in_lo ? in_lo : in_hi; + // out = in_lo ? in_lo : in_hi; if (isR6) { __ Seleqz(out, in_hi, in_lo); __ Selnez(TMP, in_lo, in_lo); @@ -522,50 +521,26 @@ static void GenNumberOfTrailingZeroes(LocationSummary* locations, in_lo = in; } - // We don't have an instruction to count the number of trailing zeroes. - // Start by flipping the bits end-for-end so we can count the number of - // leading zeroes instead. - if (isR2OrNewer) { + if (isR6) { + // We don't have an instruction to count the number of trailing zeroes. + // Start by flipping the bits end-for-end so we can count the number of + // leading zeroes instead. __ Rotr(out, in, 16); __ Wsbh(out, out); - } else { - // MIPS32r1 - // __ Rotr(out, in, 16); - __ Sll(TMP, in, 16); - __ Srl(out, in, 16); - __ Or(out, out, TMP); - // __ Wsbh(out, out); - __ LoadConst32(AT, 0x00FF00FF); - __ And(TMP, out, AT); - __ Sll(TMP, TMP, 8); - __ Srl(out, out, 8); - __ And(out, out, AT); - __ Or(out, out, TMP); - } - - if (isR6) { __ Bitswap(out, out); __ ClzR6(out, out); } else { - __ LoadConst32(AT, 0x0F0F0F0F); - __ And(TMP, out, AT); - __ Sll(TMP, TMP, 4); - __ Srl(out, out, 4); - __ And(out, out, AT); - __ Or(out, TMP, out); - __ LoadConst32(AT, 0x33333333); - __ And(TMP, out, AT); - __ Sll(TMP, TMP, 2); - __ Srl(out, out, 2); - __ And(out, out, AT); - __ Or(out, TMP, out); - __ LoadConst32(AT, 0x55555555); - __ And(TMP, out, AT); - __ Sll(TMP, TMP, 1); - __ Srl(out, out, 1); - __ And(out, out, AT); - __ Or(out, TMP, out); + // Convert trailing zeroes to trailing ones, and bits to their left + // to zeroes. + __ Addiu(TMP, in, -1); + __ Xor(out, TMP, in); + __ And(out, out, TMP); + // Count number of leading zeroes. __ ClzR2(out, out); + // Subtract number of leading zeroes from 32 to get number of trailing ones. + // Remember that the trailing ones were formerly trailing zeroes. + __ LoadConst32(TMP, 32); + __ Subu(out, TMP, out); } if (is64bit) { @@ -587,11 +562,7 @@ void IntrinsicLocationsBuilderMIPS::VisitIntegerNumberOfTrailingZeros(HInvoke* i } void IntrinsicCodeGeneratorMIPS::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) { - GenNumberOfTrailingZeroes(invoke->GetLocations(), - /* is64bit */ false, - IsR6(), - IsR2OrNewer(), - GetAssembler()); + GenNumberOfTrailingZeroes(invoke->GetLocations(), /* is64bit */ false, IsR6(), GetAssembler()); } // int java.lang.Long.numberOfTrailingZeros(long i) @@ -600,11 +571,7 @@ void IntrinsicLocationsBuilderMIPS::VisitLongNumberOfTrailingZeros(HInvoke* invo } void IntrinsicCodeGeneratorMIPS::VisitLongNumberOfTrailingZeros(HInvoke* invoke) { - GenNumberOfTrailingZeroes(invoke->GetLocations(), - /* is64bit */ true, - IsR6(), - IsR2OrNewer(), - GetAssembler()); + GenNumberOfTrailingZeroes(invoke->GetLocations(), /* is64bit */ true, IsR6(), GetAssembler()); } // int java.lang.Integer.reverse(int) -- GitLab From a388c5a6291dbfb9a8366181a752050f4898d4ff Mon Sep 17 00:00:00 2001 From: Stephen Hines Date: Wed, 2 Mar 2016 18:26:29 -0800 Subject: [PATCH 120/204] Enable clang for ARM builds. Bug: http://b/25130937 We disable the integrated assembler for the ARM interpreter because Clang can't handle the syntax present. Change-Id: I978d3f78e25819bb76ccb0f7f4b91a88e4b5a2de --- build/Android.common_build.mk | 3 +-- runtime/Android.mk | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/build/Android.common_build.mk b/build/Android.common_build.mk index 02bce411b4..5e762a3bda 100644 --- a/build/Android.common_build.mk +++ b/build/Android.common_build.mk @@ -114,8 +114,7 @@ ART_TARGET_CLANG := $(USE_CLANG_PLATFORM_BUILD) else ART_TARGET_CLANG := false endif -# b/25130937 -ART_TARGET_CLANG_arm := false +ART_TARGET_CLANG_arm := ART_TARGET_CLANG_arm64 := ART_TARGET_CLANG_mips := ART_TARGET_CLANG_mips64 := diff --git a/runtime/Android.mk b/runtime/Android.mk index 500fa1420e..84660a3195 100644 --- a/runtime/Android.mk +++ b/runtime/Android.mk @@ -501,7 +501,7 @@ endif ifeq ($$(art_target_or_host),target) $$(eval $$(call set-target-local-clang-vars)) $$(eval $$(call set-target-local-cflags-vars,$(2))) - LOCAL_CLANG_arm64 := true + LOCAL_CLANG_ASFLAGS_arm += -no-integrated-as LOCAL_CFLAGS_$(DEX2OAT_TARGET_ARCH) += -DART_DEFAULT_INSTRUCTION_SET_FEATURES="$(LIBART_TARGET_DEFAULT_INSTRUCTION_SET_FEATURES)" LOCAL_CFLAGS_$(2ND_DEX2OAT_TARGET_ARCH) += -DART_DEFAULT_INSTRUCTION_SET_FEATURES="$(2ND_LIBART_TARGET_DEFAULT_INSTRUCTION_SET_FEATURES)" else # host -- GitLab From db045bea24d28ce6ad932fec4ce055af7be530e2 Mon Sep 17 00:00:00 2001 From: Alexey Frunze Date: Thu, 3 Mar 2016 17:50:48 -0800 Subject: [PATCH 121/204] ART: Enable JitProfiling for MIPS64 Mterp Change-Id: I46bdbfd706569ebbb1d1b08b9060ff01518d0f3a --- runtime/interpreter/mterp/mips64/bincmp.S | 30 +- runtime/interpreter/mterp/mips64/footer.S | 20 +- runtime/interpreter/mterp/mips64/header.S | 9 +- runtime/interpreter/mterp/mips64/invoke.S | 3 + runtime/interpreter/mterp/mips64/op_goto.S | 28 +- runtime/interpreter/mterp/mips64/op_goto_16.S | 26 +- runtime/interpreter/mterp/mips64/op_goto_32.S | 28 +- .../mterp/mips64/op_packed_switch.S | 23 +- runtime/interpreter/mterp/mips64/zcmp.S | 30 +- runtime/interpreter/mterp/mterp.cc | 11 +- runtime/interpreter/mterp/out/mterp_mips64.S | 553 ++++++++++-------- 11 files changed, 420 insertions(+), 341 deletions(-) diff --git a/runtime/interpreter/mterp/mips64/bincmp.S b/runtime/interpreter/mterp/mips64/bincmp.S index d39c9009b7..aa5e74b3de 100644 --- a/runtime/interpreter/mterp/mips64/bincmp.S +++ b/runtime/interpreter/mterp/mips64/bincmp.S @@ -6,27 +6,27 @@ * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le */ /* if-cmp vA, vB, +CCCC */ - lh a4, 2(rPC) # a4 <- sign-extended CCCC + .extern MterpProfileBranch ext a2, rINST, 8, 4 # a2 <- A ext a3, rINST, 12, 4 # a3 <- B + lh rINST, 2(rPC) # rINST <- offset (sign-extended CCCC) GET_VREG a0, a2 # a0 <- vA GET_VREG a1, a3 # a1 <- vB - b${condition}c a0, a1, 1f - li a4, 2 # offset if branch not taken + li rINST, 2 # offset if branch not taken 1: - - dlsa rPC, a4, rPC, 1 # rPC <- rPC + CCCC * 2 - FETCH_INST # load rINST - -#if MTERP_SUSPEND - bgez a4, 2f # CCCC * 2 >= 0 => no suspend check - REFRESH_IBASE -2: -#else - lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue - bltz a4, MterpCheckSuspendAndContinue +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + jal MterpProfileBranch # (self, shadow_frame, offset) + bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST #endif - + dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2 + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + move a0, rINST # a0 <- offset + FETCH_INST # load rINST + bltz a0, MterpCheckSuspendAndContinue # suspend check if backwards branch GET_INST_OPCODE v0 # extract opcode from rINST GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/footer.S b/runtime/interpreter/mterp/mips64/footer.S index 1a2e22b672..14d5fe01f5 100644 --- a/runtime/interpreter/mterp/mips64/footer.S +++ b/runtime/interpreter/mterp/mips64/footer.S @@ -49,6 +49,7 @@ MterpPossibleException: * */ .extern MterpHandleException + .extern MterpShouldSwitchInterpreters MterpException: move a0, rSELF daddu a1, rFP, OFF_FP_SHADOWFRAME @@ -59,8 +60,11 @@ MterpException: REFRESH_IBASE daddu rPC, a0, CODEITEM_INSNS_OFFSET dlsa rPC, a1, rPC, 1 # generate new dex_pc_ptr - sd rPC, OFF_FP_DEX_PC_PTR(rFP) + /* Do we need to switch interpreters? */ + jal MterpShouldSwitchInterpreters + bnezc v0, MterpFallback /* resume execution at catch block */ + EXPORT_PC FETCH_INST GET_INST_OPCODE v0 GOTO_OPCODE v0 @@ -81,9 +85,23 @@ check1: EXPORT_PC move a0, rSELF jal MterpSuspendCheck # (self) + bnezc v0, MterpFallback # Something in the environment changed, switch interpreters GET_INST_OPCODE v0 # extract opcode from rINST GOTO_OPCODE v0 # jump to next instruction +/* + * On-stack replacement has happened, and now we've returned from the compiled method. + */ +MterpOnStackReplacement: +#if MTERP_LOGGING + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST # rINST contains offset + jal MterpLogOSR +#endif + li v0, 1 # Signal normal return + b MterpDone + /* * Bail out to reference interpreter. */ diff --git a/runtime/interpreter/mterp/mips64/header.S b/runtime/interpreter/mterp/mips64/header.S index 4c3ca9efff..dd0fbe0057 100644 --- a/runtime/interpreter/mterp/mips64/header.S +++ b/runtime/interpreter/mterp/mips64/header.S @@ -82,14 +82,7 @@ The following registers have fixed assignments: #define OFF_FP_CODE_ITEM OFF_FP(SHADOWFRAME_CODE_ITEM_OFFSET) #define OFF_FP_SHADOWFRAME (-SHADOWFRAME_VREGS_OFFSET) -/* - * - * The reference interpreter performs explicit suspect checks, which is somewhat wasteful. - * Dalvik's interpreter folded suspend checks into the jump table mechanism, and eventually - * mterp should do so as well. - */ -#define MTERP_SUSPEND 0 - +#define MTERP_PROFILE_BRANCHES 1 #define MTERP_LOGGING 0 /* diff --git a/runtime/interpreter/mterp/mips64/invoke.S b/runtime/interpreter/mterp/mips64/invoke.S index 4ae4fb1c1d..be647b618b 100644 --- a/runtime/interpreter/mterp/mips64/invoke.S +++ b/runtime/interpreter/mterp/mips64/invoke.S @@ -5,6 +5,7 @@ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ .extern $helper + .extern MterpShouldSwitchInterpreters EXPORT_PC move a0, rSELF daddu a1, rFP, OFF_FP_SHADOWFRAME @@ -13,5 +14,7 @@ jal $helper beqzc v0, MterpException FETCH_ADVANCE_INST 3 + jal MterpShouldSwitchInterpreters + bnezc v0, MterpFallback GET_INST_OPCODE v0 GOTO_OPCODE v0 diff --git a/runtime/interpreter/mterp/mips64/op_goto.S b/runtime/interpreter/mterp/mips64/op_goto.S index f2df3e4112..7c7d0ecf5a 100644 --- a/runtime/interpreter/mterp/mips64/op_goto.S +++ b/runtime/interpreter/mterp/mips64/op_goto.S @@ -5,19 +5,21 @@ * double to get a byte offset. */ /* goto +AA */ - srl a0, rINST, 8 - seb a0, a0 # a0 <- sign-extended AA - dlsa rPC, a0, rPC, 1 # rPC <- rPC + AA * 2 - FETCH_INST # load rINST - -#if MTERP_SUSPEND - bgez a0, 1f # AA * 2 >= 0 => no suspend check - REFRESH_IBASE -1: -#else - lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue - bltz a0, MterpCheckSuspendAndContinue + .extern MterpProfileBranch + srl rINST, rINST, 8 + seb rINST, rINST # rINST <- offset (sign-extended AA) +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + jal MterpProfileBranch # (self, shadow_frame, offset) + bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST #endif - + dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2 + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + move a0, rINST # a0 <- offset + FETCH_INST # load rINST + bltz a0, MterpCheckSuspendAndContinue # suspend check if backwards branch GET_INST_OPCODE v0 # extract opcode from rINST GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_goto_16.S b/runtime/interpreter/mterp/mips64/op_goto_16.S index cbf8cf2994..566e3a78f0 100644 --- a/runtime/interpreter/mterp/mips64/op_goto_16.S +++ b/runtime/interpreter/mterp/mips64/op_goto_16.S @@ -5,18 +5,20 @@ * double to get a byte offset. */ /* goto/16 +AAAA */ - lh a0, 2(rPC) # a0 <- sign-extended AAAA - dlsa rPC, a0, rPC, 1 # rPC <- rPC + AAAA * 2 - FETCH_INST # load rINST - -#if MTERP_SUSPEND - bgez a0, 1f # AA * 2 >= 0 => no suspend check - REFRESH_IBASE -1: -#else - lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue - bltz a0, MterpCheckSuspendAndContinue + .extern MterpProfileBranch + lh rINST, 2(rPC) # rINST <- offset (sign-extended AAAA) +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + jal MterpProfileBranch # (self, shadow_frame, offset) + bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST #endif - + dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2 + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + move a0, rINST # a0 <- offset + FETCH_INST # load rINST + bltz a0, MterpCheckSuspendAndContinue # suspend check if backwards branch GET_INST_OPCODE v0 # extract opcode from rINST GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_goto_32.S b/runtime/interpreter/mterp/mips64/op_goto_32.S index 4a1feac1ac..b260083ae8 100644 --- a/runtime/interpreter/mterp/mips64/op_goto_32.S +++ b/runtime/interpreter/mterp/mips64/op_goto_32.S @@ -8,20 +8,22 @@ * our "backward branch" test must be "<=0" instead of "<0". */ /* goto/32 +AAAAAAAA */ - lh a0, 2(rPC) # a0 <- aaaa (low) + .extern MterpProfileBranch + lh rINST, 2(rPC) # rINST <- aaaa (low) lh a1, 4(rPC) # a1 <- AAAA (high) - ins a0, a1, 16, 16 # a0 = sign-extended AAAAaaaa - dlsa rPC, a0, rPC, 1 # rPC <- rPC + AAAAAAAA * 2 - FETCH_INST # load rINST - -#if MTERP_SUSPEND - bgtz a0, 1f # AA * 2 > 0 => no suspend check - REFRESH_IBASE -1: -#else - lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue - blez a0, MterpCheckSuspendAndContinue + ins rINST, a1, 16, 16 # rINST <- offset (sign-extended AAAAaaaa) +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + jal MterpProfileBranch # (self, shadow_frame, offset) + bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST #endif - + dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2 + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + move a0, rINST # a0 <- offset + FETCH_INST # load rINST + blez a0, MterpCheckSuspendAndContinue # suspend check if backwards branch GET_INST_OPCODE v0 # extract opcode from rINST GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_packed_switch.S b/runtime/interpreter/mterp/mips64/op_packed_switch.S index cdbdf75321..2c6eb2f3ca 100644 --- a/runtime/interpreter/mterp/mips64/op_packed_switch.S +++ b/runtime/interpreter/mterp/mips64/op_packed_switch.S @@ -10,6 +10,7 @@ */ /* op vAA, +BBBBBBBB */ .extern $func + .extern MterpProfileBranch lh a0, 2(rPC) # a0 <- bbbb (lo) lh a1, 4(rPC) # a1 <- BBBB (hi) srl a3, rINST, 8 # a3 <- AA @@ -17,15 +18,19 @@ GET_VREG a1, a3 # a1 <- vAA dlsa a0, a0, rPC, 1 # a0 <- PC + BBBBbbbb*2 jal $func # v0 <- code-unit branch offset - dlsa rPC, v0, rPC, 1 # rPC <- rPC + offset * 2 - FETCH_INST # load rINST -#if MTERP_SUSPEND - bgtz v0, 1f # offset * 2 > 0 => no suspend check - REFRESH_IBASE -1: -#else - lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue - blez v0, MterpCheckSuspendAndContinue + move rINST, v0 +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + jal MterpProfileBranch # (self, shadow_frame, offset) + bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST #endif + dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2 + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + move a0, rINST # a0 <- offset + FETCH_INST # load rINST + blez a0, MterpCheckSuspendAndContinue # suspend check if backwards branch GET_INST_OPCODE v0 # extract opcode from rINST GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/zcmp.S b/runtime/interpreter/mterp/mips64/zcmp.S index d7ad894485..0e0477fadf 100644 --- a/runtime/interpreter/mterp/mips64/zcmp.S +++ b/runtime/interpreter/mterp/mips64/zcmp.S @@ -6,25 +6,25 @@ * For: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez */ /* if-cmp vAA, +BBBB */ - lh a4, 2(rPC) # a4 <- sign-extended BBBB + .extern MterpProfileBranch srl a2, rINST, 8 # a2 <- AA + lh rINST, 2(rPC) # rINST <- offset (sign-extended BBBB) GET_VREG a0, a2 # a0 <- vAA - b${condition}zc a0, 1f - li a4, 2 # offset if branch not taken + li rINST, 2 # offset if branch not taken 1: - - dlsa rPC, a4, rPC, 1 # rPC <- rPC + BBBB * 2 - FETCH_INST # load rINST - -#if MTERP_SUSPEND - bgez a4, 2f # BBBB * 2 >= 0 => no suspend check - REFRESH_IBASE -2: -#else - lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue - bltz a4, MterpCheckSuspendAndContinue +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + jal MterpProfileBranch # (self, shadow_frame, offset) + bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST #endif - + dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2 + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + move a0, rINST # a0 <- offset + FETCH_INST # load rINST + bltz a0, MterpCheckSuspendAndContinue # suspend check if backwards branch GET_INST_OPCODE v0 # extract opcode from rINST GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mterp.cc b/runtime/interpreter/mterp/mterp.cc index ca727f47be..10b19c5f4f 100644 --- a/runtime/interpreter/mterp/mterp.cc +++ b/runtime/interpreter/mterp/mterp.cc @@ -147,16 +147,7 @@ extern "C" bool MterpShouldSwitchInterpreters() SHARED_REQUIRES(Locks::mutator_lock_) { const instrumentation::Instrumentation* const instrumentation = Runtime::Current()->GetInstrumentation(); - bool unhandled_instrumentation; - // TODO: enable for other targets after more extensive testing. - if ((kRuntimeISA == kArm64) || (kRuntimeISA == kArm) || - (kRuntimeISA == kX86_64) || (kRuntimeISA == kX86) || - (kRuntimeISA == kMips)) { - unhandled_instrumentation = instrumentation->NonJitProfilingActive(); - } else { - unhandled_instrumentation = instrumentation->IsActive(); - } - return unhandled_instrumentation || Dbg::IsDebuggerActive(); + return instrumentation->NonJitProfilingActive() || Dbg::IsDebuggerActive(); } diff --git a/runtime/interpreter/mterp/out/mterp_mips64.S b/runtime/interpreter/mterp/out/mterp_mips64.S index 7cef823fff..a17252b2f8 100644 --- a/runtime/interpreter/mterp/out/mterp_mips64.S +++ b/runtime/interpreter/mterp/out/mterp_mips64.S @@ -89,14 +89,7 @@ The following registers have fixed assignments: #define OFF_FP_CODE_ITEM OFF_FP(SHADOWFRAME_CODE_ITEM_OFFSET) #define OFF_FP_SHADOWFRAME (-SHADOWFRAME_VREGS_OFFSET) -/* - * - * The reference interpreter performs explicit suspect checks, which is somewhat wasteful. - * Dalvik's interpreter folded suspend checks into the jump table mechanism, and eventually - * mterp should do so as well. - */ -#define MTERP_SUSPEND 0 - +#define MTERP_PROFILE_BRANCHES 1 #define MTERP_LOGGING 0 /* @@ -1107,20 +1100,22 @@ artMterpAsmInstructionStart = .L_op_nop * double to get a byte offset. */ /* goto +AA */ - srl a0, rINST, 8 - seb a0, a0 # a0 <- sign-extended AA - dlsa rPC, a0, rPC, 1 # rPC <- rPC + AA * 2 - FETCH_INST # load rINST - -#if MTERP_SUSPEND - bgez a0, 1f # AA * 2 >= 0 => no suspend check - REFRESH_IBASE -1: -#else - lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue - bltz a0, MterpCheckSuspendAndContinue + .extern MterpProfileBranch + srl rINST, rINST, 8 + seb rINST, rINST # rINST <- offset (sign-extended AA) +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + jal MterpProfileBranch # (self, shadow_frame, offset) + bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST #endif - + dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2 + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + move a0, rINST # a0 <- offset + FETCH_INST # load rINST + bltz a0, MterpCheckSuspendAndContinue # suspend check if backwards branch GET_INST_OPCODE v0 # extract opcode from rINST GOTO_OPCODE v0 # jump to next instruction @@ -1135,19 +1130,21 @@ artMterpAsmInstructionStart = .L_op_nop * double to get a byte offset. */ /* goto/16 +AAAA */ - lh a0, 2(rPC) # a0 <- sign-extended AAAA - dlsa rPC, a0, rPC, 1 # rPC <- rPC + AAAA * 2 - FETCH_INST # load rINST - -#if MTERP_SUSPEND - bgez a0, 1f # AA * 2 >= 0 => no suspend check - REFRESH_IBASE -1: -#else - lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue - bltz a0, MterpCheckSuspendAndContinue + .extern MterpProfileBranch + lh rINST, 2(rPC) # rINST <- offset (sign-extended AAAA) +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + jal MterpProfileBranch # (self, shadow_frame, offset) + bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST #endif - + dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2 + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + move a0, rINST # a0 <- offset + FETCH_INST # load rINST + bltz a0, MterpCheckSuspendAndContinue # suspend check if backwards branch GET_INST_OPCODE v0 # extract opcode from rINST GOTO_OPCODE v0 # jump to next instruction @@ -1165,21 +1162,23 @@ artMterpAsmInstructionStart = .L_op_nop * our "backward branch" test must be "<=0" instead of "<0". */ /* goto/32 +AAAAAAAA */ - lh a0, 2(rPC) # a0 <- aaaa (low) + .extern MterpProfileBranch + lh rINST, 2(rPC) # rINST <- aaaa (low) lh a1, 4(rPC) # a1 <- AAAA (high) - ins a0, a1, 16, 16 # a0 = sign-extended AAAAaaaa - dlsa rPC, a0, rPC, 1 # rPC <- rPC + AAAAAAAA * 2 - FETCH_INST # load rINST - -#if MTERP_SUSPEND - bgtz a0, 1f # AA * 2 > 0 => no suspend check - REFRESH_IBASE -1: -#else - lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue - blez a0, MterpCheckSuspendAndContinue + ins rINST, a1, 16, 16 # rINST <- offset (sign-extended AAAAaaaa) +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + jal MterpProfileBranch # (self, shadow_frame, offset) + bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST #endif - + dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2 + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + move a0, rINST # a0 <- offset + FETCH_INST # load rINST + blez a0, MterpCheckSuspendAndContinue # suspend check if backwards branch GET_INST_OPCODE v0 # extract opcode from rINST GOTO_OPCODE v0 # jump to next instruction @@ -1198,6 +1197,7 @@ artMterpAsmInstructionStart = .L_op_nop */ /* op vAA, +BBBBBBBB */ .extern MterpDoPackedSwitch + .extern MterpProfileBranch lh a0, 2(rPC) # a0 <- bbbb (lo) lh a1, 4(rPC) # a1 <- BBBB (hi) srl a3, rINST, 8 # a3 <- AA @@ -1205,16 +1205,20 @@ artMterpAsmInstructionStart = .L_op_nop GET_VREG a1, a3 # a1 <- vAA dlsa a0, a0, rPC, 1 # a0 <- PC + BBBBbbbb*2 jal MterpDoPackedSwitch # v0 <- code-unit branch offset - dlsa rPC, v0, rPC, 1 # rPC <- rPC + offset * 2 - FETCH_INST # load rINST -#if MTERP_SUSPEND - bgtz v0, 1f # offset * 2 > 0 => no suspend check - REFRESH_IBASE -1: -#else - lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue - blez v0, MterpCheckSuspendAndContinue + move rINST, v0 +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + jal MterpProfileBranch # (self, shadow_frame, offset) + bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST #endif + dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2 + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + move a0, rINST # a0 <- offset + FETCH_INST # load rINST + blez a0, MterpCheckSuspendAndContinue # suspend check if backwards branch GET_INST_OPCODE v0 # extract opcode from rINST GOTO_OPCODE v0 # jump to next instruction @@ -1234,6 +1238,7 @@ artMterpAsmInstructionStart = .L_op_nop */ /* op vAA, +BBBBBBBB */ .extern MterpDoSparseSwitch + .extern MterpProfileBranch lh a0, 2(rPC) # a0 <- bbbb (lo) lh a1, 4(rPC) # a1 <- BBBB (hi) srl a3, rINST, 8 # a3 <- AA @@ -1241,16 +1246,20 @@ artMterpAsmInstructionStart = .L_op_nop GET_VREG a1, a3 # a1 <- vAA dlsa a0, a0, rPC, 1 # a0 <- PC + BBBBbbbb*2 jal MterpDoSparseSwitch # v0 <- code-unit branch offset - dlsa rPC, v0, rPC, 1 # rPC <- rPC + offset * 2 - FETCH_INST # load rINST -#if MTERP_SUSPEND - bgtz v0, 1f # offset * 2 > 0 => no suspend check - REFRESH_IBASE -1: -#else - lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue - blez v0, MterpCheckSuspendAndContinue + move rINST, v0 +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + jal MterpProfileBranch # (self, shadow_frame, offset) + bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST #endif + dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2 + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + move a0, rINST # a0 <- offset + FETCH_INST # load rINST + blez a0, MterpCheckSuspendAndContinue # suspend check if backwards branch GET_INST_OPCODE v0 # extract opcode from rINST GOTO_OPCODE v0 # jump to next instruction @@ -1438,28 +1447,28 @@ artMterpAsmInstructionStart = .L_op_nop * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le */ /* if-cmp vA, vB, +CCCC */ - lh a4, 2(rPC) # a4 <- sign-extended CCCC + .extern MterpProfileBranch ext a2, rINST, 8, 4 # a2 <- A ext a3, rINST, 12, 4 # a3 <- B + lh rINST, 2(rPC) # rINST <- offset (sign-extended CCCC) GET_VREG a0, a2 # a0 <- vA GET_VREG a1, a3 # a1 <- vB - beqc a0, a1, 1f - li a4, 2 # offset if branch not taken + li rINST, 2 # offset if branch not taken 1: - - dlsa rPC, a4, rPC, 1 # rPC <- rPC + CCCC * 2 - FETCH_INST # load rINST - -#if MTERP_SUSPEND - bgez a4, 2f # CCCC * 2 >= 0 => no suspend check - REFRESH_IBASE -2: -#else - lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue - bltz a4, MterpCheckSuspendAndContinue +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + jal MterpProfileBranch # (self, shadow_frame, offset) + bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST #endif - + dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2 + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + move a0, rINST # a0 <- offset + FETCH_INST # load rINST + bltz a0, MterpCheckSuspendAndContinue # suspend check if backwards branch GET_INST_OPCODE v0 # extract opcode from rINST GOTO_OPCODE v0 # jump to next instruction @@ -1477,28 +1486,28 @@ artMterpAsmInstructionStart = .L_op_nop * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le */ /* if-cmp vA, vB, +CCCC */ - lh a4, 2(rPC) # a4 <- sign-extended CCCC + .extern MterpProfileBranch ext a2, rINST, 8, 4 # a2 <- A ext a3, rINST, 12, 4 # a3 <- B + lh rINST, 2(rPC) # rINST <- offset (sign-extended CCCC) GET_VREG a0, a2 # a0 <- vA GET_VREG a1, a3 # a1 <- vB - bnec a0, a1, 1f - li a4, 2 # offset if branch not taken + li rINST, 2 # offset if branch not taken 1: - - dlsa rPC, a4, rPC, 1 # rPC <- rPC + CCCC * 2 - FETCH_INST # load rINST - -#if MTERP_SUSPEND - bgez a4, 2f # CCCC * 2 >= 0 => no suspend check - REFRESH_IBASE -2: -#else - lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue - bltz a4, MterpCheckSuspendAndContinue +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + jal MterpProfileBranch # (self, shadow_frame, offset) + bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST #endif - + dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2 + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + move a0, rINST # a0 <- offset + FETCH_INST # load rINST + bltz a0, MterpCheckSuspendAndContinue # suspend check if backwards branch GET_INST_OPCODE v0 # extract opcode from rINST GOTO_OPCODE v0 # jump to next instruction @@ -1516,28 +1525,28 @@ artMterpAsmInstructionStart = .L_op_nop * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le */ /* if-cmp vA, vB, +CCCC */ - lh a4, 2(rPC) # a4 <- sign-extended CCCC + .extern MterpProfileBranch ext a2, rINST, 8, 4 # a2 <- A ext a3, rINST, 12, 4 # a3 <- B + lh rINST, 2(rPC) # rINST <- offset (sign-extended CCCC) GET_VREG a0, a2 # a0 <- vA GET_VREG a1, a3 # a1 <- vB - bltc a0, a1, 1f - li a4, 2 # offset if branch not taken + li rINST, 2 # offset if branch not taken 1: - - dlsa rPC, a4, rPC, 1 # rPC <- rPC + CCCC * 2 - FETCH_INST # load rINST - -#if MTERP_SUSPEND - bgez a4, 2f # CCCC * 2 >= 0 => no suspend check - REFRESH_IBASE -2: -#else - lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue - bltz a4, MterpCheckSuspendAndContinue +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + jal MterpProfileBranch # (self, shadow_frame, offset) + bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST #endif - + dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2 + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + move a0, rINST # a0 <- offset + FETCH_INST # load rINST + bltz a0, MterpCheckSuspendAndContinue # suspend check if backwards branch GET_INST_OPCODE v0 # extract opcode from rINST GOTO_OPCODE v0 # jump to next instruction @@ -1555,28 +1564,28 @@ artMterpAsmInstructionStart = .L_op_nop * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le */ /* if-cmp vA, vB, +CCCC */ - lh a4, 2(rPC) # a4 <- sign-extended CCCC + .extern MterpProfileBranch ext a2, rINST, 8, 4 # a2 <- A ext a3, rINST, 12, 4 # a3 <- B + lh rINST, 2(rPC) # rINST <- offset (sign-extended CCCC) GET_VREG a0, a2 # a0 <- vA GET_VREG a1, a3 # a1 <- vB - bgec a0, a1, 1f - li a4, 2 # offset if branch not taken + li rINST, 2 # offset if branch not taken 1: - - dlsa rPC, a4, rPC, 1 # rPC <- rPC + CCCC * 2 - FETCH_INST # load rINST - -#if MTERP_SUSPEND - bgez a4, 2f # CCCC * 2 >= 0 => no suspend check - REFRESH_IBASE -2: -#else - lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue - bltz a4, MterpCheckSuspendAndContinue +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + jal MterpProfileBranch # (self, shadow_frame, offset) + bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST #endif - + dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2 + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + move a0, rINST # a0 <- offset + FETCH_INST # load rINST + bltz a0, MterpCheckSuspendAndContinue # suspend check if backwards branch GET_INST_OPCODE v0 # extract opcode from rINST GOTO_OPCODE v0 # jump to next instruction @@ -1594,28 +1603,28 @@ artMterpAsmInstructionStart = .L_op_nop * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le */ /* if-cmp vA, vB, +CCCC */ - lh a4, 2(rPC) # a4 <- sign-extended CCCC + .extern MterpProfileBranch ext a2, rINST, 8, 4 # a2 <- A ext a3, rINST, 12, 4 # a3 <- B + lh rINST, 2(rPC) # rINST <- offset (sign-extended CCCC) GET_VREG a0, a2 # a0 <- vA GET_VREG a1, a3 # a1 <- vB - bgtc a0, a1, 1f - li a4, 2 # offset if branch not taken + li rINST, 2 # offset if branch not taken 1: - - dlsa rPC, a4, rPC, 1 # rPC <- rPC + CCCC * 2 - FETCH_INST # load rINST - -#if MTERP_SUSPEND - bgez a4, 2f # CCCC * 2 >= 0 => no suspend check - REFRESH_IBASE -2: -#else - lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue - bltz a4, MterpCheckSuspendAndContinue +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + jal MterpProfileBranch # (self, shadow_frame, offset) + bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST #endif - + dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2 + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + move a0, rINST # a0 <- offset + FETCH_INST # load rINST + bltz a0, MterpCheckSuspendAndContinue # suspend check if backwards branch GET_INST_OPCODE v0 # extract opcode from rINST GOTO_OPCODE v0 # jump to next instruction @@ -1633,28 +1642,28 @@ artMterpAsmInstructionStart = .L_op_nop * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le */ /* if-cmp vA, vB, +CCCC */ - lh a4, 2(rPC) # a4 <- sign-extended CCCC + .extern MterpProfileBranch ext a2, rINST, 8, 4 # a2 <- A ext a3, rINST, 12, 4 # a3 <- B + lh rINST, 2(rPC) # rINST <- offset (sign-extended CCCC) GET_VREG a0, a2 # a0 <- vA GET_VREG a1, a3 # a1 <- vB - blec a0, a1, 1f - li a4, 2 # offset if branch not taken + li rINST, 2 # offset if branch not taken 1: - - dlsa rPC, a4, rPC, 1 # rPC <- rPC + CCCC * 2 - FETCH_INST # load rINST - -#if MTERP_SUSPEND - bgez a4, 2f # CCCC * 2 >= 0 => no suspend check - REFRESH_IBASE -2: -#else - lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue - bltz a4, MterpCheckSuspendAndContinue +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + jal MterpProfileBranch # (self, shadow_frame, offset) + bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST #endif - + dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2 + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + move a0, rINST # a0 <- offset + FETCH_INST # load rINST + bltz a0, MterpCheckSuspendAndContinue # suspend check if backwards branch GET_INST_OPCODE v0 # extract opcode from rINST GOTO_OPCODE v0 # jump to next instruction @@ -1672,26 +1681,26 @@ artMterpAsmInstructionStart = .L_op_nop * For: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez */ /* if-cmp vAA, +BBBB */ - lh a4, 2(rPC) # a4 <- sign-extended BBBB + .extern MterpProfileBranch srl a2, rINST, 8 # a2 <- AA + lh rINST, 2(rPC) # rINST <- offset (sign-extended BBBB) GET_VREG a0, a2 # a0 <- vAA - beqzc a0, 1f - li a4, 2 # offset if branch not taken + li rINST, 2 # offset if branch not taken 1: - - dlsa rPC, a4, rPC, 1 # rPC <- rPC + BBBB * 2 - FETCH_INST # load rINST - -#if MTERP_SUSPEND - bgez a4, 2f # BBBB * 2 >= 0 => no suspend check - REFRESH_IBASE -2: -#else - lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue - bltz a4, MterpCheckSuspendAndContinue +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + jal MterpProfileBranch # (self, shadow_frame, offset) + bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST #endif - + dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2 + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + move a0, rINST # a0 <- offset + FETCH_INST # load rINST + bltz a0, MterpCheckSuspendAndContinue # suspend check if backwards branch GET_INST_OPCODE v0 # extract opcode from rINST GOTO_OPCODE v0 # jump to next instruction @@ -1709,26 +1718,26 @@ artMterpAsmInstructionStart = .L_op_nop * For: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez */ /* if-cmp vAA, +BBBB */ - lh a4, 2(rPC) # a4 <- sign-extended BBBB + .extern MterpProfileBranch srl a2, rINST, 8 # a2 <- AA + lh rINST, 2(rPC) # rINST <- offset (sign-extended BBBB) GET_VREG a0, a2 # a0 <- vAA - bnezc a0, 1f - li a4, 2 # offset if branch not taken + li rINST, 2 # offset if branch not taken 1: - - dlsa rPC, a4, rPC, 1 # rPC <- rPC + BBBB * 2 - FETCH_INST # load rINST - -#if MTERP_SUSPEND - bgez a4, 2f # BBBB * 2 >= 0 => no suspend check - REFRESH_IBASE -2: -#else - lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue - bltz a4, MterpCheckSuspendAndContinue +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + jal MterpProfileBranch # (self, shadow_frame, offset) + bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST #endif - + dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2 + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + move a0, rINST # a0 <- offset + FETCH_INST # load rINST + bltz a0, MterpCheckSuspendAndContinue # suspend check if backwards branch GET_INST_OPCODE v0 # extract opcode from rINST GOTO_OPCODE v0 # jump to next instruction @@ -1746,26 +1755,26 @@ artMterpAsmInstructionStart = .L_op_nop * For: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez */ /* if-cmp vAA, +BBBB */ - lh a4, 2(rPC) # a4 <- sign-extended BBBB + .extern MterpProfileBranch srl a2, rINST, 8 # a2 <- AA + lh rINST, 2(rPC) # rINST <- offset (sign-extended BBBB) GET_VREG a0, a2 # a0 <- vAA - bltzc a0, 1f - li a4, 2 # offset if branch not taken + li rINST, 2 # offset if branch not taken 1: - - dlsa rPC, a4, rPC, 1 # rPC <- rPC + BBBB * 2 - FETCH_INST # load rINST - -#if MTERP_SUSPEND - bgez a4, 2f # BBBB * 2 >= 0 => no suspend check - REFRESH_IBASE -2: -#else - lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue - bltz a4, MterpCheckSuspendAndContinue +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + jal MterpProfileBranch # (self, shadow_frame, offset) + bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST #endif - + dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2 + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + move a0, rINST # a0 <- offset + FETCH_INST # load rINST + bltz a0, MterpCheckSuspendAndContinue # suspend check if backwards branch GET_INST_OPCODE v0 # extract opcode from rINST GOTO_OPCODE v0 # jump to next instruction @@ -1783,26 +1792,26 @@ artMterpAsmInstructionStart = .L_op_nop * For: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez */ /* if-cmp vAA, +BBBB */ - lh a4, 2(rPC) # a4 <- sign-extended BBBB + .extern MterpProfileBranch srl a2, rINST, 8 # a2 <- AA + lh rINST, 2(rPC) # rINST <- offset (sign-extended BBBB) GET_VREG a0, a2 # a0 <- vAA - bgezc a0, 1f - li a4, 2 # offset if branch not taken + li rINST, 2 # offset if branch not taken 1: - - dlsa rPC, a4, rPC, 1 # rPC <- rPC + BBBB * 2 - FETCH_INST # load rINST - -#if MTERP_SUSPEND - bgez a4, 2f # BBBB * 2 >= 0 => no suspend check - REFRESH_IBASE -2: -#else - lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue - bltz a4, MterpCheckSuspendAndContinue +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + jal MterpProfileBranch # (self, shadow_frame, offset) + bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST #endif - + dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2 + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + move a0, rINST # a0 <- offset + FETCH_INST # load rINST + bltz a0, MterpCheckSuspendAndContinue # suspend check if backwards branch GET_INST_OPCODE v0 # extract opcode from rINST GOTO_OPCODE v0 # jump to next instruction @@ -1820,26 +1829,26 @@ artMterpAsmInstructionStart = .L_op_nop * For: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez */ /* if-cmp vAA, +BBBB */ - lh a4, 2(rPC) # a4 <- sign-extended BBBB + .extern MterpProfileBranch srl a2, rINST, 8 # a2 <- AA + lh rINST, 2(rPC) # rINST <- offset (sign-extended BBBB) GET_VREG a0, a2 # a0 <- vAA - bgtzc a0, 1f - li a4, 2 # offset if branch not taken + li rINST, 2 # offset if branch not taken 1: - - dlsa rPC, a4, rPC, 1 # rPC <- rPC + BBBB * 2 - FETCH_INST # load rINST - -#if MTERP_SUSPEND - bgez a4, 2f # BBBB * 2 >= 0 => no suspend check - REFRESH_IBASE -2: -#else - lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue - bltz a4, MterpCheckSuspendAndContinue +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + jal MterpProfileBranch # (self, shadow_frame, offset) + bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST #endif - + dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2 + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + move a0, rINST # a0 <- offset + FETCH_INST # load rINST + bltz a0, MterpCheckSuspendAndContinue # suspend check if backwards branch GET_INST_OPCODE v0 # extract opcode from rINST GOTO_OPCODE v0 # jump to next instruction @@ -1857,26 +1866,26 @@ artMterpAsmInstructionStart = .L_op_nop * For: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez */ /* if-cmp vAA, +BBBB */ - lh a4, 2(rPC) # a4 <- sign-extended BBBB + .extern MterpProfileBranch srl a2, rINST, 8 # a2 <- AA + lh rINST, 2(rPC) # rINST <- offset (sign-extended BBBB) GET_VREG a0, a2 # a0 <- vAA - blezc a0, 1f - li a4, 2 # offset if branch not taken + li rINST, 2 # offset if branch not taken 1: - - dlsa rPC, a4, rPC, 1 # rPC <- rPC + BBBB * 2 - FETCH_INST # load rINST - -#if MTERP_SUSPEND - bgez a4, 2f # BBBB * 2 >= 0 => no suspend check - REFRESH_IBASE -2: -#else - lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue - bltz a4, MterpCheckSuspendAndContinue +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + jal MterpProfileBranch # (self, shadow_frame, offset) + bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST #endif - + dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2 + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + move a0, rINST # a0 <- offset + FETCH_INST # load rINST + bltz a0, MterpCheckSuspendAndContinue # suspend check if backwards branch GET_INST_OPCODE v0 # extract opcode from rINST GOTO_OPCODE v0 # jump to next instruction @@ -3166,6 +3175,7 @@ artMterpAsmInstructionStart = .L_op_nop /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ .extern MterpInvokeVirtual + .extern MterpShouldSwitchInterpreters EXPORT_PC move a0, rSELF daddu a1, rFP, OFF_FP_SHADOWFRAME @@ -3174,6 +3184,8 @@ artMterpAsmInstructionStart = .L_op_nop jal MterpInvokeVirtual beqzc v0, MterpException FETCH_ADVANCE_INST 3 + jal MterpShouldSwitchInterpreters + bnezc v0, MterpFallback GET_INST_OPCODE v0 GOTO_OPCODE v0 @@ -3196,6 +3208,7 @@ artMterpAsmInstructionStart = .L_op_nop /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ .extern MterpInvokeSuper + .extern MterpShouldSwitchInterpreters EXPORT_PC move a0, rSELF daddu a1, rFP, OFF_FP_SHADOWFRAME @@ -3204,6 +3217,8 @@ artMterpAsmInstructionStart = .L_op_nop jal MterpInvokeSuper beqzc v0, MterpException FETCH_ADVANCE_INST 3 + jal MterpShouldSwitchInterpreters + bnezc v0, MterpFallback GET_INST_OPCODE v0 GOTO_OPCODE v0 @@ -3226,6 +3241,7 @@ artMterpAsmInstructionStart = .L_op_nop /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ .extern MterpInvokeDirect + .extern MterpShouldSwitchInterpreters EXPORT_PC move a0, rSELF daddu a1, rFP, OFF_FP_SHADOWFRAME @@ -3234,6 +3250,8 @@ artMterpAsmInstructionStart = .L_op_nop jal MterpInvokeDirect beqzc v0, MterpException FETCH_ADVANCE_INST 3 + jal MterpShouldSwitchInterpreters + bnezc v0, MterpFallback GET_INST_OPCODE v0 GOTO_OPCODE v0 @@ -3249,6 +3267,7 @@ artMterpAsmInstructionStart = .L_op_nop /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ .extern MterpInvokeStatic + .extern MterpShouldSwitchInterpreters EXPORT_PC move a0, rSELF daddu a1, rFP, OFF_FP_SHADOWFRAME @@ -3257,6 +3276,8 @@ artMterpAsmInstructionStart = .L_op_nop jal MterpInvokeStatic beqzc v0, MterpException FETCH_ADVANCE_INST 3 + jal MterpShouldSwitchInterpreters + bnezc v0, MterpFallback GET_INST_OPCODE v0 GOTO_OPCODE v0 @@ -3272,6 +3293,7 @@ artMterpAsmInstructionStart = .L_op_nop /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ .extern MterpInvokeInterface + .extern MterpShouldSwitchInterpreters EXPORT_PC move a0, rSELF daddu a1, rFP, OFF_FP_SHADOWFRAME @@ -3280,6 +3302,8 @@ artMterpAsmInstructionStart = .L_op_nop jal MterpInvokeInterface beqzc v0, MterpException FETCH_ADVANCE_INST 3 + jal MterpShouldSwitchInterpreters + bnezc v0, MterpFallback GET_INST_OPCODE v0 GOTO_OPCODE v0 @@ -3316,6 +3340,7 @@ artMterpAsmInstructionStart = .L_op_nop /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ .extern MterpInvokeVirtualRange + .extern MterpShouldSwitchInterpreters EXPORT_PC move a0, rSELF daddu a1, rFP, OFF_FP_SHADOWFRAME @@ -3324,6 +3349,8 @@ artMterpAsmInstructionStart = .L_op_nop jal MterpInvokeVirtualRange beqzc v0, MterpException FETCH_ADVANCE_INST 3 + jal MterpShouldSwitchInterpreters + bnezc v0, MterpFallback GET_INST_OPCODE v0 GOTO_OPCODE v0 @@ -3339,6 +3366,7 @@ artMterpAsmInstructionStart = .L_op_nop /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ .extern MterpInvokeSuperRange + .extern MterpShouldSwitchInterpreters EXPORT_PC move a0, rSELF daddu a1, rFP, OFF_FP_SHADOWFRAME @@ -3347,6 +3375,8 @@ artMterpAsmInstructionStart = .L_op_nop jal MterpInvokeSuperRange beqzc v0, MterpException FETCH_ADVANCE_INST 3 + jal MterpShouldSwitchInterpreters + bnezc v0, MterpFallback GET_INST_OPCODE v0 GOTO_OPCODE v0 @@ -3362,6 +3392,7 @@ artMterpAsmInstructionStart = .L_op_nop /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ .extern MterpInvokeDirectRange + .extern MterpShouldSwitchInterpreters EXPORT_PC move a0, rSELF daddu a1, rFP, OFF_FP_SHADOWFRAME @@ -3370,6 +3401,8 @@ artMterpAsmInstructionStart = .L_op_nop jal MterpInvokeDirectRange beqzc v0, MterpException FETCH_ADVANCE_INST 3 + jal MterpShouldSwitchInterpreters + bnezc v0, MterpFallback GET_INST_OPCODE v0 GOTO_OPCODE v0 @@ -3385,6 +3418,7 @@ artMterpAsmInstructionStart = .L_op_nop /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ .extern MterpInvokeStaticRange + .extern MterpShouldSwitchInterpreters EXPORT_PC move a0, rSELF daddu a1, rFP, OFF_FP_SHADOWFRAME @@ -3393,6 +3427,8 @@ artMterpAsmInstructionStart = .L_op_nop jal MterpInvokeStaticRange beqzc v0, MterpException FETCH_ADVANCE_INST 3 + jal MterpShouldSwitchInterpreters + bnezc v0, MterpFallback GET_INST_OPCODE v0 GOTO_OPCODE v0 @@ -3408,6 +3444,7 @@ artMterpAsmInstructionStart = .L_op_nop /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ .extern MterpInvokeInterfaceRange + .extern MterpShouldSwitchInterpreters EXPORT_PC move a0, rSELF daddu a1, rFP, OFF_FP_SHADOWFRAME @@ -3416,6 +3453,8 @@ artMterpAsmInstructionStart = .L_op_nop jal MterpInvokeInterfaceRange beqzc v0, MterpException FETCH_ADVANCE_INST 3 + jal MterpShouldSwitchInterpreters + bnezc v0, MterpFallback GET_INST_OPCODE v0 GOTO_OPCODE v0 @@ -6962,6 +7001,7 @@ artMterpAsmInstructionStart = .L_op_nop /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ .extern MterpInvokeVirtualQuick + .extern MterpShouldSwitchInterpreters EXPORT_PC move a0, rSELF daddu a1, rFP, OFF_FP_SHADOWFRAME @@ -6970,6 +7010,8 @@ artMterpAsmInstructionStart = .L_op_nop jal MterpInvokeVirtualQuick beqzc v0, MterpException FETCH_ADVANCE_INST 3 + jal MterpShouldSwitchInterpreters + bnezc v0, MterpFallback GET_INST_OPCODE v0 GOTO_OPCODE v0 @@ -6985,6 +7027,7 @@ artMterpAsmInstructionStart = .L_op_nop /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ .extern MterpInvokeVirtualQuickRange + .extern MterpShouldSwitchInterpreters EXPORT_PC move a0, rSELF daddu a1, rFP, OFF_FP_SHADOWFRAME @@ -6993,6 +7036,8 @@ artMterpAsmInstructionStart = .L_op_nop jal MterpInvokeVirtualQuickRange beqzc v0, MterpException FETCH_ADVANCE_INST 3 + jal MterpShouldSwitchInterpreters + bnezc v0, MterpFallback GET_INST_OPCODE v0 GOTO_OPCODE v0 @@ -12256,6 +12301,7 @@ MterpPossibleException: * */ .extern MterpHandleException + .extern MterpShouldSwitchInterpreters MterpException: move a0, rSELF daddu a1, rFP, OFF_FP_SHADOWFRAME @@ -12266,8 +12312,11 @@ MterpException: REFRESH_IBASE daddu rPC, a0, CODEITEM_INSNS_OFFSET dlsa rPC, a1, rPC, 1 # generate new dex_pc_ptr - sd rPC, OFF_FP_DEX_PC_PTR(rFP) + /* Do we need to switch interpreters? */ + jal MterpShouldSwitchInterpreters + bnezc v0, MterpFallback /* resume execution at catch block */ + EXPORT_PC FETCH_INST GET_INST_OPCODE v0 GOTO_OPCODE v0 @@ -12288,9 +12337,23 @@ check1: EXPORT_PC move a0, rSELF jal MterpSuspendCheck # (self) + bnezc v0, MterpFallback # Something in the environment changed, switch interpreters GET_INST_OPCODE v0 # extract opcode from rINST GOTO_OPCODE v0 # jump to next instruction +/* + * On-stack replacement has happened, and now we've returned from the compiled method. + */ +MterpOnStackReplacement: +#if MTERP_LOGGING + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST # rINST contains offset + jal MterpLogOSR +#endif + li v0, 1 # Signal normal return + b MterpDone + /* * Bail out to reference interpreter. */ -- GitLab From 4464a3efcf8dcddfb00d7db0c3add9a7acb6642e Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Thu, 3 Mar 2016 20:15:47 -0800 Subject: [PATCH 122/204] ART: Fix UTF test and monitor pool old chunks Zero-initialize buffers in utf_test. UTF conversion does not append a zero terminator, but the test uses strlen to check the byte count. Move to unique_ptr for storing old monitor-pool chunk data to fix a leak. Bug: 27156726 Change-Id: I14424c6f98cf47beab6243f83a335bd6a682c638 --- runtime/monitor_pool.cc | 2 +- runtime/monitor_pool.h | 3 ++- runtime/utf_test.cc | 6 +++--- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/runtime/monitor_pool.cc b/runtime/monitor_pool.cc index 2832e32dd1..9e78cda190 100644 --- a/runtime/monitor_pool.cc +++ b/runtime/monitor_pool.cc @@ -51,7 +51,7 @@ void MonitorPool::AllocateChunk() { memcpy(new_backing, old_backing, sizeof(uintptr_t) * capacity_); monitor_chunks_.StoreRelaxed(new_backing); capacity_ = new_capacity; - old_chunk_arrays_.push_back(old_backing); + old_chunk_arrays_.push_back(std::unique_ptr(old_backing)); VLOG(monitor) << "Resizing to capacity " << capacity_; } } diff --git a/runtime/monitor_pool.h b/runtime/monitor_pool.h index 240ca61641..de553fc99b 100644 --- a/runtime/monitor_pool.h +++ b/runtime/monitor_pool.h @@ -176,7 +176,8 @@ class MonitorPool { size_t capacity_ GUARDED_BY(Locks::allocated_monitor_ids_lock_); // To avoid race issues when resizing, we keep all the previous arrays. - std::vector old_chunk_arrays_ GUARDED_BY(Locks::allocated_monitor_ids_lock_); + std::vector> old_chunk_arrays_ + GUARDED_BY(Locks::allocated_monitor_ids_lock_); typedef TrackingAllocator Allocator; Allocator allocator_; diff --git a/runtime/utf_test.cc b/runtime/utf_test.cc index c67879b427..328492523f 100644 --- a/runtime/utf_test.cc +++ b/runtime/utf_test.cc @@ -312,8 +312,8 @@ static void codePointToSurrogatePair(uint32_t code_point, uint16_t &first, uint1 } static void testConversions(uint16_t *buf, int char_count) { - char bytes_test[8], bytes_reference[8]; - uint16_t out_buf_test[4], out_buf_reference[4]; + char bytes_test[8] = { 0 }, bytes_reference[8] = { 0 }; + uint16_t out_buf_test[4] = { 0 }, out_buf_reference[4] = { 0 }; int byte_count_test, byte_count_reference; int char_count_test, char_count_reference; @@ -349,7 +349,7 @@ static void testConversions(uint16_t *buf, int char_count) { TEST_F(UtfTest, ExhaustiveBidirectionalCodePointCheck) { for (int codePoint = 0; codePoint <= 0x10ffff; ++codePoint) { - uint16_t buf[4]; + uint16_t buf[4] = { 0 }; if (codePoint <= 0xffff) { if (codePoint >= 0xd800 && codePoint <= 0xdfff) { // According to the Unicode standard, no character will ever -- GitLab From 9775934cb141deb096183f89449ca28ec7c11798 Mon Sep 17 00:00:00 2001 From: Chris Larsen Date: Tue, 16 Feb 2016 17:10:40 -0800 Subject: [PATCH 123/204] MIPS32: Highest/Lowest One Bit Set: - int java.lang.Integer.highestOneBit(int) - int java.lang.Integer.lowestOneBit(int) - long java.lang.Long.highestOneBit(long) - long java.lang.Long.lowestOneBit(long) Change-Id: I575ec6f70cf5cac620cc5d9e49ec3f7b554b4622 --- compiler/optimizing/intrinsics_mips.cc | 118 +++++++++++++++++++++++-- 1 file changed, 113 insertions(+), 5 deletions(-) diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc index 0622610ecf..710df0a822 100644 --- a/compiler/optimizing/intrinsics_mips.cc +++ b/compiler/optimizing/intrinsics_mips.cc @@ -1663,6 +1663,119 @@ void IntrinsicCodeGeneratorMIPS::VisitDoubleIsInfinite(HInvoke* invoke) { GenIsInfinite(invoke->GetLocations(), Primitive::kPrimDouble, IsR6(), GetAssembler()); } +static void GenHighestOneBit(LocationSummary* locations, + const Primitive::Type type, + bool isR6, + MipsAssembler* assembler) { + DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong); + + if (type == Primitive::kPrimLong) { + Register in_lo = locations->InAt(0).AsRegisterPairLow(); + Register in_hi = locations->InAt(0).AsRegisterPairHigh(); + Register out_lo = locations->Out().AsRegisterPairLow(); + Register out_hi = locations->Out().AsRegisterPairHigh(); + + if (isR6) { + __ ClzR6(TMP, in_hi); + } else { + __ ClzR2(TMP, in_hi); + } + __ LoadConst32(AT, 0x80000000); + __ Srlv(out_hi, AT, TMP); + __ And(out_hi, out_hi, in_hi); + if (isR6) { + __ ClzR6(TMP, in_lo); + } else { + __ ClzR2(TMP, in_lo); + } + __ Srlv(out_lo, AT, TMP); + __ And(out_lo, out_lo, in_lo); + if (isR6) { + __ Seleqz(out_lo, out_lo, out_hi); + } else { + __ Movn(out_lo, ZERO, out_hi); + } + } else { + Register in = locations->InAt(0).AsRegister(); + Register out = locations->Out().AsRegister(); + + if (isR6) { + __ ClzR6(TMP, in); + } else { + __ ClzR2(TMP, in); + } + __ LoadConst32(AT, 0x80000000); + __ Srlv(AT, AT, TMP); // Srlv shifts in the range of [0;31] bits (lower 5 bits of arg). + __ And(out, AT, in); // So this is required for 0 (=shift by 32). + } +} + +// int java.lang.Integer.highestOneBit(int) +void IntrinsicLocationsBuilderMIPS::VisitIntegerHighestOneBit(HInvoke* invoke) { + CreateIntToIntLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitIntegerHighestOneBit(HInvoke* invoke) { + GenHighestOneBit(invoke->GetLocations(), Primitive::kPrimInt, IsR6(), GetAssembler()); +} + +// long java.lang.Long.highestOneBit(long) +void IntrinsicLocationsBuilderMIPS::VisitLongHighestOneBit(HInvoke* invoke) { + CreateIntToIntLocations(arena_, invoke, Location::kOutputOverlap); +} + +void IntrinsicCodeGeneratorMIPS::VisitLongHighestOneBit(HInvoke* invoke) { + GenHighestOneBit(invoke->GetLocations(), Primitive::kPrimLong, IsR6(), GetAssembler()); +} + +static void GenLowestOneBit(LocationSummary* locations, + const Primitive::Type type, + bool isR6, + MipsAssembler* assembler) { + DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong); + + if (type == Primitive::kPrimLong) { + Register in_lo = locations->InAt(0).AsRegisterPairLow(); + Register in_hi = locations->InAt(0).AsRegisterPairHigh(); + Register out_lo = locations->Out().AsRegisterPairLow(); + Register out_hi = locations->Out().AsRegisterPairHigh(); + + __ Subu(TMP, ZERO, in_lo); + __ And(out_lo, TMP, in_lo); + __ Subu(TMP, ZERO, in_hi); + __ And(out_hi, TMP, in_hi); + if (isR6) { + __ Seleqz(out_hi, out_hi, out_lo); + } else { + __ Movn(out_hi, ZERO, out_lo); + } + } else { + Register in = locations->InAt(0).AsRegister(); + Register out = locations->Out().AsRegister(); + + __ Subu(TMP, ZERO, in); + __ And(out, TMP, in); + } +} + +// int java.lang.Integer.lowestOneBit(int) +void IntrinsicLocationsBuilderMIPS::VisitIntegerLowestOneBit(HInvoke* invoke) { + CreateIntToIntLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitIntegerLowestOneBit(HInvoke* invoke) { + GenLowestOneBit(invoke->GetLocations(), Primitive::kPrimInt, IsR6(), GetAssembler()); +} + +// long java.lang.Long.lowestOneBit(long) +void IntrinsicLocationsBuilderMIPS::VisitLongLowestOneBit(HInvoke* invoke) { + CreateIntToIntLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitLongLowestOneBit(HInvoke* invoke) { + GenLowestOneBit(invoke->GetLocations(), Primitive::kPrimLong, IsR6(), GetAssembler()); +} + // Unimplemented intrinsics. UNIMPLEMENTED_INTRINSIC(MIPS, MathCeil) @@ -1719,11 +1832,6 @@ UNIMPLEMENTED_INTRINSIC(MIPS, MathSinh) UNIMPLEMENTED_INTRINSIC(MIPS, MathTan) UNIMPLEMENTED_INTRINSIC(MIPS, MathTanh) -UNIMPLEMENTED_INTRINSIC(MIPS, IntegerHighestOneBit) -UNIMPLEMENTED_INTRINSIC(MIPS, LongHighestOneBit) -UNIMPLEMENTED_INTRINSIC(MIPS, IntegerLowestOneBit) -UNIMPLEMENTED_INTRINSIC(MIPS, LongLowestOneBit) - UNREACHABLE_INTRINSICS(MIPS) #undef UNIMPLEMENTED_INTRINSIC -- GitLab From 457413a6d7b657ca5e4567b7be2d9c300c6cbb5b Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Fri, 4 Mar 2016 11:10:17 +0000 Subject: [PATCH 124/204] Fix lint issue. Change-Id: I549cc641510a7f941d85f3a5f38127bc6701a0a3 --- compiler/optimizing/intrinsics_arm64.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc index 2e1198c515..cc757ab8b5 100644 --- a/compiler/optimizing/intrinsics_arm64.cc +++ b/compiler/optimizing/intrinsics_arm64.cc @@ -376,13 +376,13 @@ static void GenBitCount(HInvoke* instr, bool is_long, vixl::MacroAssembler* masm Location out = instr->GetLocations()->Out(); UseScratchRegisterScope temps(masm); - Register src = InputRegisterAt(instr, 0); + Register src = InputRegisterAt(instr, 0); + Register dst = is_long ? XRegisterFrom(out) : WRegisterFrom(out); FPRegister fpr = is_long ? temps.AcquireD() : temps.AcquireS(); - Register dst = is_long ? XRegisterFrom(out) : WRegisterFrom(out); __ Fmov(fpr, src); - __ Cnt (fpr.V8B(), fpr.V8B()); - __ Addv(fpr.B(), fpr.V8B()); + __ Cnt(fpr.V8B(), fpr.V8B()); + __ Addv(fpr.B(), fpr.V8B()); __ Fmov(dst, fpr); } -- GitLab From 0c6e3342dfc5e234f05c0bf783af6cc469dbb160 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Fri, 4 Mar 2016 11:38:14 +0000 Subject: [PATCH 125/204] Move back the boot image to debuggable. Full frame deoptimization is broken with it. Partial revert of https://android-review.googlesource.com/#/c/201383/ Change-Id: I7a402d79b0882f81987e56869551840da7d553e0 --- dex2oat/dex2oat.cc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index d9a2f300d9..f25d748490 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -698,6 +698,12 @@ class Dex2Oat FINAL { Usage("Can't have both --image and (--app-image-fd or --app-image-file)"); } + if (IsBootImage()) { + // We need the boot image to always be debuggable. + // TODO: Remove this once we better deal with full frame deoptimization. + compiler_options_->debuggable_ = true; + } + if (oat_filenames_.empty() && oat_fd_ == -1) { Usage("Output must be supplied with either --oat-file or --oat-fd"); } -- GitLab From d3d0da5148063fef921613f9557520860496f2f8 Mon Sep 17 00:00:00 2001 From: Scott Wakeling Date: Mon, 29 Feb 2016 15:17:20 +0000 Subject: [PATCH 126/204] ARM64: Implement SystemArrayCopyChar intrinsic. Change-Id: I33f559139a38ddf20cacb8c997e38fa7663a4066 --- compiler/optimizing/intrinsics_arm64.cc | 238 +++++++++++++++++++++++- test/011-array-copy/src/Main.java | 12 ++ 2 files changed, 249 insertions(+), 1 deletion(-) diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc index cc757ab8b5..934b42762e 100644 --- a/compiler/optimizing/intrinsics_arm64.cc +++ b/compiler/optimizing/intrinsics_arm64.cc @@ -1707,7 +1707,243 @@ void IntrinsicCodeGeneratorARM64::VisitStringGetCharsNoCheck(HInvoke* invoke) { __ Bind(&done); } -UNIMPLEMENTED_INTRINSIC(ARM64, SystemArrayCopyChar) +// Mirrors ARRAYCOPY_SHORT_CHAR_ARRAY_THRESHOLD in libcore, so we can choose to use the native +// implementation there for longer copy lengths. +static constexpr int32_t kSystemArrayCopyThreshold = 32; + +static void SetSystemArrayCopyLocationRequires(LocationSummary* locations, + uint32_t at, + HInstruction* input) { + HIntConstant* const_input = input->AsIntConstant(); + if (const_input != nullptr && !vixl::Assembler::IsImmAddSub(const_input->GetValue())) { + locations->SetInAt(at, Location::RequiresRegister()); + } else { + locations->SetInAt(at, Location::RegisterOrConstant(input)); + } +} + +void IntrinsicLocationsBuilderARM64::VisitSystemArrayCopyChar(HInvoke* invoke) { + // Check to see if we have known failures that will cause us to have to bail out + // to the runtime, and just generate the runtime call directly. + HIntConstant* src_pos = invoke->InputAt(1)->AsIntConstant(); + HIntConstant* dst_pos = invoke->InputAt(3)->AsIntConstant(); + + // The positions must be non-negative. + if ((src_pos != nullptr && src_pos->GetValue() < 0) || + (dst_pos != nullptr && dst_pos->GetValue() < 0)) { + // We will have to fail anyways. + return; + } + + // The length must be >= 0 and not so long that we would (currently) prefer libcore's + // native implementation. + HIntConstant* length = invoke->InputAt(4)->AsIntConstant(); + if (length != nullptr) { + int32_t len = length->GetValue(); + if (len < 0 || len > kSystemArrayCopyThreshold) { + // Just call as normal. + return; + } + } + + ArenaAllocator* allocator = invoke->GetBlock()->GetGraph()->GetArena(); + LocationSummary* locations = new (allocator) LocationSummary(invoke, + LocationSummary::kCallOnSlowPath, + kIntrinsified); + // arraycopy(char[] src, int src_pos, char[] dst, int dst_pos, int length). + locations->SetInAt(0, Location::RequiresRegister()); + SetSystemArrayCopyLocationRequires(locations, 1, invoke->InputAt(1)); + locations->SetInAt(2, Location::RequiresRegister()); + SetSystemArrayCopyLocationRequires(locations, 3, invoke->InputAt(3)); + SetSystemArrayCopyLocationRequires(locations, 4, invoke->InputAt(4)); + + locations->AddTemp(Location::RequiresRegister()); + locations->AddTemp(Location::RequiresRegister()); + locations->AddTemp(Location::RequiresRegister()); +} + +static void CheckSystemArrayCopyPosition(vixl::MacroAssembler* masm, + const Location& pos, + const Register& input, + const Location& length, + SlowPathCodeARM64* slow_path, + const Register& input_len, + const Register& temp, + bool length_is_input_length = false) { + const int32_t length_offset = mirror::Array::LengthOffset().Int32Value(); + if (pos.IsConstant()) { + int32_t pos_const = pos.GetConstant()->AsIntConstant()->GetValue(); + if (pos_const == 0) { + if (!length_is_input_length) { + // Check that length(input) >= length. + __ Ldr(temp, MemOperand(input, length_offset)); + __ Cmp(temp, OperandFrom(length, Primitive::kPrimInt)); + __ B(slow_path->GetEntryLabel(), lt); + } + } else { + // Check that length(input) >= pos. + __ Ldr(input_len, MemOperand(input, length_offset)); + __ Subs(temp, input_len, pos_const); + __ B(slow_path->GetEntryLabel(), lt); + + // Check that (length(input) - pos) >= length. + __ Cmp(temp, OperandFrom(length, Primitive::kPrimInt)); + __ B(slow_path->GetEntryLabel(), lt); + } + } else if (length_is_input_length) { + // The only way the copy can succeed is if pos is zero. + __ Cbnz(WRegisterFrom(pos), slow_path->GetEntryLabel()); + } else { + // Check that pos >= 0. + Register pos_reg = WRegisterFrom(pos); + __ Tbnz(pos_reg, pos_reg.size() - 1, slow_path->GetEntryLabel()); + + // Check that pos <= length(input) && (length(input) - pos) >= length. + __ Ldr(temp, MemOperand(input, length_offset)); + __ Subs(temp, temp, pos_reg); + // Ccmp if length(input) >= pos, else definitely bail to slow path (N!=V == lt). + __ Ccmp(temp, OperandFrom(length, Primitive::kPrimInt), NFlag, ge); + __ B(slow_path->GetEntryLabel(), lt); + } +} + +// Compute base source address, base destination address, and end source address +// for System.arraycopy* intrinsics. +static void GenSystemArrayCopyAddresses(vixl::MacroAssembler* masm, + Primitive::Type type, + const Register& src, + const Location& src_pos, + const Register& dst, + const Location& dst_pos, + const Location& copy_length, + const Register& src_base, + const Register& dst_base, + const Register& src_end) { + DCHECK(type == Primitive::kPrimNot || type == Primitive::kPrimChar) + << "Unexpected element type: " + << type; + const int32_t char_size = Primitive::ComponentSize(type); + const int32_t char_size_shift = Primitive::ComponentSizeShift(type); + + uint32_t offset = mirror::Array::DataOffset(char_size).Uint32Value(); + if (src_pos.IsConstant()) { + int32_t constant = src_pos.GetConstant()->AsIntConstant()->GetValue(); + __ Add(src_base, src, char_size * constant + offset); + } else { + __ Add(src_base, src, offset); + __ Add(src_base, + src_base, + Operand(XRegisterFrom(src_pos), LSL, char_size_shift)); + } + + if (dst_pos.IsConstant()) { + int32_t constant = dst_pos.GetConstant()->AsIntConstant()->GetValue(); + __ Add(dst_base, dst, char_size * constant + offset); + } else { + __ Add(dst_base, dst, offset); + __ Add(dst_base, + dst_base, + Operand(XRegisterFrom(dst_pos), LSL, char_size_shift)); + } + + if (copy_length.IsConstant()) { + int32_t constant = copy_length.GetConstant()->AsIntConstant()->GetValue(); + __ Add(src_end, src_base, char_size * constant); + } else { + __ Add(src_end, + src_base, + Operand(XRegisterFrom(copy_length), LSL, char_size_shift)); + } +} + +void IntrinsicCodeGeneratorARM64::VisitSystemArrayCopyChar(HInvoke* invoke) { + vixl::MacroAssembler* masm = GetVIXLAssembler(); + LocationSummary* locations = invoke->GetLocations(); + Register src = XRegisterFrom(locations->InAt(0)); + Location src_pos = locations->InAt(1); + Register dst = XRegisterFrom(locations->InAt(2)); + Location dst_pos = locations->InAt(3); + Location length = locations->InAt(4); + + SlowPathCodeARM64* slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke); + codegen_->AddSlowPath(slow_path); + + // If source and destination are the same, take the slow path. Overlapping copy regions must be + // copied in reverse and we can't know in all cases if it's needed. + __ Cmp(src, dst); + __ B(slow_path->GetEntryLabel(), eq); + + // Bail out if the source is null. + __ Cbz(src, slow_path->GetEntryLabel()); + + // Bail out if the destination is null. + __ Cbz(dst, slow_path->GetEntryLabel()); + + if (!length.IsConstant()) { + // If the length is negative, bail out. + __ Tbnz(WRegisterFrom(length), kWRegSize - 1, slow_path->GetEntryLabel()); + // If the length > 32 then (currently) prefer libcore's native implementation. + __ Cmp(WRegisterFrom(length), kSystemArrayCopyThreshold); + __ B(slow_path->GetEntryLabel(), gt); + } else { + // We have already checked in the LocationsBuilder for the constant case. + DCHECK_GE(length.GetConstant()->AsIntConstant()->GetValue(), 0); + DCHECK_LE(length.GetConstant()->AsIntConstant()->GetValue(), 32); + } + + Register src_curr_addr = WRegisterFrom(locations->GetTemp(0)); + Register dst_curr_addr = WRegisterFrom(locations->GetTemp(1)); + Register src_stop_addr = WRegisterFrom(locations->GetTemp(2)); + + CheckSystemArrayCopyPosition(masm, + src_pos, + src, + length, + slow_path, + src_curr_addr, + dst_curr_addr, + false); + + CheckSystemArrayCopyPosition(masm, + dst_pos, + dst, + length, + slow_path, + src_curr_addr, + dst_curr_addr, + false); + + src_curr_addr = src_curr_addr.X(); + dst_curr_addr = dst_curr_addr.X(); + src_stop_addr = src_stop_addr.X(); + + GenSystemArrayCopyAddresses(masm, + Primitive::kPrimChar, + src, + src_pos, + dst, + dst_pos, + length, + src_curr_addr, + dst_curr_addr, + src_stop_addr); + + // Iterate over the arrays and do a raw copy of the chars. + const int32_t char_size = Primitive::ComponentSize(Primitive::kPrimChar); + UseScratchRegisterScope temps(masm); + Register tmp = temps.AcquireW(); + vixl::Label loop, done; + __ Bind(&loop); + __ Cmp(src_curr_addr, src_stop_addr); + __ B(&done, eq); + __ Ldrh(tmp, MemOperand(src_curr_addr, char_size, vixl::PostIndex)); + __ Strh(tmp, MemOperand(dst_curr_addr, char_size, vixl::PostIndex)); + __ B(&loop); + __ Bind(&done); + + __ Bind(slow_path->GetExitLabel()); +} + UNIMPLEMENTED_INTRINSIC(ARM64, SystemArrayCopy) UNIMPLEMENTED_INTRINSIC(ARM64, ReferenceGetReferent) UNIMPLEMENTED_INTRINSIC(ARM64, FloatIsInfinite) diff --git a/test/011-array-copy/src/Main.java b/test/011-array-copy/src/Main.java index 96e1dbf21a..d9b61e7acf 100644 --- a/test/011-array-copy/src/Main.java +++ b/test/011-array-copy/src/Main.java @@ -69,6 +69,11 @@ public class Main { array[i] = (long) i; } } + static void initCharArray(char[] array) { + for (int i = 0; i < ARRAY_SIZE; i++) { + array[i] = (char) i; + } + } /* * Perform an array copy operation on primitive arrays with different @@ -79,16 +84,19 @@ public class Main { short[] shortArray = new short[ARRAY_SIZE]; int[] intArray = new int[ARRAY_SIZE]; long[] longArray = new long[ARRAY_SIZE]; + char[] charArray = new char[ARRAY_SIZE]; initByteArray(byteArray); initShortArray(shortArray); initIntArray(intArray); initLongArray(longArray); + initCharArray(charArray); System.arraycopy(byteArray, srcPos, byteArray, dstPos, length); System.arraycopy(shortArray, srcPos, shortArray, dstPos, length); System.arraycopy(intArray, srcPos, intArray, dstPos, length); System.arraycopy(longArray, srcPos, longArray, dstPos, length); + System.arraycopy(charArray, srcPos, charArray, dstPos, length); for (int i = 0; i < ARRAY_SIZE; i++) { if (intArray[i] != byteArray[i]) { @@ -103,6 +111,10 @@ public class Main { System.out.println("mismatch int vs long at " + i + " : " + Arrays.toString(longArray)); break; + } else if (intArray[i] != charArray[i]) { + System.out.println("mismatch int vs char at " + i + " : " + + Arrays.toString(charArray)); + break; } } -- GitLab From 98c38e34357a73c46ad7a735dd23da0ba29a2963 Mon Sep 17 00:00:00 2001 From: Narayan Kamath Date: Fri, 4 Mar 2016 14:40:32 +0000 Subject: [PATCH 127/204] Suppress ArrayDequeTest#testForEachRemaining temporarily. Change-Id: I297a392d694b8143010084469c401f15a887f2f1 --- tools/libcore_failures.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tools/libcore_failures.txt b/tools/libcore_failures.txt index 46100ae15c..fab4599925 100644 --- a/tools/libcore_failures.txt +++ b/tools/libcore_failures.txt @@ -270,5 +270,10 @@ description: "Only work with --mode=activity", result: EXEC_FAILED, names: [ "libcore.java.io.FileTest#testJavaIoTmpdirMutable" ] +}, +{ + description: "Temporary suppressing while test is fixed", + result: EXEC_FAILED, + names: [ "org.apache.harmony.tests.java.util.ArrayDequeTest#test_forEachRemaining_iterator" ] } ] -- GitLab From 9abb2978f09643227664ab70c0677744b8f6e946 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Fri, 4 Mar 2016 14:32:59 +0000 Subject: [PATCH 128/204] Fix very rare bug around JIT code cache collection. The bug is the following: 1) JIT thread: We start a code cache collection. 2) JIT thread: We mark all code that is in the call stack of all threads. 3) Mutator thread: after marking its stack, resumes and does call that pushes JIT compiled code to the call stack. 4) Mutator thread: deoptimizes compiled code of ArtMethod Foo, and therefore updates the entry point of Foo through JitCodeCache::InvalidateCompiledCodeFor. (Note that updating the entrypoint could also be done through instrumentation). 5) JIT thread: Call JitCodeCache::RemoveUnusedAndUnmarkedCode. The method used to remove entries that were not entrypoints. It sees the compiled code for Foo but that is not an entrypoint anymore, so deletes it. 6) Mutator thread problem: it now has compiled code in its call stack that is deleted. If it's only one mutator thread, we only hit a DCHECK when walking the stack, as we are now seeing an invalid pc. The deoptimization will longjmp to the caller of that invalid entry anyway. However, if multiple mutator threads are involved, one thread might invalidate the compiled code while the other is still running it. And we end up deleting code that is in the call stack of a thread, and we will crash. The fix is to mark entrypoints before marking call stacks, so that anything a thread might jump to is marked and kept. bug:27424509 bug:23128949 bug:26846185 Change-Id: I07cd08cedd96b9900629f7535e95404f622104ea --- runtime/jit/jit_code_cache.cc | 32 ++++++++++++++++++++++---------- runtime/jit/jit_code_cache.h | 2 +- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index 8c69bc8104..e5be2a4771 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -639,21 +639,19 @@ void JitCodeCache::GarbageCollectCache(Thread* self) { Runtime::Current()->GetJit()->AddTimingLogger(logger); } -void JitCodeCache::RemoveUnusedAndUnmarkedCode(Thread* self) { +void JitCodeCache::RemoveUnmarkedCode(Thread* self) { MutexLock mu(self, lock_); ScopedCodeCacheWrite scc(code_map_.get()); - // Iterate over all compiled code and remove entries that are not marked and not - // the entrypoint of their corresponding ArtMethod. + // Iterate over all compiled code and remove entries that are not marked. for (auto it = method_code_map_.begin(); it != method_code_map_.end();) { const void* code_ptr = it->first; ArtMethod* method = it->second; uintptr_t allocation = FromCodeToAllocation(code_ptr); - const OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromCodePointer(code_ptr); - const void* entrypoint = method->GetEntryPointFromQuickCompiledCode(); - if ((entrypoint == method_header->GetEntryPoint()) || GetLiveBitmap()->Test(allocation)) { + if (GetLiveBitmap()->Test(allocation)) { ++it; } else { - if (entrypoint == GetQuickToInterpreterBridge()) { + const OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromCodePointer(code_ptr); + if (method_header->GetEntryPoint() == GetQuickToInterpreterBridge()) { method->ClearCounter(); } FreeCode(code_ptr, method); @@ -682,6 +680,19 @@ void JitCodeCache::DoCollection(Thread* self, bool collect_profiling_info) { } } + // Mark compiled code that are entrypoints of ArtMethods. Compiled code that is not + // an entry point is either: + // - an osr compiled code, that will be removed if not in a thread call stack. + // - discarded compiled code, that will be removed if not in a thread call stack. + for (const auto& it : method_code_map_) { + ArtMethod* method = it.second; + const void* code_ptr = it.first; + const OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromCodePointer(code_ptr); + if (method_header->GetEntryPoint() == method->GetEntryPointFromQuickCompiledCode()) { + GetLiveBitmap()->AtomicTestAndSet(FromCodeToAllocation(code_ptr)); + } + } + // Empty osr method map, as osr compiled code will be deleted (except the ones // on thread stacks). osr_code_map_.clear(); @@ -690,9 +701,10 @@ void JitCodeCache::DoCollection(Thread* self, bool collect_profiling_info) { // Run a checkpoint on all threads to mark the JIT compiled code they are running. MarkCompiledCodeOnThreadStacks(self); - // Remove compiled code that is not the entrypoint of their method and not in the call - // stack. - RemoveUnusedAndUnmarkedCode(self); + // At this point, mutator threads are still running, and entrypoints of methods can + // change. We do know they cannot change to a code cache entry that is not marked, + // therefore we can safely remove those entries. + RemoveUnmarkedCode(self); if (collect_profiling_info) { MutexLock mu(self, lock_); diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h index 2a41a70dcf..0bd4f7dd1b 100644 --- a/runtime/jit/jit_code_cache.h +++ b/runtime/jit/jit_code_cache.h @@ -238,7 +238,7 @@ class JitCodeCache { REQUIRES(!lock_) SHARED_REQUIRES(Locks::mutator_lock_); - void RemoveUnusedAndUnmarkedCode(Thread* self) + void RemoveUnmarkedCode(Thread* self) REQUIRES(!lock_) SHARED_REQUIRES(Locks::mutator_lock_); -- GitLab From 80d9c85e19f00d4e148ac5a7378a9222244e67e4 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Fri, 4 Mar 2016 15:28:35 +0000 Subject: [PATCH 129/204] Make sure we keep adb logs on buildbots. Change-Id: I79239967a74eaef6d10b546407b223422741d157 --- tools/setup-buildbot-device.sh | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tools/setup-buildbot-device.sh b/tools/setup-buildbot-device.sh index 45b60dc647..9e085b58b4 100755 --- a/tools/setup-buildbot-device.sh +++ b/tools/setup-buildbot-device.sh @@ -37,6 +37,14 @@ adb shell uptime echo -e "${green}Battery info${nc}" adb shell dumpsys battery +echo -e "${green}Setting adb buffer size to 32MB${nc}" +adb logcat -G 32M +adb logcat -g + +echo -e "${green}Removing adb spam filter${nc}" +adb logcat -P "" +adb logcat -p + echo -e "${green}Kill stalled dalvikvm processes${nc}" processes=$(adb shell "ps" | grep dalvikvm | awk '{print $2}') for i in $processes; do adb shell kill -9 $i; done -- GitLab From 02742409f5ce9a2b452fbb810f476b6c1c256143 Mon Sep 17 00:00:00 2001 From: David Srbecky Date: Fri, 4 Mar 2016 14:42:05 +0000 Subject: [PATCH 130/204] Re-enable CFI test for compressed symbols. This enables the test for remote unwinding (modelling debuggerd), but it does not test local unwinding since it is disabled for performance reasons in libunwind. Bug: 27391690 Change-Id: I28451576c421bbd70259d757332a178f29bf3646 --- test/137-cfi/expected.txt | 1 + test/137-cfi/run | 7 +++-- test/137-cfi/src/Main.java | 63 ++++++++++++++++++++------------------ 3 files changed, 39 insertions(+), 32 deletions(-) diff --git a/test/137-cfi/expected.txt b/test/137-cfi/expected.txt index 6a5618ebc6..8db7853696 100644 --- a/test/137-cfi/expected.txt +++ b/test/137-cfi/expected.txt @@ -1 +1,2 @@ JNI_OnLoad called +JNI_OnLoad called diff --git a/test/137-cfi/run b/test/137-cfi/run index 8ec98c11dc..ebc729bc74 100755 --- a/test/137-cfi/run +++ b/test/137-cfi/run @@ -16,9 +16,10 @@ # Test with full DWARF debugging information. # Check full signatures of methods. -${RUN} "$@" -Xcompiler-option --generate-debug-info --args --full-signatures +${RUN} "$@" -Xcompiler-option --generate-debug-info \ + --args --full-signatures --args --test-local --args --test-remote # Test with minimal compressed debugging information. # Check only method names (parameters are omitted to save space). -# Temporarily disable due to bug 27172087 (leak/race in libunwind). -# ${RUN} "$@" -Xcompiler-option --generate-mini-debug-info +# Check only remote unwinding since decompression is disabled in local unwinds (b/27391690). +${RUN} "$@" -Xcompiler-option --generate-mini-debug-info --args --test-remote diff --git a/test/137-cfi/src/Main.java b/test/137-cfi/src/Main.java index d60a4ebba8..5cfe33dc59 100644 --- a/test/137-cfi/src/Main.java +++ b/test/137-cfi/src/Main.java @@ -21,43 +21,48 @@ import java.util.Arrays; import java.util.Comparator; public class Main implements Comparator
{ - // Whether to test local unwinding. Libunwind uses linker info to find executables. As we do - // not dlopen at the moment, this doesn't work, so keep it off for now. - public final static boolean TEST_LOCAL_UNWINDING = true; + // Whether to test local unwinding. + private boolean testLocal; - // Unwinding another process, modelling debuggerd. This doesn't use the linker, so should work - // no matter whether we're using dlopen or not. - public final static boolean TEST_REMOTE_UNWINDING = true; + // Unwinding another process, modelling debuggerd. + private boolean testRemote; + // We fork ourself to create the secondary process for remote unwinding. private boolean secondary; - private boolean full_signatures; + // Expect the symbols to contain full method signatures including parameters. + private boolean fullSignatures; private boolean passed; - public Main(boolean secondary, boolean full_signatures) { - this.secondary = secondary; - this.full_signatures = full_signatures; + public Main(String[] args) throws Exception { + System.loadLibrary(args[0]); + for (String arg : args) { + if (arg.equals("--test-local")) { + testLocal = true; + } + if (arg.equals("--test-remote")) { + testRemote = true; + } + if (arg.equals("--secondary")) { + secondary = true; + } + if (arg.equals("--full-signatures")) { + fullSignatures = true; + } + } + if (!testLocal && !testRemote) { + System.out.println("No test selected."); + } } public static void main(String[] args) throws Exception { - System.loadLibrary(args[0]); - boolean secondary = false; - boolean full_signatures = false; - for (String arg : args) { - if (arg.equals("--secondary")) { - secondary = true; - } - if (arg.equals("--full-signatures")) { - full_signatures = true; - } - } - new Main(secondary, full_signatures).run(); + new Main(args).run(); } private void run() { if (secondary) { - if (!TEST_REMOTE_UNWINDING) { + if (!testRemote) { throw new RuntimeException("Should not be running secondary!"); } runSecondary(); @@ -73,11 +78,11 @@ public class Main implements Comparator
{ private void runPrimary() { // First do the in-process unwinding. - if (TEST_LOCAL_UNWINDING && !foo()) { + if (testLocal && !foo()) { System.out.println("Unwinding self failed."); } - if (!TEST_REMOTE_UNWINDING) { + if (!testRemote) { // Skip the remote step. return; } @@ -105,7 +110,7 @@ public class Main implements Comparator
{ throw new RuntimeException(e); } - if (!unwindOtherProcess(full_signatures, pid)) { + if (!unwindOtherProcess(fullSignatures, pid)) { System.out.println("Unwinding other process failed."); } } finally { @@ -163,7 +168,7 @@ public class Main implements Comparator
{ if (b) { return sleep(2, b, 1.0); } else { - return unwindInProcess(full_signatures, 1, b); + return unwindInProcess(fullSignatures, 1, b); } } @@ -171,6 +176,6 @@ public class Main implements Comparator
{ public native boolean sleep(int i, boolean b, double dummy); - public native boolean unwindInProcess(boolean full_signatures, int i, boolean b); - public native boolean unwindOtherProcess(boolean full_signatures, int pid); + public native boolean unwindInProcess(boolean fullSignatures, int i, boolean b); + public native boolean unwindOtherProcess(boolean fullSignatures, int pid); } -- GitLab From 2ed15b61105b0f8ce811c32725bb9a1b6142c3a7 Mon Sep 17 00:00:00 2001 From: David Srbecky Date: Fri, 4 Mar 2016 11:34:46 +0000 Subject: [PATCH 131/204] Cache DexRegisterMaps when writing native debug info. I might make the function more expensive in the future so I want to make sure it gets called only the minimum number of times. Change-Id: I1d09ecf1db7b54d28aaa11a152226d469f514fe7 --- compiler/debug/elf_debug_info_writer.h | 28 ++++++++++++++++++++++---- compiler/debug/elf_debug_loc_writer.h | 27 +++++++++++++++---------- 2 files changed, 40 insertions(+), 15 deletions(-) diff --git a/compiler/debug/elf_debug_info_writer.h b/compiler/debug/elf_debug_info_writer.h index eed032f88d..bddb054b80 100644 --- a/compiler/debug/elf_debug_info_writer.h +++ b/compiler/debug/elf_debug_info_writer.h @@ -173,6 +173,19 @@ class ElfCompilationUnitWriter { info_.WriteExprLoc(DW_AT_frame_base, expr); WriteLazyType(dex->GetReturnTypeDescriptor(dex_proto)); + // Decode dex register locations for all stack maps. + // It might be expensive, so do it just once and reuse the result. + std::vector dex_reg_maps; + if (mi->IsFromOptimizingCompiler()) { + const CodeInfo code_info(mi->compiled_method->GetVmapTable().data()); + StackMapEncoding encoding = code_info.ExtractEncoding(); + for (size_t s = 0; s < code_info.GetNumberOfStackMaps(); ++s) { + const StackMap& stack_map = code_info.GetStackMapAt(s, encoding); + dex_reg_maps.push_back(code_info.GetDexRegisterMapOf( + stack_map, encoding, dex_code->registers_size_)); + } + } + // Write parameters. DecodeDebugLocalInfo returns them as well, but it does not // guarantee order or uniqueness so it is safer to iterate over them manually. // DecodeDebugLocalInfo might not also be available if there is no debug info. @@ -187,7 +200,7 @@ class ElfCompilationUnitWriter { // Write the stack location of the parameter. const uint32_t vreg = dex_code->registers_size_ - dex_code->ins_size_ + arg_reg; const bool is64bitValue = false; - WriteRegLocation(mi, vreg, is64bitValue, compilation_unit.low_pc); + WriteRegLocation(mi, dex_reg_maps, vreg, is64bitValue, compilation_unit.low_pc); } arg_reg++; info_.EndTag(); @@ -206,7 +219,7 @@ class ElfCompilationUnitWriter { if (dex_code != nullptr) { // Write the stack location of the parameter. const uint32_t vreg = dex_code->registers_size_ - dex_code->ins_size_ + arg_reg; - WriteRegLocation(mi, vreg, is64bitValue, compilation_unit.low_pc); + WriteRegLocation(mi, dex_reg_maps, vreg, is64bitValue, compilation_unit.low_pc); } arg_reg += is64bitValue ? 2 : 1; info_.EndTag(); @@ -229,8 +242,13 @@ class ElfCompilationUnitWriter { WriteName(var.name_); WriteLazyType(var.descriptor_); bool is64bitValue = var.descriptor_[0] == 'D' || var.descriptor_[0] == 'J'; - WriteRegLocation(mi, var.reg_, is64bitValue, compilation_unit.low_pc, - var.start_address_, var.end_address_); + WriteRegLocation(mi, + dex_reg_maps, + var.reg_, + is64bitValue, + compilation_unit.low_pc, + var.start_address_, + var.end_address_); info_.EndTag(); } } @@ -424,12 +442,14 @@ class ElfCompilationUnitWriter { // The dex register might be valid only at some points and it might // move between machine registers and stack. void WriteRegLocation(const MethodDebugInfo* method_info, + const std::vector& dex_register_maps, uint16_t vreg, bool is64bitValue, uint32_t compilation_unit_low_pc, uint32_t dex_pc_low = 0, uint32_t dex_pc_high = 0xFFFFFFFF) { WriteDebugLocEntry(method_info, + dex_register_maps, vreg, is64bitValue, compilation_unit_low_pc, diff --git a/compiler/debug/elf_debug_loc_writer.h b/compiler/debug/elf_debug_loc_writer.h index 32f624acd3..c321b4bc4f 100644 --- a/compiler/debug/elf_debug_loc_writer.h +++ b/compiler/debug/elf_debug_loc_writer.h @@ -85,29 +85,32 @@ struct VariableLocation { // The result will cover all ranges where the variable is in scope. // PCs corresponding to stackmap with dex register map are accurate, // all other PCs are best-effort only. -std::vector GetVariableLocations(const MethodDebugInfo* method_info, - uint16_t vreg, - bool is64bitValue, - uint32_t dex_pc_low, - uint32_t dex_pc_high) { +std::vector GetVariableLocations( + const MethodDebugInfo* method_info, + const std::vector& dex_register_maps, + uint16_t vreg, + bool is64bitValue, + uint32_t dex_pc_low, + uint32_t dex_pc_high) { std::vector variable_locations; // Get stack maps sorted by pc (they might not be sorted internally). const CodeInfo code_info(method_info->compiled_method->GetVmapTable().data()); const StackMapEncoding encoding = code_info.ExtractEncoding(); - std::map stack_maps; + std::map stack_maps; // low_pc -> stack_map_index. for (uint32_t s = 0; s < code_info.GetNumberOfStackMaps(); s++) { StackMap stack_map = code_info.GetStackMapAt(s, encoding); DCHECK(stack_map.IsValid()); const uint32_t low_pc = method_info->low_pc + stack_map.GetNativePcOffset(encoding); DCHECK_LE(low_pc, method_info->high_pc); - stack_maps.emplace(low_pc, stack_map); + stack_maps.emplace(low_pc, s); } // Create entries for the requested register based on stack map data. for (auto it = stack_maps.begin(); it != stack_maps.end(); it++) { - const StackMap& stack_map = it->second; const uint32_t low_pc = it->first; + const uint32_t stack_map_index = it->second; + const StackMap& stack_map = code_info.GetStackMapAt(stack_map_index, encoding); auto next_it = it; next_it++; const uint32_t high_pc = next_it != stack_maps.end() ? next_it->first @@ -126,9 +129,9 @@ std::vector GetVariableLocations(const MethodDebugInfo* method // Find the location of the dex register. DexRegisterLocation reg_lo = DexRegisterLocation::None(); DexRegisterLocation reg_hi = DexRegisterLocation::None(); - if (stack_map.HasDexRegisterMap(encoding)) { - DexRegisterMap dex_register_map = code_info.GetDexRegisterMapOf( - stack_map, encoding, method_info->code_item->registers_size_); + DCHECK_LT(stack_map_index, dex_register_maps.size()); + DexRegisterMap dex_register_map = dex_register_maps[stack_map_index]; + if (dex_register_map.IsValid()) { reg_lo = dex_register_map.GetDexRegisterLocation( vreg, method_info->code_item->registers_size_, code_info, encoding); if (is64bitValue) { @@ -159,6 +162,7 @@ std::vector GetVariableLocations(const MethodDebugInfo* method // The dex register might be valid only at some points and it might // move between machine registers and stack. static void WriteDebugLocEntry(const MethodDebugInfo* method_info, + const std::vector& dex_register_maps, uint16_t vreg, bool is64bitValue, uint32_t compilation_unit_low_pc, @@ -175,6 +179,7 @@ static void WriteDebugLocEntry(const MethodDebugInfo* method_info, std::vector variable_locations = GetVariableLocations( method_info, + dex_register_maps, vreg, is64bitValue, dex_pc_low, -- GitLab From f1e74af7c84e0bdb37a8fc901852dfca125fc8dd Mon Sep 17 00:00:00 2001 From: Roland Levillain Date: Fri, 4 Mar 2016 15:55:51 +0000 Subject: [PATCH 132/204] Typo in comment in MIPS32 mterp. Change-Id: I2a54b94a5a4451e085ecc575db1742a4478839a6 --- runtime/interpreter/mterp/mips/binop.S | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/runtime/interpreter/mterp/mips/binop.S b/runtime/interpreter/mterp/mips/binop.S index ce09da453a..66627e2719 100644 --- a/runtime/interpreter/mterp/mips/binop.S +++ b/runtime/interpreter/mterp/mips/binop.S @@ -7,8 +7,8 @@ * * If "chkzero" is set to 1, we perform a divide-by-zero check on * vCC (a1). Useful for integer division and modulus. Note that we - * *don't* check for (INT_MIN / -1) here, because the ARM math lib - * handles it correctly. + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. * * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int, * xor-int, shl-int, shr-int, ushr-int -- GitLab From fee85561670331f38e0676baadc9be422d3d079c Mon Sep 17 00:00:00 2001 From: Roland Levillain Date: Fri, 4 Mar 2016 16:01:22 +0000 Subject: [PATCH 133/204] Ignore 145-alloc-tracking-stress failures in interpreter with CC. This run-test fails also with the interpreter on the concurrent collector configuration; disable it for now. Bug: 27467554 Change-Id: I54f76536b498ea2dedfd76d1bd77e1e26b415eb2 --- test/Android.run-test.mk | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index e05d4f54c5..c4f0171f0d 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -541,7 +541,9 @@ endif TEST_ART_BROKEN_OPTIMIZING_DEBUGGABLE_RUN_TESTS := # Tests that should fail in the read barrier configuration with the interpreter. -TEST_ART_BROKEN_INTERPRETER_READ_BARRIER_RUN_TESTS := +# 145: Test sometimes times out in read barrier configuration (b/27467554). +TEST_ART_BROKEN_INTERPRETER_READ_BARRIER_RUN_TESTS := \ + 145-alloc-tracking-stress # Tests that should fail in the read barrier configuration with the default (Quick) compiler (AOT). # Quick has no support for read barriers and punts to the interpreter, so this list is composed of -- GitLab From 96530d346917cf3877e7d0df9a05a6a3464fe432 Mon Sep 17 00:00:00 2001 From: buzbee Date: Fri, 4 Mar 2016 08:03:51 -0800 Subject: [PATCH 134/204] ART: Update arm assembly to use current syntax Some of the ancient mterp code uses old-style arm opcode names which are supported by gas, but not by clang's integrated assembler. Partial fix for internal b/27473367 Change-Id: Ic6128b98dfbf30f252e7487f802e9dfbe0a42b6a --- runtime/interpreter/mterp/arm/op_cmpg_double.S | 2 +- runtime/interpreter/mterp/arm/op_cmpg_float.S | 2 +- runtime/interpreter/mterp/arm/op_cmpl_double.S | 2 +- runtime/interpreter/mterp/arm/op_cmpl_float.S | 2 +- .../interpreter/mterp/arm/op_double_to_float.S | 2 +- .../interpreter/mterp/arm/op_float_to_double.S | 2 +- .../interpreter/mterp/arm/op_float_to_long.S | 6 +++--- runtime/interpreter/mterp/out/mterp_arm.S | 18 +++++++++--------- 8 files changed, 18 insertions(+), 18 deletions(-) diff --git a/runtime/interpreter/mterp/arm/op_cmpg_double.S b/runtime/interpreter/mterp/arm/op_cmpg_double.S index 4b05c44beb..602a4b1bfd 100644 --- a/runtime/interpreter/mterp/arm/op_cmpg_double.S +++ b/runtime/interpreter/mterp/arm/op_cmpg_double.S @@ -23,7 +23,7 @@ VREG_INDEX_TO_ADDR r3, r3 @ r3<- &vCC fldd d0, [r2] @ d0<- vBB fldd d1, [r3] @ d1<- vCC - fcmped d0, d1 @ compare (vBB, vCC) + vcmpe.f64 d0, d1 @ compare (vBB, vCC) FETCH_ADVANCE_INST 2 @ advance rPC, load rINST mov r0, #1 @ r0<- 1 (default) GET_INST_OPCODE ip @ extract opcode from rINST diff --git a/runtime/interpreter/mterp/arm/op_cmpg_float.S b/runtime/interpreter/mterp/arm/op_cmpg_float.S index d5d2df2ef5..965091f82d 100644 --- a/runtime/interpreter/mterp/arm/op_cmpg_float.S +++ b/runtime/interpreter/mterp/arm/op_cmpg_float.S @@ -23,7 +23,7 @@ VREG_INDEX_TO_ADDR r3, r3 @ r3<- &vCC flds s0, [r2] @ s0<- vBB flds s1, [r3] @ s1<- vCC - fcmpes s0, s1 @ compare (vBB, vCC) + vcmpe.f32 s0, s1 @ compare (vBB, vCC) FETCH_ADVANCE_INST 2 @ advance rPC, load rINST mov r0, #1 @ r0<- 1 (default) GET_INST_OPCODE ip @ extract opcode from rINST diff --git a/runtime/interpreter/mterp/arm/op_cmpl_double.S b/runtime/interpreter/mterp/arm/op_cmpl_double.S index 6ee53b301e..8a5e509ee8 100644 --- a/runtime/interpreter/mterp/arm/op_cmpl_double.S +++ b/runtime/interpreter/mterp/arm/op_cmpl_double.S @@ -23,7 +23,7 @@ VREG_INDEX_TO_ADDR r3, r3 @ r3<- &vCC fldd d0, [r2] @ d0<- vBB fldd d1, [r3] @ d1<- vCC - fcmped d0, d1 @ compare (vBB, vCC) + vcmpe.f64 d0, d1 @ compare (vBB, vCC) FETCH_ADVANCE_INST 2 @ advance rPC, load rINST mvn r0, #0 @ r0<- -1 (default) GET_INST_OPCODE ip @ extract opcode from rINST diff --git a/runtime/interpreter/mterp/arm/op_cmpl_float.S b/runtime/interpreter/mterp/arm/op_cmpl_float.S index 64535b68ae..9df0c2c171 100644 --- a/runtime/interpreter/mterp/arm/op_cmpl_float.S +++ b/runtime/interpreter/mterp/arm/op_cmpl_float.S @@ -23,7 +23,7 @@ VREG_INDEX_TO_ADDR r3, r3 @ r3<- &vCC flds s0, [r2] @ s0<- vBB flds s1, [r3] @ s1<- vCC - fcmpes s0, s1 @ compare (vBB, vCC) + vcmpe.f32 s0, s1 @ compare (vBB, vCC) FETCH_ADVANCE_INST 2 @ advance rPC, load rINST mvn r0, #0 @ r0<- -1 (default) GET_INST_OPCODE ip @ extract opcode from rINST diff --git a/runtime/interpreter/mterp/arm/op_double_to_float.S b/runtime/interpreter/mterp/arm/op_double_to_float.S index e327000409..98fdfbc64e 100644 --- a/runtime/interpreter/mterp/arm/op_double_to_float.S +++ b/runtime/interpreter/mterp/arm/op_double_to_float.S @@ -1 +1 @@ -%include "arm/funopNarrower.S" {"instr":"fcvtsd s0, d0"} +%include "arm/funopNarrower.S" {"instr":"vcvt.f32.f64 s0, d0"} diff --git a/runtime/interpreter/mterp/arm/op_float_to_double.S b/runtime/interpreter/mterp/arm/op_float_to_double.S index fb1892b6d0..b1e12bdc7a 100644 --- a/runtime/interpreter/mterp/arm/op_float_to_double.S +++ b/runtime/interpreter/mterp/arm/op_float_to_double.S @@ -1 +1 @@ -%include "arm/funopWider.S" {"instr":"fcvtds d0, s0"} +%include "arm/funopWider.S" {"instr":"vcvt.f64.f32 d0, s0"} diff --git a/runtime/interpreter/mterp/arm/op_float_to_long.S b/runtime/interpreter/mterp/arm/op_float_to_long.S index 24416d33d2..5c8680f133 100644 --- a/runtime/interpreter/mterp/arm/op_float_to_long.S +++ b/runtime/interpreter/mterp/arm/op_float_to_long.S @@ -17,7 +17,7 @@ f2l_doconv: cmp r0, #0 @ nonzero == yes mvnne r0, #0 @ return maxlong (7fffffff) mvnne r1, #0x80000000 - ldmnefd sp!, {r4, pc} + popne {r4, pc} mov r0, r4 @ recover arg mov r1, #0xdf000000 @ (float)minlong @@ -25,14 +25,14 @@ f2l_doconv: cmp r0, #0 @ nonzero == yes movne r0, #0 @ return minlong (80000000) movne r1, #0x80000000 - ldmnefd sp!, {r4, pc} + popne {r4, pc} mov r0, r4 @ recover arg mov r1, r4 bl __aeabi_fcmpeq @ is arg == self? cmp r0, #0 @ zero == no moveq r1, #0 @ return zero for NaN - ldmeqfd sp!, {r4, pc} + popeq {r4, pc} mov r0, r4 @ recover arg bl __aeabi_f2lz @ convert float to long diff --git a/runtime/interpreter/mterp/out/mterp_arm.S b/runtime/interpreter/mterp/out/mterp_arm.S index 2b74d4c86e..b26a63a5ee 100644 --- a/runtime/interpreter/mterp/out/mterp_arm.S +++ b/runtime/interpreter/mterp/out/mterp_arm.S @@ -1353,7 +1353,7 @@ artMterpAsmInstructionStart = .L_op_nop VREG_INDEX_TO_ADDR r3, r3 @ r3<- &vCC flds s0, [r2] @ s0<- vBB flds s1, [r3] @ s1<- vCC - fcmpes s0, s1 @ compare (vBB, vCC) + vcmpe.f32 s0, s1 @ compare (vBB, vCC) FETCH_ADVANCE_INST 2 @ advance rPC, load rINST mvn r0, #0 @ r0<- -1 (default) GET_INST_OPCODE ip @ extract opcode from rINST @@ -1392,7 +1392,7 @@ artMterpAsmInstructionStart = .L_op_nop VREG_INDEX_TO_ADDR r3, r3 @ r3<- &vCC flds s0, [r2] @ s0<- vBB flds s1, [r3] @ s1<- vCC - fcmpes s0, s1 @ compare (vBB, vCC) + vcmpe.f32 s0, s1 @ compare (vBB, vCC) FETCH_ADVANCE_INST 2 @ advance rPC, load rINST mov r0, #1 @ r0<- 1 (default) GET_INST_OPCODE ip @ extract opcode from rINST @@ -1431,7 +1431,7 @@ artMterpAsmInstructionStart = .L_op_nop VREG_INDEX_TO_ADDR r3, r3 @ r3<- &vCC fldd d0, [r2] @ d0<- vBB fldd d1, [r3] @ d1<- vCC - fcmped d0, d1 @ compare (vBB, vCC) + vcmpe.f64 d0, d1 @ compare (vBB, vCC) FETCH_ADVANCE_INST 2 @ advance rPC, load rINST mvn r0, #0 @ r0<- -1 (default) GET_INST_OPCODE ip @ extract opcode from rINST @@ -1470,7 +1470,7 @@ artMterpAsmInstructionStart = .L_op_nop VREG_INDEX_TO_ADDR r3, r3 @ r3<- &vCC fldd d0, [r2] @ d0<- vBB fldd d1, [r3] @ d1<- vCC - fcmped d0, d1 @ compare (vBB, vCC) + vcmpe.f64 d0, d1 @ compare (vBB, vCC) FETCH_ADVANCE_INST 2 @ advance rPC, load rINST mov r0, #1 @ r0<- 1 (default) GET_INST_OPCODE ip @ extract opcode from rINST @@ -3994,7 +3994,7 @@ constvalop_long_to_double: flds s0, [r3] @ s0<- vB FETCH_ADVANCE_INST 1 @ advance rPC, load rINST and r9, r9, #15 @ r9<- A - fcvtds d0, s0 @ d0<- op + vcvt.f64.f32 d0, s0 @ d0<- op CLEAR_SHADOW_PAIR r9, ip, lr @ Zero shadow regs GET_INST_OPCODE ip @ extract opcode from rINST VREG_INDEX_TO_ADDR r9, r9 @ r9<- &vA @@ -4075,7 +4075,7 @@ constvalop_long_to_double: fldd d0, [r3] @ d0<- vB FETCH_ADVANCE_INST 1 @ advance rPC, load rINST and r9, r9, #15 @ r9<- A - fcvtsd s0, d0 @ s0<- op + vcvt.f32.f64 s0, d0 @ s0<- op GET_INST_OPCODE ip @ extract opcode from rINST VREG_INDEX_TO_ADDR r9, r9 @ r9<- &vA fsts s0, [r9] @ vA<- s0 @@ -7673,7 +7673,7 @@ f2l_doconv: cmp r0, #0 @ nonzero == yes mvnne r0, #0 @ return maxlong (7fffffff) mvnne r1, #0x80000000 - ldmnefd sp!, {r4, pc} + popne {r4, pc} mov r0, r4 @ recover arg mov r1, #0xdf000000 @ (float)minlong @@ -7681,14 +7681,14 @@ f2l_doconv: cmp r0, #0 @ nonzero == yes movne r0, #0 @ return minlong (80000000) movne r1, #0x80000000 - ldmnefd sp!, {r4, pc} + popne {r4, pc} mov r0, r4 @ recover arg mov r1, r4 bl __aeabi_fcmpeq @ is arg == self? cmp r0, #0 @ zero == no moveq r1, #0 @ return zero for NaN - ldmeqfd sp!, {r4, pc} + popeq {r4, pc} mov r0, r4 @ recover arg bl __aeabi_f2lz @ convert float to long -- GitLab From 26329cd578efe9cd7e60c46fef1bfb549062a283 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Fri, 4 Mar 2016 16:43:27 +0000 Subject: [PATCH 135/204] Query declaring class before querying IsNative. IsNative checks that the declaring class is not null. Change-Id: I9b775858cdb685eb633b3d8a8a271a3d451bb8a9 --- runtime/mirror/class-inl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h index cbcb4b964b..103a8b79ef 100644 --- a/runtime/mirror/class-inl.h +++ b/runtime/mirror/class-inl.h @@ -940,7 +940,7 @@ void mirror::Class::VisitNativeRoots(Visitor& visitor, size_t pointer_size) { } for (ArtMethod& method : GetMethods(pointer_size)) { method.VisitRoots(visitor, pointer_size); - if (!method.IsNative()) { + if (method.GetDeclaringClassUnchecked() != nullptr && !method.IsNative()) { ProfilingInfo* profiling_info = method.GetProfilingInfo(pointer_size); if (profiling_info != nullptr) { profiling_info->VisitRoots(visitor); -- GitLab From 6973100705716bffce3768a8a0908d7ca1d02ec1 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Wed, 2 Mar 2016 16:08:31 -0800 Subject: [PATCH 136/204] Only visit app image classes in class loader Only update dex cache arrays of added classes since the declaring class is in image DCHECK fails for other classes in the class loader. Also some cleanup to prevent app images leaving invalid state if they get rejected. Bug: 22858531 Bug: 27431418 Change-Id: Ib2a5692a1ad78b014a1bfc6b27fb1c12bc8565e6 --- runtime/class_linker.cc | 151 ++++++++++++++++++++++++---------------- runtime/class_linker.h | 4 +- runtime/class_table.cc | 6 +- runtime/class_table.h | 61 ++++++++-------- 4 files changed, 133 insertions(+), 89 deletions(-) diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 5f26b5d7b5..e7708c9357 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -1198,7 +1198,7 @@ bool ClassLinker::UpdateAppImageClassLoadersAndDexCaches( gc::space::ImageSpace* space, Handle class_loader, Handle> dex_caches, - bool added_class_table, + ClassTable::ClassSet* new_class_set, bool* out_forward_dex_cache_array, std::string* out_error_msg) { DCHECK(out_forward_dex_cache_array != nullptr); @@ -1217,20 +1217,40 @@ bool ClassLinker::UpdateAppImageClassLoadersAndDexCaches( for (size_t i = 0; i < num_dex_caches; i++) { mirror::DexCache* const dex_cache = dex_caches->Get(i); const DexFile* const dex_file = dex_cache->GetDexFile(); - // If the oat file expects the dex cache arrays to be in the BSS, then allocate there and - // copy over the arrays. - DCHECK(dex_file != nullptr); - const size_t num_strings = dex_file->NumStringIds(); - const size_t num_types = dex_file->NumTypeIds(); - const size_t num_methods = dex_file->NumMethodIds(); - const size_t num_fields = dex_file->NumFieldIds(); - CHECK_EQ(num_strings, dex_cache->NumStrings()); - CHECK_EQ(num_types, dex_cache->NumResolvedTypes()); - CHECK_EQ(num_methods, dex_cache->NumResolvedMethods()); - CHECK_EQ(num_fields, dex_cache->NumResolvedFields()); const OatFile::OatDexFile* oat_dex_file = dex_file->GetOatDexFile(); if (oat_dex_file != nullptr && oat_dex_file->GetDexCacheArrays() != nullptr) { ++num_dex_caches_with_bss_arrays; + } + } + *out_forward_dex_cache_array = num_dex_caches_with_bss_arrays != 0; + if (*out_forward_dex_cache_array) { + if (num_dex_caches_with_bss_arrays != num_dex_caches) { + // Reject application image since we cannot forward only some of the dex cache arrays. + // TODO: We could get around this by having a dedicated forwarding slot. It should be an + // uncommon case. + *out_error_msg = StringPrintf("Dex caches in bss does not match total: %zu vs %zu", + num_dex_caches_with_bss_arrays, + num_dex_caches); + return false; + } + } + // Only add the classes to the class loader after the points where we can return false. + for (size_t i = 0; i < num_dex_caches; i++) { + mirror::DexCache* const dex_cache = dex_caches->Get(i); + const DexFile* const dex_file = dex_cache->GetDexFile(); + const OatFile::OatDexFile* oat_dex_file = dex_file->GetOatDexFile(); + if (oat_dex_file != nullptr && oat_dex_file->GetDexCacheArrays() != nullptr) { + // If the oat file expects the dex cache arrays to be in the BSS, then allocate there and + // copy over the arrays. + DCHECK(dex_file != nullptr); + const size_t num_strings = dex_file->NumStringIds(); + const size_t num_types = dex_file->NumTypeIds(); + const size_t num_methods = dex_file->NumMethodIds(); + const size_t num_fields = dex_file->NumFieldIds(); + CHECK_EQ(num_strings, dex_cache->NumStrings()); + CHECK_EQ(num_types, dex_cache->NumResolvedTypes()); + CHECK_EQ(num_methods, dex_cache->NumResolvedMethods()); + CHECK_EQ(num_fields, dex_cache->NumResolvedFields()); DexCacheArraysLayout layout(image_pointer_size_, dex_file); uint8_t* const raw_arrays = oat_dex_file->GetDexCacheArrays(); // The space is not yet visible to the GC, we can avoid the read barriers and use std::copy_n. @@ -1292,10 +1312,11 @@ bool ClassLinker::UpdateAppImageClassLoadersAndDexCaches( RegisterDexFileLocked(*dex_file, hs3.NewHandle(dex_cache)); } GcRoot* const types = dex_cache->GetResolvedTypes(); - if (!added_class_table) { + const size_t num_types = dex_cache->NumResolvedTypes(); + if (new_class_set == nullptr) { for (int32_t j = 0; j < static_cast(num_types); j++) { // The image space is not yet added to the heap, avoid read barriers. - mirror::Class* klass = types[j].Read(); + mirror::Class* klass = types[j].Read(); if (klass != nullptr) { DCHECK_NE(klass->GetStatus(), mirror::Class::kStatusError); // Update the class loader from the one in the image class loader to the one that loaded @@ -1341,14 +1362,26 @@ bool ClassLinker::UpdateAppImageClassLoadersAndDexCaches( if (kIsDebugBuild) { for (int32_t j = 0; j < static_cast(num_types); j++) { // The image space is not yet added to the heap, avoid read barriers. - mirror::Class* klass = types[j].Read(); + mirror::Class* klass = types[j].Read(); if (klass != nullptr) { DCHECK_NE(klass->GetStatus(), mirror::Class::kStatusError); if (kIsDebugBuild) { - DCHECK_EQ(table->LookupByDescriptor(klass), klass); - mirror::Class* super_class = klass->GetSuperClass(); - if (super_class != nullptr && !heap->ObjectIsInBootImageSpace(super_class)) { - CHECK_EQ(table->LookupByDescriptor(super_class), super_class); + if (new_class_set != nullptr) { + auto it = new_class_set->Find(GcRoot(klass)); + DCHECK(it != new_class_set->end()); + DCHECK_EQ(it->Read(), klass); + mirror::Class* super_class = klass->GetSuperClass(); + if (super_class != nullptr && !heap->ObjectIsInBootImageSpace(super_class)) { + auto it2 = new_class_set->Find(GcRoot(super_class)); + DCHECK(it2 != new_class_set->end()); + DCHECK_EQ(it2->Read(), super_class); + } + } else { + DCHECK_EQ(table->LookupByDescriptor(klass), klass); + mirror::Class* super_class = klass->GetSuperClass(); + if (super_class != nullptr && !heap->ObjectIsInBootImageSpace(super_class)) { + CHECK_EQ(table->LookupByDescriptor(super_class), super_class); + } } } if (kIsDebugBuild) { @@ -1362,7 +1395,6 @@ bool ClassLinker::UpdateAppImageClassLoadersAndDexCaches( DCHECK_EQ(code, oat_code) << PrettyMethod(&m); } } - VLOG(image) << "Virtual methods"; for (ArtMethod& m : klass->GetVirtualMethods(sizeof(void*))) { const void* code = m.GetEntryPointFromQuickCompiledCode(); const void* oat_code = m.IsInvokable() ? GetQuickOatCodeFor(&m) : code; @@ -1378,17 +1410,7 @@ bool ClassLinker::UpdateAppImageClassLoadersAndDexCaches( } } } - *out_forward_dex_cache_array = num_dex_caches_with_bss_arrays != 0; if (*out_forward_dex_cache_array) { - if (num_dex_caches_with_bss_arrays != num_dex_caches) { - // Reject application image since we cannot forward only some of the dex cache arrays. - // TODO: We could get around this by having a dedicated forwarding slot. It should be an - // uncommon case. - *out_error_msg = StringPrintf("Dex caches in bss does not match total: %zu vs %zu", - num_dex_caches_with_bss_arrays, - num_dex_caches); - return false; - } FixupArtMethodArrayVisitor visitor(header); header.GetImageSection(ImageHeader::kSectionArtMethods).VisitPackedArtMethods( &visitor, @@ -1396,17 +1418,11 @@ bool ClassLinker::UpdateAppImageClassLoadersAndDexCaches( sizeof(void*)); Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(class_loader.Get()); } - if (kIsDebugBuild) { - ClassTable* const class_table = class_loader.Get()->GetClassTable(); - VerifyClassInTableArtMethodVisitor visitor2(class_table); - header.GetImageSection(ImageHeader::kSectionArtMethods).VisitPackedArtMethods( - &visitor2, - space->Begin(), - sizeof(void*)); - } return true; } +// Update the class loader and resolved string dex cache array of classes. Should only be used on +// classes in the image space. class UpdateClassLoaderAndResolvedStringsVisitor { public: UpdateClassLoaderAndResolvedStringsVisitor(gc::space::ImageSpace* space, @@ -1457,6 +1473,7 @@ bool ClassLinker::AddImageSpace( Runtime* const runtime = Runtime::Current(); gc::Heap* const heap = runtime->GetHeap(); Thread* const self = Thread::Current(); + ScopedAssertNoThreadSuspension nts(self, __FUNCTION__); StackHandleScope<2> hs(self); Handle> dex_caches( hs.NewHandle(dex_caches_object->AsObjectArray())); @@ -1644,43 +1661,46 @@ bool ClassLinker::AddImageSpace( methods.VisitPackedArtMethods(&visitor, space->Begin(), image_pointer_size_); } - const ImageSection& class_table_section = header.GetImageSection(ImageHeader::kSectionClassTable); - bool added_class_table = false; - if (app_image) { - GetOrCreateAllocatorForClassLoader(class_loader.Get()); // Make sure we have a linear alloc. - } ClassTable* class_table = nullptr; { WriterMutexLock mu(self, *Locks::classlinker_classes_lock_); class_table = InsertClassTableForClassLoader(class_loader.Get()); - if (class_table_section.Size() > 0u) { - const uint64_t start_time2 = NanoTime(); - class_table->ReadFromMemory(space->Begin() + class_table_section.Offset()); - if (!app_image) { - dex_cache_boot_image_class_lookup_required_ = false; - } - VLOG(image) << "Adding class table classes took " << PrettyDuration(NanoTime() - start_time2); - added_class_table = true; + } + // If we have a class table section, read it and use it for verification in + // UpdateAppImageClassLoadersAndDexCaches. + ClassTable::ClassSet temp_set; + const ImageSection& class_table_section = header.GetImageSection(ImageHeader::kSectionClassTable); + const bool added_class_table = class_table_section.Size() > 0u; + if (added_class_table) { + const uint64_t start_time2 = NanoTime(); + size_t read_count = 0; + temp_set = ClassTable::ClassSet(space->Begin() + class_table_section.Offset(), + /*make copy*/false, + &read_count); + if (!app_image) { + dex_cache_boot_image_class_lookup_required_ = false; } + VLOG(image) << "Adding class table classes took " << PrettyDuration(NanoTime() - start_time2); } if (app_image) { bool forward_dex_cache_arrays = false; if (!UpdateAppImageClassLoadersAndDexCaches(space, class_loader, dex_caches, - added_class_table, + added_class_table ? &temp_set : nullptr, /*out*/&forward_dex_cache_arrays, /*out*/error_msg)) { return false; } + // Update class loader and resolved strings. If added_class_table is false, the resolved + // strings were forwarded UpdateAppImageClassLoadersAndDexCaches. + UpdateClassLoaderAndResolvedStringsVisitor visitor(space, + class_loader.Get(), + forward_dex_cache_arrays); if (added_class_table) { - WriterMutexLock mu(self, *Locks::classlinker_classes_lock_); - // Update class loader and resolved strings. If added_class_table is false, the resolved - // strings were already updated in UpdateAppImageClassLoadersAndDexCaches. - UpdateClassLoaderAndResolvedStringsVisitor visitor(space, - class_loader.Get(), - forward_dex_cache_arrays); - class_table->Visit(visitor); + for (GcRoot& root : temp_set) { + visitor(root.Read()); + } } // forward_dex_cache_arrays is true iff we copied all of the dex cache arrays into the .bss. // In this case, madvise away the dex cache arrays section of the image to reduce RAM usage and @@ -1699,6 +1719,19 @@ bool ClassLinker::AddImageSpace( } } } + if (added_class_table) { + WriterMutexLock mu(self, *Locks::classlinker_classes_lock_); + class_table->AddClassSet(std::move(temp_set)); + } + if (kIsDebugBuild && app_image) { + // This verification needs to happen after the classes have been added to the class loader. + // Since it ensures classes are in the class table. + VerifyClassInTableArtMethodVisitor visitor2(class_table); + header.GetImageSection(ImageHeader::kSectionArtMethods).VisitPackedArtMethods( + &visitor2, + space->Begin(), + sizeof(void*)); + } VLOG(class_linker) << "Adding image space took " << PrettyDuration(NanoTime() - start_time); return true; } diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 0a75b273e5..492a228522 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -1029,11 +1029,13 @@ class ClassLinker { SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!Locks::classlinker_classes_lock_); + // new_class_set is the set of classes that were read from the class table section in the image. + // If there was no class table section, it is null. bool UpdateAppImageClassLoadersAndDexCaches( gc::space::ImageSpace* space, Handle class_loader, Handle> dex_caches, - bool added_class_table, + ClassTable::ClassSet* new_class_set, bool* out_forward_dex_cache_array, std::string* out_error_msg) REQUIRES(!dex_lock_) diff --git a/runtime/class_table.cc b/runtime/class_table.cc index afb0556e1e..d815b1a7a3 100644 --- a/runtime/class_table.cc +++ b/runtime/class_table.cc @@ -168,8 +168,12 @@ size_t ClassTable::WriteToMemory(uint8_t* ptr) const { size_t ClassTable::ReadFromMemory(uint8_t* ptr) { size_t read_count = 0; - classes_.insert(classes_.begin(), ClassSet(ptr, /*make copy*/false, &read_count)); + AddClassSet(ClassSet(ptr, /*make copy*/false, &read_count)); return read_count; } +void ClassTable::AddClassSet(ClassSet&& set) { + classes_.insert(classes_.begin(), std::move(set)); +} + } // namespace art diff --git a/runtime/class_table.h b/runtime/class_table.h index 5f2eb48d55..0e0e860b4f 100644 --- a/runtime/class_table.h +++ b/runtime/class_table.h @@ -39,6 +39,34 @@ namespace mirror { // Each loader has a ClassTable class ClassTable { public: + class ClassDescriptorHashEquals { + public: + // uint32_t for cross compilation. + uint32_t operator()(const GcRoot& root) const NO_THREAD_SAFETY_ANALYSIS; + // Same class loader and descriptor. + bool operator()(const GcRoot& a, const GcRoot& b) const + NO_THREAD_SAFETY_ANALYSIS;; + // Same descriptor. + bool operator()(const GcRoot& a, const char* descriptor) const + NO_THREAD_SAFETY_ANALYSIS; + // uint32_t for cross compilation. + uint32_t operator()(const char* descriptor) const NO_THREAD_SAFETY_ANALYSIS; + }; + class GcRootEmptyFn { + public: + void MakeEmpty(GcRoot& item) const { + item = GcRoot(); + } + bool IsEmpty(const GcRoot& item) const { + return item.IsNull(); + } + }; + // hash set which hashes class descriptor, and compares descriptors and class loaders. Results + // should be compared for a matching Class descriptor and class loader. + typedef HashSet, GcRootEmptyFn, ClassDescriptorHashEquals, + ClassDescriptorHashEquals, TrackingAllocator, kAllocatorTagClassTable>> + ClassSet; + ClassTable(); // Used by image writer for checking. @@ -112,35 +140,12 @@ class ClassTable { REQUIRES(Locks::classlinker_classes_lock_) SHARED_REQUIRES(Locks::mutator_lock_); - private: - class ClassDescriptorHashEquals { - public: - // uint32_t for cross compilation. - uint32_t operator()(const GcRoot& root) const NO_THREAD_SAFETY_ANALYSIS; - // Same class loader and descriptor. - bool operator()(const GcRoot& a, const GcRoot& b) const - NO_THREAD_SAFETY_ANALYSIS;; - // Same descriptor. - bool operator()(const GcRoot& a, const char* descriptor) const - NO_THREAD_SAFETY_ANALYSIS; - // uint32_t for cross compilation. - uint32_t operator()(const char* descriptor) const NO_THREAD_SAFETY_ANALYSIS; - }; - class GcRootEmptyFn { - public: - void MakeEmpty(GcRoot& item) const { - item = GcRoot(); - } - bool IsEmpty(const GcRoot& item) const { - return item.IsNull(); - } - }; - // hash set which hashes class descriptor, and compares descriptors and class loaders. Results - // should be compared for a matching Class descriptor and class loader. - typedef HashSet, GcRootEmptyFn, ClassDescriptorHashEquals, - ClassDescriptorHashEquals, TrackingAllocator, kAllocatorTagClassTable>> - ClassSet; + // Add a class set to the front of classes. + void AddClassSet(ClassSet&& set) + REQUIRES(Locks::classlinker_classes_lock_) + SHARED_REQUIRES(Locks::mutator_lock_); + private: // TODO: shard lock to have one per class loader. // We have a vector to help prevent dirty pages after the zygote forks by calling FreezeSnapshot. std::vector classes_ GUARDED_BY(Locks::classlinker_classes_lock_); -- GitLab From 9a9aee662ebde18bed2a8e263c921df4f529b27b Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Fri, 4 Mar 2016 10:30:39 -0800 Subject: [PATCH 137/204] Use MarkIfNotInToSpace for SemiSpace::MarkObject SemiSpace::MarkObject is called from ModUnionScanImageRootVisitor which. These roots may be visited multiple times by the GC. One example is the allocation stack traces and a class visiting the same ArtMethod twice. Bug: 27435111 Change-Id: I721b8bcf7fe43f4c351d902c2f3422d3bb585295 --- runtime/gc/collector/semi_space-inl.h | 1 + runtime/gc/collector/semi_space.cc | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/runtime/gc/collector/semi_space-inl.h b/runtime/gc/collector/semi_space-inl.h index 12cf3dbf98..e87b5ff332 100644 --- a/runtime/gc/collector/semi_space-inl.h +++ b/runtime/gc/collector/semi_space-inl.h @@ -75,6 +75,7 @@ inline void SemiSpace::MarkObject( } obj_ptr->Assign(forward_address); } else if (!collect_from_space_only_ && !immune_spaces_.IsInImmuneRegion(obj)) { + DCHECK(!to_space_->HasAddress(obj)) << "Tried to mark " << obj << " in to-space"; BitmapSetSlowPathVisitor visitor(this); if (!mark_bitmap_->Set(obj, visitor)) { // This object was not previously marked. diff --git a/runtime/gc/collector/semi_space.cc b/runtime/gc/collector/semi_space.cc index 278469329f..f37daa54e9 100644 --- a/runtime/gc/collector/semi_space.cc +++ b/runtime/gc/collector/semi_space.cc @@ -619,7 +619,7 @@ mirror::Object* SemiSpace::MarkNonForwardedObject(mirror::Object* obj) { mirror::Object* SemiSpace::MarkObject(mirror::Object* root) { auto ref = StackReference::FromMirrorPtr(root); - MarkObject(&ref); + MarkObjectIfNotInToSpace(&ref); return ref.AsMirrorPtr(); } -- GitLab From 095a0b234a47261402bf81681eacb6092517766e Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Fri, 4 Mar 2016 16:39:33 -0800 Subject: [PATCH 138/204] Remove no thread suspension assert in AddImageSpace Heap::VisitObjects may cause thread suspension for the CC case. Change-Id: Ibd495690b87dc8731919e1b1164be834c9da02b2 --- runtime/class_linker.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index e7708c9357..cb67a17015 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -1473,7 +1473,6 @@ bool ClassLinker::AddImageSpace( Runtime* const runtime = Runtime::Current(); gc::Heap* const heap = runtime->GetHeap(); Thread* const self = Thread::Current(); - ScopedAssertNoThreadSuspension nts(self, __FUNCTION__); StackHandleScope<2> hs(self); Handle> dex_caches( hs.NewHandle(dex_caches_object->AsObjectArray())); -- GitLab From 32ce2adefb8a3d0eda59a29f5e87c1eb43eef796 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Fri, 4 Mar 2016 14:58:03 -0800 Subject: [PATCH 139/204] Add more systracing everywhere Added to: JIT DexFile functions Oat file manager Added helper ScopedTrace to prevent errors and reduce excess code. Bug: 27502458 (cherry picked from commit dabdc0fe183d4684f3cf4d70cb09d318cff81b42) Change-Id: Ifaeff8913d79eefc797380987d13cc00456266f8 --- compiler/driver/compiler_driver.cc | 4 +- dex2oat/dex2oat.cc | 3 - runtime/base/arena_allocator.cc | 2 + runtime/base/mutex.cc | 4 +- runtime/base/systrace.h | 42 +++++++++++++ runtime/base/timing_logger.cc | 3 +- runtime/class_linker.cc | 3 + runtime/dex_file.cc | 7 +++ runtime/gc/accounting/card_table.cc | 2 + runtime/gc/collector/garbage_collector.cc | 7 +-- runtime/gc/collector/mark_sweep.cc | 10 +-- runtime/gc/heap.cc | 46 +++++--------- runtime/gc/space/image_space.cc | 4 +- runtime/gc/space/rosalloc_space.cc | 3 - runtime/indirect_reference_table.cc | 2 + runtime/java_vm_ext.cc | 10 +-- runtime/jit/jit_code_cache.cc | 9 +++ runtime/jit/offline_profiling_info.cc | 4 ++ runtime/jit/profile_saver.cc | 2 + runtime/monitor.cc | 4 +- runtime/oat_file.cc | 7 +++ runtime/oat_file_manager.cc | 21 ++++--- runtime/runtime.cc | 65 ++++++++----------- runtime/thread.cc | 10 +-- runtime/thread_list.cc | 77 ++++++++++------------- runtime/trace.cc | 7 +-- runtime/verifier/method_verifier.cc | 8 +-- 27 files changed, 188 insertions(+), 178 deletions(-) create mode 100644 runtime/base/systrace.h diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 3100b6da0f..5d8e3baacb 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -16,9 +16,6 @@ #include "compiler_driver.h" -#define ATRACE_TAG ATRACE_TAG_DALVIK -#include - #include #include #include @@ -30,6 +27,7 @@ #include "art_field-inl.h" #include "art_method-inl.h" #include "base/stl_util.h" +#include "base/systrace.h" #include "base/time_utils.h" #include "base/timing_logger.h" #include "class_linker-inl.h" diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index f25d748490..ea190591e2 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -32,9 +32,6 @@ #include #endif -#define ATRACE_TAG ATRACE_TAG_DALVIK -#include - #include "arch/instruction_set_features.h" #include "arch/mips/instruction_set_features_mips.h" #include "art_method-inl.h" diff --git a/runtime/base/arena_allocator.cc b/runtime/base/arena_allocator.cc index 44af3f75b9..f871543862 100644 --- a/runtime/base/arena_allocator.cc +++ b/runtime/base/arena_allocator.cc @@ -23,6 +23,7 @@ #include "mem_map.h" #include "mutex.h" #include "thread-inl.h" +#include "systrace.h" namespace art { @@ -261,6 +262,7 @@ Arena* ArenaPool::AllocArena(size_t size) { void ArenaPool::TrimMaps() { if (!use_malloc_) { + ScopedTrace trace(__PRETTY_FUNCTION__); // Doesn't work for malloc. MutexLock lock(Thread::Current(), lock_); for (auto* arena = free_arenas_; arena != nullptr; arena = arena->next_) { diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc index 6972b3ef3f..620bf9c8b7 100644 --- a/runtime/base/mutex.cc +++ b/runtime/base/mutex.cc @@ -19,12 +19,10 @@ #include #include -#define ATRACE_TAG ATRACE_TAG_DALVIK -#include "cutils/trace.h" - #include "atomic.h" #include "base/logging.h" #include "base/time_utils.h" +#include "base/systrace.h" #include "base/value_object.h" #include "mutex-inl.h" #include "runtime.h" diff --git a/runtime/base/systrace.h b/runtime/base/systrace.h new file mode 100644 index 0000000000..3901f96b45 --- /dev/null +++ b/runtime/base/systrace.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_BASE_SYSTRACE_H_ +#define ART_RUNTIME_BASE_SYSTRACE_H_ + +#define ATRACE_TAG ATRACE_TAG_DALVIK +#include +#include +#include + +namespace art { + +class ScopedTrace { + public: + explicit ScopedTrace(const char* name) { + ATRACE_BEGIN(name); + } + + explicit ScopedTrace(const std::string& name) : ScopedTrace(name.c_str()) {} + + ~ScopedTrace() { + ATRACE_END(); + } +}; + +} // namespace art + +#endif // ART_RUNTIME_BASE_SYSTRACE_H_ diff --git a/runtime/base/timing_logger.cc b/runtime/base/timing_logger.cc index 1942e1dc1b..9a0e0d02a6 100644 --- a/runtime/base/timing_logger.cc +++ b/runtime/base/timing_logger.cc @@ -15,15 +15,14 @@ */ -#define ATRACE_TAG ATRACE_TAG_DALVIK #include -#include #include "timing_logger.h" #include "base/logging.h" #include "base/stl_util.h" #include "base/histogram-inl.h" +#include "base/systrace.h" #include "base/time_utils.h" #include "thread-inl.h" diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index cb67a17015..a13a2e337c 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -36,6 +36,7 @@ #include "base/scoped_arena_containers.h" #include "base/scoped_flock.h" #include "base/stl_util.h" +#include "base/systrace.h" #include "base/time_utils.h" #include "base/unix_file/fd_file.h" #include "base/value_object.h" @@ -7671,6 +7672,7 @@ void ClassLinker::CleanupClassLoaders() { } std::set ClassLinker::GetResolvedClasses(bool ignore_boot_classes) { + ScopedTrace trace(__PRETTY_FUNCTION__); ScopedObjectAccess soa(Thread::Current()); ScopedAssertNoThreadSuspension ants(soa.Self(), __FUNCTION__); std::set ret; @@ -7737,6 +7739,7 @@ std::set ClassLinker::GetResolvedClasses(bool ignore_bo std::unordered_set ClassLinker::GetClassDescriptorsForProfileKeys( const std::set& classes) { + ScopedTrace trace(__PRETTY_FUNCTION__); std::unordered_set ret; Thread* const self = Thread::Current(); std::unordered_map location_to_dex_file; diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc index 81a3e4b08c..4a0a6fc8ad 100644 --- a/runtime/dex_file.cc +++ b/runtime/dex_file.cc @@ -34,6 +34,7 @@ #include "base/logging.h" #include "base/stl_util.h" #include "base/stringprintf.h" +#include "base/systrace.h" #include "class_linker-inl.h" #include "dex_file-inl.h" #include "dex_file_verifier.h" @@ -116,6 +117,7 @@ bool DexFile::GetChecksum(const char* filename, uint32_t* checksum, std::string* bool DexFile::Open(const char* filename, const char* location, std::string* error_msg, std::vector>* dex_files) { + ScopedTrace trace(std::string("Open dex file ") + location); DCHECK(dex_files != nullptr) << "DexFile::Open: out-param is nullptr"; uint32_t magic; ScopedFd fd(OpenAndReadMagic(filename, &magic, error_msg)); @@ -201,6 +203,7 @@ std::unique_ptr DexFile::Open(const uint8_t* base, size_t size, const OatDexFile* oat_dex_file, bool verify, std::string* error_msg) { + ScopedTrace trace(std::string("Open dex file from RAM ") + location); std::unique_ptr dex_file = OpenMemory(base, size, location, @@ -221,6 +224,7 @@ std::unique_ptr DexFile::Open(const uint8_t* base, size_t size, std::unique_ptr DexFile::OpenFile(int fd, const char* location, bool verify, std::string* error_msg) { + ScopedTrace trace(std::string("Open dex file ") + location); CHECK(location != nullptr); std::unique_ptr map; { @@ -278,6 +282,7 @@ const char* DexFile::kClassesDex = "classes.dex"; bool DexFile::OpenZip(int fd, const std::string& location, std::string* error_msg, std::vector>* dex_files) { + ScopedTrace trace("Dex file open Zip " + std::string(location)); DCHECK(dex_files != nullptr) << "DexFile::OpenZip: out-param is nullptr"; std::unique_ptr zip_archive(ZipArchive::OpenFromFd(fd, location.c_str(), error_msg)); if (zip_archive.get() == nullptr) { @@ -303,6 +308,7 @@ std::unique_ptr DexFile::OpenMemory(const std::string& location, std::unique_ptr DexFile::Open(const ZipArchive& zip_archive, const char* entry_name, const std::string& location, std::string* error_msg, ZipOpenErrorCode* error_code) { + ScopedTrace trace("Dex file open from Zip Archive " + std::string(location)); CHECK(!location.empty()); std::unique_ptr zip_entry(zip_archive.Find(entry_name, error_msg)); if (zip_entry.get() == nullptr) { @@ -348,6 +354,7 @@ static constexpr size_t kWarnOnManyDexFilesThreshold = 100; bool DexFile::OpenFromZip(const ZipArchive& zip_archive, const std::string& location, std::string* error_msg, std::vector>* dex_files) { + ScopedTrace trace("Dex file open from Zip " + std::string(location)); DCHECK(dex_files != nullptr) << "DexFile::OpenFromZip: out-param is nullptr"; ZipOpenErrorCode error_code; std::unique_ptr dex_file(Open(zip_archive, kClassesDex, location, error_msg, diff --git a/runtime/gc/accounting/card_table.cc b/runtime/gc/accounting/card_table.cc index 1a7b1a374e..121da37389 100644 --- a/runtime/gc/accounting/card_table.cc +++ b/runtime/gc/accounting/card_table.cc @@ -17,6 +17,7 @@ #include "card_table.h" #include "base/logging.h" +#include "base/systrace.h" #include "card_table-inl.h" #include "gc/heap.h" #include "gc/space/space.h" @@ -57,6 +58,7 @@ constexpr uint8_t CardTable::kCardDirty; */ CardTable* CardTable::Create(const uint8_t* heap_begin, size_t heap_capacity) { + ScopedTrace trace(__PRETTY_FUNCTION__); /* Set up the card table */ size_t capacity = heap_capacity / kCardSize; /* Allocate an extra 256 bytes to allow fixed low-byte of base */ diff --git a/runtime/gc/collector/garbage_collector.cc b/runtime/gc/collector/garbage_collector.cc index afd0a30fe4..18c4adf608 100644 --- a/runtime/gc/collector/garbage_collector.cc +++ b/runtime/gc/collector/garbage_collector.cc @@ -18,13 +18,11 @@ #include "garbage_collector.h" -#define ATRACE_TAG ATRACE_TAG_DALVIK -#include "cutils/trace.h" - #include "base/dumpable.h" #include "base/histogram-inl.h" #include "base/logging.h" #include "base/mutex-inl.h" +#include "base/systrace.h" #include "base/time_utils.h" #include "gc/accounting/heap_bitmap.h" #include "gc/space/large_object_space.h" @@ -81,7 +79,7 @@ void GarbageCollector::ResetCumulativeStatistics() { } void GarbageCollector::Run(GcCause gc_cause, bool clear_soft_references) { - ATRACE_BEGIN(StringPrintf("%s %s GC", PrettyCause(gc_cause), GetName()).c_str()); + ScopedTrace trace(StringPrintf("%s %s GC", PrettyCause(gc_cause), GetName())); Thread* self = Thread::Current(); uint64_t start_time = NanoTime(); Iteration* current_iteration = GetCurrentIteration(); @@ -107,7 +105,6 @@ void GarbageCollector::Run(GcCause gc_cause, bool clear_soft_references) { MutexLock mu(self, pause_histogram_lock_); pause_histogram_.AdjustAndAddValue(pause_time); } - ATRACE_END(); } void GarbageCollector::SwapBitmaps() { diff --git a/runtime/gc/collector/mark_sweep.cc b/runtime/gc/collector/mark_sweep.cc index 64c8e9af04..6073fc8a78 100644 --- a/runtime/gc/collector/mark_sweep.cc +++ b/runtime/gc/collector/mark_sweep.cc @@ -22,13 +22,11 @@ #include #include -#define ATRACE_TAG ATRACE_TAG_DALVIK -#include "cutils/trace.h" - #include "base/bounded_fifo.h" #include "base/logging.h" #include "base/macros.h" #include "base/mutex-inl.h" +#include "base/systrace.h" #include "base/time_utils.h" #include "base/timing_logger.h" #include "gc/accounting/card_table-inl.h" @@ -1137,17 +1135,15 @@ class CheckpointMarkThreadRoots : public Closure, public RootVisitor { } virtual void Run(Thread* thread) OVERRIDE NO_THREAD_SAFETY_ANALYSIS { - ATRACE_BEGIN("Marking thread roots"); + ScopedTrace trace("Marking thread roots"); // Note: self is not necessarily equal to thread since thread may be suspended. Thread* const self = Thread::Current(); CHECK(thread == self || thread->IsSuspended() || thread->GetState() == kWaitingPerformingGc) << thread->GetState() << " thread " << thread << " self " << self; thread->VisitRoots(this); - ATRACE_END(); if (revoke_ros_alloc_thread_local_buffers_at_checkpoint_) { - ATRACE_BEGIN("RevokeRosAllocThreadLocalBuffers"); + ScopedTrace trace2("RevokeRosAllocThreadLocalBuffers"); mark_sweep_->GetHeap()->RevokeRosAllocThreadLocalBuffers(thread); - ATRACE_END(); } // If thread is a running mutator, then act on behalf of the garbage collector. // See the code in ThreadList::RunCheckpoint. diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index bebff0fd6b..3480483c34 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -16,9 +16,6 @@ #include "heap.h" -#define ATRACE_TAG ATRACE_TAG_DALVIK -#include - #include #include #include // For GC verification. @@ -30,6 +27,7 @@ #include "base/dumpable.h" #include "base/histogram-inl.h" #include "base/stl_util.h" +#include "base/systrace.h" #include "base/time_utils.h" #include "common_throws.h" #include "cutils/sched_policy.h" @@ -240,6 +238,7 @@ Heap::Heap(size_t initial_size, if (VLOG_IS_ON(heap) || VLOG_IS_ON(startup)) { LOG(INFO) << "Heap() entering"; } + ScopedTrace trace(__FUNCTION__); Runtime* const runtime = Runtime::Current(); // If we aren't the zygote, switch to the default non zygote allocator. This may update the // entrypoints. @@ -274,14 +273,12 @@ Heap::Heap(size_t initial_size, uint8_t* const original_requested_alloc_space_begin = requested_alloc_space_begin; for (size_t index = 0; index < image_file_names.size(); ++index) { std::string& image_name = image_file_names[index]; - ATRACE_BEGIN("ImageSpace::Create"); std::string error_msg; space::ImageSpace* boot_image_space = space::ImageSpace::CreateBootImage( image_name.c_str(), image_instruction_set, index > 0, &error_msg); - ATRACE_END(); if (boot_image_space != nullptr) { AddSpace(boot_image_space); added_image_spaces.push_back(boot_image_space); @@ -373,8 +370,8 @@ Heap::Heap(size_t initial_size, } std::string error_str; std::unique_ptr non_moving_space_mem_map; - ATRACE_BEGIN("Create heap maps"); if (separate_non_moving_space) { + ScopedTrace trace2("Create separate non moving space"); // If we are the zygote, the non moving space becomes the zygote space when we run // PreZygoteFork the first time. In this case, call the map "zygote space" since we can't // rename the mem map later. @@ -391,6 +388,7 @@ Heap::Heap(size_t initial_size, } // Attempt to create 2 mem maps at or after the requested begin. if (foreground_collector_type_ != kCollectorTypeCC) { + ScopedTrace trace2("Create main mem map"); if (separate_non_moving_space || !is_zygote) { main_mem_map_1.reset(MapAnonymousPreferredAddress(kMemMapSpaceName[0], request_begin, @@ -409,14 +407,15 @@ Heap::Heap(size_t initial_size, if (support_homogeneous_space_compaction || background_collector_type_ == kCollectorTypeSS || foreground_collector_type_ == kCollectorTypeSS) { + ScopedTrace trace2("Create main mem map 2"); main_mem_map_2.reset(MapAnonymousPreferredAddress(kMemMapSpaceName[1], main_mem_map_1->End(), capacity_, &error_str)); CHECK(main_mem_map_2.get() != nullptr) << error_str; } - ATRACE_END(); - ATRACE_BEGIN("Create spaces"); + // Create the non moving space first so that bitmaps don't take up the address range. if (separate_non_moving_space) { + ScopedTrace trace2("Add non moving space"); // Non moving space is always dlmalloc since we currently don't have support for multiple // active rosalloc spaces. const size_t size = non_moving_space_mem_map->Size(); @@ -504,9 +503,7 @@ Heap::Heap(size_t initial_size, if (main_space_backup_.get() != nullptr) { RemoveSpace(main_space_backup_.get()); } - ATRACE_END(); // Allocate the card table. - ATRACE_BEGIN("Create card table"); // We currently don't support dynamically resizing the card table. // Since we don't know where in the low_4gb the app image will be located, make the card table // cover the whole low_4gb. TODO: Extend the card table in AddSpace. @@ -517,7 +514,6 @@ Heap::Heap(size_t initial_size, card_table_.reset(accounting::CardTable::Create(reinterpret_cast(kMinHeapAddress), 4 * GB - kMinHeapAddress)); CHECK(card_table_.get() != nullptr) << "Failed to create card table"; - ATRACE_END(); if (foreground_collector_type_ == kCollectorTypeCC && kUseTableLookupReadBarrier) { rb_table_.reset(new accounting::ReadBarrierTable()); DCHECK(rb_table_->IsAllCleared()); @@ -1340,24 +1336,19 @@ void Heap::DoPendingCollectorTransition() { void Heap::Trim(Thread* self) { Runtime* const runtime = Runtime::Current(); if (!CareAboutPauseTimes()) { - ATRACE_BEGIN("Deflating monitors"); // Deflate the monitors, this can cause a pause but shouldn't matter since we don't care // about pauses. - { - ScopedSuspendAll ssa(__FUNCTION__); - uint64_t start_time = NanoTime(); - size_t count = runtime->GetMonitorList()->DeflateMonitors(); - VLOG(heap) << "Deflating " << count << " monitors took " - << PrettyDuration(NanoTime() - start_time); - } - ATRACE_END(); + ScopedTrace trace("Deflating monitors"); + ScopedSuspendAll ssa(__FUNCTION__); + uint64_t start_time = NanoTime(); + size_t count = runtime->GetMonitorList()->DeflateMonitors(); + VLOG(heap) << "Deflating " << count << " monitors took " + << PrettyDuration(NanoTime() - start_time); } TrimIndirectReferenceTables(self); TrimSpaces(self); // Trim arenas that may have been used by JIT or verifier. - ATRACE_BEGIN("Trimming arena maps"); runtime->GetArenaPool()->TrimMaps(); - ATRACE_END(); } class TrimIndirectReferenceTableClosure : public Closure { @@ -1365,9 +1356,7 @@ class TrimIndirectReferenceTableClosure : public Closure { explicit TrimIndirectReferenceTableClosure(Barrier* barrier) : barrier_(barrier) { } virtual void Run(Thread* thread) OVERRIDE NO_THREAD_SAFETY_ANALYSIS { - ATRACE_BEGIN("Trimming reference table"); thread->GetJniEnv()->locals.Trim(); - ATRACE_END(); // If thread is a running mutator, then act on behalf of the trim thread. // See the code in ThreadList::RunCheckpoint. barrier_->Pass(Thread::Current()); @@ -1379,7 +1368,7 @@ class TrimIndirectReferenceTableClosure : public Closure { void Heap::TrimIndirectReferenceTables(Thread* self) { ScopedObjectAccess soa(self); - ATRACE_BEGIN(__FUNCTION__); + ScopedTrace trace(__PRETTY_FUNCTION__); JavaVMExt* vm = soa.Vm(); // Trim globals indirect reference table. vm->TrimGlobals(); @@ -1391,7 +1380,6 @@ void Heap::TrimIndirectReferenceTables(Thread* self) { if (barrier_count != 0) { barrier.Increment(self, barrier_count); } - ATRACE_END(); } void Heap::StartGC(Thread* self, GcCause cause, CollectorType collector_type) { @@ -1410,7 +1398,7 @@ void Heap::TrimSpaces(Thread* self) { // trimming. StartGC(self, kGcCauseTrim, kCollectorTypeHeapTrim); } - ATRACE_BEGIN(__FUNCTION__); + ScopedTrace trace(__PRETTY_FUNCTION__); const uint64_t start_ns = NanoTime(); // Trim the managed spaces. uint64_t total_alloc_space_allocated = 0; @@ -1449,7 +1437,6 @@ void Heap::TrimSpaces(Thread* self) { VLOG(heap) << "Heap trim of managed (duration=" << PrettyDuration(gc_heap_end_ns - start_ns) << ", advised=" << PrettySize(managed_reclaimed) << ") heap. Managed heap utilization of " << static_cast(100 * managed_utilization) << "%."; - ATRACE_END(); } bool Heap::IsValidObjectAddress(const mirror::Object* obj) const { @@ -3434,11 +3421,10 @@ collector::GcType Heap::WaitForGcToCompleteLocked(GcCause cause, Thread* self) { running_collection_is_blocking_ = true; VLOG(gc) << "Waiting for a blocking GC " << cause; } - ATRACE_BEGIN("GC: Wait For Completion"); + ScopedTrace trace("GC: Wait For Completion"); // We must wait, change thread state then sleep on gc_complete_cond_; gc_complete_cond_->Wait(self); last_gc_type = last_gc_type_; - ATRACE_END(); } uint64_t wait_time = NanoTime() - wait_start; total_wait_time_ += wait_time; diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc index 5aaf1045f9..a4e558728e 100644 --- a/runtime/gc/space/image_space.cc +++ b/runtime/gc/space/image_space.cc @@ -27,6 +27,7 @@ #include "base/macros.h" #include "base/stl_util.h" #include "base/scoped_flock.h" +#include "base/systrace.h" #include "base/time_utils.h" #include "base/unix_file/fd_file.h" #include "gc/accounting/space_bitmap-inl.h" @@ -471,6 +472,7 @@ ImageSpace* ImageSpace::CreateBootImage(const char* image_location, const InstructionSet image_isa, bool secondary_image, std::string* error_msg) { + ScopedTrace trace(__FUNCTION__); std::string system_filename; bool has_system = false; std::string cache_filename; @@ -1167,7 +1169,7 @@ ImageSpace* ImageSpace::Init(const char* image_filename, CHECK(image_filename != nullptr); CHECK(image_location != nullptr); - TimingLogger logger(__FUNCTION__, true, false); + TimingLogger logger(__PRETTY_FUNCTION__, true, VLOG_IS_ON(image)); VLOG(image) << "ImageSpace::Init entering image_filename=" << image_filename; std::unique_ptr file; diff --git a/runtime/gc/space/rosalloc_space.cc b/runtime/gc/space/rosalloc_space.cc index fd4d0a1a47..203d3bcfe9 100644 --- a/runtime/gc/space/rosalloc_space.cc +++ b/runtime/gc/space/rosalloc_space.cc @@ -17,9 +17,6 @@ #include "rosalloc_space-inl.h" -#define ATRACE_TAG ATRACE_TAG_DALVIK -#include "cutils/trace.h" - #include "base/time_utils.h" #include "gc/accounting/card_table.h" #include "gc/accounting/space_bitmap-inl.h" diff --git a/runtime/indirect_reference_table.cc b/runtime/indirect_reference_table.cc index a5b63b4271..8e49492cf4 100644 --- a/runtime/indirect_reference_table.cc +++ b/runtime/indirect_reference_table.cc @@ -16,6 +16,7 @@ #include "indirect_reference_table-inl.h" +#include "base/systrace.h" #include "jni_internal.h" #include "nth_caller_visitor.h" #include "reference_table.h" @@ -261,6 +262,7 @@ bool IndirectReferenceTable::Remove(uint32_t cookie, IndirectRef iref) { } void IndirectReferenceTable::Trim() { + ScopedTrace trace(__PRETTY_FUNCTION__); const size_t top_index = Capacity(); auto* release_start = AlignUp(reinterpret_cast(&table_[top_index]), kPageSize); uint8_t* release_end = table_mem_map_->End(); diff --git a/runtime/java_vm_ext.cc b/runtime/java_vm_ext.cc index 191c0c7cf2..a41fd45041 100644 --- a/runtime/java_vm_ext.cc +++ b/runtime/java_vm_ext.cc @@ -16,15 +16,13 @@ #include "jni_internal.h" -#define ATRACE_TAG ATRACE_TAG_DALVIK - -#include #include #include "art_method.h" #include "base/dumpable.h" #include "base/mutex.h" #include "base/stl_util.h" +#include "base/systrace.h" #include "check_jni.h" #include "dex_file-inl.h" #include "fault_handler.h" @@ -929,11 +927,10 @@ void JavaVMExt::VisitRoots(RootVisitor* visitor) { // JNI Invocation interface. extern "C" jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) { - ATRACE_BEGIN(__FUNCTION__); + ScopedTrace trace(__FUNCTION__); const JavaVMInitArgs* args = static_cast(vm_args); if (IsBadJniVersion(args->version)) { LOG(ERROR) << "Bad JNI version passed to CreateJavaVM: " << args->version; - ATRACE_END(); return JNI_EVERSION; } RuntimeOptions options; @@ -943,7 +940,6 @@ extern "C" jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) { } bool ignore_unrecognized = args->ignoreUnrecognized; if (!Runtime::Create(options, ignore_unrecognized)) { - ATRACE_END(); return JNI_ERR; } Runtime* runtime = Runtime::Current(); @@ -952,12 +948,10 @@ extern "C" jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) { delete Thread::Current()->GetJniEnv(); delete runtime->GetJavaVM(); LOG(WARNING) << "CreateJavaVM failed"; - ATRACE_END(); return JNI_ERR; } *p_env = Thread::Current()->GetJniEnv(); *p_vm = runtime->GetJavaVM(); - ATRACE_END(); return JNI_OK; } diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index e5be2a4771..1545cb7f01 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -20,6 +20,7 @@ #include "art_method-inl.h" #include "base/stl_util.h" +#include "base/systrace.h" #include "base/time_utils.h" #include "debugger_interface.h" #include "entrypoints/runtime_asm_entrypoints.h" @@ -52,6 +53,7 @@ JitCodeCache* JitCodeCache::Create(size_t initial_capacity, size_t max_capacity, bool generate_debug_info, std::string* error_msg) { + ScopedTrace trace(__PRETTY_FUNCTION__); CHECK_GE(max_capacity, initial_capacity); // Generating debug information is mostly for using the 'perf' tool, which does @@ -255,6 +257,7 @@ void JitCodeCache::FreeCode(const void* code_ptr, ArtMethod* method ATTRIBUTE_UN } void JitCodeCache::RemoveMethodsIn(Thread* self, const LinearAlloc& alloc) { + ScopedTrace trace(__PRETTY_FUNCTION__); MutexLock mu(self, lock_); // We do not check if a code cache GC is in progress, as this method comes // with the classlinker_classes_lock_ held, and suspending ourselves could @@ -452,6 +455,7 @@ class MarkCodeClosure FINAL : public Closure { : code_cache_(code_cache), barrier_(barrier) {} void Run(Thread* thread) OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_) { + ScopedTrace trace(__PRETTY_FUNCTION__); DCHECK(thread == Thread::Current() || thread->IsSuspended()); MarkCodeVisitor visitor(thread, code_cache_); visitor.WalkStack(); @@ -552,6 +556,7 @@ bool JitCodeCache::ShouldDoFullCollection() { } void JitCodeCache::GarbageCollectCache(Thread* self) { + ScopedTrace trace(__FUNCTION__); if (!garbage_collect_code_) { MutexLock mu(self, lock_); IncreaseCodeCacheCapacity(); @@ -640,6 +645,7 @@ void JitCodeCache::GarbageCollectCache(Thread* self) { } void JitCodeCache::RemoveUnmarkedCode(Thread* self) { + ScopedTrace trace(__FUNCTION__); MutexLock mu(self, lock_); ScopedCodeCacheWrite scc(code_map_.get()); // Iterate over all compiled code and remove entries that are not marked. @@ -661,6 +667,7 @@ void JitCodeCache::RemoveUnmarkedCode(Thread* self) { } void JitCodeCache::DoCollection(Thread* self, bool collect_profiling_info) { + ScopedTrace trace(__FUNCTION__); { MutexLock mu(self, lock_); if (collect_profiling_info) { @@ -737,6 +744,7 @@ void JitCodeCache::DoCollection(Thread* self, bool collect_profiling_info) { } bool JitCodeCache::CheckLiveCompiledCodeHasProfilingInfo() { + ScopedTrace trace(__FUNCTION__); // Check that methods we have compiled do have a ProfilingInfo object. We would // have memory leaks of compiled code otherwise. for (const auto& it : method_code_map_) { @@ -867,6 +875,7 @@ void* JitCodeCache::MoreCore(const void* mspace, intptr_t increment) NO_THREAD_S void JitCodeCache::GetCompiledArtMethods(const std::set& dex_base_locations, std::vector& methods) { + ScopedTrace trace(__FUNCTION__); MutexLock mu(Thread::Current(), lock_); for (auto it : method_code_map_) { if (ContainsElement(dex_base_locations, it.second->GetDexFile()->GetBaseLocation())) { diff --git a/runtime/jit/offline_profiling_info.cc b/runtime/jit/offline_profiling_info.cc index 67c9b5f679..ecf34f57ef 100644 --- a/runtime/jit/offline_profiling_info.cc +++ b/runtime/jit/offline_profiling_info.cc @@ -26,6 +26,7 @@ #include "base/mutex.h" #include "base/scoped_flock.h" #include "base/stl_util.h" +#include "base/systrace.h" #include "base/unix_file/fd_file.h" #include "jit/profiling_info.h" #include "os.h" @@ -57,6 +58,7 @@ bool ProfileCompilationInfo::SaveProfilingInfo( return true; } + ScopedTrace trace(__PRETTY_FUNCTION__); ScopedFlock flock; std::string error; if (!flock.Init(filename.c_str(), O_RDWR | O_NOFOLLOW | O_CLOEXEC, /* block */ false, &error)) { @@ -132,6 +134,7 @@ static constexpr const char* kClassesMarker = "classes"; * app.apk:classes5.dex,218490184,39,13,49,1 **/ bool ProfileCompilationInfo::Save(int fd) { + ScopedTrace trace(__PRETTY_FUNCTION__); DCHECK_GE(fd, 0); // TODO(calin): Profile this and see how much memory it takes. If too much, // write to file directly. @@ -298,6 +301,7 @@ static int GetLineFromBuffer(char* buffer, int n, int start_from, std::string& l } bool ProfileCompilationInfo::Load(int fd) { + ScopedTrace trace(__PRETTY_FUNCTION__); DCHECK_GE(fd, 0); std::string current_line; diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc index ab26f6ffa9..bd581570ed 100644 --- a/runtime/jit/profile_saver.cc +++ b/runtime/jit/profile_saver.cc @@ -17,6 +17,7 @@ #include "profile_saver.h" #include "art_method-inl.h" +#include "base/systrace.h" #include "scoped_thread_state_change.h" #include "oat_file_manager.h" @@ -93,6 +94,7 @@ void ProfileSaver::Run() { } bool ProfileSaver::ProcessProfilingInfo() { + ScopedTrace trace(__PRETTY_FUNCTION__); uint64_t last_update_time_ns = jit_code_cache_->GetLastUpdateTimeNs(); if (!first_profile_ && last_update_time_ns - code_cache_last_update_time_ns_ < kMinimumTimeBetweenCodeCacheUpdatesNs) { diff --git a/runtime/monitor.cc b/runtime/monitor.cc index 19c71f6d97..1ce5841bed 100644 --- a/runtime/monitor.cc +++ b/runtime/monitor.cc @@ -16,14 +16,12 @@ #include "monitor.h" -#define ATRACE_TAG ATRACE_TAG_DALVIK - -#include #include #include "art_method-inl.h" #include "base/mutex.h" #include "base/stl_util.h" +#include "base/systrace.h" #include "base/time_utils.h" #include "class_linker.h" #include "dex_file-inl.h" diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc index f912598eef..c3895479b7 100644 --- a/runtime/oat_file.cc +++ b/runtime/oat_file.cc @@ -35,6 +35,7 @@ #include "art_method-inl.h" #include "base/bit_vector.h" #include "base/stl_util.h" +#include "base/systrace.h" #include "base/unix_file/fd_file.h" #include "elf_file.h" #include "elf_utils.h" @@ -746,6 +747,7 @@ ElfOatFile* ElfOatFile::OpenElfFile(File* file, bool executable, const char* abs_dex_location, std::string* error_msg) { + ScopedTrace trace("Open elf file " + location); std::unique_ptr oat_file(new ElfOatFile(location, executable)); bool success = oat_file->ElfFileOpen(file, oat_file_begin, writable, executable, error_msg); if (!success) { @@ -768,6 +770,7 @@ ElfOatFile* ElfOatFile::OpenElfFile(File* file, bool ElfOatFile::InitializeFromElfFile(ElfFile* elf_file, const char* abs_dex_location, std::string* error_msg) { + ScopedTrace trace(__PRETTY_FUNCTION__); if (IsExecutable()) { *error_msg = "Cannot initialize from elf file in executable mode."; return false; @@ -787,6 +790,7 @@ bool ElfOatFile::Load(const std::string& elf_filename, bool writable, bool executable, std::string* error_msg) { + ScopedTrace trace(__PRETTY_FUNCTION__); std::unique_ptr file(OS::OpenFileForReading(elf_filename.c_str())); if (file == nullptr) { *error_msg = StringPrintf("Failed to open oat filename for reading: %s", strerror(errno)); @@ -804,6 +808,7 @@ bool ElfOatFile::ElfFileOpen(File* file, bool writable, bool executable, std::string* error_msg) { + ScopedTrace trace(__PRETTY_FUNCTION__); // TODO: rename requested_base to oat_data_begin elf_file_.reset(ElfFile::Open(file, writable, @@ -864,6 +869,7 @@ OatFile* OatFile::Open(const std::string& filename, bool executable, const char* abs_dex_location, std::string* error_msg) { + ScopedTrace trace("Open oat file " + location); CHECK(!filename.empty()) << location; CheckLocation(location); std::unique_ptr ret; @@ -1072,6 +1078,7 @@ size_t OatFile::OatDexFile::FileSize() const { } std::unique_ptr OatFile::OatDexFile::OpenDexFile(std::string* error_msg) const { + ScopedTrace trace(__PRETTY_FUNCTION__); return DexFile::Open(dex_file_pointer_, FileSize(), dex_file_location_, diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc index ea26d58767..9ae179fc5b 100644 --- a/runtime/oat_file_manager.cc +++ b/runtime/oat_file_manager.cc @@ -16,14 +16,13 @@ #include "oat_file_manager.h" -#define ATRACE_TAG ATRACE_TAG_DALVIK -#include #include #include #include #include "base/logging.h" #include "base/stl_util.h" +#include "base/systrace.h" #include "class_linker.h" #include "dex_file-inl.h" #include "gc/scoped_gc_critical_section.h" @@ -299,6 +298,7 @@ std::vector> OatFileManager::OpenDexFilesFromOat( jobjectArray dex_elements, const OatFile** out_oat_file, std::vector* error_msgs) { + ScopedTrace trace(__FUNCTION__); CHECK(dex_location != nullptr); CHECK(error_msgs != nullptr); @@ -388,14 +388,15 @@ std::vector> OatFileManager::OpenDexFilesFromOat( ScopedSuspendAll ssa("Add image space"); runtime->GetHeap()->AddSpace(image_space.get()); } - ATRACE_BEGIN(StringPrintf("Adding image space for location %s", dex_location).c_str()); - added_image_space = runtime->GetClassLinker()->AddImageSpace(image_space.get(), - h_loader, - dex_elements, - dex_location, - /*out*/&dex_files, - /*out*/&temp_error_msg); - ATRACE_END(); + { + ScopedTrace trace2(StringPrintf("Adding image space for location %s", dex_location)); + added_image_space = runtime->GetClassLinker()->AddImageSpace(image_space.get(), + h_loader, + dex_elements, + dex_location, + /*out*/&dex_files, + /*out*/&temp_error_msg); + } if (added_image_space) { // Successfully added image space to heap, release the map so that it does not get // freed. diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 8c813b4f16..bbb79af1bb 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -23,8 +23,6 @@ #include #endif -#define ATRACE_TAG ATRACE_TAG_DALVIK -#include #include #include #include "base/memory_tool.h" @@ -58,6 +56,7 @@ #include "base/arena_allocator.h" #include "base/dumpable.h" #include "base/stl_util.h" +#include "base/systrace.h" #include "base/unix_file/fd_file.h" #include "class_linker-inl.h" #include "compiler_callbacks.h" @@ -215,7 +214,7 @@ Runtime::Runtime() } Runtime::~Runtime() { - ATRACE_BEGIN("Runtime shutdown"); + ScopedTrace trace("Runtime shutdown"); if (is_native_bridge_loaded_) { UnloadNativeBridge(); } @@ -230,40 +229,34 @@ Runtime::~Runtime() { Thread* self = Thread::Current(); const bool attach_shutdown_thread = self == nullptr; if (attach_shutdown_thread) { - ATRACE_BEGIN("Attach shutdown thread"); CHECK(AttachCurrentThread("Shutdown thread", false, nullptr, false)); - ATRACE_END(); self = Thread::Current(); } else { LOG(WARNING) << "Current thread not detached in Runtime shutdown"; } { - ATRACE_BEGIN("Wait for shutdown cond"); + ScopedTrace trace2("Wait for shutdown cond"); MutexLock mu(self, *Locks::runtime_shutdown_lock_); shutting_down_started_ = true; while (threads_being_born_ > 0) { shutdown_cond_->Wait(self); } shutting_down_ = true; - ATRACE_END(); } // Shutdown and wait for the daemons. CHECK(self != nullptr); if (IsFinishedStarting()) { - ATRACE_BEGIN("Waiting for Daemons"); + ScopedTrace trace2("Waiting for Daemons"); self->ClearException(); self->GetJniEnv()->CallStaticVoidMethod(WellKnownClasses::java_lang_Daemons, WellKnownClasses::java_lang_Daemons_stop); - ATRACE_END(); } Trace::Shutdown(); if (attach_shutdown_thread) { - ATRACE_BEGIN("Detach shutdown thread"); DetachCurrentThread(); - ATRACE_END(); self = nullptr; } @@ -271,14 +264,13 @@ Runtime::~Runtime() { heap_->WaitForGcToComplete(gc::kGcCauseBackground, self); heap_->DeleteThreadPool(); if (jit_ != nullptr) { - ATRACE_BEGIN("Delete jit"); + ScopedTrace trace2("Delete jit"); VLOG(jit) << "Deleting jit thread pool"; // Delete thread pool before the thread list since we don't want to wait forever on the // JIT compiler threads. jit_->DeleteThreadPool(); // Similarly, stop the profile saver thread before deleting the thread list. jit_->StopProfileSaver(); - ATRACE_END(); } // Make sure our internal threads are dead before we start tearing down things they're using. @@ -286,10 +278,10 @@ Runtime::~Runtime() { delete signal_catcher_; // Make sure all other non-daemon threads have terminated, and all daemon threads are suspended. - ATRACE_BEGIN("Delete thread list"); - delete thread_list_; - ATRACE_END(); - + { + ScopedTrace trace2("Delete thread list"); + delete thread_list_; + } // Delete the JIT after thread list to ensure that there is no remaining threads which could be // accessing the instrumentation when we delete it. if (jit_ != nullptr) { @@ -300,7 +292,7 @@ Runtime::~Runtime() { // Shutdown the fault manager if it was initialized. fault_manager.Shutdown(); - ATRACE_BEGIN("Delete state"); + ScopedTrace trace2("Delete state"); delete monitor_list_; delete monitor_pool_; delete class_linker_; @@ -318,12 +310,10 @@ Runtime::~Runtime() { arena_pool_.reset(); jit_arena_pool_.reset(); MemMap::Shutdown(); - ATRACE_END(); // TODO: acquire a static mutex on Runtime to avoid racing. CHECK(instance_ == nullptr || instance_ == this); instance_ = nullptr; - ATRACE_END(); } struct AbortState { @@ -562,12 +552,14 @@ bool Runtime::Start() { // Use !IsAotCompiler so that we get test coverage, tests are never the zygote. if (!IsAotCompiler()) { ScopedObjectAccess soa(self); - ATRACE_BEGIN("AddImageStringsToTable"); - GetInternTable()->AddImagesStringsToTable(heap_->GetBootImageSpaces()); - ATRACE_END(); - ATRACE_BEGIN("MoveImageClassesToClassTable"); - GetClassLinker()->AddBootImageClassesToClassTable(); - ATRACE_END(); + { + ScopedTrace trace2("AddImageStringsToTable"); + GetInternTable()->AddImagesStringsToTable(heap_->GetBootImageSpaces()); + } + { + ScopedTrace trace2("MoveImageClassesToClassTable"); + GetClassLinker()->AddBootImageClassesToClassTable(); + } } // If we are the zygote then we need to wait until after forking to create the code cache @@ -585,9 +577,10 @@ bool Runtime::Start() { // InitNativeMethods needs to be after started_ so that the classes // it touches will have methods linked to the oat file if necessary. - ATRACE_BEGIN("InitNativeMethods"); - InitNativeMethods(); - ATRACE_END(); + { + ScopedTrace trace2("InitNativeMethods"); + InitNativeMethods(); + } // Initialize well known thread group values that may be accessed threads while attaching. InitThreadGroups(self); @@ -613,9 +606,7 @@ bool Runtime::Start() { GetInstructionSetString(kRuntimeISA)); } - ATRACE_BEGIN("StartDaemonThreads"); StartDaemonThreads(); - ATRACE_END(); { ScopedObjectAccess soa(self); @@ -750,6 +741,7 @@ bool Runtime::IsDebuggable() const { } void Runtime::StartDaemonThreads() { + ScopedTrace trace(__FUNCTION__); VLOG(startup) << "Runtime::StartDaemonThreads entering"; Thread* self = Thread::Current(); @@ -893,7 +885,7 @@ void Runtime::SetSentinel(mirror::Object* sentinel) { bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { RuntimeArgumentMap runtime_options(std::move(runtime_options_in)); - ATRACE_BEGIN("Runtime::Init"); + ScopedTrace trace(__FUNCTION__); CHECK_EQ(sysconf(_SC_PAGE_SIZE), kPageSize); MemMap::Init(); @@ -960,7 +952,6 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { is_low_memory_mode_ = runtime_options.Exists(Opt::LowMemoryMode); XGcOption xgc_option = runtime_options.GetOrDefault(Opt::GcOption); - ATRACE_BEGIN("CreateHeap"); heap_ = new gc::Heap(runtime_options.GetOrDefault(Opt::MemoryInitialSize), runtime_options.GetOrDefault(Opt::HeapGrowthLimit), runtime_options.GetOrDefault(Opt::HeapMinFree), @@ -991,11 +982,9 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { xgc_option.gcstress_, runtime_options.GetOrDefault(Opt::EnableHSpaceCompactForOOM), runtime_options.GetOrDefault(Opt::HSpaceCompactForOOMMinIntervalsMs)); - ATRACE_END(); if (!heap_->HasBootImageSpace() && !allow_dex_file_fallback_) { LOG(ERROR) << "Dex file fallback disabled, cannot continue without image."; - ATRACE_END(); return false; } @@ -1105,10 +1094,8 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { CHECK_GE(GetHeap()->GetContinuousSpaces().size(), 1U); class_linker_ = new ClassLinker(intern_table_); if (GetHeap()->HasBootImageSpace()) { - ATRACE_BEGIN("InitFromImage"); std::string error_msg; bool result = class_linker_->InitFromBootImage(&error_msg); - ATRACE_END(); if (!result) { LOG(ERROR) << "Could not initialize from image: " << error_msg; return false; @@ -1251,8 +1238,6 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { VLOG(startup) << "Runtime::Init exiting"; - ATRACE_END(); - return true; } @@ -1468,10 +1453,12 @@ void Runtime::BlockSignals() { bool Runtime::AttachCurrentThread(const char* thread_name, bool as_daemon, jobject thread_group, bool create_peer) { + ScopedTrace trace(__FUNCTION__); return Thread::Attach(thread_name, as_daemon, thread_group, create_peer) != nullptr; } void Runtime::DetachCurrentThread() { + ScopedTrace trace(__FUNCTION__); Thread* self = Thread::Current(); if (self == nullptr) { LOG(FATAL) << "attempting to detach thread that is not attached"; diff --git a/runtime/thread.cc b/runtime/thread.cc index 2ee160571e..6b8c0c2e4b 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -14,11 +14,8 @@ * limitations under the License. */ -#define ATRACE_TAG ATRACE_TAG_DALVIK - #include "thread.h" -#include #include #include #include @@ -39,6 +36,7 @@ #include "base/mutex.h" #include "base/timing_logger.h" #include "base/to_str.h" +#include "base/systrace.h" #include "class_linker-inl.h" #include "debugger.h" #include "dex_file-inl.h" @@ -1119,9 +1117,8 @@ void Thread::RunCheckpointFunction() { bool found_checkpoint = false; for (uint32_t i = 0; i < kMaxCheckpoints; ++i) { if (checkpoints[i] != nullptr) { - ATRACE_BEGIN("Checkpoint function"); + ScopedTrace trace("Run checkpoint function"); checkpoints[i]->Run(this); - ATRACE_END(); found_checkpoint = true; } } @@ -1187,14 +1184,13 @@ void Thread::SetFlipFunction(Closure* function) { } void Thread::FullSuspendCheck() { + ScopedTrace trace(__FUNCTION__); VLOG(threads) << this << " self-suspending"; - ATRACE_BEGIN("Full suspend check"); // Make thread appear suspended to other threads, release mutator_lock_. tls32_.suspended_at_suspend_check = true; // Transition to suspended and back to runnable, re-acquire share on mutator_lock_. ScopedThreadSuspension(this, kSuspended); tls32_.suspended_at_suspend_check = false; - ATRACE_END(); VLOG(threads) << this << " self-reviving"; } diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc index 49d54fda00..cf515b60cb 100644 --- a/runtime/thread_list.cc +++ b/runtime/thread_list.cc @@ -16,10 +16,7 @@ #include "thread_list.h" -#define ATRACE_TAG ATRACE_TAG_DALVIK - #include -#include #include #include #include @@ -30,6 +27,7 @@ #include "base/histogram-inl.h" #include "base/mutex-inl.h" +#include "base/systrace.h" #include "base/time_utils.h" #include "base/timing_logger.h" #include "debugger.h" @@ -69,38 +67,29 @@ ThreadList::ThreadList() } ThreadList::~ThreadList() { - ATRACE_BEGIN(__FUNCTION__); + ScopedTrace trace(__PRETTY_FUNCTION__); // Detach the current thread if necessary. If we failed to start, there might not be any threads. // We need to detach the current thread here in case there's another thread waiting to join with // us. bool contains = false; + Thread* self = Thread::Current(); { - Thread* self = Thread::Current(); MutexLock mu(self, *Locks::thread_list_lock_); contains = Contains(self); } if (contains) { - ATRACE_BEGIN("DetachCurrentThread"); Runtime::Current()->DetachCurrentThread(); - ATRACE_END(); } - ATRACE_BEGIN("WaitForOtherNonDaemonThreadsToExit"); WaitForOtherNonDaemonThreadsToExit(); - ATRACE_END(); // Disable GC and wait for GC to complete in case there are still daemon threads doing // allocations. gc::Heap* const heap = Runtime::Current()->GetHeap(); heap->DisableGCForShutdown(); // In case a GC is in progress, wait for it to finish. - ATRACE_BEGIN("WaitForGcToComplete"); heap->WaitForGcToComplete(gc::kGcCauseBackground, Thread::Current()); - ATRACE_END(); // TODO: there's an unaddressed race here where a thread may attach during shutdown, see // Thread::Init. - ATRACE_BEGIN("SuspendAllDaemonThreadsForShutdown"); SuspendAllDaemonThreadsForShutdown(); - ATRACE_END(); - ATRACE_END(); } bool ThreadList::Contains(Thread* thread) { @@ -475,42 +464,42 @@ void ThreadList::SuspendAll(const char* cause, bool long_suspend) { } else { VLOG(threads) << "Thread[null] SuspendAll for " << cause << " starting..."; } - ATRACE_BEGIN("Suspending mutator threads"); - const uint64_t start_time = NanoTime(); + { + ScopedTrace trace("Suspending mutator threads"); + const uint64_t start_time = NanoTime(); - SuspendAllInternal(self, self); - // All threads are known to have suspended (but a thread may still own the mutator lock) - // Make sure this thread grabs exclusive access to the mutator lock and its protected data. + SuspendAllInternal(self, self); + // All threads are known to have suspended (but a thread may still own the mutator lock) + // Make sure this thread grabs exclusive access to the mutator lock and its protected data. #if HAVE_TIMED_RWLOCK - while (true) { - if (Locks::mutator_lock_->ExclusiveLockWithTimeout(self, kThreadSuspendTimeoutMs, 0)) { - break; - } else if (!long_suspend_) { - // Reading long_suspend without the mutator lock is slightly racy, in some rare cases, this - // could result in a thread suspend timeout. - // Timeout if we wait more than kThreadSuspendTimeoutMs seconds. - UnsafeLogFatalForThreadSuspendAllTimeout(); + while (true) { + if (Locks::mutator_lock_->ExclusiveLockWithTimeout(self, kThreadSuspendTimeoutMs, 0)) { + break; + } else if (!long_suspend_) { + // Reading long_suspend without the mutator lock is slightly racy, in some rare cases, this + // could result in a thread suspend timeout. + // Timeout if we wait more than kThreadSuspendTimeoutMs seconds. + UnsafeLogFatalForThreadSuspendAllTimeout(); + } } - } #else - Locks::mutator_lock_->ExclusiveLock(self); + Locks::mutator_lock_->ExclusiveLock(self); #endif - long_suspend_ = long_suspend; + long_suspend_ = long_suspend; - const uint64_t end_time = NanoTime(); - const uint64_t suspend_time = end_time - start_time; - suspend_all_historam_.AdjustAndAddValue(suspend_time); - if (suspend_time > kLongThreadSuspendThreshold) { - LOG(WARNING) << "Suspending all threads took: " << PrettyDuration(suspend_time); - } + const uint64_t end_time = NanoTime(); + const uint64_t suspend_time = end_time - start_time; + suspend_all_historam_.AdjustAndAddValue(suspend_time); + if (suspend_time > kLongThreadSuspendThreshold) { + LOG(WARNING) << "Suspending all threads took: " << PrettyDuration(suspend_time); + } - if (kDebugLocking) { - // Debug check that all threads are suspended. - AssertThreadsAreSuspended(self, self); + if (kDebugLocking) { + // Debug check that all threads are suspended. + AssertThreadsAreSuspended(self, self); + } } - - ATRACE_END(); ATRACE_BEGIN((std::string("Mutator threads suspended for ") + cause).c_str()); if (self != nullptr) { @@ -640,7 +629,8 @@ void ThreadList::ResumeAll() { } ATRACE_END(); - ATRACE_BEGIN("Resuming mutator threads"); + + ScopedTrace trace("Resuming mutator threads"); if (kDebugLocking) { // Debug check that all threads are suspended. @@ -672,7 +662,6 @@ void ThreadList::ResumeAll() { } Thread::resume_cond_->Broadcast(self); } - ATRACE_END(); if (self != nullptr) { VLOG(threads) << *self << " ResumeAll complete"; @@ -1117,6 +1106,7 @@ void ThreadList::UndoDebuggerSuspensions() { } void ThreadList::WaitForOtherNonDaemonThreadsToExit() { + ScopedTrace trace(__PRETTY_FUNCTION__); Thread* self = Thread::Current(); Locks::mutator_lock_->AssertNotHeld(self); while (true) { @@ -1148,6 +1138,7 @@ void ThreadList::WaitForOtherNonDaemonThreadsToExit() { } void ThreadList::SuspendAllDaemonThreadsForShutdown() { + ScopedTrace trace(__PRETTY_FUNCTION__); Thread* self = Thread::Current(); MutexLock mu(self, *Locks::thread_list_lock_); size_t daemons_left = 0; diff --git a/runtime/trace.cc b/runtime/trace.cc index 6b826414bd..b8793556d8 100644 --- a/runtime/trace.cc +++ b/runtime/trace.cc @@ -19,12 +19,10 @@ #include #include -#define ATRACE_TAG ATRACE_TAG_DALVIK -#include "cutils/trace.h" - #include "art_method-inl.h" #include "base/casts.h" #include "base/stl_util.h" +#include "base/systrace.h" #include "base/time_utils.h" #include "base/unix_file/fd_file.h" #include "class_linker.h" @@ -286,7 +284,7 @@ void* Trace::RunSamplingThread(void* arg) { while (true) { usleep(interval_us); - ATRACE_BEGIN("Profile sampling"); + ScopedTrace trace("Profile sampling"); Thread* self = Thread::Current(); Trace* the_trace; { @@ -301,7 +299,6 @@ void* Trace::RunSamplingThread(void* arg) { MutexLock mu(self, *Locks::thread_list_lock_); runtime->GetThreadList()->ForEach(GetSample, the_trace); } - ATRACE_END(); } runtime->DetachCurrentThread(); diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index f71ebfe8b7..4019656c93 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -16,9 +16,6 @@ #include "method_verifier-inl.h" -#define ATRACE_TAG ATRACE_TAG_DALVIK -#include - #include #include "art_field-inl.h" @@ -26,6 +23,7 @@ #include "base/logging.h" #include "base/mutex-inl.h" #include "base/stl_util.h" +#include "base/systrace.h" #include "base/time_utils.h" #include "class_linker.h" #include "compiler_callbacks.h" @@ -272,6 +270,7 @@ MethodVerifier::FailureKind MethodVerifier::VerifyClass(Thread* self, bool log_hard_failures, std::string* error) { DCHECK(class_def != nullptr); + ScopedTrace trace(__FUNCTION__); // A class must not be abstract and final. if ((class_def->access_flags_ & (kAccAbstract | kAccFinal)) == (kAccAbstract | kAccFinal)) { @@ -286,7 +285,6 @@ MethodVerifier::FailureKind MethodVerifier::VerifyClass(Thread* self, // empty class, probably a marker interface return kNoFailure; } - ATRACE_BEGIN("VerifyClass"); ClassDataItemIterator it(*dex_file, class_data); while (it.HasNextStaticField() || it.HasNextInstanceField()) { it.Next(); @@ -321,8 +319,6 @@ MethodVerifier::FailureKind MethodVerifier::VerifyClass(Thread* self, data1.Merge(data2); - ATRACE_END(); - if (data1.kind == kNoFailure) { return kNoFailure; } else { -- GitLab From ddec7f9c117baa2b90d06ccfdb3460a4c5caf35f Mon Sep 17 00:00:00 2001 From: Chris Larsen Date: Tue, 16 Feb 2016 12:35:04 -0800 Subject: [PATCH 140/204] MIPS64: Implement isInfinite intrinsics. - boolean java.lang.Float.isInfinite(float) - boolean java.lang.Double.isInfinite(double) Change-Id: I8e94a9cc462e6b1cf99bb14b17c1ff009c9c29a4 --- compiler/optimizing/intrinsics_mips64.cc | 37 ++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc index 45611f0ac7..617844bb18 100644 --- a/compiler/optimizing/intrinsics_mips64.cc +++ b/compiler/optimizing/intrinsics_mips64.cc @@ -1661,6 +1661,40 @@ void IntrinsicCodeGeneratorMIPS64::VisitStringNewStringFromString(HInvoke* invok __ Bind(slow_path->GetExitLabel()); } +static void GenIsInfinite(LocationSummary* locations, + bool is64bit, + Mips64Assembler* assembler) { + FpuRegister in = locations->InAt(0).AsFpuRegister(); + GpuRegister out = locations->Out().AsRegister(); + + if (is64bit) { + __ ClassD(FTMP, in); + } else { + __ ClassS(FTMP, in); + } + __ Mfc1(out, FTMP); + __ Andi(out, out, kPositiveInfinity | kNegativeInfinity); + __ Sltu(out, ZERO, out); +} + +// boolean java.lang.Float.isInfinite(float) +void IntrinsicLocationsBuilderMIPS64::VisitFloatIsInfinite(HInvoke* invoke) { + CreateFPToIntLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS64::VisitFloatIsInfinite(HInvoke* invoke) { + GenIsInfinite(invoke->GetLocations(), /* is64bit */ false, GetAssembler()); +} + +// boolean java.lang.Double.isInfinite(double) +void IntrinsicLocationsBuilderMIPS64::VisitDoubleIsInfinite(HInvoke* invoke) { + CreateFPToIntLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS64::VisitDoubleIsInfinite(HInvoke* invoke) { + GenIsInfinite(invoke->GetLocations(), /* is64bit */ true, GetAssembler()); +} + UNIMPLEMENTED_INTRINSIC(MIPS64, IntegerBitCount) UNIMPLEMENTED_INTRINSIC(MIPS64, LongBitCount) @@ -1690,9 +1724,6 @@ UNIMPLEMENTED_INTRINSIC(MIPS64, MathSinh) UNIMPLEMENTED_INTRINSIC(MIPS64, MathTan) UNIMPLEMENTED_INTRINSIC(MIPS64, MathTanh) -UNIMPLEMENTED_INTRINSIC(MIPS64, FloatIsInfinite) -UNIMPLEMENTED_INTRINSIC(MIPS64, DoubleIsInfinite) - UNIMPLEMENTED_INTRINSIC(MIPS64, IntegerHighestOneBit) UNIMPLEMENTED_INTRINSIC(MIPS64, LongHighestOneBit) UNIMPLEMENTED_INTRINSIC(MIPS64, IntegerLowestOneBit) -- GitLab From 4856ca7f652b889c176a2af747d5cc4b334fc1c6 Mon Sep 17 00:00:00 2001 From: Sebastien Hertz Date: Thu, 3 Mar 2016 18:08:17 +0100 Subject: [PATCH 141/204] Support multidex run-test with Jack Updates default-build script to compile multidex run-tests with Jack. Such test needs a 'multidex.jpp' file specifying how to partition classes in the output dex files (mainly how to stick some classes in the classes.dex file and others in a classes2.dex file). Bug: 19467889 Change-Id: I2500967ba951218c5b03166b3586a576d6dc8749 --- .../multidex.jpp | 8 +++++ test/556-invoke-super/multidex.jpp | 4 +++ .../multidex.jpp | 8 +++++ test/etc/default-build | 29 +++++++++++-------- 4 files changed, 37 insertions(+), 12 deletions(-) create mode 100644 test/462-checker-inlining-across-dex-files/multidex.jpp create mode 100644 test/556-invoke-super/multidex.jpp create mode 100644 test/569-checker-pattern-replacement/multidex.jpp diff --git a/test/462-checker-inlining-across-dex-files/multidex.jpp b/test/462-checker-inlining-across-dex-files/multidex.jpp new file mode 100644 index 0000000000..ae554566cb --- /dev/null +++ b/test/462-checker-inlining-across-dex-files/multidex.jpp @@ -0,0 +1,8 @@ +Main: + @@com.android.jack.annotations.ForceInMainDex + class Main + +AAA: + @@com.android.jack.annotations.ForceInMainDex + class AAA + diff --git a/test/556-invoke-super/multidex.jpp b/test/556-invoke-super/multidex.jpp new file mode 100644 index 0000000000..fe018019e3 --- /dev/null +++ b/test/556-invoke-super/multidex.jpp @@ -0,0 +1,4 @@ +Main: + @@com.android.jack.annotations.ForceInMainDex + class Main* + diff --git a/test/569-checker-pattern-replacement/multidex.jpp b/test/569-checker-pattern-replacement/multidex.jpp new file mode 100644 index 0000000000..cfc8ad1fc9 --- /dev/null +++ b/test/569-checker-pattern-replacement/multidex.jpp @@ -0,0 +1,8 @@ +Main: + @@com.android.jack.annotations.ForceInMainDex + class Main + +BaseInMainDex: + @@com.android.jack.annotations.ForceInMainDex + class BaseInMainDex + diff --git a/test/etc/default-build b/test/etc/default-build index 6e855ec30a..5f78496c3f 100755 --- a/test/etc/default-build +++ b/test/etc/default-build @@ -116,28 +116,33 @@ if ! [ "${HAS_SRC}" = "true" ] && ! [ "${HAS_SRC2}" = "true" ]; then SKIP_DX_MERGER="true" fi -if [ "${HAS_SRC_MULTIDEX}" = "true" ]; then - # Jack does not support this configuration unless we specify how to partition the DEX file - # with a .jpp file. - USE_JACK="false" -fi - if [ ${USE_JACK} = "true" ]; then # Jack toolchain if [ "${HAS_SRC}" = "true" ]; then - ${JACK} ${JACK_ARGS} --output-jack src.jack src - imported_jack_files="--import src.jack" + if [ "${HAS_SRC_MULTIDEX}" = "true" ]; then + # Compile src and src-multidex in the same .jack file. We will apply multidex partitioning + # when creating the output .dex file. + ${JACK} ${JACK_ARGS} --output-jack src.jack src src src-multidex + jack_extra_args="${jack_extra_args} -D jack.dex.output.policy=minimal-multidex" + jack_extra_args="${jack_extra_args} -D jack.preprocessor=true" + jack_extra_args="${jack_extra_args} -D jack.preprocessor.file=multidex.jpp" + else + ${JACK} ${JACK_ARGS} --output-jack src.jack src + fi + jack_extra_args="${jack_extra_args} --import src.jack" fi if [ "${HAS_SRC2}" = "true" ]; then ${JACK} ${JACK_ARGS} --output-jack src2.jack src2 - imported_jack_files="--import src2.jack ${imported_jack_files}" + # In case of duplicate classes, we want to take into account the classes from src2. Therefore + # we apply the 'keep-first' policy and import src2.jack file *before* the src.jack file. + jack_extra_args="${jack_extra_args} -D jack.import.type.policy=keep-first" + jack_extra_args="--import src2.jack ${jack_extra_args}" fi - # Compile jack files into a DEX file. We set jack.import.type.policy=keep-first to consider - # class definitions from src2 first. + # Compile jack files into a DEX file. if [ "${HAS_SRC}" = "true" ] || [ "${HAS_SRC2}" = "true" ]; then - ${JACK} ${JACK_ARGS} ${imported_jack_files} -D jack.import.type.policy=keep-first --output-dex . + ${JACK} ${JACK_ARGS} ${jack_extra_args} --output-dex . fi else # Legacy toolchain with javac+dx -- GitLab From 2cbbf16f3b884d052418eb2c0abd84c24f6f44a2 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Mon, 7 Mar 2016 12:32:05 +0000 Subject: [PATCH 142/204] ART: Fix RegTypeCache::FromUnresolvedMerge(). Do not assume that the BitVector memory is zero-initialized. Also fix reg_type_test memory leaks reported by valgrind by constructing ArenaStack and ScopedArenaAllocator in each individual test. Bug: 27500678 Change-Id: Ic6b2b6ebeab2c5c6e993a0232c5d4a0159aabea3 --- runtime/verifier/reg_type_cache.cc | 3 ++ runtime/verifier/reg_type_test.cc | 71 ++++++++++++++++++------------ 2 files changed, 47 insertions(+), 27 deletions(-) diff --git a/runtime/verifier/reg_type_cache.cc b/runtime/verifier/reg_type_cache.cc index 30f613c389..b171b75e97 100644 --- a/runtime/verifier/reg_type_cache.cc +++ b/runtime/verifier/reg_type_cache.cc @@ -17,6 +17,7 @@ #include "reg_type_cache-inl.h" #include "base/arena_bit_vector.h" +#include "base/bit_vector-inl.h" #include "base/casts.h" #include "base/scoped_arena_allocator.h" #include "base/stl_util.h" @@ -351,9 +352,11 @@ const RegType& RegTypeCache::FromUnresolvedMerge(const RegType& left, const RegT types.Copy(&left_merge->GetUnresolvedTypes()); left_resolved = &left_merge->GetResolvedPart(); } else if (left.IsUnresolvedTypes()) { + types.ClearAllBits(); types.SetBit(left.GetId()); left_resolved = &Zero(); } else { + types.ClearAllBits(); left_resolved = &left; } diff --git a/runtime/verifier/reg_type_test.cc b/runtime/verifier/reg_type_test.cc index 22ac7e4ab2..42a74f88e1 100644 --- a/runtime/verifier/reg_type_test.cc +++ b/runtime/verifier/reg_type_test.cc @@ -30,23 +30,14 @@ namespace art { namespace verifier { -class BaseRegTypeTest : public CommonRuntimeTest { - public: - void PostRuntimeCreate() OVERRIDE { - stack.reset(new ArenaStack(Runtime::Current()->GetArenaPool())); - allocator.reset(new ScopedArenaAllocator(stack.get())); - } - - std::unique_ptr stack; - std::unique_ptr allocator; -}; - -class RegTypeTest : public BaseRegTypeTest {}; +class RegTypeTest : public CommonRuntimeTest {}; TEST_F(RegTypeTest, ConstLoHi) { // Tests creating primitive types types. + ArenaStack stack(Runtime::Current()->GetArenaPool()); + ScopedArenaAllocator allocator(&stack); ScopedObjectAccess soa(Thread::Current()); - RegTypeCache cache(true, *allocator); + RegTypeCache cache(true, allocator); const RegType& ref_type_const_0 = cache.FromCat1Const(10, true); const RegType& ref_type_const_1 = cache.FromCat1Const(10, true); const RegType& ref_type_const_2 = cache.FromCat1Const(30, true); @@ -67,8 +58,10 @@ TEST_F(RegTypeTest, ConstLoHi) { } TEST_F(RegTypeTest, Pairs) { + ArenaStack stack(Runtime::Current()->GetArenaPool()); + ScopedArenaAllocator allocator(&stack); ScopedObjectAccess soa(Thread::Current()); - RegTypeCache cache(true, *allocator); + RegTypeCache cache(true, allocator); int64_t val = static_cast(1234); const RegType& precise_lo = cache.FromCat2ConstLo(static_cast(val), true); const RegType& precise_hi = cache.FromCat2ConstHi(static_cast(val >> 32), true); @@ -91,8 +84,10 @@ TEST_F(RegTypeTest, Pairs) { } TEST_F(RegTypeTest, Primitives) { + ArenaStack stack(Runtime::Current()->GetArenaPool()); + ScopedArenaAllocator allocator(&stack); ScopedObjectAccess soa(Thread::Current()); - RegTypeCache cache(true, *allocator); + RegTypeCache cache(true, allocator); const RegType& bool_reg_type = cache.Boolean(); EXPECT_FALSE(bool_reg_type.IsUndefined()); @@ -359,13 +354,15 @@ TEST_F(RegTypeTest, Primitives) { EXPECT_TRUE(double_reg_type.HasClass()); } -class RegTypeReferenceTest : public BaseRegTypeTest {}; +class RegTypeReferenceTest : public CommonRuntimeTest {}; TEST_F(RegTypeReferenceTest, JavalangObjectImprecise) { // Tests matching precisions. A reference type that was created precise doesn't // match the one that is imprecise. + ArenaStack stack(Runtime::Current()->GetArenaPool()); + ScopedArenaAllocator allocator(&stack); ScopedObjectAccess soa(Thread::Current()); - RegTypeCache cache(true, *allocator); + RegTypeCache cache(true, allocator); const RegType& imprecise_obj = cache.JavaLangObject(false); const RegType& precise_obj = cache.JavaLangObject(true); const RegType& precise_obj_2 = cache.FromDescriptor(nullptr, "Ljava/lang/Object;", true); @@ -379,8 +376,10 @@ TEST_F(RegTypeReferenceTest, JavalangObjectImprecise) { TEST_F(RegTypeReferenceTest, UnresolvedType) { // Tests creating unresolved types. Miss for the first time asking the cache and // a hit second time. + ArenaStack stack(Runtime::Current()->GetArenaPool()); + ScopedArenaAllocator allocator(&stack); ScopedObjectAccess soa(Thread::Current()); - RegTypeCache cache(true, *allocator); + RegTypeCache cache(true, allocator); const RegType& ref_type_0 = cache.FromDescriptor(nullptr, "Ljava/lang/DoesNotExist;", true); EXPECT_TRUE(ref_type_0.IsUnresolvedReference()); EXPECT_TRUE(ref_type_0.IsNonZeroReferenceTypes()); @@ -395,8 +394,10 @@ TEST_F(RegTypeReferenceTest, UnresolvedType) { TEST_F(RegTypeReferenceTest, UnresolvedUnintializedType) { // Tests creating types uninitialized types from unresolved types. + ArenaStack stack(Runtime::Current()->GetArenaPool()); + ScopedArenaAllocator allocator(&stack); ScopedObjectAccess soa(Thread::Current()); - RegTypeCache cache(true, *allocator); + RegTypeCache cache(true, allocator); const RegType& ref_type_0 = cache.FromDescriptor(nullptr, "Ljava/lang/DoesNotExist;", true); EXPECT_TRUE(ref_type_0.IsUnresolvedReference()); const RegType& ref_type = cache.FromDescriptor(nullptr, "Ljava/lang/DoesNotExist;", true); @@ -417,8 +418,10 @@ TEST_F(RegTypeReferenceTest, UnresolvedUnintializedType) { TEST_F(RegTypeReferenceTest, Dump) { // Tests types for proper Dump messages. + ArenaStack stack(Runtime::Current()->GetArenaPool()); + ScopedArenaAllocator allocator(&stack); ScopedObjectAccess soa(Thread::Current()); - RegTypeCache cache(true, *allocator); + RegTypeCache cache(true, allocator); const RegType& unresolved_ref = cache.FromDescriptor(nullptr, "Ljava/lang/DoesNotExist;", true); const RegType& unresolved_ref_another = cache.FromDescriptor(nullptr, "Ljava/lang/DoesNotExistEither;", true); const RegType& resolved_ref = cache.JavaLangString(); @@ -442,8 +445,10 @@ TEST_F(RegTypeReferenceTest, JavalangString) { // Add a class to the cache then look for the same class and make sure it is a // Hit the second time. Then check for the same effect when using // The JavaLangObject method instead of FromDescriptor. String class is final. + ArenaStack stack(Runtime::Current()->GetArenaPool()); + ScopedArenaAllocator allocator(&stack); ScopedObjectAccess soa(Thread::Current()); - RegTypeCache cache(true, *allocator); + RegTypeCache cache(true, allocator); const RegType& ref_type = cache.JavaLangString(); const RegType& ref_type_2 = cache.JavaLangString(); const RegType& ref_type_3 = cache.FromDescriptor(nullptr, "Ljava/lang/String;", true); @@ -462,8 +467,10 @@ TEST_F(RegTypeReferenceTest, JavalangObject) { // Add a class to the cache then look for the same class and make sure it is a // Hit the second time. Then I am checking for the same effect when using // The JavaLangObject method instead of FromDescriptor. Object Class in not final. + ArenaStack stack(Runtime::Current()->GetArenaPool()); + ScopedArenaAllocator allocator(&stack); ScopedObjectAccess soa(Thread::Current()); - RegTypeCache cache(true, *allocator); + RegTypeCache cache(true, allocator); const RegType& ref_type = cache.JavaLangObject(true); const RegType& ref_type_2 = cache.JavaLangObject(true); const RegType& ref_type_3 = cache.FromDescriptor(nullptr, "Ljava/lang/Object;", true); @@ -476,7 +483,9 @@ TEST_F(RegTypeReferenceTest, Merging) { // Tests merging logic // String and object , LUB is object. ScopedObjectAccess soa(Thread::Current()); - RegTypeCache cache_new(true, *allocator); + ArenaStack stack(Runtime::Current()->GetArenaPool()); + ScopedArenaAllocator allocator(&stack); + RegTypeCache cache_new(true, allocator); const RegType& string = cache_new.JavaLangString(); const RegType& Object = cache_new.JavaLangObject(true); EXPECT_TRUE(string.Merge(Object, &cache_new).IsJavaLangObject()); @@ -498,8 +507,10 @@ TEST_F(RegTypeReferenceTest, Merging) { TEST_F(RegTypeTest, MergingFloat) { // Testing merging logic with float and float constants. + ArenaStack stack(Runtime::Current()->GetArenaPool()); + ScopedArenaAllocator allocator(&stack); ScopedObjectAccess soa(Thread::Current()); - RegTypeCache cache_new(true, *allocator); + RegTypeCache cache_new(true, allocator); constexpr int32_t kTestConstantValue = 10; const RegType& float_type = cache_new.Float(); @@ -529,8 +540,10 @@ TEST_F(RegTypeTest, MergingFloat) { TEST_F(RegTypeTest, MergingLong) { // Testing merging logic with long and long constants. + ArenaStack stack(Runtime::Current()->GetArenaPool()); + ScopedArenaAllocator allocator(&stack); ScopedObjectAccess soa(Thread::Current()); - RegTypeCache cache_new(true, *allocator); + RegTypeCache cache_new(true, allocator); constexpr int32_t kTestConstantValue = 10; const RegType& long_lo_type = cache_new.LongLo(); @@ -583,8 +596,10 @@ TEST_F(RegTypeTest, MergingLong) { TEST_F(RegTypeTest, MergingDouble) { // Testing merging logic with double and double constants. + ArenaStack stack(Runtime::Current()->GetArenaPool()); + ScopedArenaAllocator allocator(&stack); ScopedObjectAccess soa(Thread::Current()); - RegTypeCache cache_new(true, *allocator); + RegTypeCache cache_new(true, allocator); constexpr int32_t kTestConstantValue = 10; const RegType& double_lo_type = cache_new.DoubleLo(); @@ -637,8 +652,10 @@ TEST_F(RegTypeTest, MergingDouble) { TEST_F(RegTypeTest, ConstPrecision) { // Tests creating primitive types types. + ArenaStack stack(Runtime::Current()->GetArenaPool()); + ScopedArenaAllocator allocator(&stack); ScopedObjectAccess soa(Thread::Current()); - RegTypeCache cache_new(true, *allocator); + RegTypeCache cache_new(true, allocator); const RegType& imprecise_const = cache_new.FromCat1Const(10, false); const RegType& precise_const = cache_new.FromCat1Const(10, true); -- GitLab From fe6f0b6dbfa2f5d234cd965bdefc551432c08d98 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Mon, 7 Mar 2016 13:33:37 +0000 Subject: [PATCH 143/204] Workaround for broken 'ps' command on M. Change-Id: Ifabc6bb2b0e3780306e8a3ad45531c50c7627ed1 --- tools/setup-buildbot-device.sh | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/tools/setup-buildbot-device.sh b/tools/setup-buildbot-device.sh index 9e085b58b4..1e9c763534 100755 --- a/tools/setup-buildbot-device.sh +++ b/tools/setup-buildbot-device.sh @@ -46,5 +46,13 @@ adb logcat -P "" adb logcat -p echo -e "${green}Kill stalled dalvikvm processes${nc}" -processes=$(adb shell "ps" | grep dalvikvm | awk '{print $2}') -for i in $processes; do adb shell kill -9 $i; done +# 'ps' on M can sometimes hang. +timeout 2s adb shell "ps" +if [ $? = 124 ]; then + echo -e "${green}Rebooting device to fix 'ps'${nc}" + adb reboot + adb wait-for-device root +else + processes=$(adb shell "ps" | grep dalvikvm | awk '{print $2}') + for i in $processes; do adb shell kill -9 $i; done +fi -- GitLab From de166544d6539942d3507ce47910f601c97b7532 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Mon, 7 Mar 2016 15:44:08 +0000 Subject: [PATCH 144/204] Add some debugging for zygote crash. bug:27340451 Change-Id: I212567b18959ab4dc7f1dbde2e6d4ff60fe7bed2 --- runtime/art_method.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/art_method.cc b/runtime/art_method.cc index a60f31e52e..f97ad51568 100644 --- a/runtime/art_method.cc +++ b/runtime/art_method.cc @@ -377,7 +377,7 @@ const OatQuickMethodHeader* ArtMethod::GetOatQuickMethodHeader(uintptr_t pc) { Runtime* runtime = Runtime::Current(); const void* existing_entry_point = GetEntryPointFromQuickCompiledCode(); - DCHECK(existing_entry_point != nullptr); + CHECK(existing_entry_point != nullptr) << PrettyMethod(this) << "@" << this; ClassLinker* class_linker = runtime->GetClassLinker(); if (class_linker->IsQuickGenericJniStub(existing_entry_point)) { -- GitLab From 629afaba488c5cfdd3ee9ba5b2df24af9de6f2a7 Mon Sep 17 00:00:00 2001 From: Igor Murashkin Date: Fri, 19 Feb 2016 14:56:43 -0800 Subject: [PATCH 145/204] runtime: Add CAS and fences to sun.misc.Unsafe Bug: 26264765 Change-Id: I68b482e5f9a0a26419af5e58a8d67c8c8a8e01a9 --- runtime/native/sun_misc_Unsafe.cc | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/runtime/native/sun_misc_Unsafe.cc b/runtime/native/sun_misc_Unsafe.cc index 6ffd476edf..858849f980 100644 --- a/runtime/native/sun_misc_Unsafe.cc +++ b/runtime/native/sun_misc_Unsafe.cc @@ -26,6 +26,7 @@ #include #include #include +#include namespace art { @@ -473,6 +474,18 @@ static void Unsafe_putDouble(JNIEnv* env, jobject, jobject javaObj, jlong offset obj->SetField64(MemberOffset(offset), conv.converted); } +static void Unsafe_loadFence(JNIEnv*, jobject) { + std::atomic_thread_fence(std::memory_order_acquire); +} + +static void Unsafe_storeFence(JNIEnv*, jobject) { + std::atomic_thread_fence(std::memory_order_release); +} + +static void Unsafe_fullFence(JNIEnv*, jobject) { + std::atomic_thread_fence(std::memory_order_seq_cst); +} + static JNINativeMethod gMethods[] = { NATIVE_METHOD(Unsafe, compareAndSwapInt, "!(Ljava/lang/Object;JII)Z"), NATIVE_METHOD(Unsafe, compareAndSwapLong, "!(Ljava/lang/Object;JJJ)Z"), @@ -532,6 +545,11 @@ static JNINativeMethod gMethods[] = { OVERLOADED_NATIVE_METHOD(Unsafe, putLong, "!(JJ)V", putLongJJ), OVERLOADED_NATIVE_METHOD(Unsafe, putFloat, "!(JF)V", putFloatJF), OVERLOADED_NATIVE_METHOD(Unsafe, putDouble, "!(JD)V", putDoubleJD), + + // CAS + NATIVE_METHOD(Unsafe, loadFence, "!()V"), + NATIVE_METHOD(Unsafe, storeFence, "!()V"), + NATIVE_METHOD(Unsafe, fullFence, "!()V"), }; void register_sun_misc_Unsafe(JNIEnv* env) { -- GitLab From bcd9dd7d1223eaa60f188d1a109d26e5e0143e10 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Mon, 7 Mar 2016 10:25:04 -0800 Subject: [PATCH 146/204] Don't use ScopedObjectAccess in Heap::DumpSpaces ScopedObjectAccess does not work well if the mutator lock is excusively held while in a suspend thread state. This caused deadlocks and DCHECK failures. Bug: 27493316 (cherry picked from commit 03d21bc5bed887243ff6ce3531179185ffd3532c) Change-Id: I5d67f74fc7082761e45dc1d7778b0ea7fceaaf8f --- compiler/image_writer.h | 2 +- runtime/gc/collector/mark_compact.cc | 2 +- runtime/gc/heap.cc | 1 - runtime/gc/heap.h | 4 ++-- runtime/gc/space/large_object_space.cc | 2 ++ 5 files changed, 6 insertions(+), 5 deletions(-) diff --git a/compiler/image_writer.h b/compiler/image_writer.h index dba9dd71fc..f204b28380 100644 --- a/compiler/image_writer.h +++ b/compiler/image_writer.h @@ -443,7 +443,7 @@ class ImageWriter FINAL { static Bin BinTypeForNativeRelocationType(NativeObjectRelocationType type); - uintptr_t NativeOffsetInImage(void* obj); + uintptr_t NativeOffsetInImage(void* obj) SHARED_REQUIRES(Locks::mutator_lock_); // Location of where the object will be when the image is loaded at runtime. template diff --git a/runtime/gc/collector/mark_compact.cc b/runtime/gc/collector/mark_compact.cc index 7727b2da18..6beb60608c 100644 --- a/runtime/gc/collector/mark_compact.cc +++ b/runtime/gc/collector/mark_compact.cc @@ -131,7 +131,7 @@ void MarkCompact::ProcessReferences(Thread* self) { class BitmapSetSlowPathVisitor { public: - void operator()(const mirror::Object* obj) const { + void operator()(const mirror::Object* obj) const SHARED_REQUIRES(Locks::mutator_lock_) { // Marking a large object, make sure its aligned as a sanity check. if (!IsAligned(obj)) { Runtime::Current()->GetHeap()->DumpSpaces(LOG(ERROR)); diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index 3480483c34..faa3d3bc14 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -1545,7 +1545,6 @@ std::string Heap::DumpSpaces() const { } void Heap::DumpSpaces(std::ostream& stream) const { - ScopedObjectAccess soa(Thread::Current()); for (const auto& space : continuous_spaces_) { accounting::ContinuousSpaceBitmap* live_bitmap = space->GetLiveBitmap(); accounting::ContinuousSpaceBitmap* mark_bitmap = space->GetMarkBitmap(); diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index 889069d8ae..e0a53a0cc8 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -651,8 +651,8 @@ class Heap { } } - std::string DumpSpaces() const WARN_UNUSED; - void DumpSpaces(std::ostream& stream) const; + void DumpSpaces(std::ostream& stream) const SHARED_REQUIRES(Locks::mutator_lock_); + std::string DumpSpaces() const SHARED_REQUIRES(Locks::mutator_lock_); // Dump object should only be used by the signal handler. void DumpObject(std::ostream& stream, mirror::Object* obj) NO_THREAD_SAFETY_ANALYSIS; diff --git a/runtime/gc/space/large_object_space.cc b/runtime/gc/space/large_object_space.cc index e70fe215ab..010f677885 100644 --- a/runtime/gc/space/large_object_space.cc +++ b/runtime/gc/space/large_object_space.cc @@ -27,6 +27,7 @@ #include "base/stl_util.h" #include "image.h" #include "os.h" +#include "scoped_thread_state_change.h" #include "space-inl.h" #include "thread-inl.h" @@ -190,6 +191,7 @@ size_t LargeObjectMapSpace::Free(Thread* self, mirror::Object* ptr) { MutexLock mu(self, lock_); auto it = large_objects_.find(ptr); if (UNLIKELY(it == large_objects_.end())) { + ScopedObjectAccess soa(self); Runtime::Current()->GetHeap()->DumpSpaces(LOG(INTERNAL_FATAL)); LOG(FATAL) << "Attempted to free large object " << ptr << " which was not live"; } -- GitLab From 47c1959f0c78eaf6ab17e1b80fa3ce9643e55849 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Mon, 7 Mar 2016 11:59:01 -0800 Subject: [PATCH 147/204] Increase kDumpWaitTimeout 10x for target builds New timeout is 100s, the old one was occasionally hit by overloaded device. This caused a confusing crash in the barrier destructor since not all the threads had went through the barrier yet. Bug: 27334917 (cherry picked from commit 74bdab0b14c592053ff71654ab51a0f116114a42) Change-Id: I24cf6eb6858ad93a47446aa6bbc18942a71b191e --- runtime/thread_list.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc index cf515b60cb..4c81d4f1e3 100644 --- a/runtime/thread_list.cc +++ b/runtime/thread_list.cc @@ -171,9 +171,9 @@ void ThreadList::DumpUnattachedThreads(std::ostream& os) { closedir(d); } -// Dump checkpoint timeout in milliseconds. Larger amount on the host, as dumping will invoke -// addr2line when available. -static constexpr uint32_t kDumpWaitTimeout = kIsTargetBuild ? 10000 : 20000; +// Dump checkpoint timeout in milliseconds. Larger amount on the target, since the device could be +// overloaded with ANR dumps. +static constexpr uint32_t kDumpWaitTimeout = kIsTargetBuild ? 100000 : 20000; // A closure used by Thread::Dump. class DumpCheckpoint FINAL : public Closure { -- GitLab From ebd5fc6a55f535cfdc1043878dd5366032d72fba Mon Sep 17 00:00:00 2001 From: Hiroshi Yamauchi Date: Mon, 7 Mar 2016 13:55:31 -0800 Subject: [PATCH 148/204] Revert "Query declaring class before querying IsNative." This reverts commit 26329cd578efe9cd7e60c46fef1bfb549062a283. Superceded by CL 206308. Bug: 27435111 --- runtime/mirror/class-inl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h index 103a8b79ef..cbcb4b964b 100644 --- a/runtime/mirror/class-inl.h +++ b/runtime/mirror/class-inl.h @@ -940,7 +940,7 @@ void mirror::Class::VisitNativeRoots(Visitor& visitor, size_t pointer_size) { } for (ArtMethod& method : GetMethods(pointer_size)) { method.VisitRoots(visitor, pointer_size); - if (method.GetDeclaringClassUnchecked() != nullptr && !method.IsNative()) { + if (!method.IsNative()) { ProfilingInfo* profiling_info = method.GetProfilingInfo(pointer_size); if (profiling_info != nullptr) { profiling_info->VisitRoots(visitor); -- GitLab From b79eb75ba5671b787ac0622ded2de970f8f0d2c1 Mon Sep 17 00:00:00 2001 From: Hiroshi Yamauchi Date: Mon, 7 Mar 2016 13:56:33 -0800 Subject: [PATCH 149/204] Revert "ProfilingInfo roots should be visited by the declaring class." This reverts commit 9a3be989d0aee1a6998e33813c7f70906d27f89a. Superceded by CL 206308. Bug: 27435111 --- runtime/art_method-inl.h | 6 ++++++ runtime/mirror/class-inl.h | 7 ------- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h index 12d6d8f015..ebe89bbbd2 100644 --- a/runtime/art_method-inl.h +++ b/runtime/art_method-inl.h @@ -463,6 +463,12 @@ void ArtMethod::VisitRoots(RootVisitorType& visitor, size_t pointer_size) { interface_method->VisitRoots(visitor, pointer_size); } visitor.VisitRoot(declaring_class_.AddressWithoutBarrier()); + if (!IsNative()) { + ProfilingInfo* profiling_info = GetProfilingInfo(pointer_size); + if (profiling_info != nullptr) { + profiling_info->VisitRoots(visitor); + } + } } } diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h index cbcb4b964b..19584edf7f 100644 --- a/runtime/mirror/class-inl.h +++ b/runtime/mirror/class-inl.h @@ -29,7 +29,6 @@ #include "dex_cache.h" #include "dex_file.h" #include "gc/heap-inl.h" -#include "jit/profiling_info.h" #include "iftable.h" #include "object_array-inl.h" #include "read_barrier-inl.h" @@ -940,12 +939,6 @@ void mirror::Class::VisitNativeRoots(Visitor& visitor, size_t pointer_size) { } for (ArtMethod& method : GetMethods(pointer_size)) { method.VisitRoots(visitor, pointer_size); - if (!method.IsNative()) { - ProfilingInfo* profiling_info = method.GetProfilingInfo(pointer_size); - if (profiling_info != nullptr) { - profiling_info->VisitRoots(visitor); - } - } } } -- GitLab From 0e54c0160c84894696c05af6cad9eae3690f9496 Mon Sep 17 00:00:00 2001 From: Aart Bik Date: Fri, 4 Mar 2016 12:08:31 -0800 Subject: [PATCH 150/204] Unsafe: Recognize intrinsics for 1.8 java.util.concurrent With unit test. Rationale: Recognizing the 1.8 methods as intrinsics is the first step towards providing efficient implementation on all architectures. Where not implemented (everywhere for now), the methods fall back to the JNI native or reference implementation. NOTE: needs iam's CL first! bug=26264765 Change-Id: Ife65e81689821a16cbcdd2bb2d35641c6de6aeb6 --- compiler/dex/quick/dex_file_method_inliner.cc | 49 ++++ compiler/dex/quick/dex_file_method_inliner.h | 11 + compiler/optimizing/intrinsics.cc | 18 ++ compiler/optimizing/intrinsics_arm.cc | 10 + compiler/optimizing/intrinsics_arm64.cc | 10 + compiler/optimizing/intrinsics_list.h | 8 + compiler/optimizing/intrinsics_mips.cc | 12 +- compiler/optimizing/intrinsics_mips64.cc | 10 + compiler/optimizing/intrinsics_x86.cc | 10 + compiler/optimizing/intrinsics_x86_64.cc | 10 + runtime/quick/inline_method_analyser.h | 11 + test/004-UnsafeTest/src/Main.java | 2 +- test/004-checker-UnsafeTest18/expected.txt | 2 + test/004-checker-UnsafeTest18/info.txt | 1 + test/004-checker-UnsafeTest18/src/Main.java | 252 ++++++++++++++++++ 15 files changed, 413 insertions(+), 3 deletions(-) create mode 100644 test/004-checker-UnsafeTest18/expected.txt create mode 100644 test/004-checker-UnsafeTest18/info.txt create mode 100644 test/004-checker-UnsafeTest18/src/Main.java diff --git a/compiler/dex/quick/dex_file_method_inliner.cc b/compiler/dex/quick/dex_file_method_inliner.cc index ad4ddadd2f..8f5d3ae4bd 100644 --- a/compiler/dex/quick/dex_file_method_inliner.cc +++ b/compiler/dex/quick/dex_file_method_inliner.cc @@ -101,6 +101,14 @@ static constexpr bool kIntrinsicIsStatic[] = { false, // kIntrinsicCas false, // kIntrinsicUnsafeGet false, // kIntrinsicUnsafePut + false, // kIntrinsicUnsafeGetAndAddInt, + false, // kIntrinsicUnsafeGetAndAddLong, + false, // kIntrinsicUnsafeGetAndSetInt, + false, // kIntrinsicUnsafeGetAndSetLong, + false, // kIntrinsicUnsafeGetAndSetObject, + false, // kIntrinsicUnsafeLoadFence, + false, // kIntrinsicUnsafeStoreFence, + false, // kIntrinsicUnsafeFullFence, true, // kIntrinsicSystemArrayCopyCharArray true, // kIntrinsicSystemArrayCopy }; @@ -177,6 +185,14 @@ static_assert(kIntrinsicIsStatic[kIntrinsicPoke], "Poke must be static"); static_assert(!kIntrinsicIsStatic[kIntrinsicCas], "Cas must not be static"); static_assert(!kIntrinsicIsStatic[kIntrinsicUnsafeGet], "UnsafeGet must not be static"); static_assert(!kIntrinsicIsStatic[kIntrinsicUnsafePut], "UnsafePut must not be static"); +static_assert(!kIntrinsicIsStatic[kIntrinsicUnsafeGetAndAddInt], "UnsafeGetAndAddInt must not be static"); +static_assert(!kIntrinsicIsStatic[kIntrinsicUnsafeGetAndAddLong], "UnsafeGetAndAddLong must not be static"); +static_assert(!kIntrinsicIsStatic[kIntrinsicUnsafeGetAndSetInt], "UnsafeGetAndSetInt must not be static"); +static_assert(!kIntrinsicIsStatic[kIntrinsicUnsafeGetAndSetLong], "UnsafeGetAndSetLong must not be static"); +static_assert(!kIntrinsicIsStatic[kIntrinsicUnsafeGetAndSetObject], "UnsafeGetAndSetObject must not be static"); +static_assert(!kIntrinsicIsStatic[kIntrinsicUnsafeLoadFence], "UnsafeLoadFence must not be static"); +static_assert(!kIntrinsicIsStatic[kIntrinsicUnsafeStoreFence], "UnsafeStoreFence must not be static"); +static_assert(!kIntrinsicIsStatic[kIntrinsicUnsafeFullFence], "UnsafeFullFence must not be static"); static_assert(kIntrinsicIsStatic[kIntrinsicSystemArrayCopyCharArray], "SystemArrayCopyCharArray must be static"); static_assert(kIntrinsicIsStatic[kIntrinsicSystemArrayCopy], @@ -318,6 +334,14 @@ const char* const DexFileMethodInliner::kNameCacheNames[] = { "putObject", // kNameCachePutObject "putObjectVolatile", // kNameCachePutObjectVolatile "putOrderedObject", // kNameCachePutOrderedObject + "getAndAddInt", // kNameCacheGetAndAddInt, + "getAndAddLong", // kNameCacheGetAndAddLong, + "getAndSetInt", // kNameCacheGetAndSetInt, + "getAndSetLong", // kNameCacheGetAndSetLong, + "getAndSetObject", // kNameCacheGetAndSetObject, + "loadFence", // kNameCacheLoadFence, + "storeFence", // kNameCacheStoreFence, + "fullFence", // kNameCacheFullFence, "arraycopy", // kNameCacheArrayCopy "bitCount", // kNameCacheBitCount "compare", // kNameCacheCompare @@ -404,10 +428,14 @@ const DexFileMethodInliner::ProtoDef DexFileMethodInliner::kProtoCacheDefs[] = { kClassCacheJavaLangObject, kClassCacheJavaLangObject } }, // kProtoCacheObjectJ_I { kClassCacheInt, 2, { kClassCacheJavaLangObject, kClassCacheLong } }, + // kProtoCacheObjectJI_I + { kClassCacheInt, 3, { kClassCacheJavaLangObject, kClassCacheLong, kClassCacheInt } }, // kProtoCacheObjectJI_V { kClassCacheVoid, 3, { kClassCacheJavaLangObject, kClassCacheLong, kClassCacheInt } }, // kProtoCacheObjectJ_J { kClassCacheLong, 2, { kClassCacheJavaLangObject, kClassCacheLong } }, + // kProtoCacheObjectJJ_J + { kClassCacheLong, 3, { kClassCacheJavaLangObject, kClassCacheLong, kClassCacheLong } }, // kProtoCacheObjectJJ_V { kClassCacheVoid, 3, { kClassCacheJavaLangObject, kClassCacheLong, kClassCacheLong } }, // kProtoCacheObjectJ_Object @@ -415,6 +443,9 @@ const DexFileMethodInliner::ProtoDef DexFileMethodInliner::kProtoCacheDefs[] = { // kProtoCacheObjectJObject_V { kClassCacheVoid, 3, { kClassCacheJavaLangObject, kClassCacheLong, kClassCacheJavaLangObject } }, + // kProtoCacheObjectJObject_Object + { kClassCacheJavaLangObject, 3, { kClassCacheJavaLangObject, kClassCacheLong, + kClassCacheJavaLangObject } }, // kProtoCacheCharArrayICharArrayII_V { kClassCacheVoid, 5, {kClassCacheJavaLangCharArray, kClassCacheInt, kClassCacheJavaLangCharArray, kClassCacheInt, kClassCacheInt} }, @@ -609,6 +640,16 @@ const DexFileMethodInliner::IntrinsicDef DexFileMethodInliner::kIntrinsicMethods UNSAFE_GET_PUT(Object, Object, kIntrinsicFlagIsObject), #undef UNSAFE_GET_PUT + // 1.8 + INTRINSIC(SunMiscUnsafe, GetAndAddInt, ObjectJI_I, kIntrinsicUnsafeGetAndAddInt, 0), + INTRINSIC(SunMiscUnsafe, GetAndAddLong, ObjectJJ_J, kIntrinsicUnsafeGetAndAddLong, 0), + INTRINSIC(SunMiscUnsafe, GetAndSetInt, ObjectJI_I, kIntrinsicUnsafeGetAndSetInt, 0), + INTRINSIC(SunMiscUnsafe, GetAndSetLong, ObjectJJ_J, kIntrinsicUnsafeGetAndSetLong, 0), + INTRINSIC(SunMiscUnsafe, GetAndSetObject, ObjectJObject_Object, kIntrinsicUnsafeGetAndSetObject, 0), + INTRINSIC(SunMiscUnsafe, LoadFence, _V, kIntrinsicUnsafeLoadFence, 0), + INTRINSIC(SunMiscUnsafe, StoreFence, _V, kIntrinsicUnsafeStoreFence, 0), + INTRINSIC(SunMiscUnsafe, FullFence, _V, kIntrinsicUnsafeFullFence, 0), + INTRINSIC(JavaLangSystem, ArrayCopy, CharArrayICharArrayII_V , kIntrinsicSystemArrayCopyCharArray, 0), INTRINSIC(JavaLangSystem, ArrayCopy, ObjectIObjectII_V , kIntrinsicSystemArrayCopy, @@ -815,6 +856,14 @@ bool DexFileMethodInliner::GenIntrinsic(Mir2Lir* backend, CallInfo* info) { case kIntrinsicRotateRight: case kIntrinsicRotateLeft: case kIntrinsicSignum: + case kIntrinsicUnsafeGetAndAddInt: + case kIntrinsicUnsafeGetAndAddLong: + case kIntrinsicUnsafeGetAndSetInt: + case kIntrinsicUnsafeGetAndSetLong: + case kIntrinsicUnsafeGetAndSetObject: + case kIntrinsicUnsafeLoadFence: + case kIntrinsicUnsafeStoreFence: + case kIntrinsicUnsafeFullFence: case kIntrinsicSystemArrayCopy: return false; // not implemented in quick. default: diff --git a/compiler/dex/quick/dex_file_method_inliner.h b/compiler/dex/quick/dex_file_method_inliner.h index b465db2c54..34b56cd494 100644 --- a/compiler/dex/quick/dex_file_method_inliner.h +++ b/compiler/dex/quick/dex_file_method_inliner.h @@ -227,6 +227,14 @@ class DexFileMethodInliner { kNameCachePutObject, kNameCachePutObjectVolatile, kNameCachePutOrderedObject, + kNameCacheGetAndAddInt, + kNameCacheGetAndAddLong, + kNameCacheGetAndSetInt, + kNameCacheGetAndSetLong, + kNameCacheGetAndSetObject, + kNameCacheLoadFence, + kNameCacheStoreFence, + kNameCacheFullFence, kNameCacheArrayCopy, kNameCacheBitCount, kNameCacheCompare, @@ -282,11 +290,14 @@ class DexFileMethodInliner { kProtoCacheObjectJJJ_Z, kProtoCacheObjectJObjectObject_Z, kProtoCacheObjectJ_I, + kProtoCacheObjectJI_I, kProtoCacheObjectJI_V, kProtoCacheObjectJ_J, + kProtoCacheObjectJJ_J, kProtoCacheObjectJJ_V, kProtoCacheObjectJ_Object, kProtoCacheObjectJObject_V, + kProtoCacheObjectJObject_Object, kProtoCacheCharArrayICharArrayII_V, kProtoCacheObjectIObjectII_V, kProtoCacheIICharArrayI_V, diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc index 3ed0278871..5d4c4e2950 100644 --- a/compiler/optimizing/intrinsics.cc +++ b/compiler/optimizing/intrinsics.cc @@ -472,6 +472,24 @@ static Intrinsics GetIntrinsic(InlineMethod method) { break; } + // 1.8. + case kIntrinsicUnsafeGetAndAddInt: + return Intrinsics::kUnsafeGetAndAddInt; + case kIntrinsicUnsafeGetAndAddLong: + return Intrinsics::kUnsafeGetAndAddLong; + case kIntrinsicUnsafeGetAndSetInt: + return Intrinsics::kUnsafeGetAndSetInt; + case kIntrinsicUnsafeGetAndSetLong: + return Intrinsics::kUnsafeGetAndSetLong; + case kIntrinsicUnsafeGetAndSetObject: + return Intrinsics::kUnsafeGetAndSetObject; + case kIntrinsicUnsafeLoadFence: + return Intrinsics::kUnsafeLoadFence; + case kIntrinsicUnsafeStoreFence: + return Intrinsics::kUnsafeStoreFence; + case kIntrinsicUnsafeFullFence: + return Intrinsics::kUnsafeFullFence; + // Virtual cases. case kIntrinsicReferenceGetReferent: diff --git a/compiler/optimizing/intrinsics_arm.cc b/compiler/optimizing/intrinsics_arm.cc index 69c970852d..b599d42c4b 100644 --- a/compiler/optimizing/intrinsics_arm.cc +++ b/compiler/optimizing/intrinsics_arm.cc @@ -2002,6 +2002,16 @@ UNIMPLEMENTED_INTRINSIC(ARM, LongHighestOneBit) UNIMPLEMENTED_INTRINSIC(ARM, IntegerLowestOneBit) UNIMPLEMENTED_INTRINSIC(ARM, LongLowestOneBit) +// 1.8. +UNIMPLEMENTED_INTRINSIC(ARM, UnsafeGetAndAddInt) +UNIMPLEMENTED_INTRINSIC(ARM, UnsafeGetAndAddLong) +UNIMPLEMENTED_INTRINSIC(ARM, UnsafeGetAndSetInt) +UNIMPLEMENTED_INTRINSIC(ARM, UnsafeGetAndSetLong) +UNIMPLEMENTED_INTRINSIC(ARM, UnsafeGetAndSetObject) +UNIMPLEMENTED_INTRINSIC(ARM, UnsafeLoadFence) +UNIMPLEMENTED_INTRINSIC(ARM, UnsafeStoreFence) +UNIMPLEMENTED_INTRINSIC(ARM, UnsafeFullFence) + UNREACHABLE_INTRINSICS(ARM) #undef __ diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc index 934b42762e..ccbbd43258 100644 --- a/compiler/optimizing/intrinsics_arm64.cc +++ b/compiler/optimizing/intrinsics_arm64.cc @@ -1953,6 +1953,16 @@ UNIMPLEMENTED_INTRINSIC(ARM64, LongHighestOneBit) UNIMPLEMENTED_INTRINSIC(ARM64, IntegerLowestOneBit) UNIMPLEMENTED_INTRINSIC(ARM64, LongLowestOneBit) +// 1.8. +UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndAddInt) +UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndAddLong) +UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndSetInt) +UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndSetLong) +UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndSetObject) +UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeLoadFence) +UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeStoreFence) +UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeFullFence) + UNREACHABLE_INTRINSICS(ARM64) #undef __ diff --git a/compiler/optimizing/intrinsics_list.h b/compiler/optimizing/intrinsics_list.h index b8933e1684..dd9294d486 100644 --- a/compiler/optimizing/intrinsics_list.h +++ b/compiler/optimizing/intrinsics_list.h @@ -128,6 +128,14 @@ V(UnsafePutLong, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \ V(UnsafePutLongOrdered, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \ V(UnsafePutLongVolatile, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \ + V(UnsafeGetAndAddInt, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \ + V(UnsafeGetAndAddLong, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \ + V(UnsafeGetAndSetInt, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \ + V(UnsafeGetAndSetLong, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \ + V(UnsafeGetAndSetObject, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \ + V(UnsafeLoadFence, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \ + V(UnsafeStoreFence, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \ + V(UnsafeFullFence, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \ V(ReferenceGetReferent, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) #endif // ART_COMPILER_OPTIMIZING_INTRINSICS_LIST_H_ diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc index 710df0a822..697b8fe882 100644 --- a/compiler/optimizing/intrinsics_mips.cc +++ b/compiler/optimizing/intrinsics_mips.cc @@ -1832,9 +1832,17 @@ UNIMPLEMENTED_INTRINSIC(MIPS, MathSinh) UNIMPLEMENTED_INTRINSIC(MIPS, MathTan) UNIMPLEMENTED_INTRINSIC(MIPS, MathTanh) -UNREACHABLE_INTRINSICS(MIPS) +// 1.8. +UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetAndAddInt) +UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetAndAddLong) +UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetAndSetInt) +UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetAndSetLong) +UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetAndSetObject) +UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeLoadFence) +UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeStoreFence) +UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeFullFence) -#undef UNIMPLEMENTED_INTRINSIC +UNREACHABLE_INTRINSICS(MIPS) #undef __ diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc index 617844bb18..83dff33ec0 100644 --- a/compiler/optimizing/intrinsics_mips64.cc +++ b/compiler/optimizing/intrinsics_mips64.cc @@ -1729,6 +1729,16 @@ UNIMPLEMENTED_INTRINSIC(MIPS64, LongHighestOneBit) UNIMPLEMENTED_INTRINSIC(MIPS64, IntegerLowestOneBit) UNIMPLEMENTED_INTRINSIC(MIPS64, LongLowestOneBit) +// 1.8. +UNIMPLEMENTED_INTRINSIC(MIPS64, UnsafeGetAndAddInt) +UNIMPLEMENTED_INTRINSIC(MIPS64, UnsafeGetAndAddLong) +UNIMPLEMENTED_INTRINSIC(MIPS64, UnsafeGetAndSetInt) +UNIMPLEMENTED_INTRINSIC(MIPS64, UnsafeGetAndSetLong) +UNIMPLEMENTED_INTRINSIC(MIPS64, UnsafeGetAndSetObject) +UNIMPLEMENTED_INTRINSIC(MIPS64, UnsafeLoadFence) +UNIMPLEMENTED_INTRINSIC(MIPS64, UnsafeStoreFence) +UNIMPLEMENTED_INTRINSIC(MIPS64, UnsafeFullFence) + UNREACHABLE_INTRINSICS(MIPS64) #undef __ diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc index 9a2dc4182d..048590e1be 100644 --- a/compiler/optimizing/intrinsics_x86.cc +++ b/compiler/optimizing/intrinsics_x86.cc @@ -2637,6 +2637,16 @@ UNIMPLEMENTED_INTRINSIC(X86, LongHighestOneBit) UNIMPLEMENTED_INTRINSIC(X86, IntegerLowestOneBit) UNIMPLEMENTED_INTRINSIC(X86, LongLowestOneBit) +// 1.8. +UNIMPLEMENTED_INTRINSIC(X86, UnsafeGetAndAddInt) +UNIMPLEMENTED_INTRINSIC(X86, UnsafeGetAndAddLong) +UNIMPLEMENTED_INTRINSIC(X86, UnsafeGetAndSetInt) +UNIMPLEMENTED_INTRINSIC(X86, UnsafeGetAndSetLong) +UNIMPLEMENTED_INTRINSIC(X86, UnsafeGetAndSetObject) +UNIMPLEMENTED_INTRINSIC(X86, UnsafeLoadFence) +UNIMPLEMENTED_INTRINSIC(X86, UnsafeStoreFence) +UNIMPLEMENTED_INTRINSIC(X86, UnsafeFullFence) + UNREACHABLE_INTRINSICS(X86) #undef __ diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc index 75204b4b49..35e13a6540 100644 --- a/compiler/optimizing/intrinsics_x86_64.cc +++ b/compiler/optimizing/intrinsics_x86_64.cc @@ -2715,6 +2715,16 @@ UNIMPLEMENTED_INTRINSIC(X86_64, ReferenceGetReferent) UNIMPLEMENTED_INTRINSIC(X86_64, FloatIsInfinite) UNIMPLEMENTED_INTRINSIC(X86_64, DoubleIsInfinite) +// 1.8. +UNIMPLEMENTED_INTRINSIC(X86_64, UnsafeGetAndAddInt) +UNIMPLEMENTED_INTRINSIC(X86_64, UnsafeGetAndAddLong) +UNIMPLEMENTED_INTRINSIC(X86_64, UnsafeGetAndSetInt) +UNIMPLEMENTED_INTRINSIC(X86_64, UnsafeGetAndSetLong) +UNIMPLEMENTED_INTRINSIC(X86_64, UnsafeGetAndSetObject) +UNIMPLEMENTED_INTRINSIC(X86_64, UnsafeLoadFence) +UNIMPLEMENTED_INTRINSIC(X86_64, UnsafeStoreFence) +UNIMPLEMENTED_INTRINSIC(X86_64, UnsafeFullFence) + UNREACHABLE_INTRINSICS(X86_64) #undef __ diff --git a/runtime/quick/inline_method_analyser.h b/runtime/quick/inline_method_analyser.h index 7e84b405e7..0e12d73595 100644 --- a/runtime/quick/inline_method_analyser.h +++ b/runtime/quick/inline_method_analyser.h @@ -101,6 +101,17 @@ enum InlineMethodOpcode : uint16_t { kIntrinsicCas, kIntrinsicUnsafeGet, kIntrinsicUnsafePut, + + // 1.8. + kIntrinsicUnsafeGetAndAddInt, + kIntrinsicUnsafeGetAndAddLong, + kIntrinsicUnsafeGetAndSetInt, + kIntrinsicUnsafeGetAndSetLong, + kIntrinsicUnsafeGetAndSetObject, + kIntrinsicUnsafeLoadFence, + kIntrinsicUnsafeStoreFence, + kIntrinsicUnsafeFullFence, + kIntrinsicSystemArrayCopyCharArray, kIntrinsicSystemArrayCopy, diff --git a/test/004-UnsafeTest/src/Main.java b/test/004-UnsafeTest/src/Main.java index a9a7a058e0..b2f905e0ee 100644 --- a/test/004-UnsafeTest/src/Main.java +++ b/test/004-UnsafeTest/src/Main.java @@ -40,7 +40,7 @@ public class Main { } private static Unsafe getUnsafe() throws Exception { - Class unsafeClass = Class.forName("sun.misc.Unsafe"); + Class unsafeClass = Unsafe.class; Field f = unsafeClass.getDeclaredField("theUnsafe"); f.setAccessible(true); return (Unsafe) f.get(null); diff --git a/test/004-checker-UnsafeTest18/expected.txt b/test/004-checker-UnsafeTest18/expected.txt new file mode 100644 index 0000000000..651da727af --- /dev/null +++ b/test/004-checker-UnsafeTest18/expected.txt @@ -0,0 +1,2 @@ +starting +passed diff --git a/test/004-checker-UnsafeTest18/info.txt b/test/004-checker-UnsafeTest18/info.txt new file mode 100644 index 0000000000..0fca5ebf03 --- /dev/null +++ b/test/004-checker-UnsafeTest18/info.txt @@ -0,0 +1 @@ +Test support for 1.8 sun.misc.Unsafe. diff --git a/test/004-checker-UnsafeTest18/src/Main.java b/test/004-checker-UnsafeTest18/src/Main.java new file mode 100644 index 0000000000..bb6de2ef28 --- /dev/null +++ b/test/004-checker-UnsafeTest18/src/Main.java @@ -0,0 +1,252 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.lang.reflect.Field; + +import sun.misc.Unsafe; + +/** + * Checker test on the 1.8 unsafe operations. Note, this is by no means an + * exhaustive unit test for these CAS (compare-and-swap) and fence operations. + * Instead, this test ensures the methods are recognized as intrinsic and behave + * as expected. + */ +public class Main { + + private static final Unsafe unsafe = getUnsafe(); + + private static Thread[] sThreads = new Thread[10]; + + // + // Fields accessed by setters and adders. + // + + public int i = 0; + public long l = 0; + public Object o = null; + + // + // Setters. + // + + /// CHECK-START: int Main.set32(java.lang.Object, long, int) intrinsics_recognition (after) + /// CHECK-DAG: <> InvokeVirtual intrinsic:UnsafeGetAndSetInt + /// CHECK-DAG: Return [<>] + private static int set32(Object o, long offset, int newValue) { + return unsafe.getAndSetInt(o, offset, newValue); + } + + /// CHECK-START: long Main.set64(java.lang.Object, long, long) intrinsics_recognition (after) + /// CHECK-DAG: <> InvokeVirtual intrinsic:UnsafeGetAndSetLong + /// CHECK-DAG: Return [<>] + private static long set64(Object o, long offset, long newValue) { + return unsafe.getAndSetLong(o, offset, newValue); + } + + /// CHECK-START: java.lang.Object Main.setObj(java.lang.Object, long, java.lang.Object) intrinsics_recognition (after) + /// CHECK-DAG: <> InvokeVirtual intrinsic:UnsafeGetAndSetObject + /// CHECK-DAG: Return [<>] + private static Object setObj(Object o, long offset, Object newValue) { + return unsafe.getAndSetObject(o, offset, newValue); + } + + // + // Adders. + // + + /// CHECK-START: int Main.add32(java.lang.Object, long, int) intrinsics_recognition (after) + /// CHECK-DAG: <> InvokeVirtual intrinsic:UnsafeGetAndAddInt + /// CHECK-DAG: Return [<>] + private static int add32(Object o, long offset, int delta) { + return unsafe.getAndAddInt(o, offset, delta); + } + + /// CHECK-START: long Main.add64(java.lang.Object, long, long) intrinsics_recognition (after) + /// CHECK-DAG: <> InvokeVirtual intrinsic:UnsafeGetAndAddLong + /// CHECK-DAG: Return [<>] + private static long add64(Object o, long offset, long delta) { + return unsafe.getAndAddLong(o, offset, delta); + } + + // + // Fences (native). + // + + /// CHECK-START: void Main.load() intrinsics_recognition (after) + /// CHECK-DAG: InvokeVirtual intrinsic:UnsafeLoadFence + private static void load() { + unsafe.loadFence(); + } + + /// CHECK-START: void Main.store() intrinsics_recognition (after) + /// CHECK-DAG: InvokeVirtual intrinsic:UnsafeStoreFence + private static void store() { + unsafe.storeFence(); + } + + /// CHECK-START: void Main.full() intrinsics_recognition (after) + /// CHECK-DAG: InvokeVirtual intrinsic:UnsafeFullFence + private static void full() { + unsafe.fullFence(); + } + + // + // Thread fork/join. + // + + private static void fork(Runnable r) { + for (int i = 0; i < 10; i++) { + sThreads[i] = new Thread(r); + sThreads[i].start(); + } + } + + private static void join() { + try { + for (int i = 0; i < 10; i++) { + sThreads[i].join(); + } + } catch (InterruptedException e) { + throw new Error("Failed join: " + e); + } + } + + // + // Driver. + // + + public static void main(String[] args) { + System.out.println("starting"); + + final Main m = new Main(); + + // Get the offsets. + + final long intOffset, longOffset, objOffset; + try { + Field intField = Main.class.getDeclaredField("i"); + Field longField = Main.class.getDeclaredField("l"); + Field objField = Main.class.getDeclaredField("o"); + + intOffset = unsafe.objectFieldOffset(intField); + longOffset = unsafe.objectFieldOffset(longField); + objOffset = unsafe.objectFieldOffset(objField); + + } catch (NoSuchFieldException e) { + throw new Error("No offset: " + e); + } + + // Some sanity within same thread. + + set32(m, intOffset, 3); + expectEquals32(3, m.i); + + set64(m, longOffset, 7L); + expectEquals64(7L, m.l); + + setObj(m, objOffset, m); + expectEqualsObj(m, m.o); + + add32(m, intOffset, 11); + expectEquals32(14, m.i); + + add64(m, longOffset, 13L); + expectEquals64(20L, m.l); + + // Some sanity on setters within different threads. + + fork(new Runnable() { + public void run() { + for (int i = 0; i < 10; i++) + set32(m, intOffset, i); + } + }); + join(); + expectEquals32(9, m.i); // one thread's last value wins + + fork(new Runnable() { + public void run() { + for (int i = 0; i < 10; i++) + set64(m, longOffset, (long) (100 + i)); + } + }); + join(); + expectEquals64(109L, m.l); // one thread's last value wins + + fork(new Runnable() { + public void run() { + for (int i = 0; i < 10; i++) + setObj(m, objOffset, sThreads[i]); + } + }); + join(); + expectEqualsObj(sThreads[9], m.o); // one thread's last value wins + + // Some sanity on adders within different threads. + + fork(new Runnable() { + public void run() { + for (int i = 0; i < 10; i++) + add32(m, intOffset, i + 1); + } + }); + join(); + expectEquals32(559, m.i); // all values accounted for + + fork(new Runnable() { + public void run() { + for (int i = 0; i < 10; i++) + add64(m, longOffset, (long) (i + 1)); + } + }); + join(); + expectEquals64(659L, m.l); // all values accounted for + + // TODO: the fences + + System.out.println("passed"); + } + + // Use reflection to implement "Unsafe.getUnsafe()"; + private static Unsafe getUnsafe() { + try { + Class unsafeClass = Unsafe.class; + Field f = unsafeClass.getDeclaredField("theUnsafe"); + f.setAccessible(true); + return (Unsafe) f.get(null); + } catch (Exception e) { + throw new Error("Cannot get Unsafe instance"); + } + } + + private static void expectEquals32(int expected, int result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } + + private static void expectEquals64(long expected, long result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } + + private static void expectEqualsObj(Object expected, Object result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } +} -- GitLab From 33fbf37feb959293b10d0b6e26d5e8ee7dff786c Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Mon, 7 Mar 2016 13:48:08 -0800 Subject: [PATCH 151/204] Add systrace logging to ScopedCodeCacheWrite Shows if we are taking a long time to mprotect. Bug: 27502458 (cherry picked from commit 462cd2cfa8c5fbb6548419eb8f27b448a12aca11) Change-Id: Ic0f24641f5856b13f3b949dcd53dc01ec952d171 --- runtime/jit/jit_code_cache.cc | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index 1545cb7f01..0b0f926ed1 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -169,12 +169,16 @@ bool JitCodeCache::ContainsMethod(ArtMethod* method) { return false; } -class ScopedCodeCacheWrite { +class ScopedCodeCacheWrite : ScopedTrace { public: - explicit ScopedCodeCacheWrite(MemMap* code_map) : code_map_(code_map) { + explicit ScopedCodeCacheWrite(MemMap* code_map) + : ScopedTrace("ScopedCodeCacheWrite"), + code_map_(code_map) { + ScopedTrace trace("mprotect all"); CHECKED_MPROTECT(code_map_->Begin(), code_map_->Size(), kProtAll); } ~ScopedCodeCacheWrite() { + ScopedTrace trace("mprotect code"); CHECKED_MPROTECT(code_map_->Begin(), code_map_->Size(), kProtCode); } private: -- GitLab From 60ab7c974411a1b7bb26519adff4f47a951ccee0 Mon Sep 17 00:00:00 2001 From: Hiroshi Yamauchi Date: Mon, 7 Mar 2016 16:46:23 -0800 Subject: [PATCH 152/204] Try to reenable some libcore tests with CC. CL 204125 may have fixed them. Bug: 26711853 Change-Id: I8c8052520982f80d8466922f3de6d3805073ae26 --- tools/libcore_failures_concurrent_collector.txt | 9 --------- 1 file changed, 9 deletions(-) diff --git a/tools/libcore_failures_concurrent_collector.txt b/tools/libcore_failures_concurrent_collector.txt index 19a61dc8cb..75d1eff2c3 100644 --- a/tools/libcore_failures_concurrent_collector.txt +++ b/tools/libcore_failures_concurrent_collector.txt @@ -22,14 +22,5 @@ result: EXEC_FAILED, names: ["libcore.java.lang.OldSystemTest#test_gc"], bug: 26155567 -}, -{ - description: "TimeoutException on hammerhead-concurrent-collector", - result: EXEC_FAILED, - modes: [device], - names: ["libcore.icu.RelativeDateTimeFormatterTest#test_bug25821045", - "libcore.java.text.SimpleDateFormatTest#testLocales", - "libcore.java.util.zip.ZipFileTest#testZipFileWithLotsOfEntries"], - bug: 26711853 } ] -- GitLab From 0c344f2c889520de9b2547df3818676540787a93 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Mon, 7 Mar 2016 17:41:39 -0800 Subject: [PATCH 153/204] Fix lock order violation Release class linker lock before acquiring heap bitmap lock. Bug: 27493510 (cherry picked from commit 064e9d401c49d3789b5deeeb6b423a4f551e4206) Change-Id: I7809e0f591513b85d295d43e639152ce92984f9c --- runtime/class_linker.cc | 383 ++++++++++++++++++++-------------------- 1 file changed, 194 insertions(+), 189 deletions(-) diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index a13a2e337c..01d140a0ba 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -1207,217 +1207,222 @@ bool ClassLinker::UpdateAppImageClassLoadersAndDexCaches( Thread* const self = Thread::Current(); gc::Heap* const heap = Runtime::Current()->GetHeap(); const ImageHeader& header = space->GetImageHeader(); - // Add image classes into the class table for the class loader, and fixup the dex caches and - // class loader fields. - WriterMutexLock mu(self, *Locks::classlinker_classes_lock_); - ClassTable* table = InsertClassTableForClassLoader(class_loader.Get()); - // Dex cache array fixup is all or nothing, we must reject app images that have mixed since we - // rely on clobering the dex cache arrays in the image to forward to bss. - size_t num_dex_caches_with_bss_arrays = 0; - const size_t num_dex_caches = dex_caches->GetLength(); - for (size_t i = 0; i < num_dex_caches; i++) { - mirror::DexCache* const dex_cache = dex_caches->Get(i); - const DexFile* const dex_file = dex_cache->GetDexFile(); - const OatFile::OatDexFile* oat_dex_file = dex_file->GetOatDexFile(); - if (oat_dex_file != nullptr && oat_dex_file->GetDexCacheArrays() != nullptr) { - ++num_dex_caches_with_bss_arrays; - } - } - *out_forward_dex_cache_array = num_dex_caches_with_bss_arrays != 0; - if (*out_forward_dex_cache_array) { - if (num_dex_caches_with_bss_arrays != num_dex_caches) { - // Reject application image since we cannot forward only some of the dex cache arrays. - // TODO: We could get around this by having a dedicated forwarding slot. It should be an - // uncommon case. - *out_error_msg = StringPrintf("Dex caches in bss does not match total: %zu vs %zu", - num_dex_caches_with_bss_arrays, - num_dex_caches); - return false; + { + // Add image classes into the class table for the class loader, and fixup the dex caches and + // class loader fields. + WriterMutexLock mu(self, *Locks::classlinker_classes_lock_); + ClassTable* table = InsertClassTableForClassLoader(class_loader.Get()); + // Dex cache array fixup is all or nothing, we must reject app images that have mixed since we + // rely on clobering the dex cache arrays in the image to forward to bss. + size_t num_dex_caches_with_bss_arrays = 0; + const size_t num_dex_caches = dex_caches->GetLength(); + for (size_t i = 0; i < num_dex_caches; i++) { + mirror::DexCache* const dex_cache = dex_caches->Get(i); + const DexFile* const dex_file = dex_cache->GetDexFile(); + const OatFile::OatDexFile* oat_dex_file = dex_file->GetOatDexFile(); + if (oat_dex_file != nullptr && oat_dex_file->GetDexCacheArrays() != nullptr) { + ++num_dex_caches_with_bss_arrays; + } + } + *out_forward_dex_cache_array = num_dex_caches_with_bss_arrays != 0; + if (*out_forward_dex_cache_array) { + if (num_dex_caches_with_bss_arrays != num_dex_caches) { + // Reject application image since we cannot forward only some of the dex cache arrays. + // TODO: We could get around this by having a dedicated forwarding slot. It should be an + // uncommon case. + *out_error_msg = StringPrintf("Dex caches in bss does not match total: %zu vs %zu", + num_dex_caches_with_bss_arrays, + num_dex_caches); + return false; + } } - } - // Only add the classes to the class loader after the points where we can return false. - for (size_t i = 0; i < num_dex_caches; i++) { - mirror::DexCache* const dex_cache = dex_caches->Get(i); - const DexFile* const dex_file = dex_cache->GetDexFile(); - const OatFile::OatDexFile* oat_dex_file = dex_file->GetOatDexFile(); - if (oat_dex_file != nullptr && oat_dex_file->GetDexCacheArrays() != nullptr) { - // If the oat file expects the dex cache arrays to be in the BSS, then allocate there and - // copy over the arrays. - DCHECK(dex_file != nullptr); - const size_t num_strings = dex_file->NumStringIds(); - const size_t num_types = dex_file->NumTypeIds(); - const size_t num_methods = dex_file->NumMethodIds(); - const size_t num_fields = dex_file->NumFieldIds(); - CHECK_EQ(num_strings, dex_cache->NumStrings()); - CHECK_EQ(num_types, dex_cache->NumResolvedTypes()); - CHECK_EQ(num_methods, dex_cache->NumResolvedMethods()); - CHECK_EQ(num_fields, dex_cache->NumResolvedFields()); - DexCacheArraysLayout layout(image_pointer_size_, dex_file); - uint8_t* const raw_arrays = oat_dex_file->GetDexCacheArrays(); - // The space is not yet visible to the GC, we can avoid the read barriers and use std::copy_n. - if (num_strings != 0u) { - GcRoot* const image_resolved_strings = dex_cache->GetStrings(); - GcRoot* const strings = - reinterpret_cast*>(raw_arrays + layout.StringsOffset()); - for (size_t j = 0; kIsDebugBuild && j < num_strings; ++j) { - DCHECK(strings[j].IsNull()); + // Only add the classes to the class loader after the points where we can return false. + for (size_t i = 0; i < num_dex_caches; i++) { + mirror::DexCache* const dex_cache = dex_caches->Get(i); + const DexFile* const dex_file = dex_cache->GetDexFile(); + const OatFile::OatDexFile* oat_dex_file = dex_file->GetOatDexFile(); + if (oat_dex_file != nullptr && oat_dex_file->GetDexCacheArrays() != nullptr) { + // If the oat file expects the dex cache arrays to be in the BSS, then allocate there and + // copy over the arrays. + DCHECK(dex_file != nullptr); + const size_t num_strings = dex_file->NumStringIds(); + const size_t num_types = dex_file->NumTypeIds(); + const size_t num_methods = dex_file->NumMethodIds(); + const size_t num_fields = dex_file->NumFieldIds(); + CHECK_EQ(num_strings, dex_cache->NumStrings()); + CHECK_EQ(num_types, dex_cache->NumResolvedTypes()); + CHECK_EQ(num_methods, dex_cache->NumResolvedMethods()); + CHECK_EQ(num_fields, dex_cache->NumResolvedFields()); + DexCacheArraysLayout layout(image_pointer_size_, dex_file); + uint8_t* const raw_arrays = oat_dex_file->GetDexCacheArrays(); + // The space is not yet visible to the GC, we can avoid the read barriers and use + // std::copy_n. + if (num_strings != 0u) { + GcRoot* const image_resolved_strings = dex_cache->GetStrings(); + GcRoot* const strings = + reinterpret_cast*>(raw_arrays + layout.StringsOffset()); + for (size_t j = 0; kIsDebugBuild && j < num_strings; ++j) { + DCHECK(strings[j].IsNull()); + } + std::copy_n(image_resolved_strings, num_strings, strings); + dex_cache->SetStrings(strings); } - std::copy_n(image_resolved_strings, num_strings, strings); - dex_cache->SetStrings(strings); - } - if (num_types != 0u) { - GcRoot* const image_resolved_types = dex_cache->GetResolvedTypes(); - GcRoot* const types = - reinterpret_cast*>(raw_arrays + layout.TypesOffset()); - for (size_t j = 0; kIsDebugBuild && j < num_types; ++j) { - DCHECK(types[j].IsNull()); + if (num_types != 0u) { + GcRoot* const image_resolved_types = dex_cache->GetResolvedTypes(); + GcRoot* const types = + reinterpret_cast*>(raw_arrays + layout.TypesOffset()); + for (size_t j = 0; kIsDebugBuild && j < num_types; ++j) { + DCHECK(types[j].IsNull()); + } + std::copy_n(image_resolved_types, num_types, types); + // Store a pointer to the new location for fast ArtMethod patching without requiring map. + // This leaves random garbage at the start of the dex cache array, but nobody should ever + // read from it again. + *reinterpret_cast**>(image_resolved_types) = types; + dex_cache->SetResolvedTypes(types); } - std::copy_n(image_resolved_types, num_types, types); - // Store a pointer to the new location for fast ArtMethod patching without requiring map. - // This leaves random garbage at the start of the dex cache array, but nobody should ever - // read from it again. - *reinterpret_cast**>(image_resolved_types) = types; - dex_cache->SetResolvedTypes(types); - } - if (num_methods != 0u) { - ArtMethod** const methods = reinterpret_cast( - raw_arrays + layout.MethodsOffset()); - ArtMethod** const image_resolved_methods = dex_cache->GetResolvedMethods(); - for (size_t j = 0; kIsDebugBuild && j < num_methods; ++j) { - DCHECK(methods[j] == nullptr); + if (num_methods != 0u) { + ArtMethod** const methods = reinterpret_cast( + raw_arrays + layout.MethodsOffset()); + ArtMethod** const image_resolved_methods = dex_cache->GetResolvedMethods(); + for (size_t j = 0; kIsDebugBuild && j < num_methods; ++j) { + DCHECK(methods[j] == nullptr); + } + std::copy_n(image_resolved_methods, num_methods, methods); + // Store a pointer to the new location for fast ArtMethod patching without requiring map. + *reinterpret_cast(image_resolved_methods) = methods; + dex_cache->SetResolvedMethods(methods); } - std::copy_n(image_resolved_methods, num_methods, methods); - // Store a pointer to the new location for fast ArtMethod patching without requiring map. - *reinterpret_cast(image_resolved_methods) = methods; - dex_cache->SetResolvedMethods(methods); - } - if (num_fields != 0u) { - ArtField** const fields = reinterpret_cast(raw_arrays + layout.FieldsOffset()); - for (size_t j = 0; kIsDebugBuild && j < num_fields; ++j) { - DCHECK(fields[j] == nullptr); + if (num_fields != 0u) { + ArtField** const fields = + reinterpret_cast(raw_arrays + layout.FieldsOffset()); + for (size_t j = 0; kIsDebugBuild && j < num_fields; ++j) { + DCHECK(fields[j] == nullptr); + } + std::copy_n(dex_cache->GetResolvedFields(), num_fields, fields); + dex_cache->SetResolvedFields(fields); } - std::copy_n(dex_cache->GetResolvedFields(), num_fields, fields); - dex_cache->SetResolvedFields(fields); } - } - { - WriterMutexLock mu2(self, dex_lock_); - // Make sure to do this after we update the arrays since we store the resolved types array - // in DexCacheData in RegisterDexFileLocked. We need the array pointer to be the one in the - // BSS. - mirror::DexCache* existing_dex_cache = FindDexCacheLocked(self, - *dex_file, - /*allow_failure*/true); - CHECK(existing_dex_cache == nullptr); - StackHandleScope<1> hs3(self); - RegisterDexFileLocked(*dex_file, hs3.NewHandle(dex_cache)); - } - GcRoot* const types = dex_cache->GetResolvedTypes(); - const size_t num_types = dex_cache->NumResolvedTypes(); - if (new_class_set == nullptr) { - for (int32_t j = 0; j < static_cast(num_types); j++) { - // The image space is not yet added to the heap, avoid read barriers. - mirror::Class* klass = types[j].Read(); - if (klass != nullptr) { - DCHECK_NE(klass->GetStatus(), mirror::Class::kStatusError); - // Update the class loader from the one in the image class loader to the one that loaded - // the app image. - klass->SetClassLoader(class_loader.Get()); - // The resolved type could be from another dex cache, go through the dex cache just in - // case. May be null for array classes. - if (klass->GetDexCacheStrings() != nullptr) { - DCHECK(!klass->IsArrayClass()); - klass->SetDexCacheStrings(klass->GetDexCache()->GetStrings()); - } - // If there are multiple dex caches, there may be the same class multiple times - // in different dex caches. Check for this since inserting will add duplicates - // otherwise. - if (num_dex_caches > 1) { - mirror::Class* existing = table->LookupByDescriptor(klass); - if (existing != nullptr) { - DCHECK_EQ(existing, klass) << PrettyClass(klass); + { + WriterMutexLock mu2(self, dex_lock_); + // Make sure to do this after we update the arrays since we store the resolved types array + // in DexCacheData in RegisterDexFileLocked. We need the array pointer to be the one in the + // BSS. + mirror::DexCache* existing_dex_cache = FindDexCacheLocked(self, + *dex_file, + /*allow_failure*/true); + CHECK(existing_dex_cache == nullptr); + StackHandleScope<1> hs3(self); + RegisterDexFileLocked(*dex_file, hs3.NewHandle(dex_cache)); + } + GcRoot* const types = dex_cache->GetResolvedTypes(); + const size_t num_types = dex_cache->NumResolvedTypes(); + if (new_class_set == nullptr) { + for (int32_t j = 0; j < static_cast(num_types); j++) { + // The image space is not yet added to the heap, avoid read barriers. + mirror::Class* klass = types[j].Read(); + if (klass != nullptr) { + DCHECK_NE(klass->GetStatus(), mirror::Class::kStatusError); + // Update the class loader from the one in the image class loader to the one that loaded + // the app image. + klass->SetClassLoader(class_loader.Get()); + // The resolved type could be from another dex cache, go through the dex cache just in + // case. May be null for array classes. + if (klass->GetDexCacheStrings() != nullptr) { + DCHECK(!klass->IsArrayClass()); + klass->SetDexCacheStrings(klass->GetDexCache()->GetStrings()); + } + // If there are multiple dex caches, there may be the same class multiple times + // in different dex caches. Check for this since inserting will add duplicates + // otherwise. + if (num_dex_caches > 1) { + mirror::Class* existing = table->LookupByDescriptor(klass); + if (existing != nullptr) { + DCHECK_EQ(existing, klass) << PrettyClass(klass); + } else { + table->Insert(klass); + } } else { table->Insert(klass); } - } else { - table->Insert(klass); - } - // Double checked VLOG to avoid overhead. - if (VLOG_IS_ON(image)) { - VLOG(image) << PrettyClass(klass) << " " << klass->GetStatus(); - if (!klass->IsArrayClass()) { - VLOG(image) << "From " << klass->GetDexCache()->GetDexFile()->GetBaseLocation(); - } - VLOG(image) << "Direct methods"; - for (ArtMethod& m : klass->GetDirectMethods(sizeof(void*))) { - VLOG(image) << PrettyMethod(&m); - } - VLOG(image) << "Virtual methods"; - for (ArtMethod& m : klass->GetVirtualMethods(sizeof(void*))) { - VLOG(image) << PrettyMethod(&m); + // Double checked VLOG to avoid overhead. + if (VLOG_IS_ON(image)) { + VLOG(image) << PrettyClass(klass) << " " << klass->GetStatus(); + if (!klass->IsArrayClass()) { + VLOG(image) << "From " << klass->GetDexCache()->GetDexFile()->GetBaseLocation(); + } + VLOG(image) << "Direct methods"; + for (ArtMethod& m : klass->GetDirectMethods(sizeof(void*))) { + VLOG(image) << PrettyMethod(&m); + } + VLOG(image) << "Virtual methods"; + for (ArtMethod& m : klass->GetVirtualMethods(sizeof(void*))) { + VLOG(image) << PrettyMethod(&m); + } } } } } - } - if (kIsDebugBuild) { - for (int32_t j = 0; j < static_cast(num_types); j++) { - // The image space is not yet added to the heap, avoid read barriers. - mirror::Class* klass = types[j].Read(); - if (klass != nullptr) { - DCHECK_NE(klass->GetStatus(), mirror::Class::kStatusError); - if (kIsDebugBuild) { - if (new_class_set != nullptr) { - auto it = new_class_set->Find(GcRoot(klass)); - DCHECK(it != new_class_set->end()); - DCHECK_EQ(it->Read(), klass); - mirror::Class* super_class = klass->GetSuperClass(); - if (super_class != nullptr && !heap->ObjectIsInBootImageSpace(super_class)) { - auto it2 = new_class_set->Find(GcRoot(super_class)); - DCHECK(it2 != new_class_set->end()); - DCHECK_EQ(it2->Read(), super_class); - } - } else { - DCHECK_EQ(table->LookupByDescriptor(klass), klass); - mirror::Class* super_class = klass->GetSuperClass(); - if (super_class != nullptr && !heap->ObjectIsInBootImageSpace(super_class)) { - CHECK_EQ(table->LookupByDescriptor(super_class), super_class); + if (kIsDebugBuild) { + for (int32_t j = 0; j < static_cast(num_types); j++) { + // The image space is not yet added to the heap, avoid read barriers. + mirror::Class* klass = types[j].Read(); + if (klass != nullptr) { + DCHECK_NE(klass->GetStatus(), mirror::Class::kStatusError); + if (kIsDebugBuild) { + if (new_class_set != nullptr) { + auto it = new_class_set->Find(GcRoot(klass)); + DCHECK(it != new_class_set->end()); + DCHECK_EQ(it->Read(), klass); + mirror::Class* super_class = klass->GetSuperClass(); + if (super_class != nullptr && !heap->ObjectIsInBootImageSpace(super_class)) { + auto it2 = new_class_set->Find(GcRoot(super_class)); + DCHECK(it2 != new_class_set->end()); + DCHECK_EQ(it2->Read(), super_class); + } + } else { + DCHECK_EQ(table->LookupByDescriptor(klass), klass); + mirror::Class* super_class = klass->GetSuperClass(); + if (super_class != nullptr && !heap->ObjectIsInBootImageSpace(super_class)) { + CHECK_EQ(table->LookupByDescriptor(super_class), super_class); + } } } - } - if (kIsDebugBuild) { - for (ArtMethod& m : klass->GetDirectMethods(sizeof(void*))) { - const void* code = m.GetEntryPointFromQuickCompiledCode(); - const void* oat_code = m.IsInvokable() ? GetQuickOatCodeFor(&m) : code; - if (!IsQuickResolutionStub(code) && - !IsQuickGenericJniStub(code) && - !IsQuickToInterpreterBridge(code) && - !m.IsNative()) { - DCHECK_EQ(code, oat_code) << PrettyMethod(&m); + if (kIsDebugBuild) { + for (ArtMethod& m : klass->GetDirectMethods(sizeof(void*))) { + const void* code = m.GetEntryPointFromQuickCompiledCode(); + const void* oat_code = m.IsInvokable() ? GetQuickOatCodeFor(&m) : code; + if (!IsQuickResolutionStub(code) && + !IsQuickGenericJniStub(code) && + !IsQuickToInterpreterBridge(code) && + !m.IsNative()) { + DCHECK_EQ(code, oat_code) << PrettyMethod(&m); + } } - } - for (ArtMethod& m : klass->GetVirtualMethods(sizeof(void*))) { - const void* code = m.GetEntryPointFromQuickCompiledCode(); - const void* oat_code = m.IsInvokable() ? GetQuickOatCodeFor(&m) : code; - if (!IsQuickResolutionStub(code) && - !IsQuickGenericJniStub(code) && - !IsQuickToInterpreterBridge(code) && - !m.IsNative()) { - DCHECK_EQ(code, oat_code) << PrettyMethod(&m); + for (ArtMethod& m : klass->GetVirtualMethods(sizeof(void*))) { + const void* code = m.GetEntryPointFromQuickCompiledCode(); + const void* oat_code = m.IsInvokable() ? GetQuickOatCodeFor(&m) : code; + if (!IsQuickResolutionStub(code) && + !IsQuickGenericJniStub(code) && + !IsQuickToInterpreterBridge(code) && + !m.IsNative()) { + DCHECK_EQ(code, oat_code) << PrettyMethod(&m); + } } } } } } } - } - if (*out_forward_dex_cache_array) { - FixupArtMethodArrayVisitor visitor(header); - header.GetImageSection(ImageHeader::kSectionArtMethods).VisitPackedArtMethods( - &visitor, - space->Begin(), - sizeof(void*)); - Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(class_loader.Get()); + if (*out_forward_dex_cache_array) { + ScopedTrace timing("Fixup ArtMethod dex cache arrays"); + FixupArtMethodArrayVisitor visitor(header); + header.GetImageSection(ImageHeader::kSectionArtMethods).VisitPackedArtMethods( + &visitor, + space->Begin(), + sizeof(void*)); + Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(class_loader.Get()); + } } return true; } -- GitLab From 9e9411cc6edf5fa8be0cabd11203fac09e4bac13 Mon Sep 17 00:00:00 2001 From: Roland Levillain Date: Tue, 8 Mar 2016 11:22:02 +0000 Subject: [PATCH 154/204] Re-enable test that was causing TimeoutExceptions on ARM64. The libcore test libcore.java.lang.OldSystemTest#test_gc has been failing with a java.util.concurrent.TimeoutException on ART Builbot's ARM64 concurrent collector configuration. No longer ignore this failure to see whether recent changes (namely https://android-review.googlesource.com/#/c/204125) fixed the issue. Bug: 26155567 Change-Id: I5cd3c3956cfd510bab1cb9d3b9479560f9703e6d --- tools/libcore_failures_concurrent_collector.txt | 6 ------ 1 file changed, 6 deletions(-) diff --git a/tools/libcore_failures_concurrent_collector.txt b/tools/libcore_failures_concurrent_collector.txt index 75d1eff2c3..95f0c2dcf2 100644 --- a/tools/libcore_failures_concurrent_collector.txt +++ b/tools/libcore_failures_concurrent_collector.txt @@ -16,11 +16,5 @@ names: ["jsr166.LinkedTransferQueueTest#testTransfer2", "jsr166.LinkedTransferQueueTest#testWaitingConsumer"], bug: 25883050 -}, -{ - description: "libcore.java.lang.OldSystemTest#test_gc failure on armv8-concurrent-collector.", - result: EXEC_FAILED, - names: ["libcore.java.lang.OldSystemTest#test_gc"], - bug: 26155567 } ] -- GitLab From 5d8112029d0e085c5a0099257daa4c7e29c12310 Mon Sep 17 00:00:00 2001 From: David Srbecky Date: Tue, 8 Mar 2016 13:21:22 +0000 Subject: [PATCH 155/204] Propagate InstructionSetFeatures to ElfBuilder. This is subset of CL171665 and it separates it into two. It will be needed to generate .MIPS.abiflags ELF section. Change-Id: I5557e7cb98d0fa1dc57c85cf6161e119c6d50a1a --- compiler/debug/dwarf/dwarf_test.h | 2 +- compiler/debug/elf_debug_writer.cc | 38 +++++++++++++++------- compiler/debug/elf_debug_writer.h | 6 +++- compiler/debug/elf_gnu_debugdata_writer.h | 3 +- compiler/elf_builder.h | 5 ++- compiler/elf_writer_quick.cc | 27 ++++++++++++--- compiler/elf_writer_quick.h | 2 ++ compiler/image_test.cc | 1 + compiler/jit/jit_compiler.cc | 3 +- compiler/jit/jit_compiler.h | 3 ++ compiler/oat_test.cc | 1 + compiler/optimizing/optimizing_compiler.cc | 5 ++- dex2oat/dex2oat.cc | 6 ++-- oatdump/oatdump.cc | 4 ++- 14 files changed, 81 insertions(+), 25 deletions(-) diff --git a/compiler/debug/dwarf/dwarf_test.h b/compiler/debug/dwarf/dwarf_test.h index 41bfe79c21..e2f0a65ab7 100644 --- a/compiler/debug/dwarf/dwarf_test.h +++ b/compiler/debug/dwarf/dwarf_test.h @@ -62,7 +62,7 @@ class DwarfTest : public CommonRuntimeTest { InstructionSet isa = (sizeof(typename ElfTypes::Addr) == 8) ? kX86_64 : kX86; ScratchFile file; FileOutputStream output_stream(file.GetFile()); - ElfBuilder builder(isa, &output_stream); + ElfBuilder builder(isa, nullptr, &output_stream); builder.Start(); if (!debug_info_data_.empty()) { builder.WriteSection(".debug_info", &debug_info_data_); diff --git a/compiler/debug/elf_debug_writer.cc b/compiler/debug/elf_debug_writer.cc index 01bd6797c9..79069d1aa2 100644 --- a/compiler/debug/elf_debug_writer.cc +++ b/compiler/debug/elf_debug_writer.cc @@ -91,24 +91,34 @@ static void WriteDebugSections(ElfBuilder* builder, std::vector MakeMiniDebugInfo( InstructionSet isa, + const InstructionSetFeatures* features, size_t rodata_size, size_t text_size, const ArrayRef& method_infos) { if (Is64BitInstructionSet(isa)) { - return MakeMiniDebugInfoInternal(isa, rodata_size, text_size, method_infos); + return MakeMiniDebugInfoInternal(isa, + features, + rodata_size, + text_size, + method_infos); } else { - return MakeMiniDebugInfoInternal(isa, rodata_size, text_size, method_infos); + return MakeMiniDebugInfoInternal(isa, + features, + rodata_size, + text_size, + method_infos); } } template static ArrayRef WriteDebugElfFileForMethodInternal( + const InstructionSet isa, + const InstructionSetFeatures* features, const MethodDebugInfo& method_info) { - const InstructionSet isa = method_info.compiled_method->GetInstructionSet(); std::vector buffer; buffer.reserve(KB); VectorOutputStream out("Debug ELF file", &buffer); - std::unique_ptr> builder(new ElfBuilder(isa, &out)); + std::unique_ptr> builder(new ElfBuilder(isa, features, &out)); // No program headers since the ELF file is not linked and has no allocated sections. builder->Start(false /* write_program_headers */); WriteDebugInfo(builder.get(), @@ -124,23 +134,26 @@ static ArrayRef WriteDebugElfFileForMethodInternal( return ArrayRef(result, buffer.size()); } -ArrayRef WriteDebugElfFileForMethod(const MethodDebugInfo& method_info) { - const InstructionSet isa = method_info.compiled_method->GetInstructionSet(); +ArrayRef WriteDebugElfFileForMethod(const InstructionSet isa, + const InstructionSetFeatures* features, + const MethodDebugInfo& method_info) { if (Is64BitInstructionSet(isa)) { - return WriteDebugElfFileForMethodInternal(method_info); + return WriteDebugElfFileForMethodInternal(isa, features, method_info); } else { - return WriteDebugElfFileForMethodInternal(method_info); + return WriteDebugElfFileForMethodInternal(isa, features, method_info); } } template static ArrayRef WriteDebugElfFileForClassesInternal( - const InstructionSet isa, const ArrayRef& types) + const InstructionSet isa, + const InstructionSetFeatures* features, + const ArrayRef& types) SHARED_REQUIRES(Locks::mutator_lock_) { std::vector buffer; buffer.reserve(KB); VectorOutputStream out("Debug ELF file", &buffer); - std::unique_ptr> builder(new ElfBuilder(isa, &out)); + std::unique_ptr> builder(new ElfBuilder(isa, features, &out)); // No program headers since the ELF file is not linked and has no allocated sections. builder->Start(false /* write_program_headers */); ElfDebugInfoWriter info_writer(builder.get()); @@ -159,11 +172,12 @@ static ArrayRef WriteDebugElfFileForClassesInternal( } ArrayRef WriteDebugElfFileForClasses(const InstructionSet isa, + const InstructionSetFeatures* features, const ArrayRef& types) { if (Is64BitInstructionSet(isa)) { - return WriteDebugElfFileForClassesInternal(isa, types); + return WriteDebugElfFileForClassesInternal(isa, features, types); } else { - return WriteDebugElfFileForClassesInternal(isa, types); + return WriteDebugElfFileForClassesInternal(isa, features, types); } } diff --git a/compiler/debug/elf_debug_writer.h b/compiler/debug/elf_debug_writer.h index 103b501489..d364521f1b 100644 --- a/compiler/debug/elf_debug_writer.h +++ b/compiler/debug/elf_debug_writer.h @@ -37,13 +37,17 @@ void WriteDebugInfo(ElfBuilder* builder, bool write_oat_patches); std::vector MakeMiniDebugInfo(InstructionSet isa, + const InstructionSetFeatures* features, size_t rodata_section_size, size_t text_section_size, const ArrayRef& method_infos); -ArrayRef WriteDebugElfFileForMethod(const MethodDebugInfo& method_info); +ArrayRef WriteDebugElfFileForMethod(const InstructionSet isa, + const InstructionSetFeatures* features, + const MethodDebugInfo& method_info); ArrayRef WriteDebugElfFileForClasses(const InstructionSet isa, + const InstructionSetFeatures* features, const ArrayRef& types) SHARED_REQUIRES(Locks::mutator_lock_); diff --git a/compiler/debug/elf_gnu_debugdata_writer.h b/compiler/debug/elf_gnu_debugdata_writer.h index 5c7d1c72a4..fb63d62572 100644 --- a/compiler/debug/elf_gnu_debugdata_writer.h +++ b/compiler/debug/elf_gnu_debugdata_writer.h @@ -79,13 +79,14 @@ static void XzCompress(const std::vector* src, std::vector* ds template static std::vector MakeMiniDebugInfoInternal( InstructionSet isa, + const InstructionSetFeatures* features, size_t rodata_section_size, size_t text_section_size, const ArrayRef& method_infos) { std::vector buffer; buffer.reserve(KB); VectorOutputStream out("Mini-debug-info ELF file", &buffer); - std::unique_ptr> builder(new ElfBuilder(isa, &out)); + std::unique_ptr> builder(new ElfBuilder(isa, features, &out)); builder->Start(); // Mirror .rodata and .text as NOBITS sections. // It is needed to detected relocations after compression. diff --git a/compiler/elf_builder.h b/compiler/elf_builder.h index f7da609e5d..943e2a897d 100644 --- a/compiler/elf_builder.h +++ b/compiler/elf_builder.h @@ -20,6 +20,7 @@ #include #include "arch/instruction_set.h" +#include "arch/mips/instruction_set_features_mips.h" #include "base/bit_utils.h" #include "base/casts.h" #include "base/unix_file/fd_file.h" @@ -392,8 +393,9 @@ class ElfBuilder FINAL { } }; - ElfBuilder(InstructionSet isa, OutputStream* output) + ElfBuilder(InstructionSet isa, const InstructionSetFeatures* features, OutputStream* output) : isa_(isa), + features_(features), stream_(output), rodata_(this, ".rodata", SHT_PROGBITS, SHF_ALLOC, nullptr, 0, kPageSize, 0), text_(this, ".text", SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR, nullptr, 0, kPageSize, 0), @@ -818,6 +820,7 @@ class ElfBuilder FINAL { } InstructionSet isa_; + const InstructionSetFeatures* features_; ErrorDelayingOutputStream stream_; diff --git a/compiler/elf_writer_quick.cc b/compiler/elf_writer_quick.cc index 19346ecc2b..e35662dfca 100644 --- a/compiler/elf_writer_quick.cc +++ b/compiler/elf_writer_quick.cc @@ -51,10 +51,12 @@ constexpr dwarf::CFIFormat kCFIFormat = dwarf::DW_DEBUG_FRAME_FORMAT; class DebugInfoTask : public Task { public: DebugInfoTask(InstructionSet isa, + const InstructionSetFeatures* features, size_t rodata_section_size, size_t text_section_size, const ArrayRef& method_infos) : isa_(isa), + instruction_set_features_(features), rodata_section_size_(rodata_section_size), text_section_size_(text_section_size), method_infos_(method_infos) { @@ -62,6 +64,7 @@ class DebugInfoTask : public Task { void Run(Thread*) { result_ = debug::MakeMiniDebugInfo(isa_, + instruction_set_features_, rodata_section_size_, text_section_size_, method_infos_); @@ -73,6 +76,7 @@ class DebugInfoTask : public Task { private: InstructionSet isa_; + const InstructionSetFeatures* instruction_set_features_; size_t rodata_section_size_; size_t text_section_size_; const ArrayRef& method_infos_; @@ -83,6 +87,7 @@ template class ElfWriterQuick FINAL : public ElfWriter { public: ElfWriterQuick(InstructionSet instruction_set, + const InstructionSetFeatures* features, const CompilerOptions* compiler_options, File* elf_file); ~ElfWriterQuick(); @@ -107,6 +112,7 @@ class ElfWriterQuick FINAL : public ElfWriter { std::vector* buffer); private: + const InstructionSetFeatures* instruction_set_features_; const CompilerOptions* const compiler_options_; File* const elf_file_; size_t rodata_size_; @@ -121,27 +127,36 @@ class ElfWriterQuick FINAL : public ElfWriter { }; std::unique_ptr CreateElfWriterQuick(InstructionSet instruction_set, + const InstructionSetFeatures* features, const CompilerOptions* compiler_options, File* elf_file) { if (Is64BitInstructionSet(instruction_set)) { - return MakeUnique>(instruction_set, compiler_options, elf_file); + return MakeUnique>(instruction_set, + features, + compiler_options, + elf_file); } else { - return MakeUnique>(instruction_set, compiler_options, elf_file); + return MakeUnique>(instruction_set, + features, + compiler_options, + elf_file); } } template ElfWriterQuick::ElfWriterQuick(InstructionSet instruction_set, + const InstructionSetFeatures* features, const CompilerOptions* compiler_options, File* elf_file) : ElfWriter(), + instruction_set_features_(features), compiler_options_(compiler_options), elf_file_(elf_file), rodata_size_(0u), text_size_(0u), bss_size_(0u), output_stream_(MakeUnique(MakeUnique(elf_file))), - builder_(new ElfBuilder(instruction_set, output_stream_.get())) {} + builder_(new ElfBuilder(instruction_set, features, output_stream_.get())) {} template ElfWriterQuick::~ElfWriterQuick() {} @@ -205,7 +220,11 @@ void ElfWriterQuick::PrepareDebugInfo( // Prepare the mini-debug-info in background while we do other I/O. Thread* self = Thread::Current(); debug_info_task_ = std::unique_ptr( - new DebugInfoTask(builder_->GetIsa(), rodata_size_, text_size_, method_infos)); + new DebugInfoTask(builder_->GetIsa(), + instruction_set_features_, + rodata_size_, + text_size_, + method_infos)); debug_info_thread_pool_ = std::unique_ptr( new ThreadPool("Mini-debug-info writer", 1)); debug_info_thread_pool_->AddTask(self, debug_info_task_.get()); diff --git a/compiler/elf_writer_quick.h b/compiler/elf_writer_quick.h index 347d372fe2..3d5dd39a66 100644 --- a/compiler/elf_writer_quick.h +++ b/compiler/elf_writer_quick.h @@ -26,8 +26,10 @@ namespace art { class CompilerOptions; +class InstructionSetFeatures; std::unique_ptr CreateElfWriterQuick(InstructionSet instruction_set, + const InstructionSetFeatures* features, const CompilerOptions* compiler_options, File* elf_file); diff --git a/compiler/image_test.cc b/compiler/image_test.cc index 5763cec43f..3b622b5451 100644 --- a/compiler/image_test.cc +++ b/compiler/image_test.cc @@ -99,6 +99,7 @@ void ImageTest::TestWriteRead(ImageHeader::StorageMode storage_mode) { const std::vector& dex_files = class_linker->GetBootClassPath(); std::unique_ptr elf_writer = CreateElfWriterQuick( compiler_driver_->GetInstructionSet(), + compiler_driver_->GetInstructionSetFeatures(), &compiler_driver_->GetCompilerOptions(), oat_file.GetFile()); elf_writer->Start(); diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc index 6ff1e2e95e..9a779464f6 100644 --- a/compiler/jit/jit_compiler.cc +++ b/compiler/jit/jit_compiler.cc @@ -69,7 +69,8 @@ extern "C" void jit_types_loaded(void* handle, mirror::Class** types, size_t cou DCHECK(jit_compiler != nullptr); if (jit_compiler->GetCompilerOptions()->GetGenerateDebugInfo()) { const ArrayRef types_array(types, count); - ArrayRef elf_file = debug::WriteDebugElfFileForClasses(kRuntimeISA, types_array); + ArrayRef elf_file = debug::WriteDebugElfFileForClasses( + kRuntimeISA, jit_compiler->GetCompilerDriver()->GetInstructionSetFeatures(), types_array); CreateJITCodeEntry(std::unique_ptr(elf_file.data()), elf_file.size()); } } diff --git a/compiler/jit/jit_compiler.h b/compiler/jit/jit_compiler.h index bc134fe27b..533dccf216 100644 --- a/compiler/jit/jit_compiler.h +++ b/compiler/jit/jit_compiler.h @@ -42,6 +42,9 @@ class JitCompiler { CompilerOptions* GetCompilerOptions() const { return compiler_options_.get(); } + CompilerDriver* GetCompilerDriver() const { + return compiler_driver_.get(); + } private: std::unique_ptr compiler_options_; diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc index 14fd1054c3..d22044aca3 100644 --- a/compiler/oat_test.cc +++ b/compiler/oat_test.cc @@ -176,6 +176,7 @@ class OatTest : public CommonCompilerTest { bool verify) { std::unique_ptr elf_writer = CreateElfWriterQuick( compiler_driver_->GetInstructionSet(), + compiler_driver_->GetInstructionSetFeatures(), &compiler_driver_->GetCompilerOptions(), file); elf_writer->Start(); diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index c1b4d2403d..b49b91d984 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -940,7 +940,10 @@ bool OptimizingCompiler::JitCompile(Thread* self, code_address + code_allocator.GetSize(), &compiled_method }; - ArrayRef elf_file = debug::WriteDebugElfFileForMethod(method_debug_info); + ArrayRef elf_file = debug::WriteDebugElfFileForMethod( + GetCompilerDriver()->GetInstructionSet(), + GetCompilerDriver()->GetInstructionSetFeatures(), + method_debug_info); CreateJITCodeEntryForAddress(code_address, std::unique_ptr(elf_file.data()), elf_file.size()); diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index ea190591e2..374d0d37a1 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -2105,8 +2105,10 @@ class Dex2Oat FINAL { elf_writers_.reserve(oat_files_.size()); oat_writers_.reserve(oat_files_.size()); for (const std::unique_ptr& oat_file : oat_files_) { - elf_writers_.emplace_back( - CreateElfWriterQuick(instruction_set_, compiler_options_.get(), oat_file.get())); + elf_writers_.emplace_back(CreateElfWriterQuick(instruction_set_, + instruction_set_features_.get(), + compiler_options_.get(), + oat_file.get())); elf_writers_.back()->Start(); oat_writers_.emplace_back(new OatWriter(IsBootImage(), timings_)); } diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index c1875366e9..b52f30ec33 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -115,11 +115,13 @@ class OatSymbolizer FINAL { bool Symbolize() { const InstructionSet isa = oat_file_->GetOatHeader().GetInstructionSet(); + const InstructionSetFeatures* features = InstructionSetFeatures::FromBitmap( + isa, oat_file_->GetOatHeader().GetInstructionSetFeaturesBitmap()); File* elf_file = OS::CreateEmptyFile(output_name_.c_str()); std::unique_ptr output_stream( MakeUnique(MakeUnique(elf_file))); - builder_.reset(new ElfBuilder(isa, output_stream.get())); + builder_.reset(new ElfBuilder(isa, features, output_stream.get())); builder_->Start(); -- GitLab From 316a2186b7fa9e03187d45ac0fa320f4dff1f3df Mon Sep 17 00:00:00 2001 From: Douglas Leung Date: Thu, 17 Sep 2015 15:26:25 -0700 Subject: [PATCH 156/204] Add Mips abiflags section to the oatfile. Once this patch is merged, then the workaround patch: 0acb15ead6a554a6879b29fd90726b9ea8fd98c4 in bionic can be removed. Change-Id: Ie06a3c4e384a23a77db7d04a2508edbf3a6d3933 --- compiler/elf_builder.h | 107 +++++++++++++++++++++++++++++++---- compiler/elf_writer_quick.cc | 3 + oatdump/oatdump.cc | 3 + runtime/elf.h | 4 +- 4 files changed, 106 insertions(+), 11 deletions(-) diff --git a/compiler/elf_builder.h b/compiler/elf_builder.h index 943e2a897d..0a474c6599 100644 --- a/compiler/elf_builder.h +++ b/compiler/elf_builder.h @@ -39,6 +39,7 @@ namespace art { // .rodata - DEX files and oat metadata. // .text - Compiled code. // .bss - Zero-initialized writeable section. +// .MIPS.abiflags - MIPS specific section. // .dynstr - Names for .dynsym. // .dynsym - A few oat-specific dynamic symbols. // .hash - Hash-table for .dynsym. @@ -393,6 +394,75 @@ class ElfBuilder FINAL { } }; + class AbiflagsSection FINAL : public Section { + public: + // Section with Mips abiflag info. + static constexpr uint8_t MIPS_AFL_REG_NONE = 0; // no registers + static constexpr uint8_t MIPS_AFL_REG_32 = 1; // 32-bit registers + static constexpr uint8_t MIPS_AFL_REG_64 = 2; // 64-bit registers + static constexpr uint32_t MIPS_AFL_FLAGS1_ODDSPREG = 1; // Uses odd single-prec fp regs + static constexpr uint8_t MIPS_ABI_FP_DOUBLE = 1; // -mdouble-float + static constexpr uint8_t MIPS_ABI_FP_XX = 5; // -mfpxx + static constexpr uint8_t MIPS_ABI_FP_64A = 7; // -mips32r* -mfp64 -mno-odd-spreg + + AbiflagsSection(ElfBuilder* owner, + const std::string& name, + Elf_Word type, + Elf_Word flags, + const Section* link, + Elf_Word info, + Elf_Word align, + Elf_Word entsize, + InstructionSet isa, + const InstructionSetFeatures* features) + : Section(owner, name, type, flags, link, info, align, entsize) { + if (isa == kMips || isa == kMips64) { + bool fpu32 = false; // assume mips64 values + uint8_t isa_rev = 6; // assume mips64 values + if (isa == kMips) { + // adjust for mips32 values + fpu32 = features->AsMipsInstructionSetFeatures()->Is32BitFloatingPoint(); + isa_rev = features->AsMipsInstructionSetFeatures()->IsR6() + ? 6 + : features->AsMipsInstructionSetFeatures()->IsMipsIsaRevGreaterThanEqual2() + ? (fpu32 ? 2 : 5) + : 1; + } + abiflags_.version = 0; // version of flags structure + abiflags_.isa_level = (isa == kMips) ? 32 : 64; + abiflags_.isa_rev = isa_rev; + abiflags_.gpr_size = (isa == kMips) ? MIPS_AFL_REG_32 : MIPS_AFL_REG_64; + abiflags_.cpr1_size = fpu32 ? MIPS_AFL_REG_32 : MIPS_AFL_REG_64; + abiflags_.cpr2_size = MIPS_AFL_REG_NONE; + // Set the fp_abi to MIPS_ABI_FP_64A for mips32 with 64-bit FPUs (ie: mips32 R5 and R6). + // Otherwise set to MIPS_ABI_FP_DOUBLE. + abiflags_.fp_abi = (isa == kMips && !fpu32) ? MIPS_ABI_FP_64A : MIPS_ABI_FP_DOUBLE; + abiflags_.isa_ext = 0; + abiflags_.ases = 0; + // To keep the code simple, we are not using odd FP reg for single floats for both + // mips32 and mips64 ART. Therefore we are not setting the MIPS_AFL_FLAGS1_ODDSPREG bit. + abiflags_.flags1 = 0; + abiflags_.flags2 = 0; + } + } + + Elf_Word GetSize() const { + return sizeof(abiflags_); + } + + void Write() { + this->WriteFully(&abiflags_, sizeof(abiflags_)); + } + + private: + struct { + uint16_t version; // version of this structure + uint8_t isa_level, isa_rev, gpr_size, cpr1_size, cpr2_size; + uint8_t fp_abi; + uint32_t isa_ext, ases, flags1, flags2; + } abiflags_; + }; + ElfBuilder(InstructionSet isa, const InstructionSetFeatures* features, OutputStream* output) : isa_(isa), features_(features), @@ -412,6 +482,8 @@ class ElfBuilder FINAL { debug_info_(this, ".debug_info", SHT_PROGBITS, 0, nullptr, 0, 1, 0), debug_line_(this, ".debug_line", SHT_PROGBITS, 0, nullptr, 0, 1, 0), shstrtab_(this, ".shstrtab", 0, 1), + abiflags_(this, ".MIPS.abiflags", SHT_MIPS_ABIFLAGS, SHF_ALLOC, nullptr, 0, kPageSize, 0, + isa, features), started_(false), write_program_headers_(false), loaded_size_(0u), @@ -421,6 +493,7 @@ class ElfBuilder FINAL { dynamic_.phdr_flags_ = PF_R | PF_W; dynamic_.phdr_type_ = PT_DYNAMIC; eh_frame_hdr_.phdr_type_ = PT_GNU_EH_FRAME; + abiflags_.phdr_type_ = PT_MIPS_ABIFLAGS; } ~ElfBuilder() {} @@ -522,7 +595,7 @@ class ElfBuilder FINAL { stream_.Flush(); // The main ELF header. - Elf_Ehdr elf_header = MakeElfHeader(isa_); + Elf_Ehdr elf_header = MakeElfHeader(isa_, features_); elf_header.e_shoff = section_headers_offset; elf_header.e_shnum = shdrs.size(); elf_header.e_shstrndx = shstrtab_.GetSectionIndex(); @@ -566,7 +639,12 @@ class ElfBuilder FINAL { Elf_Word rodata_address = rodata_.GetAddress(); Elf_Word text_address = RoundUp(rodata_address + rodata_size, kPageSize); Elf_Word bss_address = RoundUp(text_address + text_size, kPageSize); - Elf_Word dynstr_address = RoundUp(bss_address + bss_size, kPageSize); + Elf_Word abiflags_address = RoundUp(bss_address + bss_size, kPageSize); + Elf_Word abiflags_size = 0; + if (isa_ == kMips || isa_ == kMips64) { + abiflags_size = abiflags_.GetSize(); + } + Elf_Word dynstr_address = RoundUp(abiflags_address + abiflags_size, kPageSize); // Cache .dynstr, .dynsym and .hash data. dynstr_.Add(""); // dynstr should start with empty string. @@ -651,6 +729,12 @@ class ElfBuilder FINAL { return loaded_size_; } + void WriteMIPSabiflagsSection() { + abiflags_.Start(); + abiflags_.Write(); + abiflags_.End(); + } + // Returns true if all writes and seeks on the output stream succeeded. bool Good() { return stream_.Good(); @@ -670,7 +754,7 @@ class ElfBuilder FINAL { } private: - static Elf_Ehdr MakeElfHeader(InstructionSet isa) { + static Elf_Ehdr MakeElfHeader(InstructionSet isa, const InstructionSetFeatures* features) { Elf_Ehdr elf_header = Elf_Ehdr(); switch (isa) { case kArm: @@ -698,18 +782,20 @@ class ElfBuilder FINAL { case kMips: { elf_header.e_machine = EM_MIPS; elf_header.e_flags = (EF_MIPS_NOREORDER | - EF_MIPS_PIC | - EF_MIPS_CPIC | - EF_MIPS_ABI_O32 | - EF_MIPS_ARCH_32R2); + EF_MIPS_PIC | + EF_MIPS_CPIC | + EF_MIPS_ABI_O32 | + features->AsMipsInstructionSetFeatures()->IsR6() + ? EF_MIPS_ARCH_32R6 + : EF_MIPS_ARCH_32R2); break; } case kMips64: { elf_header.e_machine = EM_MIPS; elf_header.e_flags = (EF_MIPS_NOREORDER | - EF_MIPS_PIC | - EF_MIPS_CPIC | - EF_MIPS_ARCH_64R6); + EF_MIPS_PIC | + EF_MIPS_CPIC | + EF_MIPS_ARCH_64R6); break; } case kNone: { @@ -839,6 +925,7 @@ class ElfBuilder FINAL { Section debug_info_; Section debug_line_; StringSection shstrtab_; + AbiflagsSection abiflags_; std::vector> other_sections_; // List of used section in the order in which they were written. diff --git a/compiler/elf_writer_quick.cc b/compiler/elf_writer_quick.cc index e35662dfca..bed864b534 100644 --- a/compiler/elf_writer_quick.cc +++ b/compiler/elf_writer_quick.cc @@ -210,6 +210,9 @@ void ElfWriterQuick::WriteDynamicSection() { if (bss_size_ != 0u) { builder_->GetBss()->WriteNoBitsSection(bss_size_); } + if (builder_->GetIsa() == kMips || builder_->GetIsa() == kMips64) { + builder_->WriteMIPSabiflagsSection(); + } builder_->WriteDynamicSection(); } diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index b52f30ec33..4ca0ee58e3 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -147,6 +147,9 @@ class OatSymbolizer FINAL { bss->WriteNoBitsSection(oat_file_->BssSize()); } + if (isa == kMips || isa == kMips64) { + builder_->WriteMIPSabiflagsSection(); + } builder_->PrepareDynamicSection( elf_file->GetPath(), rodata_size, text_size, oat_file_->BssSize()); builder_->WriteDynamicSection(); diff --git a/runtime/elf.h b/runtime/elf.h index d1efc92c30..63b18c5d34 100644 --- a/runtime/elf.h +++ b/runtime/elf.h @@ -1284,6 +1284,7 @@ enum : unsigned { SHT_MIPS_REGINFO = 0x70000006, // Register usage information SHT_MIPS_OPTIONS = 0x7000000d, // General options + SHT_MIPS_ABIFLAGS = 0x7000002a, // Abiflags options SHT_HIPROC = 0x7fffffff, // Highest processor arch-specific type. SHT_LOUSER = 0x80000000, // Lowest type reserved for applications. @@ -1606,7 +1607,8 @@ enum { // MIPS program header types. PT_MIPS_REGINFO = 0x70000000, // Register usage information. PT_MIPS_RTPROC = 0x70000001, // Runtime procedure table. - PT_MIPS_OPTIONS = 0x70000002 // Options segment. + PT_MIPS_OPTIONS = 0x70000002, // Options segment. + PT_MIPS_ABIFLAGS = 0x70000003 // Abiflags segment. }; // Segment flag bits. -- GitLab From 3dd6e02dac0e4cc8a3e1cef653b3febcb7877ffb Mon Sep 17 00:00:00 2001 From: Narayan Kamath Date: Mon, 7 Mar 2016 10:57:34 +0000 Subject: [PATCH 157/204] Revert "Suppress ArrayDequeTest#testForEachRemaining temporarily." This reverts commit 98c38e34357a73c46ad7a735dd23da0ba29a2963. Fixed by commit 3de87b847fa94cce9beaf5b225404c1bc7ec475f. Change-Id: If38f1e67dd8504055236bb8a9703d8c2eb61d16c --- tools/libcore_failures.txt | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tools/libcore_failures.txt b/tools/libcore_failures.txt index fab4599925..46100ae15c 100644 --- a/tools/libcore_failures.txt +++ b/tools/libcore_failures.txt @@ -270,10 +270,5 @@ description: "Only work with --mode=activity", result: EXEC_FAILED, names: [ "libcore.java.io.FileTest#testJavaIoTmpdirMutable" ] -}, -{ - description: "Temporary suppressing while test is fixed", - result: EXEC_FAILED, - names: [ "org.apache.harmony.tests.java.util.ArrayDequeTest#test_forEachRemaining_iterator" ] } ] -- GitLab From 2d8614bf1ba44468c834d9d3d5562466637f63f2 Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Mon, 7 Mar 2016 16:31:34 -0800 Subject: [PATCH 158/204] ART: Use the right ElfBuilder for oatdump symbolizer We should not unconditionally output 32-bit ELF files. Bug: 27293423 Change-Id: Ibbb74a7807b24c46cb23d7b5867936569424e8fe --- oatdump/oatdump.cc | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index b52f30ec33..6c6c807588 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -98,6 +98,7 @@ const DexFile* OpenDexFile(const OatFile::OatDexFile* oat_dex_file, std::string* return ret; } +template class OatSymbolizer FINAL { public: OatSymbolizer(const OatFile* oat_file, const std::string& output_name) : @@ -121,7 +122,7 @@ class OatSymbolizer FINAL { File* elf_file = OS::CreateEmptyFile(output_name_.c_str()); std::unique_ptr output_stream( MakeUnique(MakeUnique(elf_file))); - builder_.reset(new ElfBuilder(isa, features, output_stream.get())); + builder_.reset(new ElfBuilder(isa, features, output_stream.get())); builder_->Start(); @@ -151,14 +152,14 @@ class OatSymbolizer FINAL { elf_file->GetPath(), rodata_size, text_size, oat_file_->BssSize()); builder_->WriteDynamicSection(); - Walk(&art::OatSymbolizer::RegisterForDedup); + Walk(&art::OatSymbolizer::RegisterForDedup); NormalizeState(); strtab->Start(); strtab->Write(""); // strtab should start with empty string. AddTrampolineSymbols(); - Walk(&art::OatSymbolizer::AddSymbol); + Walk(&art::OatSymbolizer::AddSymbol); strtab->End(); symtab->Start(); @@ -346,7 +347,7 @@ class OatSymbolizer FINAL { } const OatFile* oat_file_; - std::unique_ptr > builder_; + std::unique_ptr > builder_; std::unordered_map state_; const std::string output_name_; }; @@ -2544,8 +2545,17 @@ static int SymbolizeOat(const char* oat_filename, std::string& output_name) { return EXIT_FAILURE; } - OatSymbolizer oat_symbolizer(oat_file, output_name); - if (!oat_symbolizer.Symbolize()) { + bool result; + // Try to produce an ELF file of the same type. This is finicky, as we have used 32-bit ELF + // files for 64-bit code in the past. + if (Is64BitInstructionSet(oat_file->GetOatHeader().GetInstructionSet())) { + OatSymbolizer oat_symbolizer(oat_file, output_name); + result = oat_symbolizer.Symbolize(); + } else { + OatSymbolizer oat_symbolizer(oat_file, output_name); + result = oat_symbolizer.Symbolize(); + } + if (!result) { fprintf(stderr, "Failed to symbolize\n"); return EXIT_FAILURE; } -- GitLab From 91cc06c1814bd1d0fd6635bc3d7632a2bb7b0e7c Mon Sep 17 00:00:00 2001 From: David Srbecky Date: Mon, 7 Mar 2016 16:13:58 +0000 Subject: [PATCH 159/204] Mark DWARF lines in non-debuggable methods as "not a statement". This is a hint to the debugger that breakpoints and stepping might not function as intended (since we have limited information). Change-Id: I23c4a816182cc7548fcd69fbd00112225e7b1710 --- compiler/debug/dwarf/debug_line_opcode_writer.h | 14 ++++++++++---- compiler/debug/dwarf/dwarf_test.cc | 4 +++- compiler/debug/elf_debug_line_writer.h | 9 +++++++-- compiler/debug/method_debug_info.h | 1 + compiler/oat_writer.cc | 4 +++- compiler/optimizing/optimizing_compiler.cc | 4 +++- 6 files changed, 27 insertions(+), 9 deletions(-) diff --git a/compiler/debug/dwarf/debug_line_opcode_writer.h b/compiler/debug/dwarf/debug_line_opcode_writer.h index 58502a3f9c..b4a4d63f01 100644 --- a/compiler/debug/dwarf/debug_line_opcode_writer.h +++ b/compiler/debug/dwarf/debug_line_opcode_writer.h @@ -36,7 +36,7 @@ class DebugLineOpCodeWriter FINAL : private Writer { public: static constexpr int kOpcodeBase = 13; - static constexpr bool kDefaultIsStmt = true; + static constexpr bool kDefaultIsStmt = false; static constexpr int kLineBase = -5; static constexpr int kLineRange = 14; @@ -81,8 +81,11 @@ class DebugLineOpCodeWriter FINAL : private Writer { this->PushUleb128(column); } - void NegateStmt() { - this->PushUint8(DW_LNS_negate_stmt); + void SetIsStmt(bool is_stmt) { + if (is_stmt_ != is_stmt) { + this->PushUint8(DW_LNS_negate_stmt); + is_stmt_ = is_stmt; + } } void SetBasicBlock() { @@ -112,6 +115,7 @@ class DebugLineOpCodeWriter FINAL : private Writer { current_address_ = 0; current_file_ = 1; current_line_ = 1; + is_stmt_ = kDefaultIsStmt; } // Uncoditionally set address using the long encoding. @@ -227,7 +231,8 @@ class DebugLineOpCodeWriter FINAL : private Writer { code_factor_bits_(codeFactorBits), current_address_(0), current_file_(1), - current_line_(1) { + current_line_(1), + is_stmt_(kDefaultIsStmt) { } private: @@ -244,6 +249,7 @@ class DebugLineOpCodeWriter FINAL : private Writer { uint64_t current_address_; int current_file_; int current_line_; + bool is_stmt_; std::vector patch_locations_; DISALLOW_COPY_AND_ASSIGN(DebugLineOpCodeWriter); diff --git a/compiler/debug/dwarf/dwarf_test.cc b/compiler/debug/dwarf/dwarf_test.cc index e455d0d617..2ba3af5e10 100644 --- a/compiler/debug/dwarf/dwarf_test.cc +++ b/compiler/debug/dwarf/dwarf_test.cc @@ -217,7 +217,9 @@ TEST_F(DwarfTest, DebugLine) { DW_CHECK_NEXT("Advance Line by 2 to 3"); opcodes.SetColumn(4); DW_CHECK_NEXT("Set column to 4"); - opcodes.NegateStmt(); + opcodes.SetIsStmt(true); + DW_CHECK_NEXT("Set is_stmt to 1"); + opcodes.SetIsStmt(false); DW_CHECK_NEXT("Set is_stmt to 0"); opcodes.SetBasicBlock(); DW_CHECK_NEXT("Set basic block"); diff --git a/compiler/debug/elf_debug_line_writer.h b/compiler/debug/elf_debug_line_writer.h index d3859ca752..11be4e9844 100644 --- a/compiler/debug/elf_debug_line_writer.h +++ b/compiler/debug/elf_debug_line_writer.h @@ -184,6 +184,10 @@ class ElfDebugLineWriter { // Generate mapping opcodes from PC to Java lines. if (file_index != 0) { + // If the method was not compiled as native-debuggable, we still generate all available + // lines, but we try to prevent the debugger from stepping and setting breakpoints since + // the information is too inaccurate for that (breakpoints would be set after the calls). + const bool default_is_stmt = mi->is_native_debuggable; bool first = true; for (SrcMapElem pc2dex : pc2dex_map) { uint32_t pc = pc2dex.from_; @@ -205,13 +209,14 @@ class ElfDebugLineWriter { // Assume that any preceding code is prologue. int first_line = dex2line_map.front().line_; // Prologue is not a sensible place for a breakpoint. - opcodes.NegateStmt(); + opcodes.SetIsStmt(false); opcodes.AddRow(method_address, first_line); - opcodes.NegateStmt(); opcodes.SetPrologueEnd(); } + opcodes.SetIsStmt(default_is_stmt); opcodes.AddRow(method_address + pc, line); } else if (line != opcodes.CurrentLine()) { + opcodes.SetIsStmt(default_is_stmt); opcodes.AddRow(method_address + pc, line); } } diff --git a/compiler/debug/method_debug_info.h b/compiler/debug/method_debug_info.h index 6b3dd8c528..bb09f7e814 100644 --- a/compiler/debug/method_debug_info.h +++ b/compiler/debug/method_debug_info.h @@ -30,6 +30,7 @@ struct MethodDebugInfo { uint32_t access_flags; const DexFile::CodeItem* code_item; bool deduped; + bool is_native_debuggable; uintptr_t low_pc; uintptr_t high_pc; CompiledMethod* compiled_method; diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc index c60b02a227..50f5aba061 100644 --- a/compiler/oat_writer.cc +++ b/compiler/oat_writer.cc @@ -806,7 +806,8 @@ class OatWriter::InitCodeMethodVisitor : public OatDexMethodVisitor { } } - if (writer_->compiler_driver_->GetCompilerOptions().GenerateAnyDebugInfo()) { + const CompilerOptions& compiler_options = writer_->compiler_driver_->GetCompilerOptions(); + if (compiler_options.GenerateAnyDebugInfo()) { // Record debug information for this function if we are doing that. const uint32_t quick_code_start = quick_code_offset - writer_->oat_header_->GetExecutableOffset() - thumb_offset; @@ -817,6 +818,7 @@ class OatWriter::InitCodeMethodVisitor : public OatDexMethodVisitor { it.GetMethodAccessFlags(), it.GetMethodCodeItem(), deduped, + compiler_options.GetNativeDebuggable(), quick_code_start, quick_code_start + code_size, compiled_method}); diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index b49b91d984..42f22afd80 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -913,7 +913,8 @@ bool OptimizingCompiler::JitCompile(Thread* self, return false; } - if (GetCompilerDriver()->GetCompilerOptions().GetGenerateDebugInfo()) { + const CompilerOptions& compiler_options = GetCompilerDriver()->GetCompilerOptions(); + if (compiler_options.GetGenerateDebugInfo()) { const auto* method_header = reinterpret_cast(code); const uintptr_t code_address = reinterpret_cast(method_header->GetCode()); CompiledMethod compiled_method( @@ -936,6 +937,7 @@ bool OptimizingCompiler::JitCompile(Thread* self, access_flags, code_item, false, // deduped. + compiler_options.GetNativeDebuggable(), code_address, code_address + code_allocator.GetSize(), &compiled_method -- GitLab From b077e15d2d11b7c81aacbcd4a46c2b1e9c9ba20d Mon Sep 17 00:00:00 2001 From: Calin Juravle Date: Thu, 18 Feb 2016 18:47:37 +0000 Subject: [PATCH 160/204] Update GetDexOptNeeded to handle different levels of compilation extract-only or profile-guide oat files are considered up to date from runtime perspective as they don't necessary need (re)compilation or relocation. However, it is useful to return a more refined code to the caller so that they can decide whether or not that's good enough. For example, the package manager might decide to still compile a previous extract-only and during profile guide compilation we should always recompile even if we have an oat file. Note that dex files compiled via ClassLoaders will still be fully compiled. This change introduces: - a new key in the oat header kCompilationType to capture what type of compilation has been made. Note tha the key might be missing. The distinction is needed in order to avoid recompilation of a previous fully compiled file during profile guide compilation analysis. - a new argument to GetDexOptNeeded which tells the runtime to cast its opinion whether or not the oat file is up to date relative to the desired target type of compilation. Bug: 27189430 (cherry picked from commit d91b8a2464b99625efe03caf7d30c8372bc378ed) Change-Id: I6ce450350f388451f7bab7d285c1846d539a4b13 --- dex2oat/dex2oat.cc | 26 +-- runtime/native/dalvik_system_DexFile.cc | 54 ++---- runtime/oat.cc | 20 ++- runtime/oat.h | 8 +- runtime/oat_file.cc | 4 + runtime/oat_file.h | 2 + runtime/oat_file_assistant.cc | 208 ++++-------------------- runtime/oat_file_assistant.h | 117 ++++--------- runtime/oat_file_assistant_test.cc | 188 ++++++++++++++------- runtime/oat_file_manager.cc | 5 + 10 files changed, 260 insertions(+), 372 deletions(-) diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index d9a2f300d9..cbce0f6e91 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -1031,9 +1031,11 @@ class Dex2Oat FINAL { key_value_store_->Put( OatHeader::kDebuggableKey, compiler_options_->debuggable_ ? OatHeader::kTrueValue : OatHeader::kFalseValue); - key_value_store_->Put( - OatHeader::kExtractOnlyKey, - compiler_options_->IsExtractOnly() ? OatHeader::kTrueValue : OatHeader::kFalseValue); + if (compiler_options_->IsExtractOnly()) { + key_value_store_->Put(OatHeader::kCompilationType, OatHeader::kExtractOnlyValue); + } else if (UseProfileGuidedCompilation()) { + key_value_store_->Put(OatHeader::kCompilationType, OatHeader::kProfileGuideCompiledValue); + } } // Parse the arguments from the command line. In case of an unrecognized option or impossible @@ -1888,13 +1890,6 @@ class Dex2Oat FINAL { return success; } - bool ShouldCompileBasedOnProfiles() const { - DCHECK(UseProfileGuidedCompilation()); - // If we are given a profile, compile only if we have some data in it. - return (profile_compilation_info_ != nullptr) && - (profile_compilation_info_->GetNumberOfMethods() != 0); - } - private: template static std::vector MakeNonOwningPointerVector(const std::vector>& src) { @@ -2590,16 +2585,11 @@ static int dex2oat(int argc, char** argv) { // Parse arguments. Argument mistakes will lead to exit(EXIT_FAILURE) in UsageError. dex2oat->ParseArgs(argc, argv); - // Process profile information and assess if we need to do a profile guided compilation. + // If needed, process profile information for profile guided compilation. // This operation involves I/O. if (dex2oat->UseProfileGuidedCompilation()) { - if (dex2oat->LoadProfile()) { - if (!dex2oat->ShouldCompileBasedOnProfiles()) { - LOG(INFO) << "Skipped compilation because of insignificant profile delta"; - return EXIT_SUCCESS; - } - } else { - LOG(WARNING) << "Failed to process profile files"; + if (!dex2oat->LoadProfile()) { + LOG(ERROR) << "Failed to process profile file"; return EXIT_FAILURE; } } diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc index 6643ac2231..f1e0fa7b57 100644 --- a/runtime/native/dalvik_system_DexFile.cc +++ b/runtime/native/dalvik_system_DexFile.cc @@ -347,15 +347,14 @@ static jobjectArray DexFile_getClassNameList(JNIEnv* env, jclass, jobject cookie static jint GetDexOptNeeded(JNIEnv* env, const char* filename, - const char* pkgname, const char* instruction_set, - const jboolean defer) { + const int target_compilation_type_mask) { if ((filename == nullptr) || !OS::FileExists(filename)) { LOG(ERROR) << "DexFile_getDexOptNeeded file '" << filename << "' does not exist"; ScopedLocalRef fnfe(env, env->FindClass("java/io/FileNotFoundException")); const char* message = (filename == nullptr) ? "" : filename; env->ThrowNew(fnfe.get(), message); - return OatFileAssistant::kNoDexOptNeeded; + return -1; } const InstructionSet target_instruction_set = GetInstructionSetFromString(instruction_set); @@ -363,73 +362,52 @@ static jint GetDexOptNeeded(JNIEnv* env, ScopedLocalRef iae(env, env->FindClass("java/lang/IllegalArgumentException")); std::string message(StringPrintf("Instruction set %s is invalid.", instruction_set)); env->ThrowNew(iae.get(), message.c_str()); - return 0; + return -1; } // TODO: Verify the dex location is well formed, and throw an IOException if // not? - - OatFileAssistant oat_file_assistant(filename, target_instruction_set, false, pkgname); + OatFileAssistant oat_file_assistant(filename, target_compilation_type_mask, + target_instruction_set, false); // Always treat elements of the bootclasspath as up-to-date. if (oat_file_assistant.IsInBootClassPath()) { return OatFileAssistant::kNoDexOptNeeded; } - // TODO: Checking the profile should probably be done in the GetStatus() - // function. We have it here because GetStatus() should not be copying - // profile files. But who should be copying profile files? - if (oat_file_assistant.OdexFileIsOutOfDate()) { - // Needs recompile if profile has changed significantly. - if (Runtime::Current()->GetProfilerOptions().IsEnabled()) { - if (oat_file_assistant.IsProfileChangeSignificant()) { - if (!defer) { - oat_file_assistant.CopyProfileFile(); - } - return OatFileAssistant::kDex2OatNeeded; - } else if (oat_file_assistant.ProfileExists() - && !oat_file_assistant.OldProfileExists()) { - if (!defer) { - oat_file_assistant.CopyProfileFile(); - } - } - } - } - return oat_file_assistant.GetDexOptNeeded(); } static jint DexFile_getDexOptNeeded(JNIEnv* env, jclass, jstring javaFilename, - jstring javaPkgname, jstring javaInstructionSet, - jboolean defer) { + jint javaTargetCompilationTypeMask) { ScopedUtfChars filename(env, javaFilename); if (env->ExceptionCheck()) { - return 0; + return -1; } - NullableScopedUtfChars pkgname(env, javaPkgname); - ScopedUtfChars instruction_set(env, javaInstructionSet); if (env->ExceptionCheck()) { - return 0; + return -1; } return GetDexOptNeeded(env, filename.c_str(), - pkgname.c_str(), instruction_set.c_str(), - defer); + javaTargetCompilationTypeMask); } -// public API, null pkgname +// public API static jboolean DexFile_isDexOptNeeded(JNIEnv* env, jclass, jstring javaFilename) { const char* instruction_set = GetInstructionSetString(kRuntimeISA); ScopedUtfChars filename(env, javaFilename); - jint status = GetDexOptNeeded(env, filename.c_str(), nullptr /* pkgname */, - instruction_set, false /* defer */); + jint status = GetDexOptNeeded( + env, + filename.c_str(), + instruction_set, + OatFileAssistant::kFullCompilation | OatFileAssistant::kProfileGuideCompilation); return (status != OatFileAssistant::kNoDexOptNeeded) ? JNI_TRUE : JNI_FALSE; } @@ -445,7 +423,7 @@ static JNINativeMethod gMethods[] = { NATIVE_METHOD(DexFile, getClassNameList, "(Ljava/lang/Object;)[Ljava/lang/String;"), NATIVE_METHOD(DexFile, isDexOptNeeded, "(Ljava/lang/String;)Z"), NATIVE_METHOD(DexFile, getDexOptNeeded, - "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)I"), + "(Ljava/lang/String;Ljava/lang/String;I)I"), NATIVE_METHOD(DexFile, openDexFileNative, "(Ljava/lang/String;" "Ljava/lang/String;" diff --git a/runtime/oat.cc b/runtime/oat.cc index 4948558f84..2ac105291d 100644 --- a/runtime/oat.cc +++ b/runtime/oat.cc @@ -29,6 +29,8 @@ constexpr uint8_t OatHeader::kOatMagic[4]; constexpr uint8_t OatHeader::kOatVersion[4]; constexpr const char OatHeader::kTrueValue[]; constexpr const char OatHeader::kFalseValue[]; +constexpr const char OatHeader::kExtractOnlyValue[]; +constexpr const char OatHeader::kProfileGuideCompiledValue[]; static size_t ComputeOatHeaderSize(const SafeMap* variable_data) { size_t estimate = 0U; @@ -467,12 +469,24 @@ bool OatHeader::IsDebuggable() const { } bool OatHeader::IsExtractOnly() const { - return IsKeyEnabled(OatHeader::kExtractOnlyKey); + return KeyHasValue(kCompilationType, + kExtractOnlyValue, + sizeof(kExtractOnlyValue)); } -bool OatHeader::IsKeyEnabled(const char* key) const { +bool OatHeader::IsProfileGuideCompiled() const { + return KeyHasValue(kCompilationType, + kProfileGuideCompiledValue, + sizeof(kProfileGuideCompiledValue)); +} + +bool OatHeader::KeyHasValue(const char* key, const char* value, size_t value_size) const { const char* key_value = GetStoreValueByKey(key); - return (key_value != nullptr && strncmp(key_value, kTrueValue, sizeof(kTrueValue)) == 0); + return (key_value != nullptr && strncmp(key_value, value, value_size) == 0); +} + +bool OatHeader::IsKeyEnabled(const char* key) const { + return KeyHasValue(key, kTrueValue, sizeof(kTrueValue)); } void OatHeader::Flatten(const SafeMap* key_value_store) { diff --git a/runtime/oat.h b/runtime/oat.h index fde386f37e..0660e19ff4 100644 --- a/runtime/oat.h +++ b/runtime/oat.h @@ -38,12 +38,15 @@ class PACKED(4) OatHeader { static constexpr const char* kDex2OatHostKey = "dex2oat-host"; static constexpr const char* kPicKey = "pic"; static constexpr const char* kDebuggableKey = "debuggable"; - static constexpr const char* kExtractOnlyKey = "extract-only"; + static constexpr const char* kCompilationType = "compilation-type"; static constexpr const char* kClassPathKey = "classpath"; static constexpr const char* kBootClassPath = "bootclasspath"; static constexpr const char kTrueValue[] = "true"; static constexpr const char kFalseValue[] = "false"; + static constexpr const char kExtractOnlyValue[] = "extract-only"; + static constexpr const char kProfileGuideCompiledValue[] = "profile-guide"; + static OatHeader* Create(InstructionSet instruction_set, const InstructionSetFeatures* instruction_set_features, @@ -108,8 +111,11 @@ class PACKED(4) OatHeader { bool IsPic() const; bool IsDebuggable() const; bool IsExtractOnly() const; + bool IsProfileGuideCompiled() const; private: + bool KeyHasValue(const char* key, const char* value, size_t value_size) const; + OatHeader(InstructionSet instruction_set, const InstructionSetFeatures* instruction_set_features, uint32_t dex_file_count, diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc index f912598eef..49fbf7261e 100644 --- a/runtime/oat_file.cc +++ b/runtime/oat_file.cc @@ -1225,6 +1225,10 @@ bool OatFile::IsExtractOnly() const { return GetOatHeader().IsExtractOnly(); } +bool OatFile::IsProfileGuideCompiled() const { + return GetOatHeader().IsProfileGuideCompiled(); +} + static constexpr char kDexClassPathEncodingSeparator = '*'; std::string OatFile::EncodeDexFileDependencies(const std::vector& dex_files) { diff --git a/runtime/oat_file.h b/runtime/oat_file.h index fb91a8cdff..1084253a88 100644 --- a/runtime/oat_file.h +++ b/runtime/oat_file.h @@ -93,6 +93,8 @@ class OatFile { bool IsExtractOnly() const; + bool IsProfileGuideCompiled() const; + const std::string& GetLocation() const { return location_; } diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc index 262c932766..90712c625c 100644 --- a/runtime/oat_file_assistant.cc +++ b/runtime/oat_file_assistant.cc @@ -36,7 +36,6 @@ #include "image.h" #include "oat.h" #include "os.h" -#include "profiler.h" #include "runtime.h" #include "scoped_thread_state_change.h" #include "ScopedFd.h" @@ -45,28 +44,19 @@ namespace art { OatFileAssistant::OatFileAssistant(const char* dex_location, + const int target_compilation_type_mask, const InstructionSet isa, bool load_executable) - : OatFileAssistant(dex_location, nullptr, isa, load_executable, nullptr) { } + : OatFileAssistant(dex_location, nullptr, target_compilation_type_mask, isa, load_executable) +{ } OatFileAssistant::OatFileAssistant(const char* dex_location, const char* oat_location, + const int target_compilation_type_mask, const InstructionSet isa, bool load_executable) - : OatFileAssistant(dex_location, oat_location, isa, load_executable, nullptr) { } - -OatFileAssistant::OatFileAssistant(const char* dex_location, - const InstructionSet isa, - bool load_executable, - const char* package_name) - : OatFileAssistant(dex_location, nullptr, isa, load_executable, package_name) { } - -OatFileAssistant::OatFileAssistant(const char* dex_location, - const char* oat_location, - const InstructionSet isa, - bool load_executable, - const char* package_name) - : isa_(isa), package_name_(package_name), load_executable_(load_executable) { + : target_compilation_type_mask_(target_compilation_type_mask), isa_(isa), + load_executable_(load_executable) { CHECK(dex_location != nullptr) << "OatFileAssistant: null dex location"; dex_location_.assign(dex_location); @@ -83,18 +73,6 @@ OatFileAssistant::OatFileAssistant(const char* dex_location, cached_oat_file_name_attempted_ = true; cached_oat_file_name_found_ = true; } - - // If there is no package name given, we will not be able to find any - // profiles associated with this dex location. Preemptively mark that to - // be the case, rather than trying to find and load the profiles later. - // Similarly, if profiling is disabled. - if (package_name == nullptr - || !Runtime::Current()->GetProfilerOptions().IsEnabled()) { - profile_load_attempted_ = true; - profile_load_succeeded_ = false; - old_profile_load_attempted_ = true; - old_profile_load_succeeded_ = false; - } } OatFileAssistant::~OatFileAssistant() { @@ -138,10 +116,23 @@ bool OatFileAssistant::Lock(std::string* error_msg) { return true; } -OatFileAssistant::DexOptNeeded OatFileAssistant::GetDexOptNeeded() { - // TODO: If the profiling code is ever restored, it's worth considering - // whether we should check to see if the profile is out of date here. +// Returns the compilation mode of the given oat file. +static OatFileAssistant::CompilationType GetCompilationType(const OatFile& oat_file) { + if (oat_file.IsExtractOnly()) { + return OatFileAssistant::kExtractOnly; + } + if (oat_file.IsProfileGuideCompiled()) { + return OatFileAssistant::kProfileGuideCompilation; + } + // Assume that if the oat files is not extract-only or profile-guide compiled + // then it must be fully compiled. + // NB: this does not necessary mean that the oat file is actually fully compiled. It + // might have been compiled in a different way (e.g. interpret-only) which does + // not record a type in the header. + return OatFileAssistant::kFullCompilation; +} +OatFileAssistant::DexOptNeeded OatFileAssistant::GetDexOptNeeded() { if (OatFileIsUpToDate() || OdexFileIsUpToDate()) { return kNoDexOptNeeded; } @@ -419,6 +410,11 @@ OatFileAssistant::OatStatus OatFileAssistant::GivenOatFileStatus(const OatFile& } bool OatFileAssistant::GivenOatFileIsOutOfDate(const OatFile& file) { + // Verify the file satisfies the desired compilation type. + if ((target_compilation_type_mask_ & GetCompilationType(file)) == 0) { + return true; + } + // Verify the dex checksum. // Note: GetOatDexFile will return null if the dex checksum doesn't match // what we provide, which verifies the primary dex checksum for us. @@ -541,104 +537,6 @@ bool OatFileAssistant::GivenOatFileIsUpToDate(const OatFile& file) { return true; } -bool OatFileAssistant::ProfileExists() { - return GetProfile() != nullptr; -} - -bool OatFileAssistant::OldProfileExists() { - return GetOldProfile() != nullptr; -} - -// TODO: The IsProfileChangeSignificant implementation was copied from likely -// bit-rotted code. -bool OatFileAssistant::IsProfileChangeSignificant() { - ProfileFile* profile = GetProfile(); - if (profile == nullptr) { - return false; - } - - ProfileFile* old_profile = GetOldProfile(); - if (old_profile == nullptr) { - return false; - } - - // TODO: The following code to compare two profile files should live with - // the rest of the profiler code, not the oat file assistant code. - - // A change in profile is considered significant if X% (change_thr property) - // of the top K% (compile_thr property) samples has changed. - const ProfilerOptions& options = Runtime::Current()->GetProfilerOptions(); - const double top_k_threshold = options.GetTopKThreshold(); - const double change_threshold = options.GetTopKChangeThreshold(); - std::set top_k, old_top_k; - profile->GetTopKSamples(top_k, top_k_threshold); - old_profile->GetTopKSamples(old_top_k, top_k_threshold); - std::set diff; - std::set_difference(top_k.begin(), top_k.end(), old_top_k.begin(), - old_top_k.end(), std::inserter(diff, diff.end())); - - // TODO: consider using the usedPercentage instead of the plain diff count. - double change_percent = 100.0 * static_cast(diff.size()) - / static_cast(top_k.size()); - std::set::iterator end = diff.end(); - for (std::set::iterator it = diff.begin(); it != end; it++) { - VLOG(oat) << "Profile new in topK: " << *it; - } - - if (change_percent > change_threshold) { - VLOG(oat) << "Oat File Assistant: Profile for " << dex_location_ - << "has changed significantly: (top " - << top_k_threshold << "% samples changed in proportion of " - << change_percent << "%)"; - return true; - } - return false; -} - -// TODO: The CopyProfileFile implementation was copied from likely bit-rotted -// code. -void OatFileAssistant::CopyProfileFile() { - if (!ProfileExists()) { - return; - } - - std::string profile_name = ProfileFileName(); - std::string old_profile_name = OldProfileFileName(); - - ScopedFd src(open(old_profile_name.c_str(), O_RDONLY)); - if (src.get() == -1) { - PLOG(WARNING) << "Failed to open profile file " << old_profile_name - << ". My uid:gid is " << getuid() << ":" << getgid(); - return; - } - - struct stat stat_src; - if (fstat(src.get(), &stat_src) == -1) { - PLOG(WARNING) << "Failed to get stats for profile file " << old_profile_name - << ". My uid:gid is " << getuid() << ":" << getgid(); - return; - } - - // Create the copy with rw------- (only accessible by system) - ScopedFd dst(open(profile_name.c_str(), O_WRONLY|O_CREAT|O_TRUNC, 0600)); - if (dst.get() == -1) { - PLOG(WARNING) << "Failed to create/write prev profile file " << profile_name - << ". My uid:gid is " << getuid() << ":" << getgid(); - return; - } - -#ifdef __linux__ - if (sendfile(dst.get(), src.get(), nullptr, stat_src.st_size) == -1) { -#else - off_t len; - if (sendfile(dst.get(), src.get(), 0, &len, nullptr, 0) == -1) { -#endif - PLOG(WARNING) << "Failed to copy profile file " << old_profile_name - << " to " << profile_name << ". My uid:gid is " << getuid() - << ":" << getgid(); - } -} - bool OatFileAssistant::RelocateOatFile(const std::string* input_file, std::string* error_msg) { CHECK(error_msg != nullptr); @@ -694,6 +592,15 @@ bool OatFileAssistant::RelocateOatFile(const std::string* input_file, bool OatFileAssistant::GenerateOatFile(std::string* error_msg) { CHECK(error_msg != nullptr); + // TODO: Currently we only know how to make a fully-compiled oat file. + // Perhaps we should support generating other kinds of oat files? + if ((target_compilation_type_mask_ & kFullCompilation) == 0) { + *error_msg = "Generation of oat file for dex location " + dex_location_ + + " not attempted because full compilation was not specified" + + " as an acceptable target compilation type."; + return false; + } + Runtime* runtime = Runtime::Current(); if (!runtime->IsDex2OatEnabled()) { *error_msg = "Generation of oat file for dex location " + dex_location_ @@ -861,21 +768,6 @@ std::string OatFileAssistant::DalvikCacheDirectory() { return result; } -std::string OatFileAssistant::ProfileFileName() { - if (package_name_ != nullptr) { - return DalvikCacheDirectory() + std::string("profiles/") + package_name_; - } - return ""; -} - -std::string OatFileAssistant::OldProfileFileName() { - std::string profile_name = ProfileFileName(); - if (profile_name.empty()) { - return ""; - } - return profile_name + "@old"; -} - std::string OatFileAssistant::ImageLocation() { Runtime* runtime = Runtime::Current(); const std::vector& image_spaces = @@ -1007,34 +899,6 @@ const OatFileAssistant::ImageInfo* OatFileAssistant::GetImageInfo() { return image_info_load_succeeded_ ? &cached_image_info_ : nullptr; } -ProfileFile* OatFileAssistant::GetProfile() { - if (!profile_load_attempted_) { - CHECK(package_name_ != nullptr) - << "pakage_name_ is nullptr: " - << "profile_load_attempted_ should have been true"; - profile_load_attempted_ = true; - std::string profile_name = ProfileFileName(); - if (!profile_name.empty()) { - profile_load_succeeded_ = cached_profile_.LoadFile(profile_name); - } - } - return profile_load_succeeded_ ? &cached_profile_ : nullptr; -} - -ProfileFile* OatFileAssistant::GetOldProfile() { - if (!old_profile_load_attempted_) { - CHECK(package_name_ != nullptr) - << "pakage_name_ is nullptr: " - << "old_profile_load_attempted_ should have been true"; - old_profile_load_attempted_ = true; - std::string old_profile_name = OldProfileFileName(); - if (!old_profile_name.empty()) { - old_profile_load_succeeded_ = cached_old_profile_.LoadFile(old_profile_name); - } - } - return old_profile_load_succeeded_ ? &cached_old_profile_ : nullptr; -} - gc::space::ImageSpace* OatFileAssistant::OpenImageSpace(const OatFile* oat_file) { DCHECK(oat_file != nullptr); std::string art_file = ArtFileName(oat_file); diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h index 7b45bca946..893aea2ab9 100644 --- a/runtime/oat_file_assistant.h +++ b/runtime/oat_file_assistant.h @@ -44,9 +44,6 @@ class ImageSpace; // The oat file assistant is intended to be used with dex locations not on the // boot class path. See the IsInBootClassPath method for a way to check if the // dex location is in the boot class path. -// -// TODO: All the profiling related code is old and untested. It should either -// be restored and tested, or removed. class OatFileAssistant { public: enum DexOptNeeded { @@ -73,8 +70,8 @@ class OatFileAssistant { enum OatStatus { // kOatOutOfDate - An oat file is said to be out of date if the file does - // not exist, or is out of date with respect to the dex file or boot - // image. + // not exist, is out of date with respect to the dex file or boot image, + // or does not meet the target compilation type. kOatOutOfDate, // kOatNeedsRelocation - An oat file is said to need relocation if the @@ -88,6 +85,20 @@ class OatFileAssistant { kOatUpToDate, }; + // Represents the different compilation types of oat files that OatFileAssitant + // and external GetDexOptNeeded callers care about. + // Note: these should be able to be used as part of a mask. + enum CompilationType { + // Matches Java: dalvik.system.DexFile.COMPILATION_TYPE_FULL = 1 + kFullCompilation = 1, + + // Matches Java: dalvik.system.DexFile.COMPILATION_TYPE_PROFILE_GUIDE = 2 + kProfileGuideCompilation = 2, + + // Matches Java: dalvik.system.DexFile.COMPILATION_TYPE_EXTRACT_ONLY = 4 + kExtractOnly = 4, + }; + // Constructs an OatFileAssistant object to assist the oat file // corresponding to the given dex location with the target instruction set. // @@ -99,31 +110,28 @@ class OatFileAssistant { // Note: Currently the dex_location must have an extension. // TODO: Relax this restriction? // + // The target compilation type specifies a set of CompilationTypes that + // should be considered up to date. An oat file compiled in a way not + // included in the set is considered out of date. For example, to consider + // otherwise up-to-date fully compiled and profile-guide compiled oat + // files as up to date, but to consider extract-only files as out of date, + // specify: (kFullCompilation | kProfileGuideCompilation). + // // The isa should be either the 32 bit or 64 bit variant for the current // device. For example, on an arm device, use arm or arm64. An oat file can // be loaded executable only if the ISA matches the current runtime. - OatFileAssistant(const char* dex_location, const InstructionSet isa, + OatFileAssistant(const char* dex_location, + int target_compilation_type_mask, + const InstructionSet isa, bool load_executable); // Constructs an OatFileAssistant, providing an explicit target oat_location // to use instead of the standard oat location. - OatFileAssistant(const char* dex_location, const char* oat_location, - const InstructionSet isa, bool load_executable); - - // Constructs an OatFileAssistant, providing an additional package_name used - // solely for the purpose of locating profile files. - // - // TODO: Why is the name of the profile file based on the package name and - // not the dex location? If there is no technical reason the dex_location - // can't be used, we should prefer that instead. - OatFileAssistant(const char* dex_location, const InstructionSet isa, - bool load_executable, const char* package_name); - - // Constructs an OatFileAssistant with user specified oat location and a - // package name. - OatFileAssistant(const char* dex_location, const char* oat_location, - const InstructionSet isa, bool load_executable, - const char* package_name); + OatFileAssistant(const char* dex_location, + const char* oat_location, + int target_compilation_type_mask, + const InstructionSet isa, + bool load_executable); ~OatFileAssistant(); @@ -233,28 +241,6 @@ class OatFileAssistant { bool GivenOatFileNeedsRelocation(const OatFile& file); bool GivenOatFileIsUpToDate(const OatFile& file); - // Returns true if there is an accessible profile associated with the dex - // location. - // This returns false if profiling is disabled. - bool ProfileExists(); - - // The old profile is a file containing a previous snapshot of profiling - // information associated with the dex file code. This is used to track how - // the profiling information has changed over time. - // - // Returns true if there is an accessible old profile associated with the - // dex location. - // This returns false if profiling is disabled. - bool OldProfileExists(); - - // Returns true if there has been a significant change between the old - // profile and the current profile. - // This returns false if profiling is disabled. - bool IsProfileChangeSignificant(); - - // Copy the current profile to the old profile location. - void CopyProfileFile(); - // Generates the oat file by relocation from the named input file. // This does not check the current status before attempting to relocate the // oat file. @@ -309,16 +295,6 @@ class OatFileAssistant { // Returns an empty string if we can't get the dalvik cache directory path. std::string DalvikCacheDirectory(); - // Constructs the filename for the profile file. - // Returns an empty string if we do not have the necessary information to - // construct the filename. - std::string ProfileFileName(); - - // Constructs the filename for the old profile file. - // Returns an empty string if we do not have the necessary information to - // construct the filename. - std::string OldProfileFileName(); - // Returns the current image location. // Returns an empty string if the image location could not be retrieved. // @@ -364,35 +340,18 @@ class OatFileAssistant { // The caller shouldn't clean up or free the returned pointer. const ImageInfo* GetImageInfo(); - // Returns the loaded profile. - // Loads the profile if needed. Returns null if the profile failed - // to load. - // The caller shouldn't clean up or free the returned pointer. - ProfileFile* GetProfile(); - - // Returns the loaded old profile. - // Loads the old profile if needed. Returns null if the old profile - // failed to load. - // The caller shouldn't clean up or free the returned pointer. - ProfileFile* GetOldProfile(); - // To implement Lock(), we lock a dummy file where the oat file would go // (adding ".flock" to the target file name) and retain the lock for the // remaining lifetime of the OatFileAssistant object. ScopedFlock flock_; std::string dex_location_; + const int target_compilation_type_mask_; // In a properly constructed OatFileAssistant object, isa_ should be either // the 32 or 64 bit variant for the current device. const InstructionSet isa_ = kNone; - // The package name, used solely to find the profile file. - // This may be null in a properly constructed object. In this case, - // profile_load_attempted_ and old_profile_load_attempted_ will be true, and - // profile_load_succeeded_ and old_profile_load_succeeded_ will be false. - const char* package_name_ = nullptr; - // Whether we will attempt to load oat files executable. bool load_executable_ = false; @@ -451,18 +410,6 @@ class OatFileAssistant { bool image_info_load_succeeded_ = false; ImageInfo cached_image_info_; - // Cached value of the profile file. - // Use the GetProfile method rather than accessing these directly. - bool profile_load_attempted_ = false; - bool profile_load_succeeded_ = false; - ProfileFile cached_profile_; - - // Cached value of the profile file. - // Use the GetOldProfile method rather than accessing these directly. - bool old_profile_load_attempted_ = false; - bool old_profile_load_succeeded_ = false; - ProfileFile cached_old_profile_; - // For debugging only. // If this flag is set, the oat or odex file has been released to the user // of the OatFileAssistant object and the OatFileAssistant object is in a diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc index 83d4457a1c..4541468cb3 100644 --- a/runtime/oat_file_assistant_test.cc +++ b/runtime/oat_file_assistant_test.cc @@ -260,7 +260,7 @@ class OatFileAssistantTest : public CommonRuntimeTest { } void GenerateExtractOnlyOdexForTest(const std::string& dex_location, - const std::string& odex_location) { + const std::string& odex_location) { std::vector args; args.push_back("--dex-file=" + dex_location); args.push_back("--oat-file=" + odex_location); @@ -277,7 +277,26 @@ class OatFileAssistantTest : public CommonRuntimeTest { EXPECT_EQ(odex_file->GetOatHeader().GetImageFileLocationOatChecksum(), 0u); EXPECT_EQ(odex_file->GetOatHeader().GetImageFileLocationOatDataBegin(), 0u); EXPECT_EQ(odex_file->GetOatHeader().GetImagePatchDelta(), 0); -} + } + + void GenerateProfileGuideOdexForTest(const std::string& dex_location, + const std::string& odex_location) { + std::vector args; + args.push_back("--dex-file=" + dex_location); + args.push_back("--oat-file=" + odex_location); + ScratchFile profile_file; + args.push_back("--profile-file=" + profile_file.GetFilename()); + std::string error_msg; + ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg; + + // Verify the odex file was generated as expected. + std::unique_ptr odex_file(OatFile::Open( + odex_location.c_str(), odex_location.c_str(), nullptr, nullptr, + false, dex_location.c_str(), &error_msg)); + printf("error %s", error_msg.c_str()); + ASSERT_TRUE(odex_file.get() != nullptr) << error_msg; + EXPECT_TRUE(odex_file->IsProfileGuideCompiled()); + } private: // Reserve memory around where the image will be loaded so other memory @@ -344,7 +363,8 @@ class OatFileAssistantNoDex2OatTest : public OatFileAssistantTest { // Generate an oat file for the purposes of test, as opposed to testing // generation of oat files. static void GenerateOatForTest(const char* dex_location) { - OatFileAssistant oat_file_assistant(dex_location, kRuntimeISA, false); + OatFileAssistant oat_file_assistant(dex_location, + OatFileAssistant::kFullCompilation, kRuntimeISA, false); std::string error_msg; ASSERT_TRUE(oat_file_assistant.GenerateOatFile(&error_msg)) << error_msg; @@ -356,7 +376,8 @@ TEST_F(OatFileAssistantTest, DexNoOat) { std::string dex_location = GetScratchDir() + "/DexNoOat.jar"; Copy(GetDexSrc1(), dex_location); - OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false); + OatFileAssistant oat_file_assistant(dex_location.c_str(), + OatFileAssistant::kFullCompilation, kRuntimeISA, false); EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, oat_file_assistant.GetDexOptNeeded()); @@ -379,7 +400,8 @@ TEST_F(OatFileAssistantTest, DexNoOat) { TEST_F(OatFileAssistantTest, NoDexNoOat) { std::string dex_location = GetScratchDir() + "/NoDexNoOat.jar"; - OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true); + OatFileAssistant oat_file_assistant(dex_location.c_str(), + OatFileAssistant::kFullCompilation, kRuntimeISA, true); EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded()); EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles()); @@ -400,7 +422,8 @@ TEST_F(OatFileAssistantTest, OatUpToDate) { Copy(GetDexSrc1(), dex_location); GenerateOatForTest(dex_location.c_str()); - OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false); + OatFileAssistant oat_file_assistant(dex_location.c_str(), + OatFileAssistant::kFullCompilation, kRuntimeISA, false); EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded()); EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); @@ -422,7 +445,8 @@ TEST_F(OatFileAssistantTest, MultiDexOatUpToDate) { Copy(GetMultiDexSrc1(), dex_location); GenerateOatForTest(dex_location.c_str()); - OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true); + OatFileAssistant oat_file_assistant(dex_location.c_str(), + OatFileAssistant::kFullCompilation, kRuntimeISA, true); EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded()); EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles()); @@ -448,7 +472,8 @@ TEST_F(OatFileAssistantTest, MultiDexSecondaryOutOfDate) { // is out of date. Copy(GetMultiDexSrc2(), dex_location); - OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true); + OatFileAssistant oat_file_assistant(dex_location.c_str(), + OatFileAssistant::kFullCompilation, kRuntimeISA, true); EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, oat_file_assistant.GetDexOptNeeded()); EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles()); } @@ -475,6 +500,7 @@ TEST_F(OatFileAssistantTest, RelativeEncodedDexLocation) { // Verify we can load both dex files. OatFileAssistant oat_file_assistant(dex_location.c_str(), oat_location.c_str(), + OatFileAssistant::kFullCompilation, kRuntimeISA, true); std::unique_ptr oat_file = oat_file_assistant.GetBestOatFile(); ASSERT_TRUE(oat_file.get() != nullptr); @@ -495,7 +521,8 @@ TEST_F(OatFileAssistantTest, OatOutOfDate) { GenerateOatForTest(dex_location.c_str()); Copy(GetDexSrc2(), dex_location); - OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false); + OatFileAssistant oat_file_assistant(dex_location.c_str(), + OatFileAssistant::kFullCompilation, kRuntimeISA, false); EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, oat_file_assistant.GetDexOptNeeded()); EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); @@ -508,32 +535,6 @@ TEST_F(OatFileAssistantTest, OatOutOfDate) { EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles()); } -// Case: We have a DEX file and an extract-only ODEX file out of date relative -// to the DEX file. -// Expect: The status is kDex2OatNeeded. -TEST_F(OatFileAssistantTest, ExtractOnlyOdexOutOfDate) { - std::string dex_location = GetScratchDir() + "/ExtractOnlyOdexOutOfDate.jar"; - std::string odex_location = GetOdexDir() + "/ExtractOnlyOdexOutOfDate.odex"; - - // We create a dex, generate an oat for it, then overwrite the dex with a - // different dex to make the oat out of date. - Copy(GetDexSrc1(), dex_location); - GenerateExtractOnlyOdexForTest(dex_location.c_str(), odex_location.c_str()); - Copy(GetDexSrc2(), dex_location); - - OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false); - EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, oat_file_assistant.GetDexOptNeeded()); - - EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); - EXPECT_TRUE(oat_file_assistant.OdexFileExists()); - EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate()); - EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate()); - EXPECT_FALSE(oat_file_assistant.OatFileExists()); - EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate()); - EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate()); - EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles()); -} - // Case: We have a DEX file and an ODEX file, but no OAT file. // Expect: The status is kPatchOatNeeded. TEST_F(OatFileAssistantTest, DexOdexNoOat) { @@ -545,7 +546,8 @@ TEST_F(OatFileAssistantTest, DexOdexNoOat) { GenerateOdexForTest(dex_location, odex_location); // Verify the status. - OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false); + OatFileAssistant oat_file_assistant(dex_location.c_str(), + OatFileAssistant::kFullCompilation, kRuntimeISA, false); EXPECT_EQ(OatFileAssistant::kPatchOatNeeded, oat_file_assistant.GetDexOptNeeded()); @@ -578,7 +580,8 @@ TEST_F(OatFileAssistantTest, StrippedDexOdexNoOat) { Copy(GetStrippedDexSrc1(), dex_location); // Verify the status. - OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true); + OatFileAssistant oat_file_assistant(dex_location.c_str(), + OatFileAssistant::kFullCompilation, kRuntimeISA, true); EXPECT_EQ(OatFileAssistant::kPatchOatNeeded, oat_file_assistant.GetDexOptNeeded()); @@ -633,7 +636,8 @@ TEST_F(OatFileAssistantTest, StrippedDexOdexOat) { Copy(GetStrippedDexSrc1(), dex_location); // Verify the status. - OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true); + OatFileAssistant oat_file_assistant(dex_location.c_str(), + OatFileAssistant::kFullCompilation, kRuntimeISA, true); EXPECT_EQ(OatFileAssistant::kPatchOatNeeded, oat_file_assistant.GetDexOptNeeded()); @@ -681,7 +685,8 @@ TEST_F(OatFileAssistantTest, ResourceOnlyDex) { Copy(GetStrippedDexSrc1(), dex_location); // Verify the status. - OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true); + OatFileAssistant oat_file_assistant(dex_location.c_str(), + OatFileAssistant::kFullCompilation, kRuntimeISA, true); EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded()); @@ -724,7 +729,7 @@ TEST_F(OatFileAssistantTest, SelfRelocation) { GenerateOdexForTest(dex_location, oat_location); OatFileAssistant oat_file_assistant(dex_location.c_str(), - oat_location.c_str(), kRuntimeISA, true); + oat_location.c_str(), OatFileAssistant::kFullCompilation, kRuntimeISA, true); EXPECT_EQ(OatFileAssistant::kSelfPatchOatNeeded, oat_file_assistant.GetDexOptNeeded()); @@ -782,7 +787,7 @@ TEST_F(OatFileAssistantTest, OdexOatOverlap) { // Verify things don't go bad. OatFileAssistant oat_file_assistant(dex_location.c_str(), - oat_location.c_str(), kRuntimeISA, true); + oat_location.c_str(), OatFileAssistant::kFullCompilation, kRuntimeISA, true); EXPECT_EQ(OatFileAssistant::kPatchOatNeeded, oat_file_assistant.GetDexOptNeeded()); @@ -816,7 +821,8 @@ TEST_F(OatFileAssistantTest, DexPicOdexNoOat) { GeneratePicOdexForTest(dex_location, odex_location); // Verify the status. - OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false); + OatFileAssistant oat_file_assistant(dex_location.c_str(), + OatFileAssistant::kFullCompilation, kRuntimeISA, false); EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded()); @@ -841,7 +847,9 @@ TEST_F(OatFileAssistantTest, DexExtractOnlyOdexNoOat) { GenerateExtractOnlyOdexForTest(dex_location, odex_location); // Verify the status. - OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false); + OatFileAssistant oat_file_assistant(dex_location.c_str(), + OatFileAssistant::kFullCompilation | OatFileAssistant::kExtractOnly, + kRuntimeISA, false); EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded()); @@ -864,7 +872,8 @@ TEST_F(OatFileAssistantTest, LoadOatUpToDate) { GenerateOatForTest(dex_location.c_str()); // Load the oat using an oat file assistant. - OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true); + OatFileAssistant oat_file_assistant(dex_location.c_str(), + OatFileAssistant::kFullCompilation, kRuntimeISA, true); std::unique_ptr oat_file = oat_file_assistant.GetBestOatFile(); ASSERT_TRUE(oat_file.get() != nullptr); @@ -883,7 +892,8 @@ TEST_F(OatFileAssistantTest, LoadNoExecOatUpToDate) { GenerateOatForTest(dex_location.c_str()); // Load the oat using an oat file assistant. - OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false); + OatFileAssistant oat_file_assistant(dex_location.c_str(), + OatFileAssistant::kFullCompilation, kRuntimeISA, false); std::unique_ptr oat_file = oat_file_assistant.GetBestOatFile(); ASSERT_TRUE(oat_file.get() != nullptr); @@ -903,7 +913,8 @@ TEST_F(OatFileAssistantTest, LoadDexNoAlternateOat) { Copy(GetDexSrc1(), dex_location); OatFileAssistant oat_file_assistant( - dex_location.c_str(), oat_location.c_str(), kRuntimeISA, true); + dex_location.c_str(), oat_location.c_str(), + OatFileAssistant::kFullCompilation, kRuntimeISA, true); std::string error_msg; ASSERT_TRUE(oat_file_assistant.MakeUpToDate(&error_msg)) << error_msg; @@ -917,7 +928,8 @@ TEST_F(OatFileAssistantTest, LoadDexNoAlternateOat) { EXPECT_TRUE(OS::FileExists(oat_location.c_str())); // Verify it didn't create an oat in the default location. - OatFileAssistant ofm(dex_location.c_str(), kRuntimeISA, false); + OatFileAssistant ofm(dex_location.c_str(), + OatFileAssistant::kFullCompilation, kRuntimeISA, false); EXPECT_FALSE(ofm.OatFileExists()); } @@ -933,7 +945,8 @@ TEST_F(OatFileAssistantTest, LoadDexUnwriteableAlternateOat) { Copy(GetDexSrc1(), dex_location); OatFileAssistant oat_file_assistant( - dex_location.c_str(), oat_location.c_str(), kRuntimeISA, true); + dex_location.c_str(), oat_location.c_str(), + OatFileAssistant::kFullCompilation, kRuntimeISA, true); std::string error_msg; ASSERT_FALSE(oat_file_assistant.MakeUpToDate(&error_msg)); @@ -948,7 +961,8 @@ TEST_F(OatFileAssistantTest, GenNoDex) { std::string oat_location = GetScratchDir() + "/GenNoDex.oat"; OatFileAssistant oat_file_assistant( - dex_location.c_str(), oat_location.c_str(), kRuntimeISA, true); + dex_location.c_str(), oat_location.c_str(), + OatFileAssistant::kFullCompilation, kRuntimeISA, true); std::string error_msg; ASSERT_FALSE(oat_file_assistant.GenerateOatFile(&error_msg)); } @@ -996,7 +1010,8 @@ TEST_F(OatFileAssistantTest, NonAbsoluteDexLocation) { Copy(GetDexSrc1(), abs_dex_location); std::string dex_location = MakePathRelative(abs_dex_location); - OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true); + OatFileAssistant oat_file_assistant(dex_location.c_str(), + OatFileAssistant::kFullCompilation, kRuntimeISA, true); EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, oat_file_assistant.GetDexOptNeeded()); @@ -1013,7 +1028,8 @@ TEST_F(OatFileAssistantTest, NonAbsoluteDexLocation) { TEST_F(OatFileAssistantTest, ShortDexLocation) { std::string dex_location = "/xx"; - OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true); + OatFileAssistant oat_file_assistant(dex_location.c_str(), + OatFileAssistant::kFullCompilation, kRuntimeISA, true); EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded()); @@ -1037,7 +1053,8 @@ TEST_F(OatFileAssistantTest, LongDexExtension) { std::string dex_location = GetScratchDir() + "/LongDexExtension.jarx"; Copy(GetDexSrc1(), dex_location); - OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false); + OatFileAssistant oat_file_assistant(dex_location.c_str(), + OatFileAssistant::kFullCompilation, kRuntimeISA, false); EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, oat_file_assistant.GetDexOptNeeded()); @@ -1134,7 +1151,8 @@ TEST_F(OatFileAssistantNoDex2OatTest, LoadDexOdexNoOat) { GenerateOdexForTest(dex_location, odex_location); // Load the oat using an executable oat file assistant. - OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true); + OatFileAssistant oat_file_assistant(dex_location.c_str(), + OatFileAssistant::kFullCompilation, kRuntimeISA, true); std::unique_ptr oat_file = oat_file_assistant.GetBestOatFile(); ASSERT_TRUE(oat_file.get() != nullptr); @@ -1156,7 +1174,8 @@ TEST_F(OatFileAssistantNoDex2OatTest, LoadMultiDexOdexNoOat) { GenerateOdexForTest(dex_location, odex_location); // Load the oat using an executable oat file assistant. - OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true); + OatFileAssistant oat_file_assistant(dex_location.c_str(), + OatFileAssistant::kFullCompilation, kRuntimeISA, true); std::unique_ptr oat_file = oat_file_assistant.GetBestOatFile(); ASSERT_TRUE(oat_file.get() != nullptr); @@ -1184,6 +1203,45 @@ TEST(OatFileAssistantUtilsTest, DexFilenameToOdexFilename) { "/foo/bar/baz_noext", kArm, &odex_file, &error_msg)); } +// Case: We have a DEX file, extract-only ODEX, and fully compiled OAT. +// Expect: The status depends on the target compilation type mask. +TEST_F(OatFileAssistantTest, TargetCompilationType) { + std::string dex_location = GetScratchDir() + "/TargetCompilationType.jar"; + std::string odex_location = GetOdexDir() + "/TargetCompilationType.odex"; + Copy(GetDexSrc1(), dex_location); + GenerateExtractOnlyOdexForTest(dex_location, odex_location); + GenerateOatForTest(dex_location.c_str()); + + OatFileAssistant ofa_full(dex_location.c_str(), + OatFileAssistant::kFullCompilation, kRuntimeISA, false); + EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, ofa_full.GetDexOptNeeded()); + EXPECT_FALSE(ofa_full.IsInBootClassPath()); + EXPECT_TRUE(ofa_full.OdexFileIsOutOfDate()); + EXPECT_TRUE(ofa_full.OatFileIsUpToDate()); + + OatFileAssistant ofa_extract(dex_location.c_str(), + OatFileAssistant::kExtractOnly, kRuntimeISA, false); + EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, ofa_extract.GetDexOptNeeded()); + EXPECT_FALSE(ofa_extract.IsInBootClassPath()); + EXPECT_TRUE(ofa_extract.OdexFileIsUpToDate()); + EXPECT_TRUE(ofa_extract.OatFileIsOutOfDate()); + + OatFileAssistant ofa_profile(dex_location.c_str(), + OatFileAssistant::kProfileGuideCompilation, kRuntimeISA, false); + EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, ofa_profile.GetDexOptNeeded()); + EXPECT_FALSE(ofa_profile.IsInBootClassPath()); + EXPECT_TRUE(ofa_profile.OdexFileIsOutOfDate()); + EXPECT_TRUE(ofa_profile.OatFileIsOutOfDate()); + + OatFileAssistant ofa_extract_full(dex_location.c_str(), + OatFileAssistant::kFullCompilation | OatFileAssistant::kExtractOnly, + kRuntimeISA, false); + EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, ofa_extract_full.GetDexOptNeeded()); + EXPECT_FALSE(ofa_extract_full.IsInBootClassPath()); + EXPECT_TRUE(ofa_extract_full.OdexFileIsUpToDate()); + EXPECT_TRUE(ofa_extract_full.OatFileIsUpToDate()); +} + // Verify the dexopt status values from dalvik.system.DexFile // match the OatFileAssistant::DexOptStatus values. TEST_F(OatFileAssistantTest, DexOptStatusValues) { @@ -1218,13 +1276,31 @@ TEST_F(OatFileAssistantTest, DexOptStatusValues) { ASSERT_FALSE(self_patchoat_needed == nullptr); EXPECT_EQ(self_patchoat_needed->GetTypeAsPrimitiveType(), Primitive::kPrimInt); EXPECT_EQ(OatFileAssistant::kSelfPatchOatNeeded, self_patchoat_needed->GetInt(dexfile.Get())); + + ArtField* compilation_type_full = mirror::Class::FindStaticField( + soa.Self(), dexfile, "COMPILATION_TYPE_FULL", "I"); + ASSERT_FALSE(compilation_type_full == nullptr); + EXPECT_EQ(compilation_type_full->GetTypeAsPrimitiveType(), Primitive::kPrimInt); + EXPECT_EQ(OatFileAssistant::kFullCompilation, compilation_type_full->GetInt(dexfile.Get())); + + ArtField* compilation_type_profile_guide = mirror::Class::FindStaticField( + soa.Self(), dexfile, "COMPILATION_TYPE_PROFILE_GUIDE", "I"); + ASSERT_FALSE(compilation_type_profile_guide == nullptr); + EXPECT_EQ(compilation_type_profile_guide->GetTypeAsPrimitiveType(), Primitive::kPrimInt); + EXPECT_EQ(OatFileAssistant::kProfileGuideCompilation, + compilation_type_profile_guide->GetInt(dexfile.Get())); + + ArtField* compilation_type_extract_only = mirror::Class::FindStaticField( + soa.Self(), dexfile, "COMPILATION_TYPE_EXTRACT_ONLY", "I"); + ASSERT_FALSE(compilation_type_extract_only == nullptr); + EXPECT_EQ(compilation_type_extract_only->GetTypeAsPrimitiveType(), Primitive::kPrimInt); + EXPECT_EQ(OatFileAssistant::kExtractOnly, compilation_type_extract_only->GetInt(dexfile.Get())); } // TODO: More Tests: // * Test class linker falls back to unquickened dex for DexNoOat // * Test class linker falls back to unquickened dex for MultiDexNoOat // * Test using secondary isa -// * Test with profiling info? // * Test for status of oat while oat is being generated (how?) // * Test case where 32 and 64 bit boot class paths differ, // and we ask IsInBootClassPath for a class in exactly one of the 32 or @@ -1233,5 +1309,7 @@ TEST_F(OatFileAssistantTest, DexOptStatusValues) { // - Dex is stripped, don't have odex. // - Oat file corrupted after status check, before reload unexecutable // because it's unrelocated and no dex2oat +// * Test unrelocated specific target compilation type can be relocated to +// make it up to date. } // namespace art diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc index 3e6d0b5681..0912ba06c1 100644 --- a/runtime/oat_file_manager.cc +++ b/runtime/oat_file_manager.cc @@ -307,8 +307,13 @@ std::vector> OatFileManager::OpenDexFilesFromOat( Thread* const self = Thread::Current(); Locks::mutator_lock_->AssertNotHeld(self); Runtime* const runtime = Runtime::Current(); + + int target_compilation_type_mask = OatFileAssistant::kFullCompilation + | OatFileAssistant::kProfileGuideCompilation + | OatFileAssistant::kExtractOnly; OatFileAssistant oat_file_assistant(dex_location, oat_location, + target_compilation_type_mask, kRuntimeISA, !runtime->IsAotCompiler()); -- GitLab From f529e9bc50abb507e75ac425f6333b2406d405af Mon Sep 17 00:00:00 2001 From: Calin Juravle Date: Tue, 8 Mar 2016 12:52:52 +0000 Subject: [PATCH 161/204] Handle unexpected cases in profile saver There are some unexpected cases that should not occur in a normal run. Log warnings but avoid crashing if: - dex location is empty - we cannot figure the real paths of the locations. Bug: 27532729 (cherry picked from commit 1fae45f7d777e3971b916dda531c8648304866c8) Change-Id: I9e8f4fc2da49f47dab113795ac264c6db9b691de --- runtime/jit/profile_saver.cc | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc index 5abfa6c6a0..7f014fb660 100644 --- a/runtime/jit/profile_saver.cc +++ b/runtime/jit/profile_saver.cc @@ -301,13 +301,22 @@ void ProfileSaver::MaybeRecordDexUseInternal( const std::set& app_code_paths, const std::string& foreign_dex_profile_path, const std::string& app_data_dir) { + if (dex_location.empty()) { + LOG(WARNING) << "Asked to record foreign dex use with an empty dex location."; + return; + } if (foreign_dex_profile_path.empty()) { LOG(WARNING) << "Asked to record foreign dex use without a valid profile path "; return; } UniqueCPtr dex_location_real_path(realpath(dex_location.c_str(), nullptr)); - std::string dex_location_real_path_str(dex_location_real_path.get()); + if (dex_location_real_path == nullptr) { + PLOG(WARNING) << "Could not get realpath for " << dex_location; + } + std::string dex_location_real_path_str((dex_location_real_path == nullptr) + ? dex_location.c_str() + : dex_location_real_path.get()); if (dex_location_real_path_str.compare(0, app_data_dir.length(), app_data_dir) == 0) { // The dex location is under the application folder. Nothing to record. @@ -325,7 +334,12 @@ void ProfileSaver::MaybeRecordDexUseInternal( // to save some bytes of memory usage. for (const auto& app_code_location : app_code_paths) { UniqueCPtr real_app_code_location(realpath(app_code_location.c_str(), nullptr)); - std::string real_app_code_location_str(real_app_code_location.get()); + if (real_app_code_location == nullptr) { + PLOG(WARNING) << "Could not get realpath for " << app_code_location; + } + std::string real_app_code_location_str((real_app_code_location == nullptr) + ? app_code_location.c_str() + : real_app_code_location.get()); if (real_app_code_location_str == dex_location_real_path_str) { // The dex location belongs to the application code paths. Nothing to record. return; -- GitLab From cf283daf579e9eda586f312c3fc89444601e2525 Mon Sep 17 00:00:00 2001 From: Chris Larsen Date: Tue, 19 Jan 2016 16:45:35 -0800 Subject: [PATCH 162/204] MIPS32: java.lang.Thread, and java.lang.String intrinsics: - Thread java.lang.Thread.currentThread() - int java.lang.String.compareTo(String anotherString) - int java.lang.String.indexOf(int ch) - int java.lang.String.indexOf(int ch, int fromIndex) - java.lang.StringFactory.newStringFromBytes(byte[] data, int high, int offset, int byteCount) - java.lang.StringFactory.newStringFromChars(int offset, int charCount, char[] data) - java.lang.StringFactory.newStringFromString(String toCopy) Change-Id: I96a06ff81e1e3bf18d45760282356854efaf4945 --- compiler/optimizing/intrinsics_mips.cc | 264 +++++++++++++++++- compiler/optimizing/intrinsics_mips64.cc | 3 - runtime/arch/mips/asm_support_mips.S | 39 +++ runtime/arch/mips/quick_entrypoints_mips.S | 75 ++++- runtime/arch/mips64/asm_support_mips64.S | 34 +++ .../arch/mips64/quick_entrypoints_mips64.S | 18 +- runtime/arch/stub_test.cc | 4 +- 7 files changed, 413 insertions(+), 24 deletions(-) diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc index 697b8fe882..35ad74840f 100644 --- a/compiler/optimizing/intrinsics_mips.cc +++ b/compiler/optimizing/intrinsics_mips.cc @@ -1457,6 +1457,24 @@ void IntrinsicCodeGeneratorMIPS::VisitMemoryPokeLongNative(HInvoke* invoke) { } } +// Thread java.lang.Thread.currentThread() +void IntrinsicLocationsBuilderMIPS::VisitThreadCurrentThread(HInvoke* invoke) { + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kNoCall, + kIntrinsified); + locations->SetOut(Location::RequiresRegister()); +} + +void IntrinsicCodeGeneratorMIPS::VisitThreadCurrentThread(HInvoke* invoke) { + MipsAssembler* assembler = GetAssembler(); + Register out = invoke->GetLocations()->Out().AsRegister(); + + __ LoadFromOffset(kLoadWord, + out, + TR, + Thread::PeerOffset().Int32Value()); +} + // char java.lang.String.charAt(int index) void IntrinsicLocationsBuilderMIPS::VisitStringCharAt(HInvoke* invoke) { LocationSummary* locations = new (arena_) LocationSummary(invoke, @@ -1503,6 +1521,40 @@ void IntrinsicCodeGeneratorMIPS::VisitStringCharAt(HInvoke* invoke) { __ Bind(slow_path->GetExitLabel()); } +// int java.lang.String.compareTo(String anotherString) +void IntrinsicLocationsBuilderMIPS::VisitStringCompareTo(HInvoke* invoke) { + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kCall, + kIntrinsified); + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); + Location outLocation = calling_convention.GetReturnLocation(Primitive::kPrimInt); + locations->SetOut(Location::RegisterLocation(outLocation.AsRegister())); +} + +void IntrinsicCodeGeneratorMIPS::VisitStringCompareTo(HInvoke* invoke) { + MipsAssembler* assembler = GetAssembler(); + LocationSummary* locations = invoke->GetLocations(); + + // Note that the null check must have been done earlier. + DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0))); + + Register argument = locations->InAt(1).AsRegister(); + SlowPathCodeMIPS* slow_path = new (GetAllocator()) IntrinsicSlowPathMIPS(invoke); + codegen_->AddSlowPath(slow_path); + __ Beqz(argument, slow_path->GetEntryLabel()); + + __ LoadFromOffset(kLoadWord, + TMP, + TR, + QUICK_ENTRYPOINT_OFFSET(kMipsWordSize, + pStringCompareTo).Int32Value()); + __ Jalr(TMP); + __ Nop(); + __ Bind(slow_path->GetExitLabel()); +} + // boolean java.lang.String.equals(Object anObject) void IntrinsicLocationsBuilderMIPS::VisitStringEquals(HInvoke* invoke) { LocationSummary* locations = new (arena_) LocationSummary(invoke, @@ -1605,6 +1657,211 @@ void IntrinsicCodeGeneratorMIPS::VisitStringEquals(HInvoke* invoke) { __ Bind(&end); } +static void GenerateStringIndexOf(HInvoke* invoke, + bool start_at_zero, + MipsAssembler* assembler, + CodeGeneratorMIPS* codegen, + ArenaAllocator* allocator) { + LocationSummary* locations = invoke->GetLocations(); + Register tmp_reg = start_at_zero ? locations->GetTemp(0).AsRegister() : TMP; + + // Note that the null check must have been done earlier. + DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0))); + + // Check for code points > 0xFFFF. Either a slow-path check when we + // don't know statically, or directly dispatch if we have a constant. + SlowPathCodeMIPS* slow_path = nullptr; + if (invoke->InputAt(1)->IsIntConstant()) { + if (!IsUint<16>(invoke->InputAt(1)->AsIntConstant()->GetValue())) { + // Always needs the slow-path. We could directly dispatch to it, + // but this case should be rare, so for simplicity just put the + // full slow-path down and branch unconditionally. + slow_path = new (allocator) IntrinsicSlowPathMIPS(invoke); + codegen->AddSlowPath(slow_path); + __ B(slow_path->GetEntryLabel()); + __ Bind(slow_path->GetExitLabel()); + return; + } + } else { + Register char_reg = locations->InAt(1).AsRegister(); + // The "bltu" conditional branch tests to see if the character value + // fits in a valid 16-bit (MIPS halfword) value. If it doesn't then + // the character being searched for, if it exists in the string, is + // encoded using UTF-16 and stored in the string as two (16-bit) + // halfwords. Currently the assembly code used to implement this + // intrinsic doesn't support searching for a character stored as + // two halfwords so we fallback to using the generic implementation + // of indexOf(). + __ LoadConst32(tmp_reg, std::numeric_limits::max()); + slow_path = new (allocator) IntrinsicSlowPathMIPS(invoke); + codegen->AddSlowPath(slow_path); + __ Bltu(tmp_reg, char_reg, slow_path->GetEntryLabel()); + } + + if (start_at_zero) { + DCHECK_EQ(tmp_reg, A2); + // Start-index = 0. + __ Clear(tmp_reg); + } + + __ LoadFromOffset(kLoadWord, + TMP, + TR, + QUICK_ENTRYPOINT_OFFSET(kMipsWordSize, pIndexOf).Int32Value()); + __ Jalr(TMP); + __ Nop(); + + if (slow_path != nullptr) { + __ Bind(slow_path->GetExitLabel()); + } +} + +// int java.lang.String.indexOf(int ch) +void IntrinsicLocationsBuilderMIPS::VisitStringIndexOf(HInvoke* invoke) { + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kCall, + kIntrinsified); + // We have a hand-crafted assembly stub that follows the runtime + // calling convention. So it's best to align the inputs accordingly. + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); + Location outLocation = calling_convention.GetReturnLocation(Primitive::kPrimInt); + locations->SetOut(Location::RegisterLocation(outLocation.AsRegister())); + + // Need a temp for slow-path codepoint compare, and need to send start-index=0. + locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(2))); +} + +void IntrinsicCodeGeneratorMIPS::VisitStringIndexOf(HInvoke* invoke) { + GenerateStringIndexOf(invoke, + /* start_at_zero */ true, + GetAssembler(), + codegen_, + GetAllocator()); +} + +// int java.lang.String.indexOf(int ch, int fromIndex) +void IntrinsicLocationsBuilderMIPS::VisitStringIndexOfAfter(HInvoke* invoke) { + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kCall, + kIntrinsified); + // We have a hand-crafted assembly stub that follows the runtime + // calling convention. So it's best to align the inputs accordingly. + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); + locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2))); + Location outLocation = calling_convention.GetReturnLocation(Primitive::kPrimInt); + locations->SetOut(Location::RegisterLocation(outLocation.AsRegister())); + + // Need a temp for slow-path codepoint compare. + locations->AddTemp(Location::RequiresRegister()); +} + +void IntrinsicCodeGeneratorMIPS::VisitStringIndexOfAfter(HInvoke* invoke) { + GenerateStringIndexOf(invoke, + /* start_at_zero */ false, + GetAssembler(), + codegen_, + GetAllocator()); +} + +// java.lang.StringFactory.newStringFromBytes(byte[] data, int high, int offset, int byteCount) +void IntrinsicLocationsBuilderMIPS::VisitStringNewStringFromBytes(HInvoke* invoke) { + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kCall, + kIntrinsified); + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); + locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2))); + locations->SetInAt(3, Location::RegisterLocation(calling_convention.GetRegisterAt(3))); + Location outLocation = calling_convention.GetReturnLocation(Primitive::kPrimInt); + locations->SetOut(Location::RegisterLocation(outLocation.AsRegister())); +} + +void IntrinsicCodeGeneratorMIPS::VisitStringNewStringFromBytes(HInvoke* invoke) { + MipsAssembler* assembler = GetAssembler(); + LocationSummary* locations = invoke->GetLocations(); + + Register byte_array = locations->InAt(0).AsRegister(); + SlowPathCodeMIPS* slow_path = new (GetAllocator()) IntrinsicSlowPathMIPS(invoke); + codegen_->AddSlowPath(slow_path); + __ Beqz(byte_array, slow_path->GetEntryLabel()); + + __ LoadFromOffset(kLoadWord, + TMP, + TR, + QUICK_ENTRYPOINT_OFFSET(kMipsWordSize, pAllocStringFromBytes).Int32Value()); + __ Jalr(TMP); + __ Nop(); + codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); + __ Bind(slow_path->GetExitLabel()); +} + +// java.lang.StringFactory.newStringFromChars(int offset, int charCount, char[] data) +void IntrinsicLocationsBuilderMIPS::VisitStringNewStringFromChars(HInvoke* invoke) { + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kCall, + kIntrinsified); + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); + locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2))); + Location outLocation = calling_convention.GetReturnLocation(Primitive::kPrimInt); + locations->SetOut(Location::RegisterLocation(outLocation.AsRegister())); +} + +void IntrinsicCodeGeneratorMIPS::VisitStringNewStringFromChars(HInvoke* invoke) { + MipsAssembler* assembler = GetAssembler(); + + // No need to emit code checking whether `locations->InAt(2)` is a null + // pointer, as callers of the native method + // + // java.lang.StringFactory.newStringFromChars(int offset, int charCount, char[] data) + // + // all include a null check on `data` before calling that method. + + __ LoadFromOffset(kLoadWord, + TMP, + TR, + QUICK_ENTRYPOINT_OFFSET(kMipsWordSize, pAllocStringFromChars).Int32Value()); + __ Jalr(TMP); + __ Nop(); + codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); +} + +// java.lang.StringFactory.newStringFromString(String toCopy) +void IntrinsicLocationsBuilderMIPS::VisitStringNewStringFromString(HInvoke* invoke) { + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kCall, + kIntrinsified); + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + Location outLocation = calling_convention.GetReturnLocation(Primitive::kPrimInt); + locations->SetOut(Location::RegisterLocation(outLocation.AsRegister())); +} + +void IntrinsicCodeGeneratorMIPS::VisitStringNewStringFromString(HInvoke* invoke) { + MipsAssembler* assembler = GetAssembler(); + LocationSummary* locations = invoke->GetLocations(); + + Register string_to_copy = locations->InAt(0).AsRegister(); + SlowPathCodeMIPS* slow_path = new (GetAllocator()) IntrinsicSlowPathMIPS(invoke); + codegen_->AddSlowPath(slow_path); + __ Beqz(string_to_copy, slow_path->GetEntryLabel()); + + __ LoadFromOffset(kLoadWord, + TMP, + TR, + QUICK_ENTRYPOINT_OFFSET(kMipsWordSize, pAllocStringFromString).Int32Value()); + __ Jalr(TMP); + __ Nop(); + codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); + __ Bind(slow_path->GetExitLabel()); +} + static void GenIsInfinite(LocationSummary* locations, const Primitive::Type type, const bool isR6, @@ -1783,7 +2040,6 @@ UNIMPLEMENTED_INTRINSIC(MIPS, MathFloor) UNIMPLEMENTED_INTRINSIC(MIPS, MathRint) UNIMPLEMENTED_INTRINSIC(MIPS, MathRoundDouble) UNIMPLEMENTED_INTRINSIC(MIPS, MathRoundFloat) -UNIMPLEMENTED_INTRINSIC(MIPS, ThreadCurrentThread) UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGet) UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetVolatile) UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetLong) @@ -1802,12 +2058,6 @@ UNIMPLEMENTED_INTRINSIC(MIPS, UnsafePutLongVolatile) UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeCASInt) UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeCASLong) UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeCASObject) -UNIMPLEMENTED_INTRINSIC(MIPS, StringCompareTo) -UNIMPLEMENTED_INTRINSIC(MIPS, StringIndexOf) -UNIMPLEMENTED_INTRINSIC(MIPS, StringIndexOfAfter) -UNIMPLEMENTED_INTRINSIC(MIPS, StringNewStringFromBytes) -UNIMPLEMENTED_INTRINSIC(MIPS, StringNewStringFromChars) -UNIMPLEMENTED_INTRINSIC(MIPS, StringNewStringFromString) UNIMPLEMENTED_INTRINSIC(MIPS, ReferenceGetReferent) UNIMPLEMENTED_INTRINSIC(MIPS, StringGetCharsNoCheck) diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc index 83dff33ec0..c78a19c641 100644 --- a/compiler/optimizing/intrinsics_mips64.cc +++ b/compiler/optimizing/intrinsics_mips64.cc @@ -1503,9 +1503,6 @@ static void GenerateStringIndexOf(HInvoke* invoke, DCHECK_EQ(tmp_reg, A2); // Start-index = 0. __ Clear(tmp_reg); - } else { - __ Slt(TMP, A2, ZERO); // if fromIndex < 0 - __ Seleqz(A2, A2, TMP); // fromIndex = 0 } __ LoadFromOffset(kLoadDoubleword, diff --git a/runtime/arch/mips/asm_support_mips.S b/runtime/arch/mips/asm_support_mips.S index 51e224cbf3..801f708ad3 100644 --- a/runtime/arch/mips/asm_support_mips.S +++ b/runtime/arch/mips/asm_support_mips.S @@ -129,4 +129,43 @@ #endif // USE_HEAP_POISONING .endm +// Based on contents of creg select the minimum integer +// At the end of the macro the original value of creg is lost +.macro MINint dreg,rreg,sreg,creg + .set push + .set noat +#if defined(_MIPS_ARCH_MIPS32R6) || defined(_MIPS_ARCH_MIPS64R6) + .ifc \dreg, \rreg + selnez \dreg, \rreg, \creg + seleqz \creg, \sreg, \creg + .else + seleqz \dreg, \sreg, \creg + selnez \creg, \rreg, \creg + .endif + or \dreg, \dreg, \creg +#else + movn \dreg, \rreg, \creg + movz \dreg, \sreg, \creg +#endif + .set pop +.endm + +// Find minimum of two signed registers +.macro MINs dreg,rreg,sreg + .set push + .set noat + slt $at, \rreg, \sreg + MINint \dreg, \rreg, \sreg, $at + .set pop +.endm + +// Find minimum of two unsigned registers +.macro MINu dreg,rreg,sreg + .set push + .set noat + sltu $at, \rreg, \sreg + MINint \dreg, \rreg, \sreg, $at + .set pop +.endm + #endif // ART_RUNTIME_ARCH_MIPS_ASM_SUPPORT_MIPS_S_ diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S index 699ab3e65a..6c7d510da0 100644 --- a/runtime/arch/mips/quick_entrypoints_mips.S +++ b/runtime/arch/mips/quick_entrypoints_mips.S @@ -1416,7 +1416,7 @@ ENTRY art_quick_alloc_object_rosalloc SETUP_REFS_ONLY_CALLEE_SAVE_FRAME jal artAllocObjectFromCodeRosAlloc - move $a2 ,$s1 # Pass self as argument. + move $a2, $s1 # Pass self as argument. RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER END art_quick_alloc_object_rosalloc @@ -1744,5 +1744,74 @@ ENTRY_NO_GP art_quick_ushr_long nop END art_quick_ushr_long -UNIMPLEMENTED art_quick_indexof -UNIMPLEMENTED art_quick_string_compareto +/* java.lang.String.indexOf(int ch, int fromIndex=0) */ +ENTRY_NO_GP art_quick_indexof +/* $a0 holds address of "this" */ +/* $a1 holds "ch" */ +/* $a2 holds "fromIndex" */ + lw $t0, MIRROR_STRING_COUNT_OFFSET($a0) # this.length() + slt $at, $a2, $zero # if fromIndex < 0 +#if defined(_MIPS_ARCH_MIPS32R6) || defined(_MIPS_ARCH_MIPS64R6) + seleqz $a2, $a2, $at # fromIndex = 0; +#else + movn $a2, $zero, $at # fromIndex = 0; +#endif + subu $t0, $t0, $a2 # this.length() - fromIndex + blez $t0, 6f # if this.length()-fromIndex <= 0 + li $v0, -1 # return -1; + + sll $v0, $a2, 1 # $a0 += $a2 * 2 + addu $a0, $a0, $v0 # " " " " " + move $v0, $a2 # Set i to fromIndex. + +1: + lhu $t3, MIRROR_STRING_VALUE_OFFSET($a0) # if this.charAt(i) == ch + beq $t3, $a1, 6f # return i; + addu $a0, $a0, 2 # i++ + subu $t0, $t0, 1 # this.length() - i + bnez $t0, 1b # while this.length() - i > 0 + addu $v0, $v0, 1 # i++ + + li $v0, -1 # if this.length() - i <= 0 + # return -1; + +6: + j $ra + nop +END art_quick_indexof + + .set push + .set noat +/* java.lang.String.compareTo(String anotherString) */ +ENTRY_NO_GP art_quick_string_compareto +/* $a0 holds address of "this" */ +/* $a1 holds address of "anotherString" */ + beq $a0, $a1, 9f # this and anotherString are the same object + move $v0, $zero + + lw $a2, MIRROR_STRING_COUNT_OFFSET($a0) # this.length() + lw $a3, MIRROR_STRING_COUNT_OFFSET($a1) # anotherString.length() + MINu $t2, $a2, $a3 +# $t2 now holds min(this.length(),anotherString.length()) + + beqz $t2, 9f # while min(this.length(),anotherString.length())-i != 0 + subu $v0, $a2, $a3 # if $t2==0 return + # (this.length() - anotherString.length()) +1: + lhu $t0, MIRROR_STRING_VALUE_OFFSET($a0) # while this.charAt(i) == anotherString.charAt(i) + lhu $t1, MIRROR_STRING_VALUE_OFFSET($a1) + bne $t0, $t1, 9f # if this.charAt(i) != anotherString.charAt(i) + subu $v0, $t0, $t1 # return (this.charAt(i) - anotherString.charAt(i)) + addiu $a0, $a0, 2 # point at this.charAt(i++) + subu $t2, $t2, 1 # new value of + # min(this.length(),anotherString.length())-i + bnez $t2, 1b + addiu $a1, $a1, 2 # point at anotherString.charAt(i++) + subu $v0, $a2, $a3 + +9: + j $ra + nop +END art_quick_string_compareto + + .set pop diff --git a/runtime/arch/mips64/asm_support_mips64.S b/runtime/arch/mips64/asm_support_mips64.S index b859c708ba..786e86043e 100644 --- a/runtime/arch/mips64/asm_support_mips64.S +++ b/runtime/arch/mips64/asm_support_mips64.S @@ -83,4 +83,38 @@ #endif // USE_HEAP_POISONING .endm +// Based on contents of creg select the minimum integer +// At the end of the macro the original value of creg is lost +.macro MINint dreg,rreg,sreg,creg + .set push + .set noat + .ifc \dreg, \rreg + selnez \dreg, \rreg, \creg + seleqz \creg, \sreg, \creg + .else + seleqz \dreg, \sreg, \creg + selnez \creg, \rreg, \creg + .endif + or \dreg, \dreg, \creg + .set pop +.endm + +// Find minimum of two signed registers +.macro MINs dreg,rreg,sreg + .set push + .set noat + slt $at, \rreg, \sreg + MINint \dreg, \rreg, \sreg, $at + .set pop +.endm + +// Find minimum of two unsigned registers +.macro MINu dreg,rreg,sreg + .set push + .set noat + sltu $at, \rreg, \sreg + MINint \dreg, \rreg, \sreg, $at + .set pop +.endm + #endif // ART_RUNTIME_ARCH_MIPS64_ASM_SUPPORT_MIPS64_S_ diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S index d264c9baaf..b4e2fcc19a 100644 --- a/runtime/arch/mips64/quick_entrypoints_mips64.S +++ b/runtime/arch/mips64/quick_entrypoints_mips64.S @@ -1725,10 +1725,8 @@ ENTRY_NO_GP art_quick_string_compareto lw $a2,MIRROR_STRING_COUNT_OFFSET($a0) # this.length() lw $a3,MIRROR_STRING_COUNT_OFFSET($a1) # anotherString.length() - sltu $at,$a2,$a3 - seleqz $t2,$a3,$at - selnez $at,$a2,$at - or $t2,$t2,$at # $t2 now holds min(this.length(),anotherString.length()) + MINu $t2, $a2, $a3 +# $t2 now holds min(this.length(),anotherString.length()) beqz $t2,9f # while min(this.length(),anotherString.length())-i != 0 subu $v0,$a2,$a3 # if $t2==0 return @@ -1753,16 +1751,18 @@ END art_quick_string_compareto /* java.lang.String.indexOf(int ch, int fromIndex=0) */ ENTRY_NO_GP art_quick_indexof /* $a0 holds address of "this" */ -/* $a1 holds address of "ch" */ -/* $a2 holds address of "fromIndex" */ +/* $a1 holds "ch" */ +/* $a2 holds "fromIndex" */ lw $t0,MIRROR_STRING_COUNT_OFFSET($a0) # this.length() - subu $t0,$t0,$a2 # this.length() - offset - blez $t0,6f # if this.length()-offset <= 0 + slt $at, $a2, $zero # if fromIndex < 0 + seleqz $a2, $a2, $at # fromIndex = 0; + subu $t0,$t0,$a2 # this.length() - fromIndex + blez $t0,6f # if this.length()-fromIndex <= 0 li $v0,-1 # return -1; sll $v0,$a2,1 # $a0 += $a2 * 2 daddu $a0,$a0,$v0 # " " " " " - move $v0,$a2 # Set i to offset. + move $v0,$a2 # Set i to fromIndex. 1: lhu $t3,MIRROR_STRING_VALUE_OFFSET($a0) # if this.charAt(i) == ch diff --git a/runtime/arch/stub_test.cc b/runtime/arch/stub_test.cc index d5807e27b5..4236c287de 100644 --- a/runtime/arch/stub_test.cc +++ b/runtime/arch/stub_test.cc @@ -1205,7 +1205,7 @@ TEST_F(StubTest, AllocObjectArray) { TEST_F(StubTest, StringCompareTo) { #if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || \ - (defined(__mips__) && defined(__LP64__)) || (defined(__x86_64__) && !defined(__APPLE__)) + defined(__mips__) || (defined(__x86_64__) && !defined(__APPLE__)) // TODO: Check the "Unresolved" allocation stubs Thread* self = Thread::Current(); @@ -2054,7 +2054,7 @@ TEST_F(StubTest, IMT) { } TEST_F(StubTest, StringIndexOf) { -#if defined(__arm__) || defined(__aarch64__) || (defined(__mips__) && defined(__LP64__)) +#if defined(__arm__) || defined(__aarch64__) || defined(__mips__) Thread* self = Thread::Current(); ScopedObjectAccess soa(self); // garbage is created during ClassLinker::Init -- GitLab From 1193259cb37c9763a111825aa04718a409d07145 Mon Sep 17 00:00:00 2001 From: Aart Bik Date: Tue, 8 Mar 2016 12:42:25 -0800 Subject: [PATCH 163/204] Implement the 1.8 unsafe memory fences directly in HIR. Rationale: More efficient since it exposes full semantics to all operations on the graph and allows for proper code generation for all architectures. bug=26264765 Change-Id: Ic435886cf0645927a101a8502f0623fa573989ff --- compiler/optimizing/instruction_simplifier.cc | 16 ++++++++++++++++ compiler/optimizing/intrinsics.h | 5 ++++- compiler/optimizing/intrinsics_arm.cc | 3 --- compiler/optimizing/intrinsics_arm64.cc | 3 --- compiler/optimizing/intrinsics_mips.cc | 3 --- compiler/optimizing/intrinsics_mips64.cc | 3 --- compiler/optimizing/intrinsics_x86.cc | 3 --- compiler/optimizing/intrinsics_x86_64.cc | 3 --- test/004-checker-UnsafeTest18/src/Main.java | 18 ++++++++++++++++++ 9 files changed, 38 insertions(+), 19 deletions(-) diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index f8a9a94e62..b95ece5a31 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -94,6 +94,7 @@ class InstructionSimplifierVisitor : public HGraphDelegateVisitor { void SimplifyCompare(HInvoke* invoke, bool has_zero_op); void SimplifyIsNaN(HInvoke* invoke); void SimplifyFP2Int(HInvoke* invoke); + void SimplifyMemBarrier(HInvoke* invoke, MemBarrierKind barrier_kind); OptimizingCompilerStats* stats_; bool simplification_occurred_ = false; @@ -1594,6 +1595,12 @@ void InstructionSimplifierVisitor::SimplifyFP2Int(HInvoke* invoke) { invoke->ReplaceWithExceptInReplacementAtIndex(select, 0); // false at index 0 } +void InstructionSimplifierVisitor::SimplifyMemBarrier(HInvoke* invoke, MemBarrierKind barrier_kind) { + uint32_t dex_pc = invoke->GetDexPc(); + HMemoryBarrier* mem_barrier = new (GetGraph()->GetArena()) HMemoryBarrier(barrier_kind, dex_pc); + invoke->GetBlock()->ReplaceAndRemoveInstructionWith(invoke, mem_barrier); +} + void InstructionSimplifierVisitor::VisitInvoke(HInvoke* instruction) { switch (instruction->GetIntrinsic()) { case Intrinsics::kStringEquals: @@ -1626,6 +1633,15 @@ void InstructionSimplifierVisitor::VisitInvoke(HInvoke* instruction) { case Intrinsics::kDoubleDoubleToLongBits: SimplifyFP2Int(instruction); break; + case Intrinsics::kUnsafeLoadFence: + SimplifyMemBarrier(instruction, MemBarrierKind::kLoadAny); + break; + case Intrinsics::kUnsafeStoreFence: + SimplifyMemBarrier(instruction, MemBarrierKind::kAnyStore); + break; + case Intrinsics::kUnsafeFullFence: + SimplifyMemBarrier(instruction, MemBarrierKind::kAnyAny); + break; default: break; } diff --git a/compiler/optimizing/intrinsics.h b/compiler/optimizing/intrinsics.h index 0cec5ccfd3..3da82851a6 100644 --- a/compiler/optimizing/intrinsics.h +++ b/compiler/optimizing/intrinsics.h @@ -231,7 +231,10 @@ UNREACHABLE_INTRINSIC(Arch, LongRotateRight) \ UNREACHABLE_INTRINSIC(Arch, IntegerCompare) \ UNREACHABLE_INTRINSIC(Arch, LongCompare) \ UNREACHABLE_INTRINSIC(Arch, IntegerSignum) \ -UNREACHABLE_INTRINSIC(Arch, LongSignum) +UNREACHABLE_INTRINSIC(Arch, LongSignum) \ +UNREACHABLE_INTRINSIC(Arch, UnsafeLoadFence) \ +UNREACHABLE_INTRINSIC(Arch, UnsafeStoreFence) \ +UNREACHABLE_INTRINSIC(Arch, UnsafeFullFence) } // namespace art diff --git a/compiler/optimizing/intrinsics_arm.cc b/compiler/optimizing/intrinsics_arm.cc index b599d42c4b..c7d9a0d2ce 100644 --- a/compiler/optimizing/intrinsics_arm.cc +++ b/compiler/optimizing/intrinsics_arm.cc @@ -2008,9 +2008,6 @@ UNIMPLEMENTED_INTRINSIC(ARM, UnsafeGetAndAddLong) UNIMPLEMENTED_INTRINSIC(ARM, UnsafeGetAndSetInt) UNIMPLEMENTED_INTRINSIC(ARM, UnsafeGetAndSetLong) UNIMPLEMENTED_INTRINSIC(ARM, UnsafeGetAndSetObject) -UNIMPLEMENTED_INTRINSIC(ARM, UnsafeLoadFence) -UNIMPLEMENTED_INTRINSIC(ARM, UnsafeStoreFence) -UNIMPLEMENTED_INTRINSIC(ARM, UnsafeFullFence) UNREACHABLE_INTRINSICS(ARM) diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc index ccbbd43258..8a444412f3 100644 --- a/compiler/optimizing/intrinsics_arm64.cc +++ b/compiler/optimizing/intrinsics_arm64.cc @@ -1959,9 +1959,6 @@ UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndAddLong) UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndSetInt) UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndSetLong) UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndSetObject) -UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeLoadFence) -UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeStoreFence) -UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeFullFence) UNREACHABLE_INTRINSICS(ARM64) diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc index 697b8fe882..e159701a46 100644 --- a/compiler/optimizing/intrinsics_mips.cc +++ b/compiler/optimizing/intrinsics_mips.cc @@ -1838,9 +1838,6 @@ UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetAndAddLong) UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetAndSetInt) UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetAndSetLong) UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetAndSetObject) -UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeLoadFence) -UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeStoreFence) -UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeFullFence) UNREACHABLE_INTRINSICS(MIPS) diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc index 83dff33ec0..0dc602f1ef 100644 --- a/compiler/optimizing/intrinsics_mips64.cc +++ b/compiler/optimizing/intrinsics_mips64.cc @@ -1735,9 +1735,6 @@ UNIMPLEMENTED_INTRINSIC(MIPS64, UnsafeGetAndAddLong) UNIMPLEMENTED_INTRINSIC(MIPS64, UnsafeGetAndSetInt) UNIMPLEMENTED_INTRINSIC(MIPS64, UnsafeGetAndSetLong) UNIMPLEMENTED_INTRINSIC(MIPS64, UnsafeGetAndSetObject) -UNIMPLEMENTED_INTRINSIC(MIPS64, UnsafeLoadFence) -UNIMPLEMENTED_INTRINSIC(MIPS64, UnsafeStoreFence) -UNIMPLEMENTED_INTRINSIC(MIPS64, UnsafeFullFence) UNREACHABLE_INTRINSICS(MIPS64) diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc index 048590e1be..4c802c0a1a 100644 --- a/compiler/optimizing/intrinsics_x86.cc +++ b/compiler/optimizing/intrinsics_x86.cc @@ -2643,9 +2643,6 @@ UNIMPLEMENTED_INTRINSIC(X86, UnsafeGetAndAddLong) UNIMPLEMENTED_INTRINSIC(X86, UnsafeGetAndSetInt) UNIMPLEMENTED_INTRINSIC(X86, UnsafeGetAndSetLong) UNIMPLEMENTED_INTRINSIC(X86, UnsafeGetAndSetObject) -UNIMPLEMENTED_INTRINSIC(X86, UnsafeLoadFence) -UNIMPLEMENTED_INTRINSIC(X86, UnsafeStoreFence) -UNIMPLEMENTED_INTRINSIC(X86, UnsafeFullFence) UNREACHABLE_INTRINSICS(X86) diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc index 35e13a6540..41095cd485 100644 --- a/compiler/optimizing/intrinsics_x86_64.cc +++ b/compiler/optimizing/intrinsics_x86_64.cc @@ -2721,9 +2721,6 @@ UNIMPLEMENTED_INTRINSIC(X86_64, UnsafeGetAndAddLong) UNIMPLEMENTED_INTRINSIC(X86_64, UnsafeGetAndSetInt) UNIMPLEMENTED_INTRINSIC(X86_64, UnsafeGetAndSetLong) UNIMPLEMENTED_INTRINSIC(X86_64, UnsafeGetAndSetObject) -UNIMPLEMENTED_INTRINSIC(X86_64, UnsafeLoadFence) -UNIMPLEMENTED_INTRINSIC(X86_64, UnsafeStoreFence) -UNIMPLEMENTED_INTRINSIC(X86_64, UnsafeFullFence) UNREACHABLE_INTRINSICS(X86_64) diff --git a/test/004-checker-UnsafeTest18/src/Main.java b/test/004-checker-UnsafeTest18/src/Main.java index bb6de2ef28..bb020b9b9f 100644 --- a/test/004-checker-UnsafeTest18/src/Main.java +++ b/test/004-checker-UnsafeTest18/src/Main.java @@ -87,18 +87,36 @@ public class Main { /// CHECK-START: void Main.load() intrinsics_recognition (after) /// CHECK-DAG: InvokeVirtual intrinsic:UnsafeLoadFence + // + /// CHECK-START: void Main.load() instruction_simplifier (after) + /// CHECK-NOT: InvokeVirtual intrinsic:UnsafeLoadFence + // + /// CHECK-START: void Main.load() instruction_simplifier (after) + /// CHECK-DAG: MemoryBarrier kind:LoadAny private static void load() { unsafe.loadFence(); } /// CHECK-START: void Main.store() intrinsics_recognition (after) /// CHECK-DAG: InvokeVirtual intrinsic:UnsafeStoreFence + // + /// CHECK-START: void Main.store() instruction_simplifier (after) + /// CHECK-NOT: InvokeVirtual intrinsic:UnsafeStoreFence + // + /// CHECK-START: void Main.store() instruction_simplifier (after) + /// CHECK-DAG: MemoryBarrier kind:AnyStore private static void store() { unsafe.storeFence(); } /// CHECK-START: void Main.full() intrinsics_recognition (after) /// CHECK-DAG: InvokeVirtual intrinsic:UnsafeFullFence + // + /// CHECK-START: void Main.full() instruction_simplifier (after) + /// CHECK-NOT: InvokeVirtual intrinsic:UnsafeFullFence + // + /// CHECK-START: void Main.full() instruction_simplifier (after) + /// CHECK-DAG: MemoryBarrier kind:AnyAny private static void full() { unsafe.fullFence(); } -- GitLab From 29ab360433e0360bcccafb791b1231fe63914974 Mon Sep 17 00:00:00 2001 From: Hiroshi Yamauchi Date: Tue, 8 Mar 2016 15:17:21 -0800 Subject: [PATCH 164/204] Fix valgrind errors with MemMap::Sync(). This fixes valgrind-test-art-host-gtest-oat_test and one error in valgrind-test-art-host-gtest-image_test32. Valgrind doesn't like it if an address range that contains noaccess or uninitialized memory is passed to msync(). Temporarily lift the noaccess protection of the lower-end redzone because msync accepts a page-aligned base address only and exclude the higher-end noaccess redzone from the range. Bug: 27552451 Bug: 27384445 Change-Id: I8ccbd04c62eb30f6c6d5c732f1eb254fa09a417a --- runtime/mem_map.cc | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/runtime/mem_map.cc b/runtime/mem_map.cc index 11156c6229..421641ce39 100644 --- a/runtime/mem_map.cc +++ b/runtime/mem_map.cc @@ -590,7 +590,19 @@ void MemMap::MadviseDontNeedAndZero() { } bool MemMap::Sync() { - return msync(BaseBegin(), BaseSize(), MS_SYNC) == 0; + bool result; + if (redzone_size_ != 0) { + // To avoid valgrind errors, temporarily lift the lower-end noaccess protection before passing + // it to msync() as it only accepts page-aligned base address, and exclude the higher-end + // noaccess protection from the msync range. b/27552451. + uint8_t* base_begin = reinterpret_cast(base_begin_); + MEMORY_TOOL_MAKE_DEFINED(base_begin, begin_ - base_begin); + result = msync(BaseBegin(), End() - base_begin, MS_SYNC) == 0; + MEMORY_TOOL_MAKE_NOACCESS(base_begin, begin_ - base_begin); + } else { + result = msync(BaseBegin(), BaseSize(), MS_SYNC) == 0; + } + return result; } bool MemMap::Protect(int prot) { -- GitLab From 7c9c31ca3b94a8e0828d2d8f9747fd579ca40305 Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Tue, 8 Mar 2016 16:00:41 -0800 Subject: [PATCH 165/204] ART: Fix missing include The SwitchTable needs a function from an inl file. Change-Id: I624d71e0c0efc0c87150d7ef3be71e0b4506c75a --- compiler/optimizing/nodes.h | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index b684cc697f..ecb690ff62 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -26,6 +26,7 @@ #include "base/arena_object.h" #include "base/stl_util.h" #include "dex/compiler_enums.h" +#include "dex_instruction-inl.h" #include "entrypoints/quick/quick_entrypoints_enum.h" #include "handle.h" #include "handle_scope.h" -- GitLab From 0d0ce279ce1404f8b00d861b2fce123a71bb3312 Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Tue, 8 Mar 2016 20:29:49 -0800 Subject: [PATCH 166/204] ART: Enable native stack dumping on timeout To better investigate timeout in tests, allow dumping of native stacks. Bug: 27508829 Change-Id: Icf5420cde386cd80a1f45400c86545c89b1e2cc2 --- runtime/runtime_linux.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/runtime/runtime_linux.cc b/runtime/runtime_linux.cc index 8237b06a56..bc963c5b8c 100644 --- a/runtime/runtime_linux.cc +++ b/runtime/runtime_linux.cc @@ -36,6 +36,7 @@ namespace art { static constexpr bool kDumpHeapObjectOnSigsevg = false; static constexpr bool kUseSigRTTimeout = true; +static constexpr bool kDumpNativeStackOnTimeout = true; struct Backtrace { public: @@ -350,7 +351,9 @@ void HandleUnexpectedSignal(int signal_number, siginfo_t* info, void* raw_contex if (runtime != nullptr) { if (IsTimeoutSignal(signal_number)) { // Special timeout signal. Try to dump all threads. - runtime->GetThreadList()->DumpForSigQuit(LOG(INTERNAL_FATAL)); + // Note: Do not use DumpForSigQuit, as that might disable native unwind, but the native parts + // are of value here. + runtime->GetThreadList()->Dump(LOG(INTERNAL_FATAL), kDumpNativeStackOnTimeout); } gc::Heap* heap = runtime->GetHeap(); LOG(INTERNAL_FATAL) << "Fault message: " << runtime->GetFaultMessage(); -- GitLab From 3296585f5879a9563214f178f97ee689fc651758 Mon Sep 17 00:00:00 2001 From: Roland Levillain Date: Wed, 9 Mar 2016 11:18:02 +0000 Subject: [PATCH 167/204] Ignore 145-alloc-tracking-stress failures with JIT and CC. This run-test fails also with JIT compiling on the concurrent collector configuration; disable it for now. Bug: 27467554 Change-Id: I551008ff5b70c83706287039092f5c9e26584227 --- test/145-alloc-tracking-stress/src/Main.java | 1 - test/Android.run-test.mk | 4 +++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/test/145-alloc-tracking-stress/src/Main.java b/test/145-alloc-tracking-stress/src/Main.java index 752fdd9135..418690a2a6 100644 --- a/test/145-alloc-tracking-stress/src/Main.java +++ b/test/145-alloc-tracking-stress/src/Main.java @@ -1,5 +1,4 @@ /* - * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index c4f0171f0d..7036bdcaf5 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -567,7 +567,9 @@ TEST_ART_BROKEN_OPTIMIZING_READ_BARRIER_RUN_TESTS := \ 537-checker-arraycopy # Tests that should fail in the read barrier configuration with JIT (Optimizing compiler). -TEST_ART_BROKEN_JIT_READ_BARRIER_RUN_TESTS := +# 145: Test sometimes times out in read barrier configuration (b/27467554). +TEST_ART_BROKEN_JIT_READ_BARRIER_RUN_TESTS := \ + 145-alloc-tracking-stress ifeq ($(ART_USE_READ_BARRIER),true) ifneq (,$(filter interpreter,$(COMPILER_TYPES))) -- GitLab From 8b3f835f0cca5db53a727d1d77fc6c2430d53d51 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Wed, 9 Mar 2016 13:45:39 +0000 Subject: [PATCH 168/204] ART: Write bit fields together in ComputeSpecialAccessorInfo(). Avoid function calls between storing individual bit fields to allow the compiler (gcc/clang) to merge those writes together. Valgrind then marks the memory as "defined" while individual bit field writes would leave it "undefined" and later trigger the valgrind error: Conditional jump or move depends on uninitialised value(s) on DCHECK()s using the bit fields. Bug: 27552451 Change-Id: If6de5cbe231f99da0f974a0fc9a36c14e3dc071e --- runtime/quick/inline_method_analyser.cc | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/runtime/quick/inline_method_analyser.cc b/runtime/quick/inline_method_analyser.cc index 9b10f2e0b8..c7ccee2125 100644 --- a/runtime/quick/inline_method_analyser.cc +++ b/runtime/quick/inline_method_analyser.cc @@ -744,9 +744,12 @@ bool InlineMethodAnalyser::ComputeSpecialAccessorInfo(ArtMethod* method, return false; } DCHECK_GE(field->GetOffset().Int32Value(), 0); + // Do not interleave function calls with bit field writes to placate valgrind. Bug: 27552451. + uint32_t field_offset = field->GetOffset().Uint32Value(); + bool is_volatile = field->IsVolatile(); result->field_idx = field_idx; - result->field_offset = field->GetOffset().Int32Value(); - result->is_volatile = field->IsVolatile(); + result->field_offset = field_offset; + result->is_volatile = is_volatile ? 1u : 0u; return true; } -- GitLab From f97cf2a7d8089ca74a4920a5e0c351e070cc6e60 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Wed, 9 Mar 2016 15:36:23 +0000 Subject: [PATCH 169/204] Stop oat loading spam on host. Change-Id: I336669f49bfbfc76bb5e9d3b4f1738709e070b27 --- runtime/oat_file.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc index c3895479b7..cfecee67b9 100644 --- a/runtime/oat_file.cc +++ b/runtime/oat_file.cc @@ -169,7 +169,10 @@ bool OatFileBase::ComputeFields(uint8_t* requested_base, return false; } if (requested_base != nullptr && begin_ != requested_base) { - PrintFileToLog("/proc/self/maps", LogSeverity::WARNING); + // Host can fail this check. Do not dump there to avoid polluting the output. + if (kIsTargetBuild) { + PrintFileToLog("/proc/self/maps", LogSeverity::WARNING); + } *error_msg = StringPrintf("Failed to find oatdata symbol at expected address: " "oatdata=%p != expected=%p. See process maps in the log.", begin_, requested_base); -- GitLab From f969a209c30e3af636342d2fb7851d82a2529bf7 Mon Sep 17 00:00:00 2001 From: Roland Levillain Date: Wed, 9 Mar 2016 16:14:00 +0000 Subject: [PATCH 170/204] Fix and enable java.lang.StringFactory intrinsics. The following intrinsics were not considered by the intrinsics recognizer: - StringNewStringFromBytes - StringNewStringFromChars - StringNewStringFromString This CL enables them and add tests for them. This CL also: - Fixes the locations of the ARM64 & MIPS64 StringNewStringFromString intrinsics. - Fixes the definitions of the FOUR_ARG_DOWNCALL macros on ARM and x86, which are used to implement the art_quick_alloc_string_from_bytes* runtime entry points. - Fixes PC info (stack maps) recording in the StringNewStringFromBytes, StringNewStringFromChars and StringNewStringFromString ARM, ARM64 & MIPS64 intrinsics. Bug: 27425743 Change-Id: I38c00d3f0b2e6b64f7d3fe9146743493bef9e45c --- compiler/dex/quick/dex_file_method_inliner.cc | 7 ++ compiler/optimizing/intrinsics_arm.cc | 9 +- compiler/optimizing/intrinsics_arm64.cc | 12 +-- compiler/optimizing/intrinsics_mips64.cc | 13 +-- compiler/optimizing/intrinsics_x86.cc | 3 + compiler/optimizing/intrinsics_x86_64.cc | 3 + runtime/arch/arm/quick_entrypoints_arm.S | 13 ++- runtime/arch/x86/asm_support_x86.S | 4 + runtime/arch/x86/quick_entrypoints_x86.S | 10 +++ .../expected.txt | 3 + .../info.txt | 1 + .../src/Main.java | 83 +++++++++++++++++++ 12 files changed, 144 insertions(+), 17 deletions(-) create mode 100644 test/580-checker-string-factory-intrinsics/expected.txt create mode 100644 test/580-checker-string-factory-intrinsics/info.txt create mode 100644 test/580-checker-string-factory-intrinsics/src/Main.java diff --git a/compiler/dex/quick/dex_file_method_inliner.cc b/compiler/dex/quick/dex_file_method_inliner.cc index 8f5d3ae4bd..48c4356cfd 100644 --- a/compiler/dex/quick/dex_file_method_inliner.cc +++ b/compiler/dex/quick/dex_file_method_inliner.cc @@ -605,6 +605,13 @@ const DexFileMethodInliner::IntrinsicDef DexFileMethodInliner::kIntrinsicMethods INTRINSIC(JavaLangString, IndexOf, I_I, kIntrinsicIndexOf, kIntrinsicFlagBase0), INTRINSIC(JavaLangString, Length, _I, kIntrinsicIsEmptyOrLength, kIntrinsicFlagLength), + INTRINSIC(JavaLangStringFactory, NewStringFromBytes, ByteArrayIII_String, + kIntrinsicNewStringFromBytes, kIntrinsicFlagNone), + INTRINSIC(JavaLangStringFactory, NewStringFromChars, IICharArray_String, + kIntrinsicNewStringFromChars, kIntrinsicFlagNone), + INTRINSIC(JavaLangStringFactory, NewStringFromString, String_String, + kIntrinsicNewStringFromString, kIntrinsicFlagNone), + INTRINSIC(JavaLangThread, CurrentThread, _Thread, kIntrinsicCurrentThread, 0), INTRINSIC(LibcoreIoMemory, PeekByte, J_B, kIntrinsicPeek, kSignedByte), diff --git a/compiler/optimizing/intrinsics_arm.cc b/compiler/optimizing/intrinsics_arm.cc index b599d42c4b..34ed575bd0 100644 --- a/compiler/optimizing/intrinsics_arm.cc +++ b/compiler/optimizing/intrinsics_arm.cc @@ -1224,8 +1224,9 @@ void IntrinsicCodeGeneratorARM::VisitStringNewStringFromBytes(HInvoke* invoke) { __ LoadFromOffset( kLoadWord, LR, TR, QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pAllocStringFromBytes).Int32Value()); - codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); + CheckEntrypointTypes(); __ blx(LR); + codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); __ Bind(slow_path->GetExitLabel()); } @@ -1251,8 +1252,9 @@ void IntrinsicCodeGeneratorARM::VisitStringNewStringFromChars(HInvoke* invoke) { // all include a null check on `data` before calling that method. __ LoadFromOffset( kLoadWord, LR, TR, QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pAllocStringFromChars).Int32Value()); - codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); + CheckEntrypointTypes(); __ blx(LR); + codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); } void IntrinsicLocationsBuilderARM::VisitStringNewStringFromString(HInvoke* invoke) { @@ -1276,8 +1278,9 @@ void IntrinsicCodeGeneratorARM::VisitStringNewStringFromString(HInvoke* invoke) __ LoadFromOffset(kLoadWord, LR, TR, QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pAllocStringFromString).Int32Value()); - codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); + CheckEntrypointTypes(); __ blx(LR); + codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); __ Bind(slow_path->GetExitLabel()); } diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc index ccbbd43258..b65445766f 100644 --- a/compiler/optimizing/intrinsics_arm64.cc +++ b/compiler/optimizing/intrinsics_arm64.cc @@ -1409,8 +1409,9 @@ void IntrinsicCodeGeneratorARM64::VisitStringNewStringFromBytes(HInvoke* invoke) __ Ldr(lr, MemOperand(tr, QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, pAllocStringFromBytes).Int32Value())); - codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); + CheckEntrypointTypes(); __ Blr(lr); + codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); __ Bind(slow_path->GetExitLabel()); } @@ -1436,19 +1437,17 @@ void IntrinsicCodeGeneratorARM64::VisitStringNewStringFromChars(HInvoke* invoke) // all include a null check on `data` before calling that method. __ Ldr(lr, MemOperand(tr, QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, pAllocStringFromChars).Int32Value())); - codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); + CheckEntrypointTypes(); __ Blr(lr); + codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); } void IntrinsicLocationsBuilderARM64::VisitStringNewStringFromString(HInvoke* invoke) { - // The inputs plus one temp. LocationSummary* locations = new (arena_) LocationSummary(invoke, LocationSummary::kCall, kIntrinsified); InvokeRuntimeCallingConvention calling_convention; locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0))); - locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1))); - locations->SetInAt(2, LocationFrom(calling_convention.GetRegisterAt(2))); locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimNot)); } @@ -1464,8 +1463,9 @@ void IntrinsicCodeGeneratorARM64::VisitStringNewStringFromString(HInvoke* invoke __ Ldr(lr, MemOperand(tr, QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, pAllocStringFromString).Int32Value())); - codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); + CheckEntrypointTypes(); __ Blr(lr); + codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); __ Bind(slow_path->GetExitLabel()); } diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc index 83dff33ec0..33096cac09 100644 --- a/compiler/optimizing/intrinsics_mips64.cc +++ b/compiler/optimizing/intrinsics_mips64.cc @@ -1590,9 +1590,10 @@ void IntrinsicCodeGeneratorMIPS64::VisitStringNewStringFromBytes(HInvoke* invoke TR, QUICK_ENTRYPOINT_OFFSET(kMips64DoublewordSize, pAllocStringFromBytes).Int32Value()); - codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); + CheckEntrypointTypes(); __ Jalr(TMP); __ Nop(); + codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); __ Bind(slow_path->GetExitLabel()); } @@ -1623,20 +1624,19 @@ void IntrinsicCodeGeneratorMIPS64::VisitStringNewStringFromChars(HInvoke* invoke TR, QUICK_ENTRYPOINT_OFFSET(kMips64DoublewordSize, pAllocStringFromChars).Int32Value()); - codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); + CheckEntrypointTypes(); __ Jalr(TMP); __ Nop(); + codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); } -// java.lang.String.String(String original) +// java.lang.StringFactory.newStringFromString(String toCopy) void IntrinsicLocationsBuilderMIPS64::VisitStringNewStringFromString(HInvoke* invoke) { LocationSummary* locations = new (arena_) LocationSummary(invoke, LocationSummary::kCall, kIntrinsified); InvokeRuntimeCallingConvention calling_convention; locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); - locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); - locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2))); Location outLocation = calling_convention.GetReturnLocation(Primitive::kPrimInt); locations->SetOut(Location::RegisterLocation(outLocation.AsRegister())); } @@ -1655,9 +1655,10 @@ void IntrinsicCodeGeneratorMIPS64::VisitStringNewStringFromString(HInvoke* invok TR, QUICK_ENTRYPOINT_OFFSET(kMips64DoublewordSize, pAllocStringFromString).Int32Value()); - codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); + CheckEntrypointTypes(); __ Jalr(TMP); __ Nop(); + codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); __ Bind(slow_path->GetExitLabel()); } diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc index 048590e1be..065866396b 100644 --- a/compiler/optimizing/intrinsics_x86.cc +++ b/compiler/optimizing/intrinsics_x86.cc @@ -1546,6 +1546,7 @@ void IntrinsicCodeGeneratorX86::VisitStringNewStringFromBytes(HInvoke* invoke) { __ j(kEqual, slow_path->GetEntryLabel()); __ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pAllocStringFromBytes))); + CheckEntrypointTypes(); codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); __ Bind(slow_path->GetExitLabel()); } @@ -1571,6 +1572,7 @@ void IntrinsicCodeGeneratorX86::VisitStringNewStringFromChars(HInvoke* invoke) { // // all include a null check on `data` before calling that method. __ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pAllocStringFromChars))); + CheckEntrypointTypes(); codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); } @@ -1594,6 +1596,7 @@ void IntrinsicCodeGeneratorX86::VisitStringNewStringFromString(HInvoke* invoke) __ j(kEqual, slow_path->GetEntryLabel()); __ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pAllocStringFromString))); + CheckEntrypointTypes(); codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); __ Bind(slow_path->GetExitLabel()); } diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc index 35e13a6540..13a4bd08af 100644 --- a/compiler/optimizing/intrinsics_x86_64.cc +++ b/compiler/optimizing/intrinsics_x86_64.cc @@ -1641,6 +1641,7 @@ void IntrinsicCodeGeneratorX86_64::VisitStringNewStringFromBytes(HInvoke* invoke __ gs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pAllocStringFromBytes), /* no_rip */ true)); + CheckEntrypointTypes(); codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); __ Bind(slow_path->GetExitLabel()); } @@ -1667,6 +1668,7 @@ void IntrinsicCodeGeneratorX86_64::VisitStringNewStringFromChars(HInvoke* invoke // all include a null check on `data` before calling that method. __ gs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pAllocStringFromChars), /* no_rip */ true)); + CheckEntrypointTypes(); codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); } @@ -1691,6 +1693,7 @@ void IntrinsicCodeGeneratorX86_64::VisitStringNewStringFromString(HInvoke* invok __ gs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pAllocStringFromString), /* no_rip */ true)); + CheckEntrypointTypes(); codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); __ Bind(slow_path->GetExitLabel()); } diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S index cfcef49084..f33eebed94 100644 --- a/runtime/arch/arm/quick_entrypoints_arm.S +++ b/runtime/arch/arm/quick_entrypoints_arm.S @@ -276,7 +276,7 @@ ENTRY \name bl \entrypoint @ (field_idx, Object*, new_val, referrer, Thread*) add sp, #16 @ release out args .cfi_adjust_cfa_offset -16 - RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME @ TODO: we can clearly save an add here + RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME @ TODO: we can clearly save an add here \return END \name .endm @@ -812,14 +812,23 @@ END \name .macro FOUR_ARG_DOWNCALL name, entrypoint, return .extern \entrypoint ENTRY \name + sub sp, #12 @ alignment padding + .cfi_adjust_cfa_offset 12 + push {r3} @ Save r3 as is it used as a temp register in the + .cfi_adjust_cfa_offset 4 @ expansion of the SETUP_REFS_ONLY_CALLEE_SAVE_FRAME + .cfi_rel_offset r3, 0 @ macro below, which clobbers its arguments. SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r3, r12 @ save callee saves in case of GC + ldr r3, [sp, 32] @ restore r3 + .cfi_restore r3 + str r9, [sp, #-16]! @ expand the frame and pass Thread::Current - .pad #16 .cfi_adjust_cfa_offset 16 bl \entrypoint add sp, #16 @ strip the extra frame .cfi_adjust_cfa_offset -16 RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME + add sp, #16 @ pop r3 + padding + .cfi_adjust_cfa_offset -16 \return END \name .endm diff --git a/runtime/arch/x86/asm_support_x86.S b/runtime/arch/x86/asm_support_x86.S index 77b8e87c99..3e47209afb 100644 --- a/runtime/arch/x86/asm_support_x86.S +++ b/runtime/arch/x86/asm_support_x86.S @@ -142,6 +142,10 @@ MACRO1(POP, reg) CFI_RESTORE(REG_VAR(reg)) END_MACRO +MACRO1(CFI_RESTORE_REG, reg) + CFI_RESTORE(REG_VAR(reg)) +END_MACRO + #define UNREACHABLE int3 MACRO1(UNIMPLEMENTED,name) diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S index fbee5d7724..4be00ce523 100644 --- a/runtime/arch/x86/quick_entrypoints_x86.S +++ b/runtime/arch/x86/quick_entrypoints_x86.S @@ -686,7 +686,15 @@ END_MACRO MACRO3(FOUR_ARG_DOWNCALL, c_name, cxx_name, return_macro) DEFINE_FUNCTION VAR(c_name) + subl MACRO_LITERAL(12), %esp // alignment padding + CFI_ADJUST_CFA_OFFSET(12) + PUSH ebx // Save ebx as the expansion of the + // SETUP_REFS_ONLY_CALLEE_SAVE_FRAME + // macro below clobbers it. SETUP_REFS_ONLY_CALLEE_SAVE_FRAME ebx, ebx // save ref containing registers for GC + movl 28(%esp), %ebx // restore ebx + CFI_RESTORE_REG ebx + // Outgoing argument set up subl MACRO_LITERAL(12), %esp // alignment padding CFI_ADJUST_CFA_OFFSET(12) @@ -700,6 +708,8 @@ MACRO3(FOUR_ARG_DOWNCALL, c_name, cxx_name, return_macro) addl MACRO_LITERAL(32), %esp // pop arguments CFI_ADJUST_CFA_OFFSET(-32) RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address + addl MACRO_LITERAL(16), %esp // pop ebx + padding + CFI_ADJUST_CFA_OFFSET(-16) CALL_MACRO(return_macro) // return or deliver exception END_FUNCTION VAR(c_name) END_MACRO diff --git a/test/580-checker-string-factory-intrinsics/expected.txt b/test/580-checker-string-factory-intrinsics/expected.txt new file mode 100644 index 0000000000..86e041dad6 --- /dev/null +++ b/test/580-checker-string-factory-intrinsics/expected.txt @@ -0,0 +1,3 @@ +foo +bar +baz diff --git a/test/580-checker-string-factory-intrinsics/info.txt b/test/580-checker-string-factory-intrinsics/info.txt new file mode 100644 index 0000000000..3d01a1964a --- /dev/null +++ b/test/580-checker-string-factory-intrinsics/info.txt @@ -0,0 +1 @@ +Ensure java.lang.StringFactory intrinsics are recognized and used. diff --git a/test/580-checker-string-factory-intrinsics/src/Main.java b/test/580-checker-string-factory-intrinsics/src/Main.java new file mode 100644 index 0000000000..a2e34bffd0 --- /dev/null +++ b/test/580-checker-string-factory-intrinsics/src/Main.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Main { + + /// CHECK-START: void Main.testNewStringFromBytes() builder (after) + /// CHECK-DAG: InvokeStaticOrDirect method_name:java.lang.StringFactory.newStringFromBytes intrinsic:None + + /// CHECK-START: void Main.testNewStringFromBytes() intrinsics_recognition (after) + /// CHECK-DAG: InvokeStaticOrDirect method_name:java.lang.StringFactory.newStringFromBytes intrinsic:StringNewStringFromBytes + + public static void testNewStringFromBytes() { + byte[] bytes = { 'f', 'o', 'o' }; + String s = StringFactory.newStringFromBytes(bytes, 0, 0, 3); + System.out.println(s); + } + + // The (native) method + // + // java.lang.StringFactory.newStringFromChars(int offset, int charCount, char[] data) + // + // is recognized as intrinsic StringNewStringFromChars. However, + // because this method is not public, we cannot call it and check + // that the compiler actually intrinsifies it (as it does for the + // StringNewStringFromBytes and StringNewStringFromString + // intrinsics) with Checker. + // + // We can call a public method such as + // + // java.lang.StringFactory.newStringFromChars(char[] data) + // + // which contains a call to the former (non-public) native method. + // However, this call will not be inlined (because it is a method in + // another Dex file and which contains a call, which needs an + // environment), so we cannot use Checker here to ensure the native + // call was intrinsified either. + + /// CHECK-START: void Main.testNewStringFromChars() builder (after) + /// CHECK-DAG: InvokeStaticOrDirect method_name:java.lang.StringFactory.newStringFromChars intrinsic:None + + /// CHECK-START: void Main.testNewStringFromChars() intrinsics_recognition (after) + /// CHECK-DAG: InvokeStaticOrDirect method_name:java.lang.StringFactory.newStringFromChars intrinsic:None + + /// CHECK-START: void Main.testNewStringFromChars() inliner (after) + /// CHECK-DAG: InvokeStaticOrDirect method_name:java.lang.StringFactory.newStringFromChars intrinsic:None + + public static void testNewStringFromChars() { + char[] chars = { 'b', 'a', 'r' }; + String s = StringFactory.newStringFromChars(chars); + System.out.println(s); + } + + /// CHECK-START: void Main.testNewStringFromString() builder (after) + /// CHECK-DAG: InvokeStaticOrDirect method_name:java.lang.StringFactory.newStringFromString intrinsic:None + + /// CHECK-START: void Main.testNewStringFromString() intrinsics_recognition (after) + /// CHECK-DAG: InvokeStaticOrDirect method_name:java.lang.StringFactory.newStringFromString intrinsic:StringNewStringFromString + + public static void testNewStringFromString() { + String s1 = "baz"; + String s2 = StringFactory.newStringFromString(s1); + System.out.println(s2); + } + + public static void main(String[] args) throws Exception { + testNewStringFromBytes(); + testNewStringFromChars(); + testNewStringFromString(); + } +} -- GitLab From 3f41a0193eadf037b4003c1996151f386ca07b13 Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Thu, 18 Feb 2016 16:53:41 -0800 Subject: [PATCH 171/204] ART: Use optimizing compiler in image_test Expose some compiler options so we can run the image test with the optimizing compiler without running into a stack overflow. Also allow a variable amount of threads in CreateCompilerDriver. Use 16 as a middle ground on the host to speed up the otherwise now slowed-down test. Bug: 27240085 Bug: 27552475 Change-Id: I8db5055d32ae722c8f430903244faa9166cc4886 --- compiler/common_compiler_test.cc | 6 ++++-- compiler/common_compiler_test.h | 2 +- compiler/driver/compiler_options.h | 6 ++++++ compiler/image_test.cc | 9 +++++++-- runtime/common_runtime_test.cc | 1 + 5 files changed, 19 insertions(+), 5 deletions(-) diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc index 239bc590e9..6075cd6fbe 100644 --- a/compiler/common_compiler_test.cc +++ b/compiler/common_compiler_test.cc @@ -187,7 +187,9 @@ void CommonCompilerTest::SetUp() { } } -void CommonCompilerTest::CreateCompilerDriver(Compiler::Kind kind, InstructionSet isa) { +void CommonCompilerTest::CreateCompilerDriver(Compiler::Kind kind, + InstructionSet isa, + size_t number_of_threads) { compiler_driver_.reset(new CompilerDriver(compiler_options_.get(), verification_results_.get(), method_inliner_map_.get(), @@ -198,7 +200,7 @@ void CommonCompilerTest::CreateCompilerDriver(Compiler::Kind kind, InstructionSe GetImageClasses(), GetCompiledClasses(), GetCompiledMethods(), - /* thread_count */ 2, + number_of_threads, /* dump_stats */ true, /* dump_passes */ true, timer_.get(), diff --git a/compiler/common_compiler_test.h b/compiler/common_compiler_test.h index 7e0fbabff8..9552143080 100644 --- a/compiler/common_compiler_test.h +++ b/compiler/common_compiler_test.h @@ -93,7 +93,7 @@ class CommonCompilerTest : public CommonRuntimeTest { const char* method_name, const char* signature) SHARED_REQUIRES(Locks::mutator_lock_); - void CreateCompilerDriver(Compiler::Kind kind, InstructionSet isa); + void CreateCompilerDriver(Compiler::Kind kind, InstructionSet isa, size_t number_of_threads = 2U); void ReserveImageSpace(); diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h index a220959288..4db82a638d 100644 --- a/compiler/driver/compiler_options.h +++ b/compiler/driver/compiler_options.h @@ -159,10 +159,16 @@ class CompilerOptions FINAL { size_t GetInlineDepthLimit() const { return inline_depth_limit_; } + void SetInlineDepthLimit(size_t limit) { + inline_depth_limit_ = limit; + } size_t GetInlineMaxCodeUnits() const { return inline_max_code_units_; } + void SetInlineMaxCodeUnits(size_t units) { + inline_max_code_units_ = units; + } double GetTopKProfileThreshold() const { return top_k_profile_threshold_; diff --git a/compiler/image_test.cc b/compiler/image_test.cc index 3b622b5451..91579e9daf 100644 --- a/compiler/image_test.cc +++ b/compiler/image_test.cc @@ -24,6 +24,7 @@ #include "class_linker-inl.h" #include "common_compiler_test.h" #include "debug/method_debug_info.h" +#include "driver/compiler_options.h" #include "elf_writer.h" #include "elf_writer_quick.h" #include "gc/space/image_space.h" @@ -48,8 +49,12 @@ class ImageTest : public CommonCompilerTest { }; void ImageTest::TestWriteRead(ImageHeader::StorageMode storage_mode) { - // TODO: Test does not currently work with optimizing. - CreateCompilerDriver(Compiler::kQuick, kRuntimeISA); + CreateCompilerDriver(Compiler::kOptimizing, kRuntimeISA, kIsTargetBuild ? 2U : 16U); + + // Set inline filter values. + compiler_options_->SetInlineDepthLimit(CompilerOptions::kDefaultInlineDepthLimit); + compiler_options_->SetInlineMaxCodeUnits(CompilerOptions::kDefaultInlineMaxCodeUnits); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); // Enable write for dex2dex. for (const DexFile* dex_file : class_linker->GetBootClassPath()) { diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc index 3df9101613..729957f318 100644 --- a/runtime/common_runtime_test.cc +++ b/runtime/common_runtime_test.cc @@ -406,6 +406,7 @@ void CommonRuntimeTestImpl::TearDown() { int rmdir_cache_result = rmdir(dalvik_cache_.c_str()); ASSERT_EQ(0, rmdir_cache_result); TearDownAndroidData(android_data_, true); + dalvik_cache_.clear(); // icu4c has a fixed 10-element array "gCommonICUDataArray". // If we run > 10 tests, we fill that array and u_setCommonData fails. -- GitLab From b6e20ae17d0881a66c22532e4152ce6779454a92 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Mon, 7 Mar 2016 14:29:04 +0000 Subject: [PATCH 172/204] Clear inline caches at each full GC. This fixes occasional failures of 141-class-unload. Also fix a bug where clearing inline caches also cleared the dex pc associated with it. bug:26846185 bug:23128949 Change-Id: I77bf1dee229d7764c3cc21440829c7fba7b37001 --- compiler/jit/jit_compiler.cc | 7 +++- compiler/optimizing/inliner.cc | 63 +++++++++++++++++++---------- runtime/gc/heap.cc | 8 ++++ runtime/jit/jit_code_cache.cc | 29 ++++++++++++- runtime/jit/jit_code_cache.h | 10 +++++ runtime/jit/profiling_info.cc | 4 +- runtime/jit/profiling_info.h | 30 ++++++++++++-- test/141-class-unload/src/Main.java | 1 + 8 files changed, 122 insertions(+), 30 deletions(-) diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc index 6ff1e2e95e..eeb25763a7 100644 --- a/compiler/jit/jit_compiler.cc +++ b/compiler/jit/jit_compiler.cc @@ -168,13 +168,14 @@ JitCompiler::JitCompiler() { compiler_driver_->SetDedupeEnabled(false); compiler_driver_->SetSupportBootImageFixup(false); + size_t thread_count = compiler_driver_->GetThreadCount(); if (compiler_options_->GetGenerateDebugInfo()) { #ifdef __ANDROID__ const char* prefix = "/data/misc/trace"; #else const char* prefix = "/tmp"; #endif - DCHECK_EQ(compiler_driver_->GetThreadCount(), 1u) + DCHECK_EQ(thread_count, 1u) << "Generating debug info only works with one compiler thread"; std::string perf_filename = std::string(prefix) + "/perf-" + std::to_string(getpid()) + ".map"; perf_file_.reset(OS::CreateEmptyFileWriteOnly(perf_filename.c_str())); @@ -183,6 +184,10 @@ JitCompiler::JitCompiler() { " Are you on a user build? Perf only works on userdebug/eng builds"; } } + + size_t inline_depth_limit = compiler_driver_->GetCompilerOptions().GetInlineDepthLimit(); + DCHECK_LT(thread_count * inline_depth_limit, std::numeric_limits::max()) + << "ProfilingInfo's inline counter can potentially overflow"; } JitCompiler::~JitCompiler() { diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index 3e3719e6ea..bbdac262c4 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -28,6 +28,8 @@ #include "driver/dex_compilation_unit.h" #include "instruction_simplifier.h" #include "intrinsics.h" +#include "jit/jit.h" +#include "jit/jit_code_cache.h" #include "mirror/class_loader.h" #include "mirror/dex_cache.h" #include "nodes.h" @@ -220,6 +222,20 @@ static uint32_t FindClassIndexIn(mirror::Class* cls, return index; } +class ScopedProfilingInfoInlineUse { + public: + explicit ScopedProfilingInfoInlineUse(ArtMethod* method) : method_(method) { + Runtime::Current()->GetJit()->GetCodeCache()->NotifyInliningOf(method_, Thread::Current()); + } + + ~ScopedProfilingInfoInlineUse() { + Runtime::Current()->GetJit()->GetCodeCache()->DoneInlining(method_, Thread::Current()); + } + + private: + ArtMethod* const method_; +}; + bool HInliner::TryInline(HInvoke* invoke_instruction) { if (invoke_instruction->IsInvokeUnresolved()) { return false; // Don't bother to move further if we know the method is unresolved. @@ -272,29 +288,32 @@ bool HInliner::TryInline(HInvoke* invoke_instruction) { // Check if we can use an inline cache. ArtMethod* caller = graph_->GetArtMethod(); size_t pointer_size = class_linker->GetImagePointerSize(); - // Under JIT, we should always know the caller. - DCHECK(!Runtime::Current()->UseJit() || (caller != nullptr)); - if (caller != nullptr && caller->GetProfilingInfo(pointer_size) != nullptr) { + if (Runtime::Current()->UseJit()) { + // Under JIT, we should always know the caller. + DCHECK(caller != nullptr); + ScopedProfilingInfoInlineUse spiis(caller); ProfilingInfo* profiling_info = caller->GetProfilingInfo(pointer_size); - const InlineCache& ic = *profiling_info->GetInlineCache(invoke_instruction->GetDexPc()); - if (ic.IsUnitialized()) { - VLOG(compiler) << "Interface or virtual call to " - << PrettyMethod(method_index, caller_dex_file) - << " is not hit and not inlined"; - return false; - } else if (ic.IsMonomorphic()) { - MaybeRecordStat(kMonomorphicCall); - return TryInlineMonomorphicCall(invoke_instruction, resolved_method, ic); - } else if (ic.IsPolymorphic()) { - MaybeRecordStat(kPolymorphicCall); - return TryInlinePolymorphicCall(invoke_instruction, resolved_method, ic); - } else { - DCHECK(ic.IsMegamorphic()); - VLOG(compiler) << "Interface or virtual call to " - << PrettyMethod(method_index, caller_dex_file) - << " is megamorphic and not inlined"; - MaybeRecordStat(kMegamorphicCall); - return false; + if (profiling_info != nullptr) { + const InlineCache& ic = *profiling_info->GetInlineCache(invoke_instruction->GetDexPc()); + if (ic.IsUnitialized()) { + VLOG(compiler) << "Interface or virtual call to " + << PrettyMethod(method_index, caller_dex_file) + << " is not hit and not inlined"; + return false; + } else if (ic.IsMonomorphic()) { + MaybeRecordStat(kMonomorphicCall); + return TryInlineMonomorphicCall(invoke_instruction, resolved_method, ic); + } else if (ic.IsPolymorphic()) { + MaybeRecordStat(kPolymorphicCall); + return TryInlinePolymorphicCall(invoke_instruction, resolved_method, ic); + } else { + DCHECK(ic.IsMegamorphic()); + VLOG(compiler) << "Interface or virtual call to " + << PrettyMethod(method_index, caller_dex_file) + << " is megamorphic and not inlined"; + MaybeRecordStat(kMegamorphicCall); + return false; + } } } diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index 3480483c34..01dff190f0 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -59,6 +59,8 @@ #include "heap-inl.h" #include "image.h" #include "intern_table.h" +#include "jit/jit.h" +#include "jit/jit_code_cache.h" #include "mirror/class-inl.h" #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" @@ -2669,6 +2671,12 @@ collector::GcType Heap::CollectGarbageInternal(collector::GcType gc_type, // permanantly disabled. b/17942071 concurrent_start_bytes_ = std::numeric_limits::max(); } + + if ((gc_type == collector::kGcTypeFull) && runtime->UseJit()) { + // It's time to clear all inline caches, in case some classes can be unloaded. + runtime->GetJit()->GetCodeCache()->ClearGcRootsInInlineCaches(self); + } + CHECK(collector != nullptr) << "Could not find garbage collector with collector_type=" << static_cast(collector_type_) << " and gc_type=" << gc_type; diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index 1545cb7f01..4f87e5bab5 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -293,6 +293,15 @@ void JitCodeCache::RemoveMethodsIn(Thread* self, const LinearAlloc& alloc) { } } +void JitCodeCache::ClearGcRootsInInlineCaches(Thread* self) { + MutexLock mu(self, lock_); + for (ProfilingInfo* info : profiling_infos_) { + if (!info->IsInUseByCompiler()) { + info->ClearGcRootsInInlineCaches(); + } + } +} + uint8_t* JitCodeCache::CommitCodeInternal(Thread* self, ArtMethod* method, const uint8_t* mapping_table, @@ -675,7 +684,7 @@ void JitCodeCache::DoCollection(Thread* self, bool collect_profiling_info) { // Also remove the saved entry point from the ProfilingInfo objects. for (ProfilingInfo* info : profiling_infos_) { const void* ptr = info->GetMethod()->GetEntryPointFromQuickCompiledCode(); - if (!ContainsPc(ptr) && !info->IsMethodBeingCompiled()) { + if (!ContainsPc(ptr) && !info->IsInUseByCompiler()) { info->GetMethod()->SetProfilingInfo(nullptr); } info->SetSavedEntryPoint(nullptr); @@ -727,7 +736,7 @@ void JitCodeCache::DoCollection(Thread* self, bool collect_profiling_info) { // code cache collection. if (ContainsPc(ptr) && info->GetMethod()->GetProfilingInfo(sizeof(void*)) == nullptr) { // We clear the inline caches as classes in it might be stalled. - info->ClearInlineCaches(); + info->ClearGcRootsInInlineCaches(); // Do a fence to make sure the clearing is seen before attaching to the method. QuasiAtomic::ThreadFenceRelease(); info->GetMethod()->SetProfilingInfo(info); @@ -915,6 +924,22 @@ bool JitCodeCache::NotifyCompilationOf(ArtMethod* method, Thread* self, bool osr return true; } +void JitCodeCache::NotifyInliningOf(ArtMethod* method, Thread* self) { + MutexLock mu(self, lock_); + ProfilingInfo* info = method->GetProfilingInfo(sizeof(void*)); + if (info != nullptr) { + info->IncrementInlineUse(); + } +} + +void JitCodeCache::DoneInlining(ArtMethod* method, Thread* self) { + MutexLock mu(self, lock_); + ProfilingInfo* info = method->GetProfilingInfo(sizeof(void*)); + if (info != nullptr) { + info->DecrementInlineUse(); + } +} + void JitCodeCache::DoneCompiling(ArtMethod* method, Thread* self ATTRIBUTE_UNUSED) { ProfilingInfo* info = method->GetProfilingInfo(sizeof(void*)); DCHECK(info->IsMethodBeingCompiled()); diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h index 0bd4f7dd1b..113bebfa65 100644 --- a/runtime/jit/jit_code_cache.h +++ b/runtime/jit/jit_code_cache.h @@ -71,10 +71,18 @@ class JitCodeCache { SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!lock_); + void NotifyInliningOf(ArtMethod* method, Thread* self) + SHARED_REQUIRES(Locks::mutator_lock_) + REQUIRES(!lock_); + void DoneCompiling(ArtMethod* method, Thread* self) SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!lock_); + void DoneInlining(ArtMethod* method, Thread* self) + SHARED_REQUIRES(Locks::mutator_lock_) + REQUIRES(!lock_); + // Allocate and write code and its metadata to the code cache. uint8_t* CommitCode(Thread* self, ArtMethod* method, @@ -143,6 +151,8 @@ class JitCodeCache { REQUIRES(Locks::classlinker_classes_lock_) SHARED_REQUIRES(Locks::mutator_lock_); + void ClearGcRootsInInlineCaches(Thread* self) REQUIRES(!lock_); + // Create a 'ProfileInfo' for 'method'. If 'retry_allocation' is true, // will collect and retry if the first allocation is unsuccessful. ProfilingInfo* AddProfilingInfo(Thread* self, diff --git a/runtime/jit/profiling_info.cc b/runtime/jit/profiling_info.cc index 3820592c4c..07c8051214 100644 --- a/runtime/jit/profiling_info.cc +++ b/runtime/jit/profiling_info.cc @@ -97,8 +97,8 @@ void ProfilingInfo::AddInvokeInfo(uint32_t dex_pc, mirror::Class* cls) { } } } - // Unsuccessfull - cache is full, making it megamorphic. - DCHECK(cache->IsMegamorphic()); + // Unsuccessfull - cache is full, making it megamorphic. We do not DCHECK it though, + // as the garbage collector might clear the entries concurrently. } } // namespace art diff --git a/runtime/jit/profiling_info.h b/runtime/jit/profiling_info.h index a8c056c7c9..73c1a1edb0 100644 --- a/runtime/jit/profiling_info.h +++ b/runtime/jit/profiling_info.h @@ -134,8 +134,27 @@ class ProfilingInfo { return saved_entry_point_; } - void ClearInlineCaches() { - memset(&cache_, 0, number_of_inline_caches_ * sizeof(InlineCache)); + void ClearGcRootsInInlineCaches() { + for (size_t i = 0; i < number_of_inline_caches_; ++i) { + InlineCache* cache = &cache_[i]; + memset(&cache->classes_[0], + 0, + InlineCache::kIndividualCacheSize * sizeof(GcRoot)); + } + } + + void IncrementInlineUse() { + DCHECK_NE(current_inline_uses_, std::numeric_limits::max()); + current_inline_uses_++; + } + + void DecrementInlineUse() { + DCHECK_GT(current_inline_uses_, 0); + current_inline_uses_--; + } + + bool IsInUseByCompiler() const { + return IsMethodBeingCompiled() || (current_inline_uses_ > 0); } private: @@ -143,8 +162,9 @@ class ProfilingInfo { : number_of_inline_caches_(entries.size()), method_(method), is_method_being_compiled_(false), + current_inline_uses_(0), saved_entry_point_(nullptr) { - ClearInlineCaches(); + memset(&cache_, 0, number_of_inline_caches_ * sizeof(InlineCache)); for (size_t i = 0; i < number_of_inline_caches_; ++i) { cache_[i].dex_pc_ = entries[i]; } @@ -161,6 +181,10 @@ class ProfilingInfo { // TODO: Make the JIT code cache lock global. bool is_method_being_compiled_; + // When the compiler inlines the method associated to this ProfilingInfo, + // it updates this counter so that the GC does not try to clear the inline caches. + uint16_t current_inline_uses_; + // Entry point of the corresponding ArtMethod, while the JIT code cache // is poking for the liveness of compiled code. const void* saved_entry_point_; diff --git a/test/141-class-unload/src/Main.java b/test/141-class-unload/src/Main.java index bcb697a396..15683b0b1e 100644 --- a/test/141-class-unload/src/Main.java +++ b/test/141-class-unload/src/Main.java @@ -181,6 +181,7 @@ public class Main { Class intHolder = loader.loadClass("IntHolder"); Method loadLibrary = intHolder.getDeclaredMethod("loadLibrary", String.class); loadLibrary.invoke(intHolder, nativeLibraryName); + waitForCompilation(intHolder); return new WeakReference(loader); } -- GitLab From 8b8f6d6e85f46718b6865c962614d3d34fd33db0 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Tue, 8 Mar 2016 16:50:20 -0800 Subject: [PATCH 173/204] Use per character suffix for multi image E.g. if you pull the oat files from the device, you get oat files with the following names: system@framework@boot.art But the name stored in the image header is: boot.art So we need to append system@framework@ as a file name prefix to each image file to get the actual one. Required for adding oatdump support for app images. Bug: 22858531 Bug: 27408512 (cherry picked from commit 05752398db7f25d7892b62cb39615eee468f1db8) Change-Id: Ia6fb34b137c5e285818d39c3b9794cd4ce6c3219 --- runtime/gc/space/image_space.cc | 63 ++++++++++++--------------------- 1 file changed, 22 insertions(+), 41 deletions(-) diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc index a4e558728e..9ecd391e4d 100644 --- a/runtime/gc/space/image_space.cc +++ b/runtime/gc/space/image_space.cc @@ -1535,50 +1535,31 @@ void ImageSpace::CreateMultiImageLocations(const std::string& input_image_file_n // images[0] is f/c/d/e.art // ---------------------------------------------- // images[1] is g/h/i/j.art -> /a/b/h/i/j.art - - // Derive pattern. - std::vector left; - Split(input_image_file_name, '/', &left); - std::vector right; - Split(images[0], '/', &right); - - size_t common = 1; - while (common < left.size() && common < right.size()) { - if (left[left.size() - common - 1] != right[right.size() - common - 1]) { - break; - } - common++; - } - - std::vector prefix_vector(left.begin(), left.end() - common); - std::string common_prefix = Join(prefix_vector, '/'); - if (!common_prefix.empty() && common_prefix[0] != '/' && input_image_file_name[0] == '/') { - common_prefix = "/" + common_prefix; - } + const std::string& first_image = images[0]; + // Length of common suffix. + size_t common = 0; + while (common < input_image_file_name.size() && + common < first_image.size() && + *(input_image_file_name.end() - common - 1) == *(first_image.end() - common - 1)) { + ++common; + } + // We want to replace the prefix of the input image with the prefix of the boot class path. + // This handles the case where the image file contains @ separators. + // Example image_file_name is oats/system@framework@boot.art + // images[0] is .../arm/boot.art + // means that the image name prefix will be oats/system@framework@ + // so that the other images are openable. + const size_t old_prefix_length = first_image.size() - common; + const std::string new_prefix = input_image_file_name.substr( + 0, + input_image_file_name.size() - common); // Apply pattern to images[1] .. images[n]. for (size_t i = 1; i < images.size(); ++i) { - std::string image = images[i]; - - size_t rslash = std::string::npos; - for (size_t j = 0; j < common; ++j) { - if (rslash != std::string::npos) { - rslash--; - } - - rslash = image.rfind('/', rslash); - if (rslash == std::string::npos) { - rslash = 0; - } - if (rslash == 0) { - break; - } - } - std::string image_part = image.substr(rslash); - - std::string new_image = common_prefix + (StartsWith(image_part, "/") ? "" : "/") + - image_part; - image_file_names->push_back(new_image); + const std::string& image = images[i]; + CHECK_GT(image.length(), old_prefix_length); + std::string suffix = image.substr(old_prefix_length); + image_file_names->push_back(new_prefix + suffix); } } -- GitLab From 1a7beaea6f5f12e8248544a6dbe74b90c3b723b5 Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Wed, 9 Mar 2016 15:52:21 -0800 Subject: [PATCH 174/204] ART: Fix assumption in class profile collection The dex cache may contain erroneous classes. Filter, instead of DCHECK. Bug: 27500691 (cherry picked from commit 4309f6129795ade42591b872e44a33cec95f4e79) Change-Id: Ic99bca3a7e54a5e7893c801ec5ac92cbf690cd67 --- runtime/class_linker.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 01d140a0ba..d51a1f7ecc 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -7714,7 +7714,10 @@ std::set ClassLinker::GetResolvedClasses(bool ignore_bo } ++num_resolved; DCHECK(!klass->IsProxyClass()); - DCHECK(klass->IsResolved()); + if (!klass->IsResolved()) { + DCHECK(klass->IsErroneous()); + continue; + } mirror::DexCache* klass_dex_cache = klass->GetDexCache(); if (klass_dex_cache == dex_cache) { const size_t class_def_idx = klass->GetDexClassDefIndex(); -- GitLab From 6f6b134e39e7f01cf6c029ce1f95fcadb642cbc3 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Wed, 9 Mar 2016 11:14:50 -0800 Subject: [PATCH 175/204] Use PwriteFully and write image header last Prevent corrupted images if dex2oat gets killed or if the image writer is interrupted during writing. Bug: 22858531 Bug: 27561308 (cherry picked from commit dba5a70977da0a28cec2bfc8261d52a177738477) Change-Id: If4f2c43bcc3cf918b5d2780f1709225b5a4ce116 --- compiler/image_writer.cc | 36 +++++++++++++++++--------- runtime/base/unix_file/fd_file.cc | 19 +++++++++++--- runtime/base/unix_file/fd_file.h | 4 +++ runtime/base/unix_file/fd_file_test.cc | 28 ++++++++++++++++++++ 4 files changed, 72 insertions(+), 15 deletions(-) diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc index 871435b85f..b1b971f6ba 100644 --- a/compiler/image_writer.cc +++ b/compiler/image_writer.cc @@ -266,17 +266,9 @@ bool ImageWriter::Write(int image_fd, << PrettyDuration(NanoTime() - compress_start_time); } - // Write header first, as uncompressed. - image_header->data_size_ = data_size; - if (!image_file->WriteFully(image_info.image_->Begin(), sizeof(ImageHeader))) { - PLOG(ERROR) << "Failed to write image file header " << image_filename; - image_file->Erase(); - return false; - } - // Write out the image + fields + methods. const bool is_compressed = compressed_data != nullptr; - if (!image_file->WriteFully(image_data_to_write, data_size)) { + if (!image_file->PwriteFully(image_data_to_write, data_size, sizeof(ImageHeader))) { PLOG(ERROR) << "Failed to write image file data " << image_filename; image_file->Erase(); return false; @@ -291,13 +283,33 @@ bool ImageWriter::Write(int image_fd, if (!is_compressed) { CHECK_EQ(bitmap_position_in_file, bitmap_section.Offset()); } - if (!image_file->Write(reinterpret_cast(image_info.image_bitmap_->Begin()), - bitmap_section.Size(), - bitmap_position_in_file)) { + if (!image_file->PwriteFully(reinterpret_cast(image_info.image_bitmap_->Begin()), + bitmap_section.Size(), + bitmap_position_in_file)) { PLOG(ERROR) << "Failed to write image file " << image_filename; image_file->Erase(); return false; } + + int err = image_file->Flush(); + if (err < 0) { + PLOG(ERROR) << "Failed to flush image file " << image_filename << " with result " << err; + image_file->Erase(); + return false; + } + + // Write header last in case the compiler gets killed in the middle of image writing. + // We do not want to have a corrupted image with a valid header. + // The header is uncompressed since it contains whether the image is compressed or not. + image_header->data_size_ = data_size; + if (!image_file->PwriteFully(reinterpret_cast(image_info.image_->Begin()), + sizeof(ImageHeader), + 0)) { + PLOG(ERROR) << "Failed to write image file header " << image_filename; + image_file->Erase(); + return false; + } + CHECK_EQ(bitmap_position_in_file + bitmap_section.Size(), static_cast(image_file->GetLength())); if (image_file->FlushCloseOrErase() != 0) { diff --git a/runtime/base/unix_file/fd_file.cc b/runtime/base/unix_file/fd_file.cc index 4672948f31..e4097dd3de 100644 --- a/runtime/base/unix_file/fd_file.cc +++ b/runtime/base/unix_file/fd_file.cc @@ -234,21 +234,34 @@ bool FdFile::PreadFully(void* buffer, size_t byte_count, size_t offset) { return ReadFullyGeneric(fd_, buffer, byte_count, offset); } -bool FdFile::WriteFully(const void* buffer, size_t byte_count) { +template +bool FdFile::WriteFullyGeneric(const void* buffer, size_t byte_count, size_t offset) { DCHECK(!read_only_mode_); - const char* ptr = static_cast(buffer); moveTo(GuardState::kBase, GuardState::kClosed, "Writing into closed file."); + DCHECK(kUseOffset || offset == 0u); + const char* ptr = static_cast(buffer); while (byte_count > 0) { - ssize_t bytes_written = TEMP_FAILURE_RETRY(write(fd_, ptr, byte_count)); + ssize_t bytes_written = kUseOffset + ? TEMP_FAILURE_RETRY(pwrite(fd_, ptr, byte_count, offset)) + : TEMP_FAILURE_RETRY(write(fd_, ptr, byte_count)); if (bytes_written == -1) { return false; } byte_count -= bytes_written; // Reduce the number of remaining bytes. ptr += bytes_written; // Move the buffer forward. + offset += static_cast(bytes_written); } return true; } +bool FdFile::PwriteFully(const void* buffer, size_t byte_count, size_t offset) { + return WriteFullyGeneric(buffer, byte_count, offset); +} + +bool FdFile::WriteFully(const void* buffer, size_t byte_count) { + return WriteFullyGeneric(buffer, byte_count, 0u); +} + bool FdFile::Copy(FdFile* input_file, int64_t offset, int64_t size) { DCHECK(!read_only_mode_); off_t off = static_cast(offset); diff --git a/runtime/base/unix_file/fd_file.h b/runtime/base/unix_file/fd_file.h index 8040afe9b7..16cd44f4ef 100644 --- a/runtime/base/unix_file/fd_file.h +++ b/runtime/base/unix_file/fd_file.h @@ -79,6 +79,7 @@ class FdFile : public RandomAccessFile { bool ReadFully(void* buffer, size_t byte_count) WARN_UNUSED; bool PreadFully(void* buffer, size_t byte_count, size_t offset) WARN_UNUSED; bool WriteFully(const void* buffer, size_t byte_count) WARN_UNUSED; + bool PwriteFully(const void* buffer, size_t byte_count, size_t offset) WARN_UNUSED; // Copy data from another file. bool Copy(FdFile* input_file, int64_t offset, int64_t size); @@ -119,6 +120,9 @@ class FdFile : public RandomAccessFile { GuardState guard_state_; private: + template + bool WriteFullyGeneric(const void* buffer, size_t byte_count, size_t offset); + int fd_; std::string file_path_; bool auto_close_; diff --git a/runtime/base/unix_file/fd_file_test.cc b/runtime/base/unix_file/fd_file_test.cc index ecf607c892..9bc87e5bb9 100644 --- a/runtime/base/unix_file/fd_file_test.cc +++ b/runtime/base/unix_file/fd_file_test.cc @@ -110,6 +110,34 @@ TEST_F(FdFileTest, ReadFullyWithOffset) { ASSERT_EQ(file.Close(), 0); } +TEST_F(FdFileTest, ReadWriteFullyWithOffset) { + // New scratch file, zero-length. + art::ScratchFile tmp; + FdFile file; + ASSERT_TRUE(file.Open(tmp.GetFilename(), O_RDWR)); + EXPECT_GE(file.Fd(), 0); + EXPECT_TRUE(file.IsOpened()); + + const char* test_string = "This is a test string"; + size_t length = strlen(test_string) + 1; + const size_t offset = 12; + std::unique_ptr offset_read_string(new char[length]); + std::unique_ptr read_string(new char[length]); + + // Write scratch data to file that we can read back into. + EXPECT_TRUE(file.PwriteFully(test_string, length, offset)); + ASSERT_EQ(file.Flush(), 0); + + // Test reading both the offsets. + EXPECT_TRUE(file.PreadFully(&offset_read_string[0], length, offset)); + EXPECT_STREQ(test_string, &offset_read_string[0]); + + EXPECT_TRUE(file.PreadFully(&read_string[0], length, 0u)); + EXPECT_NE(memcmp(&read_string[0], test_string, length), 0); + + ASSERT_EQ(file.Close(), 0); +} + TEST_F(FdFileTest, Copy) { art::ScratchFile src_tmp; FdFile src; -- GitLab From 197160d47f34238cb5e7444fa4c2de300db8e2c6 Mon Sep 17 00:00:00 2001 From: David Srbecky Date: Mon, 7 Mar 2016 17:33:57 +0000 Subject: [PATCH 176/204] Refactor MethodDebugInfo (input of DWARF writer). Do not pass CompiledMethod pointer through since it is only available during AOT compile but not during JIT compile or at runtime. Creating mock CompiledMethod just pass data is proving increasingly tricky, so copy the fields that we need to MethodDebugInfo instead. Change-Id: I820297b41e769fcac488c0ff2d2ea0492bb13ed8 --- compiler/debug/elf_compilation_unit.h | 5 ++- compiler/debug/elf_debug_frame_writer.h | 14 +++---- compiler/debug/elf_debug_info_writer.h | 24 ++++++------ compiler/debug/elf_debug_line_writer.h | 24 +++++------- compiler/debug/elf_debug_loc_writer.h | 43 ++++++++++++---------- compiler/debug/elf_debug_writer.cc | 7 +++- compiler/debug/elf_symtab_writer.h | 28 +++++--------- compiler/debug/method_debug_info.h | 18 ++++----- compiler/elf_builder.h | 29 ++++++--------- compiler/oat_writer.cc | 34 ++++++++++------- compiler/optimizing/optimizing_compiler.cc | 43 +++++++++------------- oatdump/oatdump.cc | 21 ++++++++--- 12 files changed, 142 insertions(+), 148 deletions(-) diff --git a/compiler/debug/elf_compilation_unit.h b/compiler/debug/elf_compilation_unit.h index f725f45e15..b1d89ebeb2 100644 --- a/compiler/debug/elf_compilation_unit.h +++ b/compiler/debug/elf_compilation_unit.h @@ -27,8 +27,9 @@ namespace debug { struct ElfCompilationUnit { std::vector methods; size_t debug_line_offset = 0; - uintptr_t low_pc = std::numeric_limits::max(); - uintptr_t high_pc = 0; + bool is_code_address_text_relative; // Is the address offset from start of .text section? + uint64_t code_address = std::numeric_limits::max(); + uint64_t code_end = 0; }; } // namespace debug diff --git a/compiler/debug/elf_debug_frame_writer.h b/compiler/debug/elf_debug_frame_writer.h index f6d9b169c4..badbd93cb0 100644 --- a/compiler/debug/elf_debug_frame_writer.h +++ b/compiler/debug/elf_debug_frame_writer.h @@ -200,8 +200,8 @@ void WriteCFISection(ElfBuilder* builder, sorted_method_infos.begin(), sorted_method_infos.end(), [](const MethodDebugInfo* lhs, const MethodDebugInfo* rhs) { - ArrayRef l = lhs->compiled_method->GetCFIInfo(); - ArrayRef r = rhs->compiled_method->GetCFIInfo(); + ArrayRef l = lhs->cfi; + ArrayRef r = rhs->cfi; return std::lexicographical_compare(l.begin(), l.end(), r.begin(), r.end()); }); @@ -212,9 +212,6 @@ void WriteCFISection(ElfBuilder* builder, { cfi_section->Start(); const bool is64bit = Is64BitInstructionSet(builder->GetIsa()); - const Elf_Addr text_address = builder->GetText()->Exists() - ? builder->GetText()->GetAddress() - : 0; const Elf_Addr cfi_address = cfi_section->GetAddress(); const Elf_Addr cie_address = cfi_address; Elf_Addr buffer_address = cfi_address; @@ -225,9 +222,10 @@ void WriteCFISection(ElfBuilder* builder, buffer.clear(); for (const MethodDebugInfo* mi : sorted_method_infos) { if (!mi->deduped) { // Only one FDE per unique address. - ArrayRef opcodes = mi->compiled_method->GetCFIInfo(); + ArrayRef opcodes = mi->cfi; if (!opcodes.empty()) { - const Elf_Addr code_address = text_address + mi->low_pc; + const Elf_Addr code_address = mi->code_address + + (mi->is_code_address_text_relative ? builder->GetText()->GetAddress() : 0); if (format == dwarf::DW_EH_FRAME_FORMAT) { binary_search_table.push_back( dchecked_integral_cast(code_address)); @@ -235,7 +233,7 @@ void WriteCFISection(ElfBuilder* builder, dchecked_integral_cast(buffer_address)); } WriteFDE(is64bit, cfi_address, cie_address, - code_address, mi->high_pc - mi->low_pc, + code_address, mi->code_size, opcodes, format, buffer_address, &buffer, &patch_locations); cfi_section->WriteFully(buffer.data(), buffer.size()); diff --git a/compiler/debug/elf_debug_info_writer.h b/compiler/debug/elf_debug_info_writer.h index bddb054b80..af74d4c5b5 100644 --- a/compiler/debug/elf_debug_info_writer.h +++ b/compiler/debug/elf_debug_info_writer.h @@ -117,17 +117,17 @@ class ElfCompilationUnitWriter { void Write(const ElfCompilationUnit& compilation_unit) { CHECK(!compilation_unit.methods.empty()); - const Elf_Addr text_address = owner_->builder_->GetText()->Exists() + const Elf_Addr base_address = compilation_unit.is_code_address_text_relative ? owner_->builder_->GetText()->GetAddress() : 0; - const uintptr_t cu_size = compilation_unit.high_pc - compilation_unit.low_pc; + const uint64_t cu_size = compilation_unit.code_end - compilation_unit.code_address; using namespace dwarf; // NOLINT. For easy access to DWARF constants. info_.StartTag(DW_TAG_compile_unit); info_.WriteString(DW_AT_producer, "Android dex2oat"); info_.WriteData1(DW_AT_language, DW_LANG_Java); info_.WriteString(DW_AT_comp_dir, "$JAVA_SRC_ROOT"); - info_.WriteAddr(DW_AT_low_pc, text_address + compilation_unit.low_pc); + info_.WriteAddr(DW_AT_low_pc, base_address + compilation_unit.code_address); info_.WriteUdata(DW_AT_high_pc, dchecked_integral_cast(cu_size)); info_.WriteSecOffset(DW_AT_stmt_list, compilation_unit.debug_line_offset); @@ -165,8 +165,8 @@ class ElfCompilationUnitWriter { int start_depth = info_.Depth(); info_.StartTag(DW_TAG_subprogram); WriteName(dex->GetMethodName(dex_method)); - info_.WriteAddr(DW_AT_low_pc, text_address + mi->low_pc); - info_.WriteUdata(DW_AT_high_pc, dchecked_integral_cast(mi->high_pc-mi->low_pc)); + info_.WriteAddr(DW_AT_low_pc, base_address + mi->code_address); + info_.WriteUdata(DW_AT_high_pc, mi->code_size); std::vector expr_buffer; Expression expr(&expr_buffer); expr.WriteOpCallFrameCfa(); @@ -176,8 +176,8 @@ class ElfCompilationUnitWriter { // Decode dex register locations for all stack maps. // It might be expensive, so do it just once and reuse the result. std::vector dex_reg_maps; - if (mi->IsFromOptimizingCompiler()) { - const CodeInfo code_info(mi->compiled_method->GetVmapTable().data()); + if (mi->code_info != nullptr) { + const CodeInfo code_info(mi->code_info); StackMapEncoding encoding = code_info.ExtractEncoding(); for (size_t s = 0; s < code_info.GetNumberOfStackMaps(); ++s) { const StackMap& stack_map = code_info.GetStackMapAt(s, encoding); @@ -200,7 +200,7 @@ class ElfCompilationUnitWriter { // Write the stack location of the parameter. const uint32_t vreg = dex_code->registers_size_ - dex_code->ins_size_ + arg_reg; const bool is64bitValue = false; - WriteRegLocation(mi, dex_reg_maps, vreg, is64bitValue, compilation_unit.low_pc); + WriteRegLocation(mi, dex_reg_maps, vreg, is64bitValue, compilation_unit.code_address); } arg_reg++; info_.EndTag(); @@ -219,7 +219,7 @@ class ElfCompilationUnitWriter { if (dex_code != nullptr) { // Write the stack location of the parameter. const uint32_t vreg = dex_code->registers_size_ - dex_code->ins_size_ + arg_reg; - WriteRegLocation(mi, dex_reg_maps, vreg, is64bitValue, compilation_unit.low_pc); + WriteRegLocation(mi, dex_reg_maps, vreg, is64bitValue, compilation_unit.code_address); } arg_reg += is64bitValue ? 2 : 1; info_.EndTag(); @@ -246,7 +246,7 @@ class ElfCompilationUnitWriter { dex_reg_maps, var.reg_, is64bitValue, - compilation_unit.low_pc, + compilation_unit.code_address, var.start_address_, var.end_address_); info_.EndTag(); @@ -445,14 +445,14 @@ class ElfCompilationUnitWriter { const std::vector& dex_register_maps, uint16_t vreg, bool is64bitValue, - uint32_t compilation_unit_low_pc, + uint64_t compilation_unit_code_address, uint32_t dex_pc_low = 0, uint32_t dex_pc_high = 0xFFFFFFFF) { WriteDebugLocEntry(method_info, dex_register_maps, vreg, is64bitValue, - compilation_unit_low_pc, + compilation_unit_code_address, dex_pc_low, dex_pc_high, owner_->builder_->GetIsa(), diff --git a/compiler/debug/elf_debug_line_writer.h b/compiler/debug/elf_debug_line_writer.h index 11be4e9844..ed26d96603 100644 --- a/compiler/debug/elf_debug_line_writer.h +++ b/compiler/debug/elf_debug_line_writer.h @@ -53,7 +53,7 @@ class ElfDebugLineWriter { // Returns the number of bytes written. size_t WriteCompilationUnit(ElfCompilationUnit& compilation_unit) { const bool is64bit = Is64BitInstructionSet(builder_->GetIsa()); - const Elf_Addr text_address = builder_->GetText()->Exists() + const Elf_Addr base_address = compilation_unit.is_code_address_text_relative ? builder_->GetText()->GetAddress() : 0; @@ -90,37 +90,31 @@ class ElfDebugLineWriter { } uint32_t prologue_end = std::numeric_limits::max(); - ArrayRef pc2dex_map; - std::vector pc2dex_map_from_stack_maps; - if (mi->IsFromOptimizingCompiler()) { + std::vector pc2dex_map; + if (mi->code_info != nullptr) { // Use stack maps to create mapping table from pc to dex. - const CodeInfo code_info(mi->compiled_method->GetVmapTable().data()); + const CodeInfo code_info(mi->code_info); const StackMapEncoding encoding = code_info.ExtractEncoding(); + pc2dex_map.reserve(code_info.GetNumberOfStackMaps()); for (uint32_t s = 0; s < code_info.GetNumberOfStackMaps(); s++) { StackMap stack_map = code_info.GetStackMapAt(s, encoding); DCHECK(stack_map.IsValid()); const uint32_t pc = stack_map.GetNativePcOffset(encoding); const int32_t dex = stack_map.GetDexPc(encoding); - pc2dex_map_from_stack_maps.push_back({pc, dex}); + pc2dex_map.push_back({pc, dex}); if (stack_map.HasDexRegisterMap(encoding)) { // Guess that the first map with local variables is the end of prologue. prologue_end = std::min(prologue_end, pc); } } - std::sort(pc2dex_map_from_stack_maps.begin(), - pc2dex_map_from_stack_maps.end()); - pc2dex_map = ArrayRef(pc2dex_map_from_stack_maps); - } else { - // Use the mapping table provided by the quick compiler. - pc2dex_map = mi->compiled_method->GetSrcMappingTable(); - prologue_end = 0; + std::sort(pc2dex_map.begin(), pc2dex_map.end()); } if (pc2dex_map.empty()) { continue; } - Elf_Addr method_address = text_address + mi->low_pc; + Elf_Addr method_address = base_address + mi->code_address; PositionInfos dex2line_map; const DexFile* dex = mi->dex_file; @@ -226,7 +220,7 @@ class ElfDebugLineWriter { opcodes.AddRow(method_address, 0); } - opcodes.AdvancePC(text_address + mi->high_pc); + opcodes.AdvancePC(method_address + mi->code_size); opcodes.EndSequence(); } std::vector buffer; diff --git a/compiler/debug/elf_debug_loc_writer.h b/compiler/debug/elf_debug_loc_writer.h index c321b4bc4f..2d4fff4d14 100644 --- a/compiler/debug/elf_debug_loc_writer.h +++ b/compiler/debug/elf_debug_loc_writer.h @@ -74,8 +74,8 @@ static Reg GetDwarfFpReg(InstructionSet isa, int machine_reg) { } struct VariableLocation { - uint32_t low_pc; - uint32_t high_pc; + uint32_t low_pc; // Relative to compilation unit. + uint32_t high_pc; // Relative to compilation unit. DexRegisterLocation reg_lo; // May be None if the location is unknown. DexRegisterLocation reg_hi; // Most significant bits of 64-bit value. }; @@ -90,19 +90,23 @@ std::vector GetVariableLocations( const std::vector& dex_register_maps, uint16_t vreg, bool is64bitValue, + uint64_t compilation_unit_code_address, uint32_t dex_pc_low, uint32_t dex_pc_high) { std::vector variable_locations; // Get stack maps sorted by pc (they might not be sorted internally). - const CodeInfo code_info(method_info->compiled_method->GetVmapTable().data()); + const CodeInfo code_info(method_info->code_info); const StackMapEncoding encoding = code_info.ExtractEncoding(); std::map stack_maps; // low_pc -> stack_map_index. for (uint32_t s = 0; s < code_info.GetNumberOfStackMaps(); s++) { StackMap stack_map = code_info.GetStackMapAt(s, encoding); DCHECK(stack_map.IsValid()); - const uint32_t low_pc = method_info->low_pc + stack_map.GetNativePcOffset(encoding); - DCHECK_LE(low_pc, method_info->high_pc); + const uint32_t pc_offset = stack_map.GetNativePcOffset(encoding); + DCHECK_LE(pc_offset, method_info->code_size); + DCHECK_LE(compilation_unit_code_address, method_info->code_address); + const uint32_t low_pc = dchecked_integral_cast( + method_info->code_address + pc_offset - compilation_unit_code_address); stack_maps.emplace(low_pc, s); } @@ -113,8 +117,9 @@ std::vector GetVariableLocations( const StackMap& stack_map = code_info.GetStackMapAt(stack_map_index, encoding); auto next_it = it; next_it++; - const uint32_t high_pc = next_it != stack_maps.end() ? next_it->first - : method_info->high_pc; + const uint32_t high_pc = next_it != stack_maps.end() + ? next_it->first + : method_info->code_address + method_info->code_size - compilation_unit_code_address; DCHECK_LE(low_pc, high_pc); if (low_pc == high_pc) { continue; // Ignore if the address range is empty. @@ -165,7 +170,7 @@ static void WriteDebugLocEntry(const MethodDebugInfo* method_info, const std::vector& dex_register_maps, uint16_t vreg, bool is64bitValue, - uint32_t compilation_unit_low_pc, + uint64_t compilation_unit_code_address, uint32_t dex_pc_low, uint32_t dex_pc_high, InstructionSet isa, @@ -173,7 +178,7 @@ static void WriteDebugLocEntry(const MethodDebugInfo* method_info, std::vector* debug_loc_buffer, std::vector* debug_ranges_buffer) { using Kind = DexRegisterLocation::Kind; - if (!method_info->IsFromOptimizingCompiler()) { + if (method_info->code_info == nullptr || dex_register_maps.empty()) { return; } @@ -182,6 +187,7 @@ static void WriteDebugLocEntry(const MethodDebugInfo* method_info, dex_register_maps, vreg, is64bitValue, + compilation_unit_code_address, dex_pc_low, dex_pc_high); @@ -202,9 +208,8 @@ static void WriteDebugLocEntry(const MethodDebugInfo* method_info, const Kind kind = reg_loc.GetKind(); const int32_t value = reg_loc.GetValue(); if (kind == Kind::kInStack) { - const size_t frame_size = method_info->compiled_method->GetFrameSizeInBytes(); // The stack offset is relative to SP. Make it relative to CFA. - expr.WriteOpFbreg(value - frame_size); + expr.WriteOpFbreg(value - method_info->frame_size_in_bytes); if (piece == 0 && reg_hi.GetKind() == Kind::kInStack && reg_hi.GetValue() == value + 4) { break; // the high word is correctly implied by the low word. @@ -249,11 +254,11 @@ static void WriteDebugLocEntry(const MethodDebugInfo* method_info, if (expr.size() > 0) { if (is64bit) { - debug_loc.PushUint64(variable_location.low_pc - compilation_unit_low_pc); - debug_loc.PushUint64(variable_location.high_pc - compilation_unit_low_pc); + debug_loc.PushUint64(variable_location.low_pc); + debug_loc.PushUint64(variable_location.high_pc); } else { - debug_loc.PushUint32(variable_location.low_pc - compilation_unit_low_pc); - debug_loc.PushUint32(variable_location.high_pc - compilation_unit_low_pc); + debug_loc.PushUint32(variable_location.low_pc); + debug_loc.PushUint32(variable_location.high_pc); } // Write the expression. debug_loc.PushUint16(expr.size()); @@ -283,11 +288,11 @@ static void WriteDebugLocEntry(const MethodDebugInfo* method_info, high_pc = variable_locations[++i].high_pc; } if (is64bit) { - debug_ranges.PushUint64(low_pc - compilation_unit_low_pc); - debug_ranges.PushUint64(high_pc - compilation_unit_low_pc); + debug_ranges.PushUint64(low_pc); + debug_ranges.PushUint64(high_pc); } else { - debug_ranges.PushUint32(low_pc - compilation_unit_low_pc); - debug_ranges.PushUint32(high_pc - compilation_unit_low_pc); + debug_ranges.PushUint32(low_pc); + debug_ranges.PushUint32(high_pc); } } // Write end-of-list entry. diff --git a/compiler/debug/elf_debug_writer.cc b/compiler/debug/elf_debug_writer.cc index 79069d1aa2..4f2a007492 100644 --- a/compiler/debug/elf_debug_writer.cc +++ b/compiler/debug/elf_debug_writer.cc @@ -62,8 +62,11 @@ static void WriteDebugSections(ElfBuilder* builder, } ElfCompilationUnit& cu = compilation_units.back(); cu.methods.push_back(&mi); - cu.low_pc = std::min(cu.low_pc, mi.low_pc); - cu.high_pc = std::max(cu.high_pc, mi.high_pc); + // All methods must have the same addressing mode otherwise the min/max below does not work. + DCHECK_EQ(cu.methods.front()->is_code_address_text_relative, mi.is_code_address_text_relative); + cu.is_code_address_text_relative = mi.is_code_address_text_relative; + cu.code_address = std::min(cu.code_address, mi.code_address); + cu.code_end = std::max(cu.code_end, mi.code_address + mi.code_size); last_source_file = source_file; } diff --git a/compiler/debug/elf_symtab_writer.h b/compiler/debug/elf_symtab_writer.h index 41508f44b4..0a199dac35 100644 --- a/compiler/debug/elf_symtab_writer.h +++ b/compiler/debug/elf_symtab_writer.h @@ -47,12 +47,12 @@ static void WriteDebugSymbols(ElfBuilder* builder, return; } - // Find all addresses (low_pc) which contain deduped methods. + // Find all addresses which contain deduped methods. // The first instance of method is not marked deduped_, but the rest is. - std::unordered_set deduped_addresses; + std::unordered_set deduped_addresses; for (const MethodDebugInfo& info : method_infos) { if (info.deduped) { - deduped_addresses.insert(info.low_pc); + deduped_addresses.insert(info.code_address); } } @@ -65,33 +65,25 @@ static void WriteDebugSymbols(ElfBuilder* builder, continue; // Add symbol only for the first instance. } std::string name = PrettyMethod(info.dex_method_index, *info.dex_file, with_signature); - if (deduped_addresses.find(info.low_pc) != deduped_addresses.end()) { + if (deduped_addresses.find(info.code_address) != deduped_addresses.end()) { name += " [DEDUPED]"; } // If we write method names without signature, we might see the same name multiple times. size_t name_offset = (name == last_name ? last_name_offset : strtab->Write(name)); - const auto* text = builder->GetText()->Exists() ? builder->GetText() : nullptr; - const bool is_relative = (text != nullptr); - uint32_t low_pc = info.low_pc; + const auto* text = info.is_code_address_text_relative ? builder->GetText() : nullptr; + uint64_t address = info.code_address + (text != nullptr ? text->GetAddress() : 0); // Add in code delta, e.g., thumb bit 0 for Thumb2 code. - low_pc += info.compiled_method->CodeDelta(); - symtab->Add(name_offset, - text, - low_pc, - is_relative, - info.high_pc - info.low_pc, - STB_GLOBAL, - STT_FUNC); + address += CompiledMethod::CodeDelta(info.isa); + symtab->Add(name_offset, text, address, info.code_size, STB_GLOBAL, STT_FUNC); // Conforming to aaelf, add $t mapping symbol to indicate start of a sequence of thumb2 // instructions, so that disassembler tools can correctly disassemble. // Note that even if we generate just a single mapping symbol, ARM's Streamline // requires it to match function symbol. Just address 0 does not work. - if (info.compiled_method->GetInstructionSet() == kThumb2) { + if (info.isa == kThumb2) { if (!generated_mapping_symbol || !kGenerateSingleArmMappingSymbol) { - symtab->Add(strtab->Write("$t"), text, info.low_pc & ~1, - is_relative, 0, STB_LOCAL, STT_NOTYPE); + symtab->Add(strtab->Write("$t"), text, address & ~1, 0, STB_LOCAL, STT_NOTYPE); generated_mapping_symbol = true; } } diff --git a/compiler/debug/method_debug_info.h b/compiler/debug/method_debug_info.h index bb09f7e814..1ccc705ff9 100644 --- a/compiler/debug/method_debug_info.h +++ b/compiler/debug/method_debug_info.h @@ -29,18 +29,16 @@ struct MethodDebugInfo { uint32_t dex_method_index; uint32_t access_flags; const DexFile::CodeItem* code_item; + InstructionSet isa; bool deduped; bool is_native_debuggable; - uintptr_t low_pc; - uintptr_t high_pc; - CompiledMethod* compiled_method; - - bool IsFromOptimizingCompiler() const { - return compiled_method->GetQuickCode().size() > 0 && - compiled_method->GetVmapTable().size() > 0 && - compiled_method->GetGcMap().size() == 0 && - code_item != nullptr; - } + bool is_optimized; + bool is_code_address_text_relative; // Is the address offset from start of .text section? + uint64_t code_address; + uint32_t code_size; + uint32_t frame_size_in_bytes; + const uint8_t* code_info; + ArrayRef cfi; }; } // namespace debug diff --git a/compiler/elf_builder.h b/compiler/elf_builder.h index 943e2a897d..42f4e41827 100644 --- a/compiler/elf_builder.h +++ b/compiler/elf_builder.h @@ -164,12 +164,6 @@ class ElfBuilder FINAL { } } - // Returns true if the section was written to disk. - // (Used to check whether we have .text when writing JIT debug info) - bool Exists() const { - return finished_; - } - // Get the location of this section in virtual memory. Elf_Addr GetAddress() const { CHECK(started_); @@ -363,16 +357,18 @@ class ElfBuilder FINAL { void Add(Elf_Word name, const Section* section, Elf_Addr addr, - bool is_relative, Elf_Word size, uint8_t binding, - uint8_t type, - uint8_t other = 0) { - DCHECK(section != nullptr || !is_relative); - Elf_Addr abs_addr = addr + (is_relative ? section->GetAddress() : 0); - Elf_Word section_index = - (section != nullptr) ? section->GetSectionIndex() : static_cast(SHN_ABS); - Add(name, section_index, abs_addr, size, binding, type, other); + uint8_t type) { + Elf_Word section_index; + if (section != nullptr) { + DCHECK_LE(section->GetAddress(), addr); + DCHECK_LE(addr, section->GetAddress() + section->GetSize()); + section_index = section->GetSectionIndex(); + } else { + section_index = static_cast(SHN_ABS); + } + Add(name, section_index, addr, size, binding, type); } void Add(Elf_Word name, @@ -380,13 +376,12 @@ class ElfBuilder FINAL { Elf_Addr addr, Elf_Word size, uint8_t binding, - uint8_t type, - uint8_t other = 0) { + uint8_t type) { Elf_Sym sym = Elf_Sym(); sym.st_name = name; sym.st_value = addr; sym.st_size = size; - sym.st_other = other; + sym.st_other = 0; sym.st_shndx = section_index; sym.st_info = (binding << 4) + (type & 0xf); CachedSection::Add(&sym, sizeof(sym)); diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc index 50f5aba061..2b511fc082 100644 --- a/compiler/oat_writer.cc +++ b/compiler/oat_writer.cc @@ -807,21 +807,27 @@ class OatWriter::InitCodeMethodVisitor : public OatDexMethodVisitor { } const CompilerOptions& compiler_options = writer_->compiler_driver_->GetCompilerOptions(); - if (compiler_options.GenerateAnyDebugInfo()) { + // Exclude quickened dex methods (code_size == 0) since they have no native code. + if (compiler_options.GenerateAnyDebugInfo() && code_size != 0) { + bool has_code_info = method_header->IsOptimized(); // Record debug information for this function if we are doing that. - const uint32_t quick_code_start = quick_code_offset - - writer_->oat_header_->GetExecutableOffset() - thumb_offset; - writer_->method_info_.push_back(debug::MethodDebugInfo { - dex_file_, - class_def_index_, - it.GetMemberIndex(), - it.GetMethodAccessFlags(), - it.GetMethodCodeItem(), - deduped, - compiler_options.GetNativeDebuggable(), - quick_code_start, - quick_code_start + code_size, - compiled_method}); + debug::MethodDebugInfo info; + info.dex_file = dex_file_; + info.class_def_index = class_def_index_; + info.dex_method_index = it.GetMemberIndex(); + info.access_flags = it.GetMethodAccessFlags(); + info.code_item = it.GetMethodCodeItem(); + info.isa = compiled_method->GetInstructionSet(); + info.deduped = deduped; + info.is_native_debuggable = compiler_options.GetNativeDebuggable(); + info.is_optimized = method_header->IsOptimized(); + info.is_code_address_text_relative = true; + info.code_address = code_offset - writer_->oat_header_->GetExecutableOffset(); + info.code_size = code_size; + info.frame_size_in_bytes = compiled_method->GetFrameSizeInBytes(); + info.code_info = has_code_info ? compiled_method->GetVmapTable().data() : nullptr; + info.cfi = compiled_method->GetCFIInfo(); + writer_->method_info_.push_back(info); } if (kIsDebugBuild) { diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index 42f22afd80..47581b1f0d 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -917,35 +917,26 @@ bool OptimizingCompiler::JitCompile(Thread* self, if (compiler_options.GetGenerateDebugInfo()) { const auto* method_header = reinterpret_cast(code); const uintptr_t code_address = reinterpret_cast(method_header->GetCode()); - CompiledMethod compiled_method( - GetCompilerDriver(), - codegen->GetInstructionSet(), - ArrayRef(code_allocator.GetMemory()), - codegen->HasEmptyFrame() ? 0 : codegen->GetFrameSize(), - codegen->GetCoreSpillMask(), - codegen->GetFpuSpillMask(), - ArrayRef(), - ArrayRef(), // mapping_table. - ArrayRef(stack_map_data, stack_map_size), - ArrayRef(), // native_gc_map. - ArrayRef(*codegen->GetAssembler()->cfi().data()), - ArrayRef()); - debug::MethodDebugInfo method_debug_info { - dex_file, - class_def_idx, - method_idx, - access_flags, - code_item, - false, // deduped. - compiler_options.GetNativeDebuggable(), - code_address, - code_address + code_allocator.GetSize(), - &compiled_method - }; + debug::MethodDebugInfo info; + info.dex_file = dex_file; + info.class_def_index = class_def_idx; + info.dex_method_index = method_idx; + info.access_flags = access_flags; + info.code_item = code_item; + info.isa = codegen->GetInstructionSet(); + info.deduped = false; + info.is_native_debuggable = compiler_options.GetNativeDebuggable(); + info.is_optimized = true; + info.is_code_address_text_relative = false; + info.code_address = code_address; + info.code_size = code_allocator.GetSize(); + info.frame_size_in_bytes = method_header->GetFrameSizeInBytes(); + info.code_info = stack_map_size == 0 ? nullptr : stack_map_data; + info.cfi = ArrayRef(*codegen->GetAssembler()->cfi().data()); ArrayRef elf_file = debug::WriteDebugElfFileForMethod( GetCompilerDriver()->GetInstructionSet(), GetCompilerDriver()->GetInstructionSetFeatures(), - method_debug_info); + info); CreateJITCodeEntryForAddress(code_address, std::unique_ptr(elf_file.data()), elf_file.size()); diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index 6c6c807588..285803ceb4 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -177,8 +177,13 @@ class OatSymbolizer FINAL { uint64_t symbol_value = code_offset - oat_file_->GetOatHeader().GetExecutableOffset(); // Specifying 0 as the symbol size means that the symbol lasts until the next symbol or until // the end of the section in case of the last symbol. - builder_->GetSymTab()->Add(name_offset, builder_->GetText(), symbol_value, - /* is_relative */ true, /* size */ 0, STB_GLOBAL, STT_FUNC); + builder_->GetSymTab()->Add( + name_offset, + builder_->GetText(), + builder_->GetText()->GetAddress() + symbol_value, + /* size */ 0, + STB_GLOBAL, + STT_FUNC); } } @@ -330,9 +335,15 @@ class OatSymbolizer FINAL { } int name_offset = builder_->GetStrTab()->Write(pretty_name); - builder_->GetSymTab()->Add(name_offset, builder_->GetText(), - oat_method.GetCodeOffset() - oat_file_->GetOatHeader().GetExecutableOffset(), - true, oat_method.GetQuickCodeSize(), STB_GLOBAL, STT_FUNC); + uint64_t address = oat_method.GetCodeOffset() - + oat_file_->GetOatHeader().GetExecutableOffset() + + builder_->GetText()->GetAddress(); + builder_->GetSymTab()->Add(name_offset, + builder_->GetText(), + address, + oat_method.GetQuickCodeSize(), + STB_GLOBAL, + STT_FUNC); } } -- GitLab From fe736b775c7774bb2f7072e022099ca47dd21a3c Mon Sep 17 00:00:00 2001 From: David Srbecky Date: Wed, 9 Mar 2016 11:44:44 +0000 Subject: [PATCH 177/204] Allow generation of native debug info for multiple methods. Slight tweak in the API which I will need in the near future. Change-Id: I45954ae16710bc941a9a06a3a17c70798315ca53 --- compiler/debug/elf_debug_writer.cc | 23 +++++++------ compiler/debug/elf_debug_writer.h | 40 ++++++++++++---------- compiler/optimizing/optimizing_compiler.cc | 4 +-- 3 files changed, 36 insertions(+), 31 deletions(-) diff --git a/compiler/debug/elf_debug_writer.cc b/compiler/debug/elf_debug_writer.cc index 4f2a007492..0ca7370f98 100644 --- a/compiler/debug/elf_debug_writer.cc +++ b/compiler/debug/elf_debug_writer.cc @@ -114,10 +114,10 @@ std::vector MakeMiniDebugInfo( } template -static ArrayRef WriteDebugElfFileForMethodInternal( - const InstructionSet isa, +static ArrayRef WriteDebugElfFileForMethodsInternal( + InstructionSet isa, const InstructionSetFeatures* features, - const MethodDebugInfo& method_info) { + const ArrayRef& method_infos) { std::vector buffer; buffer.reserve(KB); VectorOutputStream out("Debug ELF file", &buffer); @@ -125,7 +125,7 @@ static ArrayRef WriteDebugElfFileForMethodInternal( // No program headers since the ELF file is not linked and has no allocated sections. builder->Start(false /* write_program_headers */); WriteDebugInfo(builder.get(), - ArrayRef(&method_info, 1), + method_infos, dwarf::DW_DEBUG_FRAME_FORMAT, false /* write_oat_patches */); builder->End(); @@ -137,19 +137,20 @@ static ArrayRef WriteDebugElfFileForMethodInternal( return ArrayRef(result, buffer.size()); } -ArrayRef WriteDebugElfFileForMethod(const InstructionSet isa, - const InstructionSetFeatures* features, - const MethodDebugInfo& method_info) { +ArrayRef WriteDebugElfFileForMethods( + InstructionSet isa, + const InstructionSetFeatures* features, + const ArrayRef& method_infos) { if (Is64BitInstructionSet(isa)) { - return WriteDebugElfFileForMethodInternal(isa, features, method_info); + return WriteDebugElfFileForMethodsInternal(isa, features, method_infos); } else { - return WriteDebugElfFileForMethodInternal(isa, features, method_info); + return WriteDebugElfFileForMethodsInternal(isa, features, method_infos); } } template static ArrayRef WriteDebugElfFileForClassesInternal( - const InstructionSet isa, + InstructionSet isa, const InstructionSetFeatures* features, const ArrayRef& types) SHARED_REQUIRES(Locks::mutator_lock_) { @@ -174,7 +175,7 @@ static ArrayRef WriteDebugElfFileForClassesInternal( return ArrayRef(result, buffer.size()); } -ArrayRef WriteDebugElfFileForClasses(const InstructionSet isa, +ArrayRef WriteDebugElfFileForClasses(InstructionSet isa, const InstructionSetFeatures* features, const ArrayRef& types) { if (Is64BitInstructionSet(isa)) { diff --git a/compiler/debug/elf_debug_writer.h b/compiler/debug/elf_debug_writer.h index d364521f1b..7f5d24d06e 100644 --- a/compiler/debug/elf_debug_writer.h +++ b/compiler/debug/elf_debug_writer.h @@ -31,24 +31,28 @@ namespace debug { struct MethodDebugInfo; template -void WriteDebugInfo(ElfBuilder* builder, - const ArrayRef& method_infos, - dwarf::CFIFormat cfi_format, - bool write_oat_patches); - -std::vector MakeMiniDebugInfo(InstructionSet isa, - const InstructionSetFeatures* features, - size_t rodata_section_size, - size_t text_section_size, - const ArrayRef& method_infos); - -ArrayRef WriteDebugElfFileForMethod(const InstructionSet isa, - const InstructionSetFeatures* features, - const MethodDebugInfo& method_info); - -ArrayRef WriteDebugElfFileForClasses(const InstructionSet isa, - const InstructionSetFeatures* features, - const ArrayRef& types) +void WriteDebugInfo( + ElfBuilder* builder, + const ArrayRef& method_infos, + dwarf::CFIFormat cfi_format, + bool write_oat_patches); + +std::vector MakeMiniDebugInfo( + InstructionSet isa, + const InstructionSetFeatures* features, + size_t rodata_section_size, + size_t text_section_size, + const ArrayRef& method_infos); + +ArrayRef WriteDebugElfFileForMethods( + InstructionSet isa, + const InstructionSetFeatures* features, + const ArrayRef& method_infos); + +ArrayRef WriteDebugElfFileForClasses( + InstructionSet isa, + const InstructionSetFeatures* features, + const ArrayRef& types) SHARED_REQUIRES(Locks::mutator_lock_); } // namespace debug diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index 47581b1f0d..64bb919ef5 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -933,10 +933,10 @@ bool OptimizingCompiler::JitCompile(Thread* self, info.frame_size_in_bytes = method_header->GetFrameSizeInBytes(); info.code_info = stack_map_size == 0 ? nullptr : stack_map_data; info.cfi = ArrayRef(*codegen->GetAssembler()->cfi().data()); - ArrayRef elf_file = debug::WriteDebugElfFileForMethod( + ArrayRef elf_file = debug::WriteDebugElfFileForMethods( GetCompilerDriver()->GetInstructionSet(), GetCompilerDriver()->GetInstructionSetFeatures(), - info); + ArrayRef(&info, 1)); CreateJITCodeEntryForAddress(code_address, std::unique_ptr(elf_file.data()), elf_file.size()); -- GitLab From 31f477eb7f28c9e4647df3c18f7e4271436640be Mon Sep 17 00:00:00 2001 From: Goran Jakovljevic Date: Tue, 8 Mar 2016 15:26:51 +0100 Subject: [PATCH 178/204] MIPS32: Fix test 082-inline-execute This is fixing the test when run with --debuggable flag. Change-Id: Ifd11c28a77f1c6f6fe5951c5c27309187b29d3ee --- compiler/optimizing/intrinsics_mips.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc index e159701a46..ed0b5b7548 100644 --- a/compiler/optimizing/intrinsics_mips.cc +++ b/compiler/optimizing/intrinsics_mips.cc @@ -1464,7 +1464,9 @@ void IntrinsicLocationsBuilderMIPS::VisitStringCharAt(HInvoke* invoke) { kIntrinsified); locations->SetInAt(0, Location::RequiresRegister()); locations->SetInAt(1, Location::RequiresRegister()); - locations->SetOut(Location::SameAsFirstInput()); + // The inputs will be considered live at the last instruction and restored. This will overwrite + // the output with kNoOutputOverlap. + locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); } void IntrinsicCodeGeneratorMIPS::VisitStringCharAt(HInvoke* invoke) { -- GitLab From 83f080ac824d0964941c3fbaa957cac874f827b0 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Tue, 8 Mar 2016 16:50:21 +0000 Subject: [PATCH 179/204] Tweak JIT hotness threshold. bug:23128949 bug:27398183 Change-Id: Iffde6ba064e54546827cb8fc2a670baedf2e2409 --- runtime/jit/jit.cc | 36 +++++++++++++++++++++++++++++------- runtime/jit/jit.h | 3 +-- runtime/parsed_options.cc | 5 +++++ runtime/runtime_options.def | 3 ++- 4 files changed, 37 insertions(+), 10 deletions(-) diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc index 7dbd89cedb..5bd9a6b76b 100644 --- a/runtime/jit/jit.cc +++ b/runtime/jit/jit.cc @@ -41,20 +41,42 @@ static constexpr bool kEnableOnStackReplacement = true; JitOptions* JitOptions::CreateFromRuntimeArguments(const RuntimeArgumentMap& options) { auto* jit_options = new JitOptions; jit_options->use_jit_ = options.GetOrDefault(RuntimeArgumentMap::UseJIT); + jit_options->code_cache_initial_capacity_ = options.GetOrDefault(RuntimeArgumentMap::JITCodeCacheInitialCapacity); jit_options->code_cache_max_capacity_ = options.GetOrDefault(RuntimeArgumentMap::JITCodeCacheMaxCapacity); - jit_options->compile_threshold_ = - options.GetOrDefault(RuntimeArgumentMap::JITCompileThreshold); - // TODO(ngeoffray): Make this a proper option. - jit_options->osr_threshold_ = jit_options->compile_threshold_ * 2; - jit_options->warmup_threshold_ = - options.GetOrDefault(RuntimeArgumentMap::JITWarmupThreshold); jit_options->dump_info_on_shutdown_ = options.Exists(RuntimeArgumentMap::DumpJITInfoOnShutdown); jit_options->save_profiling_info_ = - options.GetOrDefault(RuntimeArgumentMap::JITSaveProfilingInfo);; + options.GetOrDefault(RuntimeArgumentMap::JITSaveProfilingInfo); + + jit_options->compile_threshold_ = options.GetOrDefault(RuntimeArgumentMap::JITCompileThreshold); + if (jit_options->compile_threshold_ > std::numeric_limits::max()) { + LOG(FATAL) << "Method compilation threshold is above its internal limit."; + } + + if (options.Exists(RuntimeArgumentMap::JITWarmupThreshold)) { + jit_options->warmup_threshold_ = *options.Get(RuntimeArgumentMap::JITWarmupThreshold); + if (jit_options->warmup_threshold_ > std::numeric_limits::max()) { + LOG(FATAL) << "Method warmup threshold is above its internal limit."; + } + } else { + jit_options->warmup_threshold_ = jit_options->compile_threshold_ / 2; + } + + if (options.Exists(RuntimeArgumentMap::JITOsrThreshold)) { + jit_options->osr_threshold_ = *options.Get(RuntimeArgumentMap::JITOsrThreshold); + if (jit_options->osr_threshold_ > std::numeric_limits::max()) { + LOG(FATAL) << "Method on stack replacement threshold is above its internal limit."; + } + } else { + jit_options->osr_threshold_ = jit_options->compile_threshold_ * 2; + if (jit_options->osr_threshold_ > std::numeric_limits::max()) { + jit_options->osr_threshold_ = std::numeric_limits::max(); + } + } + return jit_options; } diff --git a/runtime/jit/jit.h b/runtime/jit/jit.h index ee416d8772..d5c213416a 100644 --- a/runtime/jit/jit.h +++ b/runtime/jit/jit.h @@ -43,8 +43,7 @@ class JitOptions; class Jit { public: static constexpr bool kStressMode = kIsDebugBuild; - static constexpr size_t kDefaultCompileThreshold = kStressMode ? 2 : 500; - static constexpr size_t kDefaultWarmupThreshold = kDefaultCompileThreshold / 2; + static constexpr size_t kDefaultCompileThreshold = kStressMode ? 2 : 10000; virtual ~Jit(); static Jit* Create(JitOptions* options, std::string* error_msg); diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc index d64aa432fc..60403f9752 100644 --- a/runtime/parsed_options.cc +++ b/runtime/parsed_options.cc @@ -166,6 +166,9 @@ std::unique_ptr ParsedOptions::MakeParser(bool ignore_unrecognize .Define("-Xjitwarmupthreshold:_") .WithType() .IntoKey(M::JITWarmupThreshold) + .Define("-Xjitosrthreshold:_") + .WithType() + .IntoKey(M::JITOsrThreshold) .Define("-Xjitsaveprofilinginfo") .WithValue(true) .IntoKey(M::JITSaveProfilingInfo) @@ -694,6 +697,8 @@ void ParsedOptions::Usage(const char* fmt, ...) { UsageMessage(stream, " -Xusejit:booleanvalue\n"); UsageMessage(stream, " -Xjitinitialsize:N\n"); UsageMessage(stream, " -Xjitmaxsize:N\n"); + UsageMessage(stream, " -Xjitwarmupthreshold:integervalue\n"); + UsageMessage(stream, " -Xjitosrthreshold:integervalue\n"); UsageMessage(stream, " -X[no]relocate\n"); UsageMessage(stream, " -X[no]dex2oat (Whether to invoke dex2oat on the application)\n"); UsageMessage(stream, " -X[no]image-dex2oat (Whether to create and use a boot image)\n"); diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def index 838d1a9649..3fd9905a60 100644 --- a/runtime/runtime_options.def +++ b/runtime/runtime_options.def @@ -69,7 +69,8 @@ RUNTIME_OPTIONS_KEY (bool, EnableHSpaceCompactForOOM, true) RUNTIME_OPTIONS_KEY (bool, UseJIT, false) RUNTIME_OPTIONS_KEY (bool, DumpNativeStackOnSigQuit, true) RUNTIME_OPTIONS_KEY (unsigned int, JITCompileThreshold, jit::Jit::kDefaultCompileThreshold) -RUNTIME_OPTIONS_KEY (unsigned int, JITWarmupThreshold, jit::Jit::kDefaultWarmupThreshold) +RUNTIME_OPTIONS_KEY (unsigned int, JITWarmupThreshold) +RUNTIME_OPTIONS_KEY (unsigned int, JITOsrThreshold) RUNTIME_OPTIONS_KEY (MemoryKiB, JITCodeCacheInitialCapacity, jit::JitCodeCache::kInitialCapacity) RUNTIME_OPTIONS_KEY (MemoryKiB, JITCodeCacheMaxCapacity, jit::JitCodeCache::kMaxCapacity) RUNTIME_OPTIONS_KEY (bool, JITSaveProfilingInfo, false) -- GitLab From d52765768b634c6f32a9bddd5c1269f26d32ea3a Mon Sep 17 00:00:00 2001 From: Roland Levillain Date: Thu, 10 Mar 2016 15:16:31 +0000 Subject: [PATCH 180/204] Disable some image_test cases with concurrent collector. The following image_test cases sometimes fail on ART Builbot's x86 concurrent collector configuration: - ImageTest.WriteReadUncompressed - ImageTest.WriteReadLZ4 - ImageTest.WriteReadLZ4HC Disable them to make the build turn green again, while we investigate the failures. Bug: 27578460 Change-Id: I46126e4690e6300e3bfa771c1b6a560272ecb1da --- compiler/common_compiler_test.h | 7 +++++++ compiler/image_test.cc | 3 +++ 2 files changed, 10 insertions(+) diff --git a/compiler/common_compiler_test.h b/compiler/common_compiler_test.h index 9552143080..7c2c844e6f 100644 --- a/compiler/common_compiler_test.h +++ b/compiler/common_compiler_test.h @@ -122,6 +122,13 @@ class CommonCompilerTest : public CommonRuntimeTest { return; \ } +// TODO: When read barrier works with all tests, get rid of this. +#define TEST_DISABLED_FOR_READ_BARRIER() \ + if (kUseReadBarrier) { \ + printf("WARNING: TEST DISABLED FOR READ BARRIER\n"); \ + return; \ + } + // TODO: When read barrier works with all compilers in use, get rid of this. #define TEST_DISABLED_FOR_READ_BARRIER_WITH_QUICK() \ if (kUseReadBarrier && GetCompilerKind() == Compiler::kQuick) { \ diff --git a/compiler/image_test.cc b/compiler/image_test.cc index 91579e9daf..7779e44519 100644 --- a/compiler/image_test.cc +++ b/compiler/image_test.cc @@ -288,14 +288,17 @@ void ImageTest::TestWriteRead(ImageHeader::StorageMode storage_mode) { } TEST_F(ImageTest, WriteReadUncompressed) { + TEST_DISABLED_FOR_READ_BARRIER(); // b/27578460 TestWriteRead(ImageHeader::kStorageModeUncompressed); } TEST_F(ImageTest, WriteReadLZ4) { + TEST_DISABLED_FOR_READ_BARRIER(); // b/27578460 TestWriteRead(ImageHeader::kStorageModeLZ4); } TEST_F(ImageTest, WriteReadLZ4HC) { + TEST_DISABLED_FOR_READ_BARRIER(); // b/27578460 TestWriteRead(ImageHeader::kStorageModeLZ4HC); } -- GitLab From d1c4045fb4d4703642f3f79985727b9a12cf5c49 Mon Sep 17 00:00:00 2001 From: Aart Bik Date: Wed, 2 Mar 2016 16:06:13 -0800 Subject: [PATCH 181/204] Avoid generating dead code on frame enter/exit. This includes stack operations and, on x86, call/pop to read PC. bug=26997690 Rationale: (1) If method is fully intrinsified, and makes no calls in slow path or uses special input, no need to require current method. (2) Invoke instructions with HasPcRelativeDexCache() generate code that reads the PC (call/pop) on x86. However, if the invoke is an intrinsic that is later replaced with actual code, this PC reading code may be dead. Example X86 (before/after): 0x0000108c: 83EC0C sub esp, 12 0x0000108f: 890424 mov [esp], eax <-- not needed 0x00001092: E800000000 call +0 (0x00001097) 0x00001097: 58 pop eax <-- dead code to read PC 0x00001098: F30FB8C1 popcnt eax, ecx 0x0000109c: F30FB8DA popcnt ebx, edx 0x000010a0: 03D8 add ebx, eax 0x000010a2: 89D8 mov eax, ebx 0x000010a4: 83C40C add esp, 12 <-- not needed 0x000010a7: C3 ret 0x0000103c: F30FB8C1 popcnt eax, ecx 0x00001040: F30FB8DA popcnt ebx, edx 0x00001044: 03D8 add ebx, eax 0x00001046: 89D8 mov eax, ebx 0x00001048: C3 ret Example ARM64 (before/after): 0x0000103c: f81e0fe0 str x0, [sp, #-32]! 0x00001040: f9000ffe str lr, [sp, #24] 0x00001044: dac01020 clz x0, x1 0x00001048: f9400ffe ldr lr, [sp, #24] 0x0000104c: 910083ff add sp, sp, #0x20 (32) 0x00001050: d65f03c0 ret 0x0000103c: dac01020 clz x0, x1 0x00001040: d65f03c0 ret Change-Id: I8377db80c9a901a08fff4624927cf4a6e585da0c --- compiler/optimizing/code_generator.cc | 12 +++++-- compiler/optimizing/optimizing_compiler.cc | 8 +++-- compiler/optimizing/pc_relative_fixups_x86.cc | 31 ++++++++++++++++--- compiler/optimizing/pc_relative_fixups_x86.h | 11 +++++-- 4 files changed, 51 insertions(+), 11 deletions(-) diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index 967d156cf6..af50363e31 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -543,8 +543,16 @@ void CodeGenerator::AllocateLocations(HInstruction* instruction) { DCHECK(CheckTypeConsistency(instruction)); LocationSummary* locations = instruction->GetLocations(); if (!instruction->IsSuspendCheckEntry()) { - if (locations != nullptr && locations->CanCall()) { - MarkNotLeaf(); + if (locations != nullptr) { + if (locations->CanCall()) { + MarkNotLeaf(); + } else if (locations->Intrinsified() && + instruction->IsInvokeStaticOrDirect() && + !instruction->AsInvokeStaticOrDirect()->HasCurrentMethodInput()) { + // A static method call that has been fully intrinsified, and cannot call on the slow + // path or refer to the current method directly, no longer needs current method. + return; + } } if (instruction->NeedsCurrentMethod()) { SetRequiresCurrentMethod(); diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index 42f22afd80..4ecd1e66d8 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -20,7 +20,7 @@ #include #include -#ifdef ART_ENABLE_CODEGEN_arm64 +#ifdef ART_ENABLE_CODEGEN_arm #include "dex_cache_array_fixups_arm.h" #endif @@ -431,6 +431,7 @@ static void MaybeRunInliner(HGraph* graph, static void RunArchOptimizations(InstructionSet instruction_set, HGraph* graph, + CodeGenerator* codegen, OptimizingCompilerStats* stats, PassObserver* pass_observer) { ArenaAllocator* arena = graph->GetArena(); @@ -466,7 +467,8 @@ static void RunArchOptimizations(InstructionSet instruction_set, #endif #ifdef ART_ENABLE_CODEGEN_x86 case kX86: { - x86::PcRelativeFixups* pc_relative_fixups = new (arena) x86::PcRelativeFixups(graph, stats); + x86::PcRelativeFixups* pc_relative_fixups = + new (arena) x86::PcRelativeFixups(graph, codegen, stats); HOptimization* x86_optimizations[] = { pc_relative_fixups }; @@ -561,7 +563,7 @@ static void RunOptimizations(HGraph* graph, }; RunOptimizations(optimizations2, arraysize(optimizations2), pass_observer); - RunArchOptimizations(driver->GetInstructionSet(), graph, stats, pass_observer); + RunArchOptimizations(driver->GetInstructionSet(), graph, codegen, stats, pass_observer); AllocateRegisters(graph, codegen, pass_observer); } diff --git a/compiler/optimizing/pc_relative_fixups_x86.cc b/compiler/optimizing/pc_relative_fixups_x86.cc index a6f14616bf..d281a9fc6c 100644 --- a/compiler/optimizing/pc_relative_fixups_x86.cc +++ b/compiler/optimizing/pc_relative_fixups_x86.cc @@ -16,6 +16,7 @@ #include "pc_relative_fixups_x86.h" #include "code_generator_x86.h" +#include "intrinsics_x86.h" namespace art { namespace x86 { @@ -25,7 +26,10 @@ namespace x86 { */ class PCRelativeHandlerVisitor : public HGraphVisitor { public: - explicit PCRelativeHandlerVisitor(HGraph* graph) : HGraphVisitor(graph), base_(nullptr) {} + PCRelativeHandlerVisitor(HGraph* graph, CodeGenerator* codegen) + : HGraphVisitor(graph), + codegen_(down_cast(codegen)), + base_(nullptr) {} void MoveBaseIfNeeded() { if (base_ != nullptr) { @@ -146,7 +150,6 @@ class PCRelativeHandlerVisitor : public HGraphVisitor { if (base_ != nullptr) { return; } - // Insert the base at the start of the entry block, move it to a better // position later in MoveBaseIfNeeded(). base_ = new (GetGraph()->GetArena()) HX86ComputeBaseMethodAddress(); @@ -180,7 +183,9 @@ class PCRelativeHandlerVisitor : public HGraphVisitor { } bool base_added = false; - if (invoke_static_or_direct != nullptr && invoke_static_or_direct->HasPcRelativeDexCache()) { + if (invoke_static_or_direct != nullptr && + invoke_static_or_direct->HasPcRelativeDexCache() && + !WillHaveCallFreeIntrinsicsCodeGen(invoke)) { InitializePCRelativeBasePointer(); // Add the extra parameter base_. invoke_static_or_direct->AddSpecialInput(base_); @@ -215,6 +220,24 @@ class PCRelativeHandlerVisitor : public HGraphVisitor { } } + bool WillHaveCallFreeIntrinsicsCodeGen(HInvoke* invoke) { + if (invoke->GetIntrinsic() != Intrinsics::kNone) { + // This invoke may have intrinsic code generation defined. However, we must + // now also determine if this code generation is truly there and call-free + // (not unimplemented, no bail on instruction features, or call on slow path). + // This is done by actually calling the locations builder on the instruction + // and clearing out the locations once result is known. We assume this + // call only has creating locations as side effects! + IntrinsicLocationsBuilderX86 builder(codegen_); + bool success = builder.TryDispatch(invoke) && !invoke->GetLocations()->CanCall(); + invoke->SetLocations(nullptr); + return success; + } + return false; + } + + CodeGeneratorX86* codegen_; + // The generated HX86ComputeBaseMethodAddress in the entry block needed as an // input to the HX86LoadFromConstantTable instructions. HX86ComputeBaseMethodAddress* base_; @@ -226,7 +249,7 @@ void PcRelativeFixups::Run() { // that can be live-in at the irreducible loop header. return; } - PCRelativeHandlerVisitor visitor(graph_); + PCRelativeHandlerVisitor visitor(graph_, codegen_); visitor.VisitInsertionOrder(); visitor.MoveBaseIfNeeded(); } diff --git a/compiler/optimizing/pc_relative_fixups_x86.h b/compiler/optimizing/pc_relative_fixups_x86.h index af708acb03..03de2fcece 100644 --- a/compiler/optimizing/pc_relative_fixups_x86.h +++ b/compiler/optimizing/pc_relative_fixups_x86.h @@ -21,14 +21,21 @@ #include "optimization.h" namespace art { + +class CodeGenerator; + namespace x86 { class PcRelativeFixups : public HOptimization { public: - PcRelativeFixups(HGraph* graph, OptimizingCompilerStats* stats) - : HOptimization(graph, "pc_relative_fixups_x86", stats) {} + PcRelativeFixups(HGraph* graph, CodeGenerator* codegen, OptimizingCompilerStats* stats) + : HOptimization(graph, "pc_relative_fixups_x86", stats), + codegen_(codegen) {} void Run() OVERRIDE; + + private: + CodeGenerator* codegen_; }; } // namespace x86 -- GitLab From 07f6818ef68046d4749963b3bd59f7e93cf43fa9 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Thu, 10 Mar 2016 17:25:50 +0000 Subject: [PATCH 182/204] ART: Do not use vixld - workaround to fix dex2oatds. This is a quick workaround for ODR violations caused by linking libvixl.a compiled without VIXL_DEBUG with the libartd-compiler.a compiled with VIXL_DEBUG. Bug: 27588884 Change-Id: Ib1af165f177f125f03cdd99777dff4c2912f6405 --- build/Android.common_build.mk | 1 - build/Android.gtest.mk | 4 ++-- compiler/Android.mk | 4 ++-- disassembler/Android.mk | 2 +- runtime/simulator/Android.mk | 2 +- 5 files changed, 6 insertions(+), 7 deletions(-) diff --git a/build/Android.common_build.mk b/build/Android.common_build.mk index 02bce411b4..fd84d05d2b 100644 --- a/build/Android.common_build.mk +++ b/build/Android.common_build.mk @@ -335,7 +335,6 @@ art_non_debug_cflags := \ art_debug_cflags := \ $(ART_DEBUG_OPT_FLAG) \ -DDYNAMIC_ANNOTATIONS_ENABLED=1 \ - -DVIXL_DEBUG \ -UNDEBUG art_host_non_debug_cflags := $(art_non_debug_cflags) diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index b3832ac3b6..fda4f5d534 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -573,7 +573,7 @@ define define-art-gtest ifeq ($$(art_target_or_host),target) $$(eval $$(call set-target-local-clang-vars)) $$(eval $$(call set-target-local-cflags-vars,debug)) - LOCAL_SHARED_LIBRARIES += libdl libicuuc libicui18n libnativehelper libz libcutils libvixld + LOCAL_SHARED_LIBRARIES += libdl libicuuc libicui18n libnativehelper libz libcutils libvixl LOCAL_MODULE_PATH_32 := $$(ART_TARGET_NATIVETEST_OUT)/$$(ART_TARGET_ARCH_32) LOCAL_MODULE_PATH_64 := $$(ART_TARGET_NATIVETEST_OUT)/$$(ART_TARGET_ARCH_64) LOCAL_MULTILIB := both @@ -611,7 +611,7 @@ test-art-target-gtest-$$(art_gtest_name): $$(ART_TEST_TARGET_GTEST_$$(art_gtest_ LOCAL_CLANG := $$(ART_HOST_CLANG) LOCAL_CFLAGS += $$(ART_HOST_CFLAGS) $$(ART_HOST_DEBUG_CFLAGS) LOCAL_ASFLAGS += $$(ART_HOST_ASFLAGS) - LOCAL_SHARED_LIBRARIES += libicuuc-host libicui18n-host libnativehelper libziparchive-host libz-host libvixld + LOCAL_SHARED_LIBRARIES += libicuuc-host libicui18n-host libnativehelper libziparchive-host libz-host libvixl LOCAL_LDLIBS := $(ART_HOST_LDLIBS) -lpthread -ldl LOCAL_IS_HOST_MODULE := true LOCAL_MULTILIB := both diff --git a/compiler/Android.mk b/compiler/Android.mk index 7a257b649f..11ee6dd3a1 100644 --- a/compiler/Android.mk +++ b/compiler/Android.mk @@ -330,9 +330,9 @@ $$(ENUM_OPERATOR_OUT_GEN): $$(GENERATED_SRC_DIR)/%_operator_out.cc : $(LOCAL_PAT # Vixl assembly support for ARM64 targets. ifeq ($$(art_ndebug_or_debug),debug) ifeq ($$(art_static_or_shared), static) - LOCAL_WHOLESTATIC_LIBRARIES += libvixld + LOCAL_WHOLESTATIC_LIBRARIES += libvixl else - LOCAL_SHARED_LIBRARIES += libvixld + LOCAL_SHARED_LIBRARIES += libvixl endif else ifeq ($$(art_static_or_shared), static) diff --git a/disassembler/Android.mk b/disassembler/Android.mk index 039986ce2b..bf563c7660 100644 --- a/disassembler/Android.mk +++ b/disassembler/Android.mk @@ -89,7 +89,7 @@ define build-libart-disassembler LOCAL_NATIVE_COVERAGE := $(ART_COVERAGE) # For disassembler_arm64. ifeq ($$(art_ndebug_or_debug),debug) - LOCAL_SHARED_LIBRARIES += libvixld + LOCAL_SHARED_LIBRARIES += libvixl else LOCAL_SHARED_LIBRARIES += libvixl endif diff --git a/runtime/simulator/Android.mk b/runtime/simulator/Android.mk index c154eb6346..5c71da6255 100644 --- a/runtime/simulator/Android.mk +++ b/runtime/simulator/Android.mk @@ -86,7 +86,7 @@ define build-libart-simulator LOCAL_NATIVE_COVERAGE := $(ART_COVERAGE) # For simulator_arm64. ifeq ($$(art_ndebug_or_debug),debug) - LOCAL_SHARED_LIBRARIES += libvixld + LOCAL_SHARED_LIBRARIES += libvixl else LOCAL_SHARED_LIBRARIES += libvixl endif -- GitLab From bab661653057de838607ee7ba80105e2092da386 Mon Sep 17 00:00:00 2001 From: Aart Bik Date: Thu, 10 Mar 2016 10:59:10 -0800 Subject: [PATCH 183/204] Tests for round() method. Rationale: While working on some ideas on implementing round(), I wrote these tests. We might as well use them to ensure any future work does not break (test also provides an easy place to add future "difficult" values). bug=26327751 Change-Id: I911a3e1e5fe45147bc1b7214225c9aec4e979965 --- test/580-checker-round/expected.txt | 1 + test/580-checker-round/info.txt | 1 + test/580-checker-round/src/Main.java | 172 +++++++++++++++++++++++++++ 3 files changed, 174 insertions(+) create mode 100644 test/580-checker-round/expected.txt create mode 100644 test/580-checker-round/info.txt create mode 100644 test/580-checker-round/src/Main.java diff --git a/test/580-checker-round/expected.txt b/test/580-checker-round/expected.txt new file mode 100644 index 0000000000..b0aad4deb5 --- /dev/null +++ b/test/580-checker-round/expected.txt @@ -0,0 +1 @@ +passed diff --git a/test/580-checker-round/info.txt b/test/580-checker-round/info.txt new file mode 100644 index 0000000000..d6397fd13d --- /dev/null +++ b/test/580-checker-round/info.txt @@ -0,0 +1 @@ +Unit test for float/double rounding. diff --git a/test/580-checker-round/src/Main.java b/test/580-checker-round/src/Main.java new file mode 100644 index 0000000000..9e248ef95a --- /dev/null +++ b/test/580-checker-round/src/Main.java @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Main { + + /// CHECK-START: int Main.round32(float) intrinsics_recognition (after) + /// CHECK-DAG: <> InvokeStaticOrDirect intrinsic:MathRoundFloat + /// CHECK-DAG: Return [<>] + private static int round32(float f) { + return Math.round(f); + } + + /// CHECK-START: long Main.round64(double) intrinsics_recognition (after) + /// CHECK-DAG: <> InvokeStaticOrDirect intrinsic:MathRoundDouble + /// CHECK-DAG: Return [<>] + private static long round64(double d) { + return Math.round(d); + } + + public static void main(String args[]) { + // A few obvious numbers. + expectEquals32(-2147483648, round32(Float.NEGATIVE_INFINITY)); + expectEquals32(-2, round32(-1.51f)); + expectEquals32(-1, round32(-1.2f)); + expectEquals32(-1, round32(-1.0f)); + expectEquals32(-1, round32(-0.51f)); + expectEquals32(0, round32(-0.2f)); + expectEquals32(0, round32(-0.0f)); + expectEquals32(0, round32(+0.0f)); + expectEquals32(0, round32(+0.2f)); + expectEquals32(1, round32(+0.5f)); + expectEquals32(1, round32(+1.0f)); + expectEquals32(1, round32(+1.2f)); + expectEquals32(2, round32(+1.5f)); + expectEquals32(2147483647, round32(Float.POSITIVE_INFINITY)); + + // Some others. + for (int i = -100; i <= 100; ++i) { + expectEquals32(i - 1, round32((float) i - 0.51f)); + expectEquals32(i, round32((float) i)); + expectEquals32(i + 1, round32((float) i + 0.5f)); + } + for (float f = -1.5f; f <= -1.499f; f = Math.nextAfter(f, Float.POSITIVE_INFINITY)) { + expectEquals32(-1, round32(f)); + } + + // Some harder. + float[] fvals = { + -16777215.5f, + -16777215.0f, + -0.4999f, + 0.4999f, + 16777215.0f, + 16777215.5f + }; + int[] ivals = { + -16777216, + -16777215, + 0, + 0, + 16777215, + 16777216 + }; + for (int i = 0; i < fvals.length; i++) { + expectEquals32(ivals[i], round32(fvals[i])); + } + + // A few NaN numbers. + float[] fnans = { + Float.intBitsToFloat(0x7f800001), + Float.intBitsToFloat(0x7fa00000), + Float.intBitsToFloat(0x7fc00000), + Float.intBitsToFloat(0x7fffffff), + Float.intBitsToFloat(0xff800001), + Float.intBitsToFloat(0xffa00000), + Float.intBitsToFloat(0xffc00000), + Float.intBitsToFloat(0xffffffff) + }; + for (int i = 0; i < fnans.length; i++) { + expectEquals32(0, round32(fnans[i])); + } + + // A few obvious numbers. + expectEquals64(-9223372036854775808L, round64(Double.NEGATIVE_INFINITY)); + expectEquals64(-2L, round64(-1.51d)); + expectEquals64(-1L, round64(-1.2d)); + expectEquals64(-1L, round64(-1.0d)); + expectEquals64(-1L, round64(-0.51d)); + expectEquals64(0L, round64(-0.2d)); + expectEquals64(0L, round64(-0.0d)); + expectEquals64(0L, round64(+0.0d)); + expectEquals64(0L, round64(+0.2d)); + expectEquals64(1L, round64(+0.5d)); + expectEquals64(1L, round64(+1.0d)); + expectEquals64(1L, round64(+1.2d)); + expectEquals64(2L, round64(+1.5d)); + expectEquals64(9223372036854775807L, round64(Double.POSITIVE_INFINITY)); + + // Some others. + for (long l = -100; l <= 100; ++l) { + expectEquals64(l - 1, round64((double) l - 0.51d)); + expectEquals64(l + 1, round64((double) l + 0.5d)); + expectEquals64(l + 1, round64((double) l + 0.5d)); + } + for (double d = -1.5d; d <= -1.49999999999d; d = Math.nextAfter(d, Double.POSITIVE_INFINITY)) { + expectEquals64(-1L, round64(d)); + } + + // Some harder. + double[] dvals = { + -9007199254740991.5d, + -9007199254740991.0d, + -0.49999999999999994d, + 0.49999999999999994d, + 9007199254740991.0d, + 9007199254740991.5d + }; + long[] lvals = { + -9007199254740992L, + -9007199254740991L, + 0L, + 0L, + 9007199254740991L, + 9007199254740992L + }; + for (int i = 0; i < dvals.length; i++) { + expectEquals64(lvals[i], round64(dvals[i])); + } + + // A few NaN numbers. + double[] dnans = { + Double.longBitsToDouble(0x7ff0000000000001L), + Double.longBitsToDouble(0x7ff4000000000000L), + Double.longBitsToDouble(0x7ff8000000000000L), + Double.longBitsToDouble(0x7fffffffffffffffL), + Double.longBitsToDouble(0xfff0000000000001L), + Double.longBitsToDouble(0xfff4000000000000L), + Double.longBitsToDouble(0xfff8000000000000L), + Double.longBitsToDouble(0xffffffffffffffffL) + }; + for (int i = 0; i < dnans.length; i++) { + expectEquals64(0L, round64(dnans[i])); + } + + System.out.println("passed"); + } + + private static void expectEquals32(int expected, int result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } + + private static void expectEquals64(long expected, long result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } +} -- GitLab From f193878a242f5b86db62986a9b2cba5d99505c17 Mon Sep 17 00:00:00 2001 From: Christopher Ferris Date: Wed, 9 Mar 2016 16:03:00 -0800 Subject: [PATCH 184/204] Log when why an Unwind failed. Bug: 27449879 Change-Id: If7ae5f0991da6fd64dd45e22d69eecef1388a28e --- runtime/utils.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/runtime/utils.cc b/runtime/utils.cc index 13564a6a0f..472a85c042 100644 --- a/runtime/utils.cc +++ b/runtime/utils.cc @@ -1120,7 +1120,8 @@ void DumpNativeStack(std::ostream& os, pid_t tid, BacktraceMap* existing_map, co } std::unique_ptr backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, tid, map)); if (!backtrace->Unwind(0, reinterpret_cast(ucontext_ptr))) { - os << prefix << "(backtrace::Unwind failed for thread " << tid << ")\n"; + os << prefix << "(backtrace::Unwind failed for thread " << tid + << ": " << backtrace->GetErrorString(backtrace->GetError()) << ")\n"; return; } else if (backtrace->NumFrames() == 0) { os << prefix << "(no native stack frames for thread " << tid << ")\n"; -- GitLab From daed5d81e2fdb9d1e03ee6c34567347b92dcfb22 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Thu, 10 Mar 2016 10:49:35 -0800 Subject: [PATCH 185/204] Allocate interrupted exception before re-acquiring lock Allocating the monitor exception after acquiring the lock can cause a deadlock in the following scenario: Reference queue daemon interrupted from wait() on ReferenceQueue.class. Tries to allocate exception and starts a GC that blocks on WaitForGcToComplete. Some other thread is already doing a GC, and tries to enqueue the cleared refereneces. This deadlocks trying to lock Reference.class in ReferenceQueue.add. Bug: 27508829 Change-Id: Icbc18b6b8d1e906c3f7413810d6cefdda06eb921 --- runtime/monitor.cc | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/runtime/monitor.cc b/runtime/monitor.cc index 1ce5841bed..a262c7a8f3 100644 --- a/runtime/monitor.cc +++ b/runtime/monitor.cc @@ -497,6 +497,24 @@ void Monitor::Wait(Thread* self, int64_t ms, int32_t ns, self->SetWaitMonitor(nullptr); } + // Allocate the interrupted exception not holding the monitor lock since it may cause a GC. + // If the GC requires acquiring the monitor for enqueuing cleared references, this would + // cause a deadlock if the monitor is held. + if (was_interrupted && interruptShouldThrow) { + /* + * We were interrupted while waiting, or somebody interrupted an + * un-interruptible thread earlier and we're bailing out immediately. + * + * The doc sayeth: "The interrupted status of the current thread is + * cleared when this exception is thrown." + */ + { + MutexLock mu(self, *self->GetWaitMutex()); + self->SetInterruptedLocked(false); + } + self->ThrowNewException("Ljava/lang/InterruptedException;", nullptr); + } + // Re-acquire the monitor and lock. Lock(self); monitor_lock_.Lock(self); @@ -516,21 +534,6 @@ void Monitor::Wait(Thread* self, int64_t ms, int32_t ns, RemoveFromWaitSet(self); monitor_lock_.Unlock(self); - - if (was_interrupted && interruptShouldThrow) { - /* - * We were interrupted while waiting, or somebody interrupted an - * un-interruptible thread earlier and we're bailing out immediately. - * - * The doc sayeth: "The interrupted status of the current thread is - * cleared when this exception is thrown." - */ - { - MutexLock mu(self, *self->GetWaitMutex()); - self->SetInterruptedLocked(false); - } - self->ThrowNewException("Ljava/lang/InterruptedException;", nullptr); - } } void Monitor::Notify(Thread* self) { -- GitLab From 057134bdf40981555a8bf56ab8d703a503b40f8f Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Thu, 10 Mar 2016 08:33:45 -0800 Subject: [PATCH 186/204] ART: Release all resource on MonitorPool destruction To be valgrind-clean, we need to release the current metadata and all chunks on destruction. Bug: 27156726 Change-Id: Ia51cea139a6e9669975b6ac045f5223cd68f1f6a --- runtime/monitor_pool.cc | 24 ++++++++++++++++++++++-- runtime/monitor_pool.h | 10 ++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/runtime/monitor_pool.cc b/runtime/monitor_pool.cc index 9e78cda190..ce38e4f108 100644 --- a/runtime/monitor_pool.cc +++ b/runtime/monitor_pool.cc @@ -42,11 +42,12 @@ void MonitorPool::AllocateChunk() { if (capacity_ == 0U) { // Initialization. capacity_ = kInitialChunkStorage; - uintptr_t* new_backing = new uintptr_t[capacity_]; + uintptr_t* new_backing = new uintptr_t[capacity_](); + DCHECK(monitor_chunks_.LoadRelaxed() == nullptr); monitor_chunks_.StoreRelaxed(new_backing); } else { size_t new_capacity = 2 * capacity_; - uintptr_t* new_backing = new uintptr_t[new_capacity]; + uintptr_t* new_backing = new uintptr_t[new_capacity](); uintptr_t* old_backing = monitor_chunks_.LoadRelaxed(); memcpy(new_backing, old_backing, sizeof(uintptr_t) * capacity_); monitor_chunks_.StoreRelaxed(new_backing); @@ -88,6 +89,25 @@ void MonitorPool::AllocateChunk() { first_free_ = last; } +void MonitorPool::FreeInternal() { + // This is on shutdown with NO_THREAD_SAFETY_ANALYSIS, can't/don't need to lock. + uintptr_t* backing = monitor_chunks_.LoadRelaxed(); + DCHECK(backing != nullptr); + DCHECK_GT(capacity_, 0U); + DCHECK_GT(num_chunks_, 0U); + + for (size_t i = 0; i < capacity_; ++i) { + if (i < num_chunks_) { + DCHECK_NE(backing[i], 0U); + allocator_.deallocate(reinterpret_cast(backing[i]), kChunkSize); + } else { + DCHECK_EQ(backing[i], 0U); + } + } + + delete[] backing; +} + Monitor* MonitorPool::CreateMonitorInPool(Thread* self, Thread* owner, mirror::Object* obj, int32_t hash_code) SHARED_REQUIRES(Locks::mutator_lock_) { diff --git a/runtime/monitor_pool.h b/runtime/monitor_pool.h index de553fc99b..875b3fe73d 100644 --- a/runtime/monitor_pool.h +++ b/runtime/monitor_pool.h @@ -104,6 +104,12 @@ class MonitorPool { #endif } + ~MonitorPool() { +#ifdef __LP64__ + FreeInternal(); +#endif + } + private: #ifdef __LP64__ // When we create a monitor pool, threads have not been initialized, yet, so ignore thread-safety @@ -112,6 +118,10 @@ class MonitorPool { void AllocateChunk() REQUIRES(Locks::allocated_monitor_ids_lock_); + // Release all chunks and metadata. This is done on shutdown, where threads have been destroyed, + // so ignore thead-safety analysis. + void FreeInternal() NO_THREAD_SAFETY_ANALYSIS; + Monitor* CreateMonitorInPool(Thread* self, Thread* owner, mirror::Object* obj, int32_t hash_code) SHARED_REQUIRES(Locks::mutator_lock_); -- GitLab From 8d1594da6e97cd11580baf30ee4b75d4e7e2616a Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Tue, 1 Mar 2016 14:38:37 -0800 Subject: [PATCH 187/204] ART: Allow unwinding unattached threads Partial revert of commit ed8b723c5f3989d2593ec21c65c96d6d8bf25579. Make it (constexpr) configurable whether we allow unwinding native stacks of unattached threads. Bug: 27449879 Change-Id: I307a80c9a0166f33fa8a41e492d7eb0d77d0e44f --- runtime/thread_list.cc | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc index 4c81d4f1e3..afb11d33e9 100644 --- a/runtime/thread_list.cc +++ b/runtime/thread_list.cc @@ -57,6 +57,10 @@ static constexpr useconds_t kThreadSuspendInitialSleepUs = 0; static constexpr useconds_t kThreadSuspendMaxYieldUs = 3000; static constexpr useconds_t kThreadSuspendMaxSleepUs = 5000; +// Whether we should try to dump the native stack of unattached threads. See commit ed8b723 for +// some history. +static constexpr bool kDumpUnattachedThreadNativeStack = true; + ThreadList::ThreadList() : suspend_all_count_(0), debug_suspend_all_count_(0), @@ -138,9 +142,7 @@ static void DumpUnattachedThread(std::ostream& os, pid_t tid) NO_THREAD_SAFETY_A // refactor DumpState to avoid skipping analysis. Thread::DumpState(os, nullptr, tid); DumpKernelStack(os, tid, " kernel: ", false); - // TODO: Reenable this when the native code in system_server can handle it. - // Currently "adb shell kill -3 `pid system_server`" will cause it to exit. - if (false) { + if (kDumpUnattachedThreadNativeStack) { DumpNativeStack(os, tid, nullptr, " native: "); } os << "\n"; -- GitLab From 07e3ca9a46801990d662c48d0fddedc63c4c053c Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Fri, 11 Mar 2016 09:57:57 +0000 Subject: [PATCH 188/204] Fix thread race when fetching the ProfilingInfo object. Problem is: 1) Compiler fetches the ProfilingInfo of A, it's null. 2) Mutator creates the ProfilingInfo. 3) Compiler notifies it's not using A anymore, calls ProfilingInfo::DecrementInlineUse -> Crash as we expected ProfilingInfo::IncrementUse to be called before. Also update some namings to better reflect what is going on. Change-Id: I55ea4c5d81988131467095e18a0d13a8be9d0ef7 --- compiler/optimizing/inliner.cc | 26 +++++++++++++++++++------- runtime/jit/jit_code_cache.cc | 10 +++++----- runtime/jit/jit_code_cache.h | 8 ++++++-- runtime/jit/profiling_info.h | 3 ++- 4 files changed, 32 insertions(+), 15 deletions(-) diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index bbdac262c4..d861e39c8b 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -224,16 +224,29 @@ static uint32_t FindClassIndexIn(mirror::Class* cls, class ScopedProfilingInfoInlineUse { public: - explicit ScopedProfilingInfoInlineUse(ArtMethod* method) : method_(method) { - Runtime::Current()->GetJit()->GetCodeCache()->NotifyInliningOf(method_, Thread::Current()); + explicit ScopedProfilingInfoInlineUse(ArtMethod* method, Thread* self) + : method_(method), + self_(self), + // Fetch the profiling info ahead of using it. If it's null when fetching, + // we should not call JitCodeCache::DoneInlining. + profiling_info_( + Runtime::Current()->GetJit()->GetCodeCache()->NotifyCompilerUse(method, self)) { } ~ScopedProfilingInfoInlineUse() { - Runtime::Current()->GetJit()->GetCodeCache()->DoneInlining(method_, Thread::Current()); + if (profiling_info_ != nullptr) { + size_t pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); + DCHECK_EQ(profiling_info_, method_->GetProfilingInfo(pointer_size)); + Runtime::Current()->GetJit()->GetCodeCache()->DoneCompilerUse(method_, self_); + } } + ProfilingInfo* GetProfilingInfo() const { return profiling_info_; } + private: ArtMethod* const method_; + Thread* const self_; + ProfilingInfo* const profiling_info_; }; bool HInliner::TryInline(HInvoke* invoke_instruction) { @@ -287,15 +300,14 @@ bool HInliner::TryInline(HInvoke* invoke_instruction) { // Check if we can use an inline cache. ArtMethod* caller = graph_->GetArtMethod(); - size_t pointer_size = class_linker->GetImagePointerSize(); if (Runtime::Current()->UseJit()) { // Under JIT, we should always know the caller. DCHECK(caller != nullptr); - ScopedProfilingInfoInlineUse spiis(caller); - ProfilingInfo* profiling_info = caller->GetProfilingInfo(pointer_size); + ScopedProfilingInfoInlineUse spiis(caller, soa.Self()); + ProfilingInfo* profiling_info = spiis.GetProfilingInfo(); if (profiling_info != nullptr) { const InlineCache& ic = *profiling_info->GetInlineCache(invoke_instruction->GetDexPc()); - if (ic.IsUnitialized()) { + if (ic.IsUninitialized()) { VLOG(compiler) << "Interface or virtual call to " << PrettyMethod(method_index, caller_dex_file) << " is not hit and not inlined"; diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index 050bb68336..af47da63c4 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -928,20 +928,20 @@ bool JitCodeCache::NotifyCompilationOf(ArtMethod* method, Thread* self, bool osr return true; } -void JitCodeCache::NotifyInliningOf(ArtMethod* method, Thread* self) { +ProfilingInfo* JitCodeCache::NotifyCompilerUse(ArtMethod* method, Thread* self) { MutexLock mu(self, lock_); ProfilingInfo* info = method->GetProfilingInfo(sizeof(void*)); if (info != nullptr) { info->IncrementInlineUse(); } + return info; } -void JitCodeCache::DoneInlining(ArtMethod* method, Thread* self) { +void JitCodeCache::DoneCompilerUse(ArtMethod* method, Thread* self) { MutexLock mu(self, lock_); ProfilingInfo* info = method->GetProfilingInfo(sizeof(void*)); - if (info != nullptr) { - info->DecrementInlineUse(); - } + DCHECK(info != nullptr); + info->DecrementInlineUse(); } void JitCodeCache::DoneCompiling(ArtMethod* method, Thread* self ATTRIBUTE_UNUSED) { diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h index 113bebfa65..98dd70dcf9 100644 --- a/runtime/jit/jit_code_cache.h +++ b/runtime/jit/jit_code_cache.h @@ -71,7 +71,11 @@ class JitCodeCache { SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!lock_); - void NotifyInliningOf(ArtMethod* method, Thread* self) + // Notify to the code cache that the compiler wants to use the + // profiling info of `method` to drive optimizations, + // and therefore ensure the returned profiling info object is not + // collected. + ProfilingInfo* NotifyCompilerUse(ArtMethod* method, Thread* self) SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!lock_); @@ -79,7 +83,7 @@ class JitCodeCache { SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!lock_); - void DoneInlining(ArtMethod* method, Thread* self) + void DoneCompilerUse(ArtMethod* method, Thread* self) SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!lock_); diff --git a/runtime/jit/profiling_info.h b/runtime/jit/profiling_info.h index 73c1a1edb0..55d627ab48 100644 --- a/runtime/jit/profiling_info.h +++ b/runtime/jit/profiling_info.h @@ -56,10 +56,11 @@ class InlineCache { mirror::Class* GetMonomorphicType() const SHARED_REQUIRES(Locks::mutator_lock_) { // Note that we cannot ensure the inline cache is actually monomorphic // at this point, as other threads may have updated it. + DCHECK(!classes_[0].IsNull()); return classes_[0].Read(); } - bool IsUnitialized() const { + bool IsUninitialized() const { return classes_[0].IsNull(); } -- GitLab From bdd7935c2adc3ad190ee87958e714a36f33cedae Mon Sep 17 00:00:00 2001 From: Anton Shamin Date: Mon, 15 Feb 2016 12:48:36 +0600 Subject: [PATCH 189/204] Revert "Revert "Revert "Revert "Change condition to opposite if lhs is constant"""" This reverts commit d4aee949b3dd976295201b5310f13aa2df40afa1. Change-Id: I505b8c9863c310a3a708f580b00d425b750c9541 --- compiler/optimizing/instruction_simplifier.cc | 72 ++++++++++++++++++- compiler/optimizing/nodes.h | 16 ++++- .../src/Main.java | 33 ++++++++- test/537-checker-jump-over-jump/src/Main.java | 2 +- 4 files changed, 117 insertions(+), 6 deletions(-) diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index b95ece5a31..049901b882 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -70,6 +70,10 @@ class InstructionSimplifierVisitor : public HGraphDelegateVisitor { void VisitGreaterThanOrEqual(HGreaterThanOrEqual* condition) OVERRIDE; void VisitLessThan(HLessThan* condition) OVERRIDE; void VisitLessThanOrEqual(HLessThanOrEqual* condition) OVERRIDE; + void VisitBelow(HBelow* condition) OVERRIDE; + void VisitBelowOrEqual(HBelowOrEqual* condition) OVERRIDE; + void VisitAbove(HAbove* condition) OVERRIDE; + void VisitAboveOrEqual(HAboveOrEqual* condition) OVERRIDE; void VisitDiv(HDiv* instruction) OVERRIDE; void VisitMul(HMul* instruction) OVERRIDE; void VisitNeg(HNeg* instruction) OVERRIDE; @@ -559,6 +563,36 @@ void InstructionSimplifierVisitor::VisitSuspendCheck(HSuspendCheck* check) { block->RemoveInstruction(check); } +static HCondition* GetOppositeConditionSwapOps(ArenaAllocator* arena, HInstruction* cond) { + HInstruction *lhs = cond->InputAt(0); + HInstruction *rhs = cond->InputAt(1); + switch (cond->GetKind()) { + case HInstruction::kEqual: + return new (arena) HEqual(rhs, lhs); + case HInstruction::kNotEqual: + return new (arena) HNotEqual(rhs, lhs); + case HInstruction::kLessThan: + return new (arena) HGreaterThan(rhs, lhs); + case HInstruction::kLessThanOrEqual: + return new (arena) HGreaterThanOrEqual(rhs, lhs); + case HInstruction::kGreaterThan: + return new (arena) HLessThan(rhs, lhs); + case HInstruction::kGreaterThanOrEqual: + return new (arena) HLessThanOrEqual(rhs, lhs); + case HInstruction::kBelow: + return new (arena) HAbove(rhs, lhs); + case HInstruction::kBelowOrEqual: + return new (arena) HAboveOrEqual(rhs, lhs); + case HInstruction::kAbove: + return new (arena) HBelow(rhs, lhs); + case HInstruction::kAboveOrEqual: + return new (arena) HBelowOrEqual(rhs, lhs); + default: + LOG(FATAL) << "Unknown ConditionType " << cond->GetKind(); + } + return nullptr; +} + void InstructionSimplifierVisitor::VisitEqual(HEqual* equal) { HInstruction* input_const = equal->GetConstantRight(); if (input_const != nullptr) { @@ -982,13 +1016,47 @@ void InstructionSimplifierVisitor::VisitLessThanOrEqual(HLessThanOrEqual* condit VisitCondition(condition); } -// TODO: unsigned comparisons too? +void InstructionSimplifierVisitor::VisitBelow(HBelow* condition) { + VisitCondition(condition); +} + +void InstructionSimplifierVisitor::VisitBelowOrEqual(HBelowOrEqual* condition) { + VisitCondition(condition); +} + +void InstructionSimplifierVisitor::VisitAbove(HAbove* condition) { + VisitCondition(condition); +} + +void InstructionSimplifierVisitor::VisitAboveOrEqual(HAboveOrEqual* condition) { + VisitCondition(condition); +} void InstructionSimplifierVisitor::VisitCondition(HCondition* condition) { - // Try to fold an HCompare into this HCondition. + // Reverse condition if left is constant. Our code generators prefer constant + // on the right hand side. + if (condition->GetLeft()->IsConstant() && !condition->GetRight()->IsConstant()) { + HBasicBlock* block = condition->GetBlock(); + HCondition* replacement = GetOppositeConditionSwapOps(block->GetGraph()->GetArena(), condition); + // If it is a fp we must set the opposite bias. + if (replacement != nullptr) { + if (condition->IsLtBias()) { + replacement->SetBias(ComparisonBias::kGtBias); + } else if (condition->IsGtBias()) { + replacement->SetBias(ComparisonBias::kLtBias); + } + block->ReplaceAndRemoveInstructionWith(condition, replacement); + RecordSimplification(); + + condition = replacement; + } + } HInstruction* left = condition->GetLeft(); HInstruction* right = condition->GetRight(); + + // Try to fold an HCompare into this HCondition. + // We can only replace an HCondition which compares a Compare to 0. // Both 'dx' and 'jack' generate a compare to 0 when compiling a // condition with a long, float or double comparison as input. diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index ecb690ff62..b3c51a1dbd 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -2964,6 +2964,8 @@ class HCondition : public HBinaryOperation { virtual IfCondition GetOppositeCondition() const = 0; bool IsGtBias() const { return GetBias() == ComparisonBias::kGtBias; } + bool IsLtBias() const { return GetBias() == ComparisonBias::kLtBias; } + ComparisonBias GetBias() const { return GetPackedField(); } void SetBias(ComparisonBias bias) { SetPackedField(bias); } @@ -2974,13 +2976,23 @@ class HCondition : public HBinaryOperation { bool IsFPConditionTrueIfNaN() const { DCHECK(Primitive::IsFloatingPointType(InputAt(0)->GetType())) << InputAt(0)->GetType(); IfCondition if_cond = GetCondition(); - return IsGtBias() ? ((if_cond == kCondGT) || (if_cond == kCondGE)) : (if_cond == kCondNE); + if (if_cond == kCondNE) { + return true; + } else if (if_cond == kCondEQ) { + return false; + } + return ((if_cond == kCondGT) || (if_cond == kCondGE)) && IsGtBias(); } bool IsFPConditionFalseIfNaN() const { DCHECK(Primitive::IsFloatingPointType(InputAt(0)->GetType())) << InputAt(0)->GetType(); IfCondition if_cond = GetCondition(); - return IsGtBias() ? ((if_cond == kCondLT) || (if_cond == kCondLE)) : (if_cond == kCondEQ); + if (if_cond == kCondEQ) { + return true; + } else if (if_cond == kCondNE) { + return false; + } + return ((if_cond == kCondLT) || (if_cond == kCondLE)) && IsGtBias(); } protected: diff --git a/test/458-checker-instruction-simplification/src/Main.java b/test/458-checker-instruction-simplification/src/Main.java index 8640148795..dd4ffe45e4 100644 --- a/test/458-checker-instruction-simplification/src/Main.java +++ b/test/458-checker-instruction-simplification/src/Main.java @@ -1601,6 +1601,34 @@ public class Main { return (short) (value & 0x17fff); } + /// CHECK-START: int Main.intReverseCondition(int) instruction_simplifier (before) + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> IntConstant 42 + /// CHECK-DAG: <> LessThanOrEqual [<>,<>] + + /// CHECK-START: int Main.intReverseCondition(int) instruction_simplifier (after) + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> IntConstant 42 + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] + + public static int intReverseCondition(int i) { + return (42 > i) ? 13 : 54; + } + + /// CHECK-START: int Main.intReverseConditionNaN(int) instruction_simplifier (before) + /// CHECK-DAG: <> DoubleConstant 42 + /// CHECK-DAG: <> InvokeStaticOrDirect + /// CHECK-DAG: <> Compare [<>,<>] + + /// CHECK-START: int Main.intReverseConditionNaN(int) instruction_simplifier (after) + /// CHECK-DAG: <> DoubleConstant 42 + /// CHECK-DAG: <> InvokeStaticOrDirect + /// CHECK-DAG: <> Equal [<>,<>] + + public static int intReverseConditionNaN(int i) { + return (42 != Math.sqrt(i)) ? 13 : 54; + } + public static int runSmaliTest(String name, boolean input) { try { Class c = Class.forName("SmaliTests"); @@ -1611,7 +1639,7 @@ public class Main { } } - public static void main(String[] args) { +public static void main(String[] args) { int arg = 123456; assertLongEquals(Add0(arg), arg); @@ -1740,6 +1768,9 @@ public class Main { assertIntEquals(intAnd0x17fffToShort(Integer.MIN_VALUE), 0); assertIntEquals(intAnd0x17fffToShort(Integer.MAX_VALUE), Short.MAX_VALUE); + assertIntEquals(intReverseCondition(41), 13); + assertIntEquals(intReverseConditionNaN(-5), 13); + for (String condition : new String[] { "Equal", "NotEqual" }) { for (String constant : new String[] { "True", "False" }) { for (String side : new String[] { "Rhs", "Lhs" }) { diff --git a/test/537-checker-jump-over-jump/src/Main.java b/test/537-checker-jump-over-jump/src/Main.java index cf9a69d28e..7a58e8b1ac 100644 --- a/test/537-checker-jump-over-jump/src/Main.java +++ b/test/537-checker-jump-over-jump/src/Main.java @@ -24,7 +24,7 @@ public class Main { // /// CHECK: If /// CHECK-NEXT: cmp - /// CHECK-NEXT: jnl/ge + /// CHECK-NEXT: jle/ng // /// CHECK-DAG: <> StaticFieldGet /// CHECK-DAG: NullCheck [<>] -- GitLab From 7fc6350f6f1ab04b52b9cd7542e0790528296cbe Mon Sep 17 00:00:00 2001 From: Artem Serov Date: Tue, 9 Feb 2016 17:15:29 +0000 Subject: [PATCH 190/204] Integrate BitwiseNegated into shared framework. Share implementation between arm and arm64. Change-Id: I0dd12e772cb23b4c181fd0b1e2a447470b1d8702 --- compiler/optimizing/code_generator_arm.cc | 65 +++++++++ compiler/optimizing/code_generator_arm64.cc | 5 +- compiler/optimizing/graph_visualizer.cc | 6 +- .../optimizing/instruction_simplifier_arm.cc | 13 ++ .../optimizing/instruction_simplifier_arm.h | 2 + .../instruction_simplifier_arm64.cc | 55 ++----- .../optimizing/instruction_simplifier_arm64.h | 4 - .../instruction_simplifier_shared.cc | 43 ++++++ .../instruction_simplifier_shared.h | 3 + compiler/optimizing/nodes.h | 2 +- compiler/optimizing/nodes_arm64.h | 60 -------- compiler/optimizing/nodes_shared.h | 60 ++++++++ test/564-checker-negbitwise/src/Main.java | 137 +++++++++++++++--- 13 files changed, 316 insertions(+), 139 deletions(-) diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index aa9b01f30b..0b7fefafdd 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -5727,6 +5727,71 @@ void InstructionCodeGeneratorARM::VisitXor(HXor* instruction) { HandleBitwiseOperation(instruction); } + +void LocationsBuilderARM::VisitBitwiseNegatedRight(HBitwiseNegatedRight* instruction) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); + DCHECK(instruction->GetResultType() == Primitive::kPrimInt + || instruction->GetResultType() == Primitive::kPrimLong); + + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); +} + +void InstructionCodeGeneratorARM::VisitBitwiseNegatedRight(HBitwiseNegatedRight* instruction) { + LocationSummary* locations = instruction->GetLocations(); + Location first = locations->InAt(0); + Location second = locations->InAt(1); + Location out = locations->Out(); + + if (instruction->GetResultType() == Primitive::kPrimInt) { + Register first_reg = first.AsRegister(); + ShifterOperand second_reg(second.AsRegister()); + Register out_reg = out.AsRegister(); + + switch (instruction->GetOpKind()) { + case HInstruction::kAnd: + __ bic(out_reg, first_reg, second_reg); + break; + case HInstruction::kOr: + __ orn(out_reg, first_reg, second_reg); + break; + // There is no EON on arm. + case HInstruction::kXor: + default: + LOG(FATAL) << "Unexpected instruction " << instruction->DebugName(); + UNREACHABLE(); + } + return; + + } else { + DCHECK_EQ(instruction->GetResultType(), Primitive::kPrimLong); + Register first_low = first.AsRegisterPairLow(); + Register first_high = first.AsRegisterPairHigh(); + ShifterOperand second_low(second.AsRegisterPairLow()); + ShifterOperand second_high(second.AsRegisterPairHigh()); + Register out_low = out.AsRegisterPairLow(); + Register out_high = out.AsRegisterPairHigh(); + + switch (instruction->GetOpKind()) { + case HInstruction::kAnd: + __ bic(out_low, first_low, second_low); + __ bic(out_high, first_high, second_high); + break; + case HInstruction::kOr: + __ orn(out_low, first_low, second_low); + __ orn(out_high, first_high, second_high); + break; + // There is no EON on arm. + case HInstruction::kXor: + default: + LOG(FATAL) << "Unexpected instruction " << instruction->DebugName(); + UNREACHABLE(); + } + } +} + void InstructionCodeGeneratorARM::GenerateAndConst(Register out, Register first, uint32_t value) { // Optimize special cases for individual halfs of `and-long` (`and` is simplified earlier). if (value == 0xffffffffu) { diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index 985dc056f6..89b9e2c599 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -1862,7 +1862,7 @@ void InstructionCodeGeneratorARM64::VisitAnd(HAnd* instruction) { HandleBinaryOp(instruction); } -void LocationsBuilderARM64::VisitArm64BitwiseNegatedRight(HArm64BitwiseNegatedRight* instr) { +void LocationsBuilderARM64::VisitBitwiseNegatedRight(HBitwiseNegatedRight* instr) { DCHECK(Primitive::IsIntegralType(instr->GetType())) << instr->GetType(); LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instr); locations->SetInAt(0, Location::RequiresRegister()); @@ -1871,8 +1871,7 @@ void LocationsBuilderARM64::VisitArm64BitwiseNegatedRight(HArm64BitwiseNegatedRi locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); } -void InstructionCodeGeneratorARM64::VisitArm64BitwiseNegatedRight( - HArm64BitwiseNegatedRight* instr) { +void InstructionCodeGeneratorARM64::VisitBitwiseNegatedRight(HBitwiseNegatedRight* instr) { Register dst = OutputRegister(instr); Register lhs = InputRegisterAt(instr, 0); Register rhs = InputRegisterAt(instr, 1); diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc index b9638f2027..4f1e90cd7f 100644 --- a/compiler/optimizing/graph_visualizer.cc +++ b/compiler/optimizing/graph_visualizer.cc @@ -440,13 +440,13 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor { void VisitMultiplyAccumulate(HMultiplyAccumulate* instruction) OVERRIDE { StartAttributeStream("kind") << instruction->GetOpKind(); } -#endif -#ifdef ART_ENABLE_CODEGEN_arm64 - void VisitArm64BitwiseNegatedRight(HArm64BitwiseNegatedRight* instruction) OVERRIDE { + void VisitBitwiseNegatedRight(HBitwiseNegatedRight* instruction) OVERRIDE { StartAttributeStream("kind") << instruction->GetOpKind(); } +#endif +#ifdef ART_ENABLE_CODEGEN_arm64 void VisitArm64DataProcWithShifterOp(HArm64DataProcWithShifterOp* instruction) OVERRIDE { StartAttributeStream("kind") << instruction->GetInstrKind() << "+" << instruction->GetOpKind(); if (HArm64DataProcWithShifterOp::IsShiftOp(instruction->GetOpKind())) { diff --git a/compiler/optimizing/instruction_simplifier_arm.cc b/compiler/optimizing/instruction_simplifier_arm.cc index db1f9a79aa..cd026b8770 100644 --- a/compiler/optimizing/instruction_simplifier_arm.cc +++ b/compiler/optimizing/instruction_simplifier_arm.cc @@ -26,5 +26,18 @@ void InstructionSimplifierArmVisitor::VisitMul(HMul* instruction) { } } +void InstructionSimplifierArmVisitor::VisitOr(HOr* instruction) { + if (TryMergeNegatedInput(instruction)) { + RecordSimplification(); + } +} + +void InstructionSimplifierArmVisitor::VisitAnd(HAnd* instruction) { + if (TryMergeNegatedInput(instruction)) { + RecordSimplification(); + } +} + + } // namespace arm } // namespace art diff --git a/compiler/optimizing/instruction_simplifier_arm.h b/compiler/optimizing/instruction_simplifier_arm.h index 379b95d6ae..14c940eb21 100644 --- a/compiler/optimizing/instruction_simplifier_arm.h +++ b/compiler/optimizing/instruction_simplifier_arm.h @@ -36,6 +36,8 @@ class InstructionSimplifierArmVisitor : public HGraphVisitor { } void VisitMul(HMul* instruction) OVERRIDE; + void VisitOr(HOr* instruction) OVERRIDE; + void VisitAnd(HAnd* instruction) OVERRIDE; OptimizingCompilerStats* stats_; }; diff --git a/compiler/optimizing/instruction_simplifier_arm64.cc b/compiler/optimizing/instruction_simplifier_arm64.cc index c2bbdccc29..f00d960877 100644 --- a/compiler/optimizing/instruction_simplifier_arm64.cc +++ b/compiler/optimizing/instruction_simplifier_arm64.cc @@ -180,51 +180,10 @@ bool InstructionSimplifierArm64Visitor::TryMergeIntoUsersShifterOperand(HInstruc return true; } -bool InstructionSimplifierArm64Visitor::TryMergeNegatedInput(HBinaryOperation* op) { - DCHECK(op->IsAnd() || op->IsOr() || op->IsXor()) << op->DebugName(); - HInstruction* left = op->GetLeft(); - HInstruction* right = op->GetRight(); - - // Only consider the case where there is exactly one Not, with 2 Not's De - // Morgan's laws should be applied instead. - if (left->IsNot() ^ right->IsNot()) { - HInstruction* hnot = (left->IsNot() ? left : right); - HInstruction* hother = (left->IsNot() ? right : left); - - // Only do the simplification if the Not has only one use and can thus be - // safely removed. Even though ARM64 negated bitwise operations do not have - // an immediate variant (only register), we still do the simplification when - // `hother` is a constant, because it removes an instruction if the constant - // cannot be encoded as an immediate: - // mov r0, #large_constant - // neg r2, r1 - // and r0, r0, r2 - // becomes: - // mov r0, #large_constant - // bic r0, r0, r1 - if (hnot->HasOnlyOneNonEnvironmentUse()) { - // Replace code looking like - // NOT tmp, mask - // AND dst, src, tmp (respectively ORR, EOR) - // with - // BIC dst, src, mask (respectively ORN, EON) - HInstruction* src = hnot->AsNot()->GetInput(); - - HArm64BitwiseNegatedRight* neg_op = new (GetGraph()->GetArena()) - HArm64BitwiseNegatedRight(op->GetType(), op->GetKind(), hother, src, op->GetDexPc()); - - op->GetBlock()->ReplaceAndRemoveInstructionWith(op, neg_op); - hnot->GetBlock()->RemoveInstruction(hnot); - RecordSimplification(); - return true; - } - } - - return false; -} - void InstructionSimplifierArm64Visitor::VisitAnd(HAnd* instruction) { - TryMergeNegatedInput(instruction); + if (TryMergeNegatedInput(instruction)) { + RecordSimplification(); + } } void InstructionSimplifierArm64Visitor::VisitArrayGet(HArrayGet* instruction) { @@ -248,7 +207,9 @@ void InstructionSimplifierArm64Visitor::VisitMul(HMul* instruction) { } void InstructionSimplifierArm64Visitor::VisitOr(HOr* instruction) { - TryMergeNegatedInput(instruction); + if (TryMergeNegatedInput(instruction)) { + RecordSimplification(); + } } void InstructionSimplifierArm64Visitor::VisitShl(HShl* instruction) { @@ -284,7 +245,9 @@ void InstructionSimplifierArm64Visitor::VisitUShr(HUShr* instruction) { } void InstructionSimplifierArm64Visitor::VisitXor(HXor* instruction) { - TryMergeNegatedInput(instruction); + if (TryMergeNegatedInput(instruction)) { + RecordSimplification(); + } } } // namespace arm64 diff --git a/compiler/optimizing/instruction_simplifier_arm64.h b/compiler/optimizing/instruction_simplifier_arm64.h index cf8458713f..338120bbbc 100644 --- a/compiler/optimizing/instruction_simplifier_arm64.h +++ b/compiler/optimizing/instruction_simplifier_arm64.h @@ -51,10 +51,6 @@ class InstructionSimplifierArm64Visitor : public HGraphVisitor { return TryMergeIntoShifterOperand(use, bitfield_op, true); } - // For bitwise operations (And/Or/Xor) with a negated input, try to use - // a negated bitwise instruction. - bool TryMergeNegatedInput(HBinaryOperation* op); - // HInstruction visitors, sorted alphabetically. void VisitAnd(HAnd* instruction) OVERRIDE; void VisitArrayGet(HArrayGet* instruction) OVERRIDE; diff --git a/compiler/optimizing/instruction_simplifier_shared.cc b/compiler/optimizing/instruction_simplifier_shared.cc index 45d196fa6d..a11b5bd5c3 100644 --- a/compiler/optimizing/instruction_simplifier_shared.cc +++ b/compiler/optimizing/instruction_simplifier_shared.cc @@ -186,4 +186,47 @@ bool TryCombineMultiplyAccumulate(HMul* mul, InstructionSet isa) { return false; } + +bool TryMergeNegatedInput(HBinaryOperation* op) { + DCHECK(op->IsAnd() || op->IsOr() || op->IsXor()) << op->DebugName(); + HInstruction* left = op->GetLeft(); + HInstruction* right = op->GetRight(); + + // Only consider the case where there is exactly one Not, with 2 Not's De + // Morgan's laws should be applied instead. + if (left->IsNot() ^ right->IsNot()) { + HInstruction* hnot = (left->IsNot() ? left : right); + HInstruction* hother = (left->IsNot() ? right : left); + + // Only do the simplification if the Not has only one use and can thus be + // safely removed. Even though ARM64 negated bitwise operations do not have + // an immediate variant (only register), we still do the simplification when + // `hother` is a constant, because it removes an instruction if the constant + // cannot be encoded as an immediate: + // mov r0, #large_constant + // neg r2, r1 + // and r0, r0, r2 + // becomes: + // mov r0, #large_constant + // bic r0, r0, r1 + if (hnot->HasOnlyOneNonEnvironmentUse()) { + // Replace code looking like + // NOT tmp, mask + // AND dst, src, tmp (respectively ORR, EOR) + // with + // BIC dst, src, mask (respectively ORN, EON) + HInstruction* src = hnot->AsNot()->GetInput(); + + HBitwiseNegatedRight* neg_op = new (hnot->GetBlock()->GetGraph()->GetArena()) + HBitwiseNegatedRight(op->GetType(), op->GetKind(), hother, src, op->GetDexPc()); + + op->GetBlock()->ReplaceAndRemoveInstructionWith(op, neg_op); + hnot->GetBlock()->RemoveInstruction(hnot); + return true; + } + } + + return false; +} + } // namespace art diff --git a/compiler/optimizing/instruction_simplifier_shared.h b/compiler/optimizing/instruction_simplifier_shared.h index 9832ecc058..b1fe8f4756 100644 --- a/compiler/optimizing/instruction_simplifier_shared.h +++ b/compiler/optimizing/instruction_simplifier_shared.h @@ -22,6 +22,9 @@ namespace art { bool TryCombineMultiplyAccumulate(HMul* mul, InstructionSet isa); +// For bitwise operations (And/Or/Xor) with a negated input, try to use +// a negated bitwise instruction. +bool TryMergeNegatedInput(HBinaryOperation* op); } // namespace art diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index ecb690ff62..be304f610d 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -1265,6 +1265,7 @@ class HLoopInformationOutwardIterator : public ValueObject { #define FOR_EACH_CONCRETE_INSTRUCTION_SHARED(M) #else #define FOR_EACH_CONCRETE_INSTRUCTION_SHARED(M) \ + M(BitwiseNegatedRight, Instruction) \ M(MultiplyAccumulate, Instruction) #endif @@ -1279,7 +1280,6 @@ class HLoopInformationOutwardIterator : public ValueObject { #define FOR_EACH_CONCRETE_INSTRUCTION_ARM64(M) #else #define FOR_EACH_CONCRETE_INSTRUCTION_ARM64(M) \ - M(Arm64BitwiseNegatedRight, Instruction) \ M(Arm64DataProcWithShifterOp, Instruction) \ M(Arm64IntermediateAddress, Instruction) #endif diff --git a/compiler/optimizing/nodes_arm64.h b/compiler/optimizing/nodes_arm64.h index 75a71e78b8..173852a55d 100644 --- a/compiler/optimizing/nodes_arm64.h +++ b/compiler/optimizing/nodes_arm64.h @@ -118,66 +118,6 @@ class HArm64IntermediateAddress : public HExpression<2> { DISALLOW_COPY_AND_ASSIGN(HArm64IntermediateAddress); }; -class HArm64BitwiseNegatedRight : public HBinaryOperation { - public: - HArm64BitwiseNegatedRight(Primitive::Type result_type, - InstructionKind op, - HInstruction* left, - HInstruction* right, - uint32_t dex_pc = kNoDexPc) - : HBinaryOperation(result_type, left, right, SideEffects::None(), dex_pc), - op_kind_(op) { - DCHECK(op == HInstruction::kAnd || op == HInstruction::kOr || op == HInstruction::kXor) << op; - } - - template - auto Compute(T x, U y) const -> decltype(x & ~y) { - static_assert(std::is_same::value && - std::is_same::value, - "Inconsistent negated bitwise types"); - switch (op_kind_) { - case HInstruction::kAnd: - return x & ~y; - case HInstruction::kOr: - return x | ~y; - case HInstruction::kXor: - return x ^ ~y; - default: - LOG(FATAL) << "Unreachable"; - UNREACHABLE(); - } - } - - HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE { - return GetBlock()->GetGraph()->GetIntConstant( - Compute(x->GetValue(), y->GetValue()), GetDexPc()); - } - HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE { - return GetBlock()->GetGraph()->GetLongConstant( - Compute(x->GetValue(), y->GetValue()), GetDexPc()); - } - HConstant* Evaluate(HFloatConstant* x ATTRIBUTE_UNUSED, - HFloatConstant* y ATTRIBUTE_UNUSED) const OVERRIDE { - LOG(FATAL) << DebugName() << " is not defined for float values"; - UNREACHABLE(); - } - HConstant* Evaluate(HDoubleConstant* x ATTRIBUTE_UNUSED, - HDoubleConstant* y ATTRIBUTE_UNUSED) const OVERRIDE { - LOG(FATAL) << DebugName() << " is not defined for double values"; - UNREACHABLE(); - } - - InstructionKind GetOpKind() const { return op_kind_; } - - DECLARE_INSTRUCTION(Arm64BitwiseNegatedRight); - - private: - // Specifies the bitwise operation, which will be then negated. - const InstructionKind op_kind_; - - DISALLOW_COPY_AND_ASSIGN(HArm64BitwiseNegatedRight); -}; - } // namespace art #endif // ART_COMPILER_OPTIMIZING_NODES_ARM64_H_ diff --git a/compiler/optimizing/nodes_shared.h b/compiler/optimizing/nodes_shared.h index b04b622838..c10c718ff4 100644 --- a/compiler/optimizing/nodes_shared.h +++ b/compiler/optimizing/nodes_shared.h @@ -53,6 +53,66 @@ class HMultiplyAccumulate : public HExpression<3> { DISALLOW_COPY_AND_ASSIGN(HMultiplyAccumulate); }; +class HBitwiseNegatedRight : public HBinaryOperation { + public: + HBitwiseNegatedRight(Primitive::Type result_type, + InstructionKind op, + HInstruction* left, + HInstruction* right, + uint32_t dex_pc = kNoDexPc) + : HBinaryOperation(result_type, left, right, SideEffects::None(), dex_pc), + op_kind_(op) { + DCHECK(op == HInstruction::kAnd || op == HInstruction::kOr || op == HInstruction::kXor) << op; + } + + template + auto Compute(T x, U y) const -> decltype(x & ~y) { + static_assert(std::is_same::value && + std::is_same::value, + "Inconsistent negated bitwise types"); + switch (op_kind_) { + case HInstruction::kAnd: + return x & ~y; + case HInstruction::kOr: + return x | ~y; + case HInstruction::kXor: + return x ^ ~y; + default: + LOG(FATAL) << "Unreachable"; + UNREACHABLE(); + } + } + + HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetIntConstant( + Compute(x->GetValue(), y->GetValue()), GetDexPc()); + } + HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetLongConstant( + Compute(x->GetValue(), y->GetValue()), GetDexPc()); + } + HConstant* Evaluate(HFloatConstant* x ATTRIBUTE_UNUSED, + HFloatConstant* y ATTRIBUTE_UNUSED) const OVERRIDE { + LOG(FATAL) << DebugName() << " is not defined for float values"; + UNREACHABLE(); + } + HConstant* Evaluate(HDoubleConstant* x ATTRIBUTE_UNUSED, + HDoubleConstant* y ATTRIBUTE_UNUSED) const OVERRIDE { + LOG(FATAL) << DebugName() << " is not defined for double values"; + UNREACHABLE(); + } + + InstructionKind GetOpKind() const { return op_kind_; } + + DECLARE_INSTRUCTION(BitwiseNegatedRight); + + private: + // Specifies the bitwise operation, which will be then negated. + const InstructionKind op_kind_; + + DISALLOW_COPY_AND_ASSIGN(HBitwiseNegatedRight); +}; + } // namespace art #endif // ART_COMPILER_OPTIMIZING_NODES_SHARED_H_ diff --git a/test/564-checker-negbitwise/src/Main.java b/test/564-checker-negbitwise/src/Main.java index 3de7be7161..ccb8ff4fdf 100644 --- a/test/564-checker-negbitwise/src/Main.java +++ b/test/564-checker-negbitwise/src/Main.java @@ -45,7 +45,7 @@ public class Main { /// CHECK-START-ARM64: int Main.$opt$noinline$notAnd(int, int) instruction_simplifier_arm64 (after) /// CHECK: <> ParameterValue /// CHECK: <> ParameterValue - /// CHECK: <> Arm64BitwiseNegatedRight [<>,<>] kind:And + /// CHECK: <> BitwiseNegatedRight [<>,<>] kind:And /// CHECK: Return [<>] /// CHECK-START-ARM64: int Main.$opt$noinline$notAnd(int, int) instruction_simplifier_arm64 (after) @@ -55,6 +55,27 @@ public class Main { /// CHECK-START-ARM64: int Main.$opt$noinline$notAnd(int, int) disassembly (after) /// CHECK: bic w{{\d+}}, w{{\d+}}, w{{\d+}} + + /// CHECK-START-ARM: int Main.$opt$noinline$notAnd(int, int) instruction_simplifier_arm (before) + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> Not [<>] + /// CHECK: <> And [<>,<>] + /// CHECK: Return [<>] + + /// CHECK-START-ARM: int Main.$opt$noinline$notAnd(int, int) instruction_simplifier_arm (after) + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> BitwiseNegatedRight [<>,<>] kind:And + /// CHECK: Return [<>] + + /// CHECK-START-ARM: int Main.$opt$noinline$notAnd(int, int) instruction_simplifier_arm (after) + /// CHECK-NOT: Not + /// CHECK-NOT: And + + /// CHECK-START-ARM: int Main.$opt$noinline$notAnd(int, int) disassembly (after) + /// CHECK: bic.w r{{\d+}}, r{{\d+}}, r{{\d+}} + public static int $opt$noinline$notAnd(int base, int mask) { if (doThrow) throw new Error(); return base & ~mask; @@ -74,7 +95,7 @@ public class Main { /// CHECK-START-ARM64: long Main.$opt$noinline$notOr(long, long) instruction_simplifier_arm64 (after) /// CHECK: <> ParameterValue /// CHECK: <> ParameterValue - /// CHECK: <> Arm64BitwiseNegatedRight [<>,<>] kind:Or + /// CHECK: <> BitwiseNegatedRight [<>,<>] kind:Or /// CHECK: Return [<>] /// CHECK-START-ARM64: long Main.$opt$noinline$notOr(long, long) instruction_simplifier_arm64 (after) @@ -84,6 +105,27 @@ public class Main { /// CHECK-START-ARM64: long Main.$opt$noinline$notOr(long, long) disassembly (after) /// CHECK: orn x{{\d+}}, x{{\d+}}, x{{\d+}} + + /// CHECK-START-ARM: long Main.$opt$noinline$notOr(long, long) instruction_simplifier_arm (before) + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> Not [<>] + /// CHECK: <> Or [<>,<>] + /// CHECK: Return [<>] + + /// CHECK-START-ARM: long Main.$opt$noinline$notOr(long, long) instruction_simplifier_arm (after) + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> BitwiseNegatedRight [<>,<>] kind:Or + /// CHECK: Return [<>] + + /// CHECK-START-ARM: long Main.$opt$noinline$notOr(long, long) instruction_simplifier_arm (after) + /// CHECK-NOT: Not + /// CHECK-NOT: Or + + /// CHECK-START-ARM: long Main.$opt$noinline$notOr(long, long) disassembly (after) + /// CHECK: orn.w r{{\d+}}, r{{\d+}}, r{{\d+}} + public static long $opt$noinline$notOr(long base, long mask) { if (doThrow) throw new Error(); return base | ~mask; @@ -103,7 +145,7 @@ public class Main { /// CHECK-START-ARM64: int Main.$opt$noinline$notXor(int, int) instruction_simplifier_arm64 (after) /// CHECK: <> ParameterValue /// CHECK: <> ParameterValue - /// CHECK: <> Arm64BitwiseNegatedRight [<>,<>] kind:Xor + /// CHECK: <> BitwiseNegatedRight [<>,<>] kind:Xor /// CHECK: Return [<>] /// CHECK-START-ARM64: int Main.$opt$noinline$notXor(int, int) instruction_simplifier_arm64 (after) @@ -113,39 +155,63 @@ public class Main { /// CHECK-START-ARM64: int Main.$opt$noinline$notXor(int, int) disassembly (after) /// CHECK: eon w{{\d+}}, w{{\d+}}, w{{\d+}} + + /// CHECK-START-ARM: int Main.$opt$noinline$notXor(int, int) instruction_simplifier_arm (before) + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> Not [<>] + /// CHECK: <> Xor [<>,<>] + /// CHECK: Return [<>] + + /// CHECK-START-ARM: int Main.$opt$noinline$notXor(int, int) instruction_simplifier_arm (after) + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> Not [<>] + /// CHECK: <> Xor [<>,<>] + /// CHECK: Return [<>] + + /// CHECK-START-ARM: int Main.$opt$noinline$notXor(int, int) instruction_simplifier_arm (after) + /// CHECK-NOT: BitwiseNegatedRight + public static int $opt$noinline$notXor(int base, int mask) { if (doThrow) throw new Error(); return base ^ ~mask; } /** - * Check that the transformation is also done when the base is a constant. + * Check that transformation is done when the argument is a constant. */ - /// CHECK-START-ARM64: int Main.$opt$noinline$notXorConstant(int) instruction_simplifier_arm64 (before) - /// CHECK: <> ParameterValue + /// CHECK-START-ARM64: int Main.$opt$noinline$notAndConstant(int) instruction_simplifier_arm64 (before) + /// CHECK: <> ParameterValue /// CHECK: <> IntConstant - /// CHECK: <> Not [<>] - /// CHECK: <> Xor [<>,<>] + /// CHECK: <> Not [<>] + /// CHECK: <> And [<>,<>] /// CHECK: Return [<>] - /// CHECK-START-ARM64: int Main.$opt$noinline$notXorConstant(int) instruction_simplifier_arm64 (after) - /// CHECK: <> ParameterValue + /// CHECK-START-ARM64: int Main.$opt$noinline$notAndConstant(int) instruction_simplifier_arm64 (after) + /// CHECK: <> ParameterValue /// CHECK: <> IntConstant - /// CHECK: <> Arm64BitwiseNegatedRight [<>,<>] kind:Xor + /// CHECK: <> BitwiseNegatedRight [<>,<>] kind:And /// CHECK: Return [<>] - /// CHECK-START-ARM64: int Main.$opt$noinline$notXorConstant(int) instruction_simplifier_arm64 (after) - /// CHECK-NOT: Not - /// CHECK-NOT: Xor - /// CHECK-START-ARM64: int Main.$opt$noinline$notXorConstant(int) disassembly (after) - /// CHECK: mov <>, #0xf - /// CHECK: eon w{{\d+}}, <>, w{{\d+}} + /// CHECK-START-ARM: int Main.$opt$noinline$notAndConstant(int) instruction_simplifier_arm (before) + /// CHECK: <> ParameterValue + /// CHECK: <> IntConstant + /// CHECK: <> Not [<>] + /// CHECK: <> And [<>,<>] + /// CHECK: Return [<>] + + /// CHECK-START-ARM: int Main.$opt$noinline$notAndConstant(int) instruction_simplifier_arm (after) + /// CHECK: <> ParameterValue + /// CHECK: <> IntConstant + /// CHECK: <> BitwiseNegatedRight [<>,<>] kind:And + /// CHECK: Return [<>] - public static int $opt$noinline$notXorConstant(int mask) { + public static int $opt$noinline$notAndConstant(int mask) { if (doThrow) throw new Error(); - return 0xf ^ ~mask; + return 0xf & ~mask; } /** @@ -173,7 +239,31 @@ public class Main { /// CHECK: Return [<>] /// CHECK-START-ARM64: int Main.$opt$noinline$notAndMultipleUses(int, int) instruction_simplifier_arm64 (after) - /// CHECK-NOT: Arm64BitwiseNegatedRight + /// CHECK-NOT: BitwiseNegatedRight + + + /// CHECK-START-ARM: int Main.$opt$noinline$notAndMultipleUses(int, int) instruction_simplifier_arm (before) + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> IntConstant + /// CHECK: <> Not [<>] + /// CHECK: <> And [<>,<>] + /// CHECK: <> And [<>,<>] + /// CHECK: <> Add [<>,<>] + /// CHECK: Return [<>] + + /// CHECK-START-ARM: int Main.$opt$noinline$notAndMultipleUses(int, int) instruction_simplifier_arm (after) + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> IntConstant + /// CHECK: <> Not [<>] + /// CHECK: <> And [<>,<>] + /// CHECK: <> And [<>,<>] + /// CHECK: <> Add [<>,<>] + /// CHECK: Return [<>] + + /// CHECK-START-ARM: int Main.$opt$noinline$notAndMultipleUses(int, int) instruction_simplifier_arm (after) + /// CHECK-NOT: BitwiseNegatedRight public static int $opt$noinline$notAndMultipleUses(int base, int mask) { if (doThrow) throw new Error(); @@ -189,7 +279,10 @@ public class Main { // have been applied then Not/Not/Or is replaced by And/Not. /// CHECK-START-ARM64: int Main.$opt$noinline$deMorganOr(int, int) instruction_simplifier_arm64 (after) - /// CHECK-NOT: Arm64BitwiseNegatedRight + /// CHECK-NOT: BitwiseNegatedRight + + /// CHECK-START-ARM: int Main.$opt$noinline$deMorganOr(int, int) instruction_simplifier_arm (after) + /// CHECK-NOT: BitwiseNegatedRight public static int $opt$noinline$deMorganOr(int a, int b) { if (doThrow) throw new Error(); @@ -200,7 +293,7 @@ public class Main { assertIntEquals(0xe, $opt$noinline$notAnd(0xf, 0x1)); assertLongEquals(~0x0, $opt$noinline$notOr(0xf, 0x1)); assertIntEquals(~0xe, $opt$noinline$notXor(0xf, 0x1)); - assertIntEquals(~0xe, $opt$noinline$notXorConstant(0x1)); + assertIntEquals(0xe, $opt$noinline$notAndConstant(0x1)); assertIntEquals(0xe, $opt$noinline$notAndMultipleUses(0xf, 0x1)); assertIntEquals(~0x1, $opt$noinline$deMorganOr(0x3, 0x1)); } -- GitLab From 18401b748a3180f52e42547ede22d1b184fe8c43 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Fri, 11 Mar 2016 13:35:51 +0000 Subject: [PATCH 191/204] Fix invariant in reference type propagation. Also some cleanups. Change-Id: I7f0ec7d06b4bab10dbfa230c757447d311658f93 --- compiler/optimizing/nodes.cc | 8 +++- compiler/optimizing/nodes.h | 2 + .../optimizing/reference_type_propagation.cc | 21 ++++----- test/581-rtp/expected.txt | 0 test/581-rtp/info.txt | 2 + test/581-rtp/src/Main.java | 44 +++++++++++++++++++ 6 files changed, 63 insertions(+), 14 deletions(-) create mode 100644 test/581-rtp/expected.txt create mode 100644 test/581-rtp/info.txt create mode 100644 test/581-rtp/src/Main.java diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index 77ded29b49..98766a31a6 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -2181,7 +2181,9 @@ static void CheckAgainstUpperBound(ReferenceTypeInfo rti, ReferenceTypeInfo uppe DCHECK(upper_bound_rti.IsSupertypeOf(rti)) << " upper_bound_rti: " << upper_bound_rti << " rti: " << rti; - DCHECK(!upper_bound_rti.GetTypeHandle()->CannotBeAssignedFromOtherTypes() || rti.IsExact()); + DCHECK(!upper_bound_rti.GetTypeHandle()->CannotBeAssignedFromOtherTypes() || rti.IsExact()) + << " upper_bound_rti: " << upper_bound_rti + << " rti: " << rti; } } @@ -2215,6 +2217,10 @@ ReferenceTypeInfo ReferenceTypeInfo::Create(TypeHandle type_handle, bool is_exac if (kIsDebugBuild) { ScopedObjectAccess soa(Thread::Current()); DCHECK(IsValidHandle(type_handle)); + if (!is_exact) { + DCHECK(!type_handle->CannotBeAssignedFromOtherTypes()) + << "Callers of ReferenceTypeInfo::Create should ensure is_exact is properly computed"; + } } return ReferenceTypeInfo(type_handle, is_exact); } diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index ecb690ff62..22551f518a 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -509,6 +509,8 @@ class HGraph : public ArenaObject { // before cursor. HInstruction* InsertOppositeCondition(HInstruction* cond, HInstruction* cursor); + ReferenceTypeInfo GetInexactObjectRti() const { return inexact_object_rti_; } + private: void RemoveInstructionsAsUsersFromDeadBlocks(const ArenaBitVector& visited) const; void RemoveDeadBlocks(const ArenaBitVector& visited); diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc index deaa415ed4..75356c848b 100644 --- a/compiler/optimizing/reference_type_propagation.cc +++ b/compiler/optimizing/reference_type_propagation.cc @@ -432,11 +432,10 @@ void ReferenceTypePropagation::RTPVisitor::SetClassAsTypeInfo(HInstruction* inst } else if (klass != nullptr) { ScopedObjectAccess soa(Thread::Current()); ReferenceTypeInfo::TypeHandle handle = handle_cache_->NewHandle(klass); - is_exact = is_exact || klass->CannotBeAssignedFromOtherTypes(); + is_exact = is_exact || handle->CannotBeAssignedFromOtherTypes(); instr->SetReferenceTypeInfo(ReferenceTypeInfo::Create(handle, is_exact)); } else { - instr->SetReferenceTypeInfo( - ReferenceTypeInfo::Create(handle_cache_->GetObjectClassHandle(), /* is_exact */ false)); + instr->SetReferenceTypeInfo(instr->GetBlock()->GetGraph()->GetInexactObjectRti()); } } @@ -518,8 +517,7 @@ void ReferenceTypePropagation::RTPVisitor::VisitUnresolvedInstanceFieldGet( HUnresolvedInstanceFieldGet* instr) { // TODO: Use descriptor to get the actual type. if (instr->GetFieldType() == Primitive::kPrimNot) { - instr->SetReferenceTypeInfo( - ReferenceTypeInfo::Create(handle_cache_->GetObjectClassHandle(), /* is_exact */ false)); + instr->SetReferenceTypeInfo(instr->GetBlock()->GetGraph()->GetInexactObjectRti()); } } @@ -527,8 +525,7 @@ void ReferenceTypePropagation::RTPVisitor::VisitUnresolvedStaticFieldGet( HUnresolvedStaticFieldGet* instr) { // TODO: Use descriptor to get the actual type. if (instr->GetFieldType() == Primitive::kPrimNot) { - instr->SetReferenceTypeInfo( - ReferenceTypeInfo::Create(handle_cache_->GetObjectClassHandle(), /* is_exact */ false)); + instr->SetReferenceTypeInfo(instr->GetBlock()->GetGraph()->GetInexactObjectRti()); } } @@ -724,12 +721,11 @@ void ReferenceTypePropagation::UpdateArrayGet(HArrayGet* instr, HandleCache* han if (handle->IsObjectArrayClass()) { ReferenceTypeInfo::TypeHandle component_handle = handle_cache->NewHandle(handle->GetComponentType()); - instr->SetReferenceTypeInfo( - ReferenceTypeInfo::Create(component_handle, /* is_exact */ false)); + bool is_exact = component_handle->CannotBeAssignedFromOtherTypes(); + instr->SetReferenceTypeInfo(ReferenceTypeInfo::Create(component_handle, is_exact)); } else { // We don't know what the parent actually is, so we fallback to object. - instr->SetReferenceTypeInfo( - ReferenceTypeInfo::Create(handle_cache->GetObjectClassHandle(), /* is_exact */ false)); + instr->SetReferenceTypeInfo(instr->GetBlock()->GetGraph()->GetInexactObjectRti()); } } @@ -811,8 +807,7 @@ void ReferenceTypePropagation::UpdatePhi(HPhi* instr) { if (first_input_index_not_null == input_count) { // All inputs are NullConstants, set the type to object. // This may happen in the presence of inlining. - instr->SetReferenceTypeInfo( - ReferenceTypeInfo::Create(handle_cache_.GetObjectClassHandle(), /* is_exact */ false)); + instr->SetReferenceTypeInfo(instr->GetBlock()->GetGraph()->GetInexactObjectRti()); return; } diff --git a/test/581-rtp/expected.txt b/test/581-rtp/expected.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/581-rtp/info.txt b/test/581-rtp/info.txt new file mode 100644 index 0000000000..b57449ae48 --- /dev/null +++ b/test/581-rtp/info.txt @@ -0,0 +1,2 @@ +Regression test for the reference type propagation pass +of the optimizing compiler that used to break invariants. diff --git a/test/581-rtp/src/Main.java b/test/581-rtp/src/Main.java new file mode 100644 index 0000000000..09f6f6c096 --- /dev/null +++ b/test/581-rtp/src/Main.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public final class Main { + + /// CHECK-START: void Main.main(String[]) builder (after) + /// CHECK: StaticFieldGet klass:Main[] exact: true + /// CHECK: ArrayGet klass:Main exact:true + /// CHECK: BoundType klass:Main exact:true + public static void main(String[] args) { + Object o = null; + Main f = a[0]; + for (int i = 0; i < 2; ++i) { + // We used to crash in the fixed point iteration of + // the reference type propagation while handling the instanceof: + // we were expecting `o` to get the same exact-ness as the + // `HBoundType` but the typing of the `ArrayGet` used to not + // propagate the exact-ness. + if (o instanceof Main) { + field = o; + } + o = f; + } + if (field != null) { + throw new Error("Expected null"); + } + } + + static Main[] a = new Main[1]; + static Object field; +} -- GitLab From 3c5462364daee17e651299f1c370f965f34baed8 Mon Sep 17 00:00:00 2001 From: David Srbecky Date: Fri, 11 Mar 2016 11:49:20 +0000 Subject: [PATCH 192/204] Allow duplicated methods in different DWARF line tables. This makes the compilation units more self-contained. If method is mentioned in the compilation unit, we can also find its line table there. Otherwise, we would have to search through all of them. Change-Id: I0cdfb9006e796e41e123fc1f4fecd15312570068 --- compiler/debug/elf_debug_line_writer.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/compiler/debug/elf_debug_line_writer.h b/compiler/debug/elf_debug_line_writer.h index 11be4e9844..935861839f 100644 --- a/compiler/debug/elf_debug_line_writer.h +++ b/compiler/debug/elf_debug_line_writer.h @@ -18,6 +18,7 @@ #define ART_COMPILER_DEBUG_ELF_DEBUG_LINE_WRITER_H_ #include +#include #include "compiled_method.h" #include "debug/dwarf/debug_line_opcode_writer.h" @@ -81,11 +82,14 @@ class ElfDebugLineWriter { case kX86_64: break; } + std::unordered_set seen_addresses(compilation_unit.methods.size()); dwarf::DebugLineOpCodeWriter<> opcodes(is64bit, code_factor_bits_); for (const MethodDebugInfo* mi : compilation_unit.methods) { // Ignore function if we have already generated line table for the same address. // It would confuse the debugger and the DWARF specification forbids it. - if (mi->deduped) { + // We allow the line table for method to be replicated in different compilation unit. + // This ensures that each compilation unit contains line table for all its methods. + if (!seen_addresses.insert(mi->low_pc).second) { continue; } -- GitLab From 8862fac4a0b97d827d2808146d2d79b8d799b998 Mon Sep 17 00:00:00 2001 From: David Srbecky Date: Fri, 11 Mar 2016 14:34:47 +0000 Subject: [PATCH 193/204] Revert "Allow duplicated methods in different DWARF line tables." This reverts commit 3c5462364daee17e651299f1c370f965f34baed8. Change-Id: Icc63c47cd53abeff2a470ae84715012a1ef988ac --- compiler/debug/elf_debug_line_writer.h | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/compiler/debug/elf_debug_line_writer.h b/compiler/debug/elf_debug_line_writer.h index 935861839f..11be4e9844 100644 --- a/compiler/debug/elf_debug_line_writer.h +++ b/compiler/debug/elf_debug_line_writer.h @@ -18,7 +18,6 @@ #define ART_COMPILER_DEBUG_ELF_DEBUG_LINE_WRITER_H_ #include -#include #include "compiled_method.h" #include "debug/dwarf/debug_line_opcode_writer.h" @@ -82,14 +81,11 @@ class ElfDebugLineWriter { case kX86_64: break; } - std::unordered_set seen_addresses(compilation_unit.methods.size()); dwarf::DebugLineOpCodeWriter<> opcodes(is64bit, code_factor_bits_); for (const MethodDebugInfo* mi : compilation_unit.methods) { // Ignore function if we have already generated line table for the same address. // It would confuse the debugger and the DWARF specification forbids it. - // We allow the line table for method to be replicated in different compilation unit. - // This ensures that each compilation unit contains line table for all its methods. - if (!seen_addresses.insert(mi->low_pc).second) { + if (mi->deduped) { continue; } -- GitLab From ddc4055abab40740e689d91e9a4fe20a7b8e5d28 Mon Sep 17 00:00:00 2001 From: Goran Jakovljevic Date: Fri, 11 Mar 2016 15:22:18 +0100 Subject: [PATCH 194/204] MIPS: Don't use $t8 for calling entrypoints When jumping to entrypoints, $t9 register must be used. Value of $gp is calculated based on value from $t9 and it should contain the address of the first instruction of the entrypoint. This CL enables MIPS32 and MIPS64 to boot. Change-Id: Idf0f7f479cceafabb88b1b087da190dbda86e1c3 --- compiler/optimizing/intrinsics_mips.cc | 20 ++++++++++---------- compiler/optimizing/intrinsics_mips64.cc | 20 ++++++++++---------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc index f1a6e3de05..c306cf93a1 100644 --- a/compiler/optimizing/intrinsics_mips.cc +++ b/compiler/optimizing/intrinsics_mips.cc @@ -1548,11 +1548,11 @@ void IntrinsicCodeGeneratorMIPS::VisitStringCompareTo(HInvoke* invoke) { __ Beqz(argument, slow_path->GetEntryLabel()); __ LoadFromOffset(kLoadWord, - TMP, + T9, TR, QUICK_ENTRYPOINT_OFFSET(kMipsWordSize, pStringCompareTo).Int32Value()); - __ Jalr(TMP); + __ Jalr(T9); __ Nop(); __ Bind(slow_path->GetExitLabel()); } @@ -1707,10 +1707,10 @@ static void GenerateStringIndexOf(HInvoke* invoke, } __ LoadFromOffset(kLoadWord, - TMP, + T9, TR, QUICK_ENTRYPOINT_OFFSET(kMipsWordSize, pIndexOf).Int32Value()); - __ Jalr(TMP); + __ Jalr(T9); __ Nop(); if (slow_path != nullptr) { @@ -1793,10 +1793,10 @@ void IntrinsicCodeGeneratorMIPS::VisitStringNewStringFromBytes(HInvoke* invoke) __ Beqz(byte_array, slow_path->GetEntryLabel()); __ LoadFromOffset(kLoadWord, - TMP, + T9, TR, QUICK_ENTRYPOINT_OFFSET(kMipsWordSize, pAllocStringFromBytes).Int32Value()); - __ Jalr(TMP); + __ Jalr(T9); __ Nop(); codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); __ Bind(slow_path->GetExitLabel()); @@ -1826,10 +1826,10 @@ void IntrinsicCodeGeneratorMIPS::VisitStringNewStringFromChars(HInvoke* invoke) // all include a null check on `data` before calling that method. __ LoadFromOffset(kLoadWord, - TMP, + T9, TR, QUICK_ENTRYPOINT_OFFSET(kMipsWordSize, pAllocStringFromChars).Int32Value()); - __ Jalr(TMP); + __ Jalr(T9); __ Nop(); codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); } @@ -1855,10 +1855,10 @@ void IntrinsicCodeGeneratorMIPS::VisitStringNewStringFromString(HInvoke* invoke) __ Beqz(string_to_copy, slow_path->GetEntryLabel()); __ LoadFromOffset(kLoadWord, - TMP, + T9, TR, QUICK_ENTRYPOINT_OFFSET(kMipsWordSize, pAllocStringFromString).Int32Value()); - __ Jalr(TMP); + __ Jalr(T9); __ Nop(); codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); __ Bind(slow_path->GetExitLabel()); diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc index 5ec5b86d1e..cf973aa841 100644 --- a/compiler/optimizing/intrinsics_mips64.cc +++ b/compiler/optimizing/intrinsics_mips64.cc @@ -1356,10 +1356,10 @@ void IntrinsicCodeGeneratorMIPS64::VisitStringCompareTo(HInvoke* invoke) { __ Beqzc(argument, slow_path->GetEntryLabel()); __ LoadFromOffset(kLoadDoubleword, - TMP, + T9, TR, QUICK_ENTRYPOINT_OFFSET(kMips64DoublewordSize, pStringCompareTo).Int32Value()); - __ Jalr(TMP); + __ Jalr(T9); __ Nop(); __ Bind(slow_path->GetExitLabel()); } @@ -1506,11 +1506,11 @@ static void GenerateStringIndexOf(HInvoke* invoke, } __ LoadFromOffset(kLoadDoubleword, - TMP, + T9, TR, QUICK_ENTRYPOINT_OFFSET(kMips64DoublewordSize, pIndexOf).Int32Value()); CheckEntrypointTypes(); - __ Jalr(TMP); + __ Jalr(T9); __ Nop(); if (slow_path != nullptr) { @@ -1583,12 +1583,12 @@ void IntrinsicCodeGeneratorMIPS64::VisitStringNewStringFromBytes(HInvoke* invoke __ Beqzc(byte_array, slow_path->GetEntryLabel()); __ LoadFromOffset(kLoadDoubleword, - TMP, + T9, TR, QUICK_ENTRYPOINT_OFFSET(kMips64DoublewordSize, pAllocStringFromBytes).Int32Value()); CheckEntrypointTypes(); - __ Jalr(TMP); + __ Jalr(T9); __ Nop(); codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); __ Bind(slow_path->GetExitLabel()); @@ -1617,12 +1617,12 @@ void IntrinsicCodeGeneratorMIPS64::VisitStringNewStringFromChars(HInvoke* invoke // // all include a null check on `data` before calling that method. __ LoadFromOffset(kLoadDoubleword, - TMP, + T9, TR, QUICK_ENTRYPOINT_OFFSET(kMips64DoublewordSize, pAllocStringFromChars).Int32Value()); CheckEntrypointTypes(); - __ Jalr(TMP); + __ Jalr(T9); __ Nop(); codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); } @@ -1648,12 +1648,12 @@ void IntrinsicCodeGeneratorMIPS64::VisitStringNewStringFromString(HInvoke* invok __ Beqzc(string_to_copy, slow_path->GetEntryLabel()); __ LoadFromOffset(kLoadDoubleword, - TMP, + T9, TR, QUICK_ENTRYPOINT_OFFSET(kMips64DoublewordSize, pAllocStringFromString).Int32Value()); CheckEntrypointTypes(); - __ Jalr(TMP); + __ Jalr(T9); __ Nop(); codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); __ Bind(slow_path->GetExitLabel()); -- GitLab From 252fa90d25eb3fa9c4189304d021533c9657fea7 Mon Sep 17 00:00:00 2001 From: David Srbecky Date: Fri, 11 Mar 2016 14:25:00 +0000 Subject: [PATCH 195/204] DWARF: Compensate for compiler's off-by-one instruction. The compiler generates stackmaps with PC value which is *after* the instruction rather PC of the instruction itself. This causes trouble when generating native line number mapping since the branch instruction itself will not be mapped, and consequently we get incorrect line number in backtraces. Add code to compensate for this. See the in-code comment for more details. Change-Id: I72c992e6d08a767f314290d562421b251ae60732 --- compiler/debug/elf_debug_line_writer.h | 34 ++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/compiler/debug/elf_debug_line_writer.h b/compiler/debug/elf_debug_line_writer.h index ed26d96603..0d3673d3cc 100644 --- a/compiler/debug/elf_debug_line_writer.h +++ b/compiler/debug/elf_debug_line_writer.h @@ -114,6 +114,40 @@ class ElfDebugLineWriter { continue; } + // Compensate for compiler's off-by-one-instruction error. + // + // The compiler generates stackmap with PC *after* the branch instruction + // (because this is the PC which is easier to obtain when unwinding). + // + // However, the debugger is more clever and it will ask us for line-number + // mapping at the location of the branch instruction (since the following + // instruction could belong to other line, this is the correct thing to do). + // + // So we really want to just decrement the PC by one instruction so that the + // branch instruction is covered as well. However, we do not know the size + // of the previous instruction, and we can not subtract just a fixed amount + // (the debugger would trust us that the PC is valid; it might try to set + // breakpoint there at some point, and setting breakpoint in mid-instruction + // would make the process crash in spectacular way). + // + // Therefore, we say that the PC which the compiler gave us for the stackmap + // is the end of its associated address range, and we use the PC from the + // previous stack map as the start of the range. This ensures that the PC is + // valid and that the branch instruction is covered. + // + // This ensures we have correct line number mapping at call sites (which is + // important for backtraces), but there is nothing we can do for non-call + // sites (so stepping through optimized code in debugger is not possible). + // + // We do not adjust the stackmaps if the code was compiled as debuggable. + // In that case, the stackmaps should accurately cover all instructions. + if (!mi->is_native_debuggable) { + for (size_t i = pc2dex_map.size() - 1; i > 0; --i) { + pc2dex_map[i].from_ = pc2dex_map[i - 1].from_; + } + pc2dex_map[0].from_ = 0; + } + Elf_Addr method_address = base_address + mi->code_address; PositionInfos dex2line_map; -- GitLab From 5120923295ba161bad1dd459747eb6a6d2b18c7e Mon Sep 17 00:00:00 2001 From: Roland Levillain Date: Fri, 11 Mar 2016 16:27:27 +0000 Subject: [PATCH 196/204] Typo in comment in art::mirror::Object::IdentityHashCode. Change-Id: I5380d1c34deda32c668e48ed5f80e96b29f5b0b5 --- runtime/mirror/object.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/mirror/object.cc b/runtime/mirror/object.cc index 4d941302f9..701c600822 100644 --- a/runtime/mirror/object.cc +++ b/runtime/mirror/object.cc @@ -183,7 +183,7 @@ int32_t Object::IdentityHashCode() const { break; } case LockWord::kFatLocked: { - // Already inflated, return the has stored in the monitor. + // Already inflated, return the hash stored in the monitor. Monitor* monitor = lw.FatLockMonitor(); DCHECK(monitor != nullptr); return monitor->GetHashCode(); -- GitLab From b14a5ede4b232d5f7709001d1e0b90e6910d9306 Mon Sep 17 00:00:00 2001 From: David Srbecky Date: Fri, 11 Mar 2016 16:54:22 +0000 Subject: [PATCH 197/204] Minor optimizations of debug::WriteCFISection. The method might be passed method infos without CFI in some cases. Use the sorting phase as a chance to filter them out. This makes sure we do not allocate memory and sort methods if there is in fact no work to do. Also change the sort to stable - for the sake of determinism. Change-Id: I97d57d77e8b709d0d49d6971f66b955efcbb57b0 --- compiler/debug/elf_debug_frame_writer.h | 62 ++++++++++++------------- 1 file changed, 29 insertions(+), 33 deletions(-) diff --git a/compiler/debug/elf_debug_frame_writer.h b/compiler/debug/elf_debug_frame_writer.h index badbd93cb0..f9d33c1c30 100644 --- a/compiler/debug/elf_debug_frame_writer.h +++ b/compiler/debug/elf_debug_frame_writer.h @@ -175,18 +175,6 @@ void WriteCFISection(ElfBuilder* builder, CHECK(format == dwarf::DW_DEBUG_FRAME_FORMAT || format == dwarf::DW_EH_FRAME_FORMAT); typedef typename ElfTypes::Addr Elf_Addr; - if (method_infos.empty()) { - return; - } - - std::vector binary_search_table; - std::vector patch_locations; - if (format == dwarf::DW_EH_FRAME_FORMAT) { - binary_search_table.reserve(2 * method_infos.size()); - } else { - patch_locations.reserve(method_infos.size()); - } - // The methods can be written in any order. // Let's therefore sort them in the lexicographical order of the opcodes. // This has no effect on its own. However, if the final .debug_frame section is @@ -194,9 +182,14 @@ void WriteCFISection(ElfBuilder* builder, std::vector sorted_method_infos; sorted_method_infos.reserve(method_infos.size()); for (size_t i = 0; i < method_infos.size(); i++) { - sorted_method_infos.push_back(&method_infos[i]); + if (!method_infos[i].cfi.empty() && !method_infos[i].deduped) { + sorted_method_infos.push_back(&method_infos[i]); + } } - std::sort( + if (sorted_method_infos.empty()) { + return; + } + std::stable_sort( sorted_method_infos.begin(), sorted_method_infos.end(), [](const MethodDebugInfo* lhs, const MethodDebugInfo* rhs) { @@ -205,6 +198,14 @@ void WriteCFISection(ElfBuilder* builder, return std::lexicographical_compare(l.begin(), l.end(), r.begin(), r.end()); }); + std::vector binary_search_table; + std::vector patch_locations; + if (format == dwarf::DW_EH_FRAME_FORMAT) { + binary_search_table.reserve(2 * sorted_method_infos.size()); + } else { + patch_locations.reserve(sorted_method_infos.size()); + } + // Write .eh_frame/.debug_frame section. auto* cfi_section = (format == dwarf::DW_DEBUG_FRAME_FORMAT ? builder->GetDebugFrame() @@ -221,26 +222,21 @@ void WriteCFISection(ElfBuilder* builder, buffer_address += buffer.size(); buffer.clear(); for (const MethodDebugInfo* mi : sorted_method_infos) { - if (!mi->deduped) { // Only one FDE per unique address. - ArrayRef opcodes = mi->cfi; - if (!opcodes.empty()) { - const Elf_Addr code_address = mi->code_address + - (mi->is_code_address_text_relative ? builder->GetText()->GetAddress() : 0); - if (format == dwarf::DW_EH_FRAME_FORMAT) { - binary_search_table.push_back( - dchecked_integral_cast(code_address)); - binary_search_table.push_back( - dchecked_integral_cast(buffer_address)); - } - WriteFDE(is64bit, cfi_address, cie_address, - code_address, mi->code_size, - opcodes, format, buffer_address, &buffer, - &patch_locations); - cfi_section->WriteFully(buffer.data(), buffer.size()); - buffer_address += buffer.size(); - buffer.clear(); - } + DCHECK(!mi->deduped); + DCHECK(!mi->cfi.empty()); + const Elf_Addr code_address = mi->code_address + + (mi->is_code_address_text_relative ? builder->GetText()->GetAddress() : 0); + if (format == dwarf::DW_EH_FRAME_FORMAT) { + binary_search_table.push_back(dchecked_integral_cast(code_address)); + binary_search_table.push_back(dchecked_integral_cast(buffer_address)); } + WriteFDE(is64bit, cfi_address, cie_address, + code_address, mi->code_size, + mi->cfi, format, buffer_address, &buffer, + &patch_locations); + cfi_section->WriteFully(buffer.data(), buffer.size()); + buffer_address += buffer.size(); + buffer.clear(); } cfi_section->End(); } -- GitLab From 09c2a6be63337ee060e2d54bd01cf18be7301d29 Mon Sep 17 00:00:00 2001 From: David Srbecky Date: Fri, 11 Mar 2016 17:11:44 +0000 Subject: [PATCH 198/204] Add trampoline symbols to native debug info. The debugger needs them to unwind through the trampolines and to understand what is happening in the call stack. Change-Id: Ia554058c3796788adcd7336d620a7734eb366905 --- compiler/debug/elf_debug_info_writer.h | 2 + compiler/debug/elf_debug_line_writer.h | 1 + compiler/debug/elf_debug_writer.cc | 63 ++++++++++++++-------- compiler/debug/elf_debug_writer.h | 5 ++ compiler/debug/elf_symtab_writer.h | 27 ++++++---- compiler/debug/method_debug_info.h | 5 +- compiler/oat_writer.cc | 3 +- compiler/oat_writer.h | 4 ++ compiler/optimizing/optimizing_compiler.cc | 3 +- dex2oat/dex2oat.cc | 3 ++ 10 files changed, 80 insertions(+), 36 deletions(-) diff --git a/compiler/debug/elf_debug_info_writer.h b/compiler/debug/elf_debug_info_writer.h index af74d4c5b5..a6e6f8b5da 100644 --- a/compiler/debug/elf_debug_info_writer.h +++ b/compiler/debug/elf_debug_info_writer.h @@ -46,6 +46,7 @@ static void LocalInfoCallback(void* ctx, const DexFile::LocalInfo& entry) { static std::vector GetParamNames(const MethodDebugInfo* mi) { std::vector names; if (mi->code_item != nullptr) { + DCHECK(mi->dex_file != nullptr); const uint8_t* stream = mi->dex_file->GetDebugInfoStream(mi->code_item); if (stream != nullptr) { DecodeUnsignedLeb128(&stream); // line. @@ -133,6 +134,7 @@ class ElfCompilationUnitWriter { const char* last_dex_class_desc = nullptr; for (auto mi : compilation_unit.methods) { + DCHECK(mi->dex_file != nullptr); const DexFile* dex = mi->dex_file; const DexFile::CodeItem* dex_code = mi->code_item; const DexFile::MethodId& dex_method = dex->GetMethodId(mi->dex_method_index); diff --git a/compiler/debug/elf_debug_line_writer.h b/compiler/debug/elf_debug_line_writer.h index ed26d96603..9875c71080 100644 --- a/compiler/debug/elf_debug_line_writer.h +++ b/compiler/debug/elf_debug_line_writer.h @@ -117,6 +117,7 @@ class ElfDebugLineWriter { Elf_Addr method_address = base_address + mi->code_address; PositionInfos dex2line_map; + DCHECK(mi->dex_file != nullptr); const DexFile* dex = mi->dex_file; if (!dex->DecodeDebugPositionInfo(mi->code_item, PositionInfoCallback, &dex2line_map)) { continue; diff --git a/compiler/debug/elf_debug_writer.cc b/compiler/debug/elf_debug_writer.cc index 0ca7370f98..4dd802495c 100644 --- a/compiler/debug/elf_debug_writer.cc +++ b/compiler/debug/elf_debug_writer.cc @@ -39,35 +39,31 @@ void WriteDebugInfo(ElfBuilder* builder, const ArrayRef& method_infos, dwarf::CFIFormat cfi_format, bool write_oat_patches) { - // Add methods to .symtab. + // Write .strtab and .symtab. WriteDebugSymbols(builder, method_infos, true /* with_signature */); - // Generate CFI (stack unwinding information). + + // Write .debug_frame. WriteCFISection(builder, method_infos, cfi_format, write_oat_patches); - // Write DWARF .debug_* sections. - WriteDebugSections(builder, method_infos, write_oat_patches); -} -template -static void WriteDebugSections(ElfBuilder* builder, - const ArrayRef& method_infos, - bool write_oat_patches) { // Group the methods into compilation units based on source file. std::vector compilation_units; const char* last_source_file = nullptr; for (const MethodDebugInfo& mi : method_infos) { - auto& dex_class_def = mi.dex_file->GetClassDef(mi.class_def_index); - const char* source_file = mi.dex_file->GetSourceFile(dex_class_def); - if (compilation_units.empty() || source_file != last_source_file) { - compilation_units.push_back(ElfCompilationUnit()); + if (mi.dex_file != nullptr) { + auto& dex_class_def = mi.dex_file->GetClassDef(mi.class_def_index); + const char* source_file = mi.dex_file->GetSourceFile(dex_class_def); + if (compilation_units.empty() || source_file != last_source_file) { + compilation_units.push_back(ElfCompilationUnit()); + } + ElfCompilationUnit& cu = compilation_units.back(); + cu.methods.push_back(&mi); + // All methods must have the same addressing mode otherwise the min/max below does not work. + DCHECK_EQ(cu.methods.front()->is_code_address_text_relative, mi.is_code_address_text_relative); + cu.is_code_address_text_relative = mi.is_code_address_text_relative; + cu.code_address = std::min(cu.code_address, mi.code_address); + cu.code_end = std::max(cu.code_end, mi.code_address + mi.code_size); + last_source_file = source_file; } - ElfCompilationUnit& cu = compilation_units.back(); - cu.methods.push_back(&mi); - // All methods must have the same addressing mode otherwise the min/max below does not work. - DCHECK_EQ(cu.methods.front()->is_code_address_text_relative, mi.is_code_address_text_relative); - cu.is_code_address_text_relative = mi.is_code_address_text_relative; - cu.code_address = std::min(cu.code_address, mi.code_address); - cu.code_end = std::max(cu.code_end, mi.code_address + mi.code_size); - last_source_file = source_file; } // Write .debug_line section. @@ -185,6 +181,31 @@ ArrayRef WriteDebugElfFileForClasses(InstructionSet isa, } } +std::vector MakeTrampolineInfos(const OatHeader& header) { + std::map trampolines = { + { "interpreterToInterpreterBridge", header.GetInterpreterToInterpreterBridgeOffset() }, + { "interpreterToCompiledCodeBridge", header.GetInterpreterToCompiledCodeBridgeOffset() }, + { "jniDlsymLookup", header.GetJniDlsymLookupOffset() }, + { "quickGenericJniTrampoline", header.GetQuickGenericJniTrampolineOffset() }, + { "quickImtConflictTrampoline", header.GetQuickImtConflictTrampolineOffset() }, + { "quickResolutionTrampoline", header.GetQuickResolutionTrampolineOffset() }, + { "quickToInterpreterBridge", header.GetQuickToInterpreterBridgeOffset() }, + }; + std::vector result; + for (const auto& it : trampolines) { + if (it.second != 0) { + MethodDebugInfo info = MethodDebugInfo(); + info.trampoline_name = it.first; + info.isa = header.GetInstructionSet(); + info.is_code_address_text_relative = true; + info.code_address = it.second - header.GetExecutableOffset(); + info.code_size = 0; // The symbol lasts until the next symbol. + result.push_back(std::move(info)); + } + } + return result; +} + // Explicit instantiations template void WriteDebugInfo( ElfBuilder* builder, diff --git a/compiler/debug/elf_debug_writer.h b/compiler/debug/elf_debug_writer.h index 7f5d24d06e..736370e2d3 100644 --- a/compiler/debug/elf_debug_writer.h +++ b/compiler/debug/elf_debug_writer.h @@ -17,6 +17,8 @@ #ifndef ART_COMPILER_DEBUG_ELF_DEBUG_WRITER_H_ #define ART_COMPILER_DEBUG_ELF_DEBUG_WRITER_H_ +#include + #include "base/macros.h" #include "base/mutex.h" #include "debug/dwarf/dwarf_constants.h" @@ -24,6 +26,7 @@ #include "utils/array_ref.h" namespace art { +class OatHeader; namespace mirror { class Class; } @@ -55,6 +58,8 @@ ArrayRef WriteDebugElfFileForClasses( const ArrayRef& types) SHARED_REQUIRES(Locks::mutator_lock_); +std::vector MakeTrampolineInfos(const OatHeader& oat_header); + } // namespace debug } // namespace art diff --git a/compiler/debug/elf_symtab_writer.h b/compiler/debug/elf_symtab_writer.h index 0a199dac35..045edddd77 100644 --- a/compiler/debug/elf_symtab_writer.h +++ b/compiler/debug/elf_symtab_writer.h @@ -39,7 +39,7 @@ template static void WriteDebugSymbols(ElfBuilder* builder, const ArrayRef& method_infos, bool with_signature) { - bool generated_mapping_symbol = false; + uint64_t mapping_symbol_address = std::numeric_limits::max(); auto* strtab = builder->GetStrTab(); auto* symtab = builder->GetSymTab(); @@ -64,12 +64,20 @@ static void WriteDebugSymbols(ElfBuilder* builder, if (info.deduped) { continue; // Add symbol only for the first instance. } - std::string name = PrettyMethod(info.dex_method_index, *info.dex_file, with_signature); - if (deduped_addresses.find(info.code_address) != deduped_addresses.end()) { - name += " [DEDUPED]"; + size_t name_offset; + if (info.trampoline_name != nullptr) { + name_offset = strtab->Write(info.trampoline_name); + } else { + DCHECK(info.dex_file != nullptr); + std::string name = PrettyMethod(info.dex_method_index, *info.dex_file, with_signature); + if (deduped_addresses.find(info.code_address) != deduped_addresses.end()) { + name += " [DEDUPED]"; + } + // If we write method names without signature, we might see the same name multiple times. + name_offset = (name == last_name ? last_name_offset : strtab->Write(name)); + last_name = std::move(name); + last_name_offset = name_offset; } - // If we write method names without signature, we might see the same name multiple times. - size_t name_offset = (name == last_name ? last_name_offset : strtab->Write(name)); const auto* text = info.is_code_address_text_relative ? builder->GetText() : nullptr; uint64_t address = info.code_address + (text != nullptr ? text->GetAddress() : 0); @@ -82,14 +90,11 @@ static void WriteDebugSymbols(ElfBuilder* builder, // Note that even if we generate just a single mapping symbol, ARM's Streamline // requires it to match function symbol. Just address 0 does not work. if (info.isa == kThumb2) { - if (!generated_mapping_symbol || !kGenerateSingleArmMappingSymbol) { + if (address < mapping_symbol_address || !kGenerateSingleArmMappingSymbol) { symtab->Add(strtab->Write("$t"), text, address & ~1, 0, STB_LOCAL, STT_NOTYPE); - generated_mapping_symbol = true; + mapping_symbol_address = address; } } - - last_name = std::move(name); - last_name_offset = name_offset; } strtab->End(); diff --git a/compiler/debug/method_debug_info.h b/compiler/debug/method_debug_info.h index 1ccc705ff9..ed1da2c26e 100644 --- a/compiler/debug/method_debug_info.h +++ b/compiler/debug/method_debug_info.h @@ -24,7 +24,8 @@ namespace art { namespace debug { struct MethodDebugInfo { - const DexFile* dex_file; + const char* trampoline_name; + const DexFile* dex_file; // Native methods (trampolines) do not reference dex file. size_t class_def_index; uint32_t dex_method_index; uint32_t access_flags; @@ -37,7 +38,7 @@ struct MethodDebugInfo { uint64_t code_address; uint32_t code_size; uint32_t frame_size_in_bytes; - const uint8_t* code_info; + const void* code_info; ArrayRef cfi; }; diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc index 2b511fc082..c2f19c9d61 100644 --- a/compiler/oat_writer.cc +++ b/compiler/oat_writer.cc @@ -811,7 +811,8 @@ class OatWriter::InitCodeMethodVisitor : public OatDexMethodVisitor { if (compiler_options.GenerateAnyDebugInfo() && code_size != 0) { bool has_code_info = method_header->IsOptimized(); // Record debug information for this function if we are doing that. - debug::MethodDebugInfo info; + debug::MethodDebugInfo info = debug::MethodDebugInfo(); + info.trampoline_name = nullptr; info.dex_file = dex_file_; info.class_def_index = class_def_index_; info.dex_method_index = it.GetMemberIndex(); diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h index 74aab4efd0..5e7a4a37d1 100644 --- a/compiler/oat_writer.h +++ b/compiler/oat_writer.h @@ -202,6 +202,10 @@ class OatWriter { ~OatWriter(); + void AddMethodDebugInfos(const std::vector& infos) { + method_info_.insert(method_info_.end(), infos.begin(), infos.end()); + } + ArrayRef GetMethodDebugInfo() const { return ArrayRef(method_info_); } diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index cc1a8064b1..7a82063bba 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -919,7 +919,8 @@ bool OptimizingCompiler::JitCompile(Thread* self, if (compiler_options.GetGenerateDebugInfo()) { const auto* method_header = reinterpret_cast(code); const uintptr_t code_address = reinterpret_cast(method_header->GetCode()); - debug::MethodDebugInfo info; + debug::MethodDebugInfo info = debug::MethodDebugInfo(); + info.trampoline_name = nullptr; info.dex_file = dex_file; info.class_def_index = class_def_idx; info.dex_method_index = method_idx; diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 44e7fc9017..a14a295f25 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -46,6 +46,7 @@ #include "class_linker.h" #include "compiler.h" #include "compiler_callbacks.h" +#include "debug/elf_debug_writer.h" #include "debug/method_debug_info.h" #include "dex/pass_manager.h" #include "dex/quick/dex_file_to_method_inliner_map.h" @@ -1687,6 +1688,8 @@ class Dex2Oat FINAL { std::unique_ptr& elf_writer = elf_writers_[i]; std::unique_ptr& oat_writer = oat_writers_[i]; + oat_writer->AddMethodDebugInfos(debug::MakeTrampolineInfos(oat_writer->GetOatHeader())); + // We need to mirror the layout of the ELF file in the compressed debug-info. // Therefore PrepareDebugInfo() relies on the SetLoadedSectionSizes() call further above. elf_writer->PrepareDebugInfo(oat_writer->GetMethodDebugInfo()); -- GitLab From 5d950769b607b4f76413212db640a32d796911de Mon Sep 17 00:00:00 2001 From: David Srbecky Date: Mon, 7 Mar 2016 20:47:29 +0000 Subject: [PATCH 199/204] Symbolize native debug information for AOTed methods in boot.oat. The oatdump symbolizer will produce all DWARF information now, not just the symtab. This allows us to recover almost all native debug information after the fact, even for non-debuggable oat files. The main drawback is that non-debuggable oat files do not have enough stackmaps to provide reasonable line stepping experience. We might also be missing inlined methods from backtraces. Change-Id: I73ff6f43a3a0e5a0907af353f74f04e9b0129590 --- dex2oat/dex2oat.cc | 3 + oatdump/oatdump.cc | 212 ++++++++---------------------- runtime/oat.cc | 4 + runtime/oat.h | 2 + runtime/oat_quick_method_header.h | 12 +- 5 files changed, 75 insertions(+), 158 deletions(-) diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index a14a295f25..ce4f38a9e3 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -1035,6 +1035,9 @@ class Dex2Oat FINAL { key_value_store_->Put( OatHeader::kDebuggableKey, compiler_options_->debuggable_ ? OatHeader::kTrueValue : OatHeader::kFalseValue); + key_value_store_->Put( + OatHeader::kNativeDebuggableKey, + compiler_options_->native_debuggable_ ? OatHeader::kTrueValue : OatHeader::kFalseValue); if (compiler_options_->IsExtractOnly()) { key_value_store_->Put(OatHeader::kCompilationType, OatHeader::kExtractOnlyValue); } else if (UseProfileGuidedCompilation()) { diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index 0e17fc2efa..3ed57664bc 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -32,6 +32,8 @@ #include "base/unix_file/fd_file.h" #include "class_linker.h" #include "class_linker-inl.h" +#include "debug/elf_debug_writer.h" +#include "debug/method_debug_info.h" #include "dex_file-inl.h" #include "dex_instruction.h" #include "disassembler.h" @@ -106,14 +108,6 @@ class OatSymbolizer FINAL { output_name_(output_name.empty() ? "symbolized.oat" : output_name) { } - typedef void (OatSymbolizer::*Callback)(const DexFile::ClassDef&, - uint32_t, - const OatFile::OatMethod&, - const DexFile&, - uint32_t, - const DexFile::CodeItem*, - uint32_t); - bool Symbolize() { const InstructionSet isa = oat_file_->GetOatHeader().GetInstructionSet(); const InstructionSetFeatures* features = InstructionSetFeatures::FromBitmap( @@ -129,8 +123,6 @@ class OatSymbolizer FINAL { auto* rodata = builder_->GetRoData(); auto* text = builder_->GetText(); auto* bss = builder_->GetBss(); - auto* strtab = builder_->GetStrTab(); - auto* symtab = builder_->GetSymTab(); rodata->Start(); const uint8_t* rodata_begin = oat_file_->Begin(); @@ -155,69 +147,31 @@ class OatSymbolizer FINAL { elf_file->GetPath(), rodata_size, text_size, oat_file_->BssSize()); builder_->WriteDynamicSection(); - Walk(&art::OatSymbolizer::RegisterForDedup); - - NormalizeState(); - - strtab->Start(); - strtab->Write(""); // strtab should start with empty string. - AddTrampolineSymbols(); - Walk(&art::OatSymbolizer::AddSymbol); - strtab->End(); + Walk(); + for (const auto& trampoline : debug::MakeTrampolineInfos(oat_file_->GetOatHeader())) { + method_debug_infos_.push_back(trampoline); + } - symtab->Start(); - symtab->Write(); - symtab->End(); + debug::WriteDebugInfo(builder_.get(), + ArrayRef(method_debug_infos_), + dwarf::DW_DEBUG_FRAME_FORMAT, + true /* write_oat_patches */); builder_->End(); return builder_->Good(); } - void AddTrampolineSymbol(const char* name, uint32_t code_offset) { - if (code_offset != 0) { - uint32_t name_offset = builder_->GetStrTab()->Write(name); - uint64_t symbol_value = code_offset - oat_file_->GetOatHeader().GetExecutableOffset(); - // Specifying 0 as the symbol size means that the symbol lasts until the next symbol or until - // the end of the section in case of the last symbol. - builder_->GetSymTab()->Add( - name_offset, - builder_->GetText(), - builder_->GetText()->GetAddress() + symbol_value, - /* size */ 0, - STB_GLOBAL, - STT_FUNC); - } - } - - void AddTrampolineSymbols() { - const OatHeader& oat_header = oat_file_->GetOatHeader(); - AddTrampolineSymbol("interpreterToInterpreterBridge", - oat_header.GetInterpreterToInterpreterBridgeOffset()); - AddTrampolineSymbol("interpreterToCompiledCodeBridge", - oat_header.GetInterpreterToCompiledCodeBridgeOffset()); - AddTrampolineSymbol("jniDlsymLookup", - oat_header.GetJniDlsymLookupOffset()); - AddTrampolineSymbol("quickGenericJniTrampoline", - oat_header.GetQuickGenericJniTrampolineOffset()); - AddTrampolineSymbol("quickImtConflictTrampoline", - oat_header.GetQuickImtConflictTrampolineOffset()); - AddTrampolineSymbol("quickResolutionTrampoline", - oat_header.GetQuickResolutionTrampolineOffset()); - AddTrampolineSymbol("quickToInterpreterBridge", - oat_header.GetQuickToInterpreterBridgeOffset()); - } - - void Walk(Callback callback) { + void Walk() { std::vector oat_dex_files = oat_file_->GetOatDexFiles(); for (size_t i = 0; i < oat_dex_files.size(); i++) { const OatFile::OatDexFile* oat_dex_file = oat_dex_files[i]; CHECK(oat_dex_file != nullptr); - WalkOatDexFile(oat_dex_file, callback); + WalkOatDexFile(oat_dex_file); } } - void WalkOatDexFile(const OatFile::OatDexFile* oat_dex_file, Callback callback) { + void WalkOatDexFile(const OatFile::OatDexFile* oat_dex_file) { std::string error_msg; const DexFile* const dex_file = OpenDexFile(oat_dex_file, &error_msg); if (dex_file == nullptr) { @@ -226,13 +180,12 @@ class OatSymbolizer FINAL { for (size_t class_def_index = 0; class_def_index < dex_file->NumClassDefs(); class_def_index++) { - const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index); const OatFile::OatClass oat_class = oat_dex_file->GetOatClass(class_def_index); OatClassType type = oat_class.GetType(); switch (type) { case kOatClassAllCompiled: case kOatClassSomeCompiled: - WalkOatClass(oat_class, *dex_file, class_def, callback); + WalkOatClass(oat_class, *dex_file, class_def_index); break; case kOatClassNoneCompiled: @@ -243,8 +196,10 @@ class OatSymbolizer FINAL { } } - void WalkOatClass(const OatFile::OatClass& oat_class, const DexFile& dex_file, - const DexFile::ClassDef& class_def, Callback callback) { + void WalkOatClass(const OatFile::OatClass& oat_class, + const DexFile& dex_file, + uint32_t class_def_index) { + const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index); const uint8_t* class_data = dex_file.GetClassData(class_def); if (class_data == nullptr) { // empty class such as a marker interface? return; @@ -252,117 +207,62 @@ class OatSymbolizer FINAL { // Note: even if this is an interface or a native class, we still have to walk it, as there // might be a static initializer. ClassDataItemIterator it(dex_file, class_data); - SkipAllFields(&it); uint32_t class_method_idx = 0; - while (it.HasNextDirectMethod()) { - const OatFile::OatMethod oat_method = oat_class.GetOatMethod(class_method_idx); - WalkOatMethod(class_def, class_method_idx, oat_method, dex_file, it.GetMemberIndex(), - it.GetMethodCodeItem(), it.GetMethodAccessFlags(), callback); - class_method_idx++; - it.Next(); - } - while (it.HasNextVirtualMethod()) { - const OatFile::OatMethod oat_method = oat_class.GetOatMethod(class_method_idx); - WalkOatMethod(class_def, class_method_idx, oat_method, dex_file, it.GetMemberIndex(), - it.GetMethodCodeItem(), it.GetMethodAccessFlags(), callback); - class_method_idx++; - it.Next(); + for (; it.HasNextStaticField(); it.Next()) { /* skip */ } + for (; it.HasNextInstanceField(); it.Next()) { /* skip */ } + for (; it.HasNextDirectMethod() || it.HasNextVirtualMethod(); it.Next()) { + WalkOatMethod(oat_class.GetOatMethod(class_method_idx++), + dex_file, + class_def_index, + it.GetMemberIndex(), + it.GetMethodCodeItem(), + it.GetMethodAccessFlags()); } DCHECK(!it.HasNext()); } - void WalkOatMethod(const DexFile::ClassDef& class_def, uint32_t class_method_index, - const OatFile::OatMethod& oat_method, const DexFile& dex_file, - uint32_t dex_method_idx, const DexFile::CodeItem* code_item, - uint32_t method_access_flags, Callback callback) { + void WalkOatMethod(const OatFile::OatMethod& oat_method, + const DexFile& dex_file, + uint32_t class_def_index, + uint32_t dex_method_index, + const DexFile::CodeItem* code_item, + uint32_t method_access_flags) { if ((method_access_flags & kAccAbstract) != 0) { // Abstract method, no code. return; } - if (oat_method.GetCodeOffset() == 0) { + const OatHeader& oat_header = oat_file_->GetOatHeader(); + const OatQuickMethodHeader* method_header = oat_method.GetOatQuickMethodHeader(); + if (method_header == nullptr || method_header->GetCodeSize() == 0) { // No code. return; } - (this->*callback)(class_def, class_method_index, oat_method, dex_file, dex_method_idx, code_item, - method_access_flags); - } - - void RegisterForDedup(const DexFile::ClassDef& class_def ATTRIBUTE_UNUSED, - uint32_t class_method_index ATTRIBUTE_UNUSED, - const OatFile::OatMethod& oat_method, - const DexFile& dex_file ATTRIBUTE_UNUSED, - uint32_t dex_method_idx ATTRIBUTE_UNUSED, - const DexFile::CodeItem* code_item ATTRIBUTE_UNUSED, - uint32_t method_access_flags ATTRIBUTE_UNUSED) { - state_[oat_method.GetCodeOffset()]++; - } - - void NormalizeState() { - for (auto& x : state_) { - if (x.second == 1) { - state_[x.first] = 0; - } - } - } - - enum class DedupState { // private - kNotDeduplicated, - kDeduplicatedFirst, - kDeduplicatedOther - }; - DedupState IsDuplicated(uint32_t offset) { - if (state_[offset] == 0) { - return DedupState::kNotDeduplicated; - } - if (state_[offset] == 1) { - return DedupState::kDeduplicatedOther; - } - state_[offset] = 1; - return DedupState::kDeduplicatedFirst; - } - - void AddSymbol(const DexFile::ClassDef& class_def ATTRIBUTE_UNUSED, - uint32_t class_method_index ATTRIBUTE_UNUSED, - const OatFile::OatMethod& oat_method, - const DexFile& dex_file, - uint32_t dex_method_idx, - const DexFile::CodeItem* code_item ATTRIBUTE_UNUSED, - uint32_t method_access_flags ATTRIBUTE_UNUSED) { - DedupState dedup = IsDuplicated(oat_method.GetCodeOffset()); - if (dedup != DedupState::kDeduplicatedOther) { - std::string pretty_name = PrettyMethod(dex_method_idx, dex_file, true); - - if (dedup == DedupState::kDeduplicatedFirst) { - pretty_name = "[Dedup]" + pretty_name; - } - - int name_offset = builder_->GetStrTab()->Write(pretty_name); - uint64_t address = oat_method.GetCodeOffset() - - oat_file_->GetOatHeader().GetExecutableOffset() + - builder_->GetText()->GetAddress(); - builder_->GetSymTab()->Add(name_offset, - builder_->GetText(), - address, - oat_method.GetQuickCodeSize(), - STB_GLOBAL, - STT_FUNC); - } + debug::MethodDebugInfo info = debug::MethodDebugInfo(); + info.trampoline_name = nullptr; + info.dex_file = &dex_file; + info.class_def_index = class_def_index; + info.dex_method_index = dex_method_index; + info.access_flags = method_access_flags; + info.code_item = code_item; + info.isa = oat_header.GetInstructionSet(); + info.deduped = !seen_offsets_.insert(oat_method.GetCodeOffset()).second; + info.is_native_debuggable = oat_header.IsNativeDebuggable(); + info.is_optimized = method_header->IsOptimized(); + info.is_code_address_text_relative = true; + info.code_address = oat_method.GetCodeOffset() - oat_header.GetExecutableOffset(); + info.code_size = method_header->GetCodeSize(); + info.frame_size_in_bytes = method_header->GetFrameSizeInBytes(); + info.code_info = info.is_optimized ? method_header->GetOptimizedCodeInfoPtr() : nullptr; + info.cfi = ArrayRef(); + method_debug_infos_.push_back(info); } private: - static void SkipAllFields(ClassDataItemIterator* it) { - while (it->HasNextStaticField()) { - it->Next(); - } - while (it->HasNextInstanceField()) { - it->Next(); - } - } - const OatFile* oat_file_; std::unique_ptr > builder_; - std::unordered_map state_; + std::vector method_debug_infos_; + std::unordered_set seen_offsets_; const std::string output_name_; }; diff --git a/runtime/oat.cc b/runtime/oat.cc index 2ac105291d..ed99cbabbd 100644 --- a/runtime/oat.cc +++ b/runtime/oat.cc @@ -468,6 +468,10 @@ bool OatHeader::IsDebuggable() const { return IsKeyEnabled(OatHeader::kDebuggableKey); } +bool OatHeader::IsNativeDebuggable() const { + return IsKeyEnabled(OatHeader::kNativeDebuggableKey); +} + bool OatHeader::IsExtractOnly() const { return KeyHasValue(kCompilationType, kExtractOnlyValue, diff --git a/runtime/oat.h b/runtime/oat.h index 0660e19ff4..1d6c076c1b 100644 --- a/runtime/oat.h +++ b/runtime/oat.h @@ -38,6 +38,7 @@ class PACKED(4) OatHeader { static constexpr const char* kDex2OatHostKey = "dex2oat-host"; static constexpr const char* kPicKey = "pic"; static constexpr const char* kDebuggableKey = "debuggable"; + static constexpr const char* kNativeDebuggableKey = "native-debuggable"; static constexpr const char* kCompilationType = "compilation-type"; static constexpr const char* kClassPathKey = "classpath"; static constexpr const char* kBootClassPath = "bootclasspath"; @@ -110,6 +111,7 @@ class PACKED(4) OatHeader { size_t GetHeaderSize() const; bool IsPic() const; bool IsDebuggable() const; + bool IsNativeDebuggable() const; bool IsExtractOnly() const; bool IsProfileGuideCompiled() const; diff --git a/runtime/oat_quick_method_header.h b/runtime/oat_quick_method_header.h index 2b7eca2859..59c37e4915 100644 --- a/runtime/oat_quick_method_header.h +++ b/runtime/oat_quick_method_header.h @@ -63,16 +63,24 @@ class PACKED(4) OatQuickMethodHeader { return gc_map_offset_ == 0 && vmap_table_offset_ != 0; } - CodeInfo GetOptimizedCodeInfo() const { + const void* GetOptimizedCodeInfoPtr() const { DCHECK(IsOptimized()); const void* data = reinterpret_cast(code_ - vmap_table_offset_); - return CodeInfo(data); + return data; + } + + CodeInfo GetOptimizedCodeInfo() const { + return CodeInfo(GetOptimizedCodeInfoPtr()); } const uint8_t* GetCode() const { return code_; } + uint32_t GetCodeSize() const { + return code_size_; + } + const uint8_t* GetNativeGcMap() const { return (gc_map_offset_ == 0) ? nullptr : code_ - gc_map_offset_; } -- GitLab From 6a6b38fbed22688fac7e061450a8a9c64a685faf Mon Sep 17 00:00:00 2001 From: David Srbecky Date: Fri, 11 Mar 2016 14:35:45 +0000 Subject: [PATCH 200/204] Revert "Revert "Allow duplicated methods in different DWARF line tables."" This reverts commit 8862fac4a0b97d827d2808146d2d79b8d799b998. Change-Id: I4d5629df4580b7ac08a5cb04924c56eecad3ad25 --- compiler/debug/elf_debug_line_writer.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/compiler/debug/elf_debug_line_writer.h b/compiler/debug/elf_debug_line_writer.h index 9875c71080..3fe8c3b085 100644 --- a/compiler/debug/elf_debug_line_writer.h +++ b/compiler/debug/elf_debug_line_writer.h @@ -17,6 +17,7 @@ #ifndef ART_COMPILER_DEBUG_ELF_DEBUG_LINE_WRITER_H_ #define ART_COMPILER_DEBUG_ELF_DEBUG_LINE_WRITER_H_ +#include #include #include "compiled_method.h" @@ -81,11 +82,14 @@ class ElfDebugLineWriter { case kX86_64: break; } + std::unordered_set seen_addresses(compilation_unit.methods.size()); dwarf::DebugLineOpCodeWriter<> opcodes(is64bit, code_factor_bits_); for (const MethodDebugInfo* mi : compilation_unit.methods) { // Ignore function if we have already generated line table for the same address. // It would confuse the debugger and the DWARF specification forbids it. - if (mi->deduped) { + // We allow the line table for method to be replicated in different compilation unit. + // This ensures that each compilation unit contains line table for all its methods. + if (!seen_addresses.insert(mi->code_address).second) { continue; } -- GitLab From dc15e8919c152357f3fcb40afcf3412d9b155c8a Mon Sep 17 00:00:00 2001 From: Hiroshi Yamauchi Date: Thu, 10 Mar 2016 13:49:14 -0800 Subject: [PATCH 201/204] Suppress a valgrind libc issue. To prevent our valgrind tests from failing. Bug: 27596582 Bug: 27156726 Change-Id: Ic55256bd09fdffcf2a0c8fa4fa7951bc648b86a8 --- build/Android.gtest.mk | 3 ++- test/valgrind-suppressions.txt | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 test/valgrind-suppressions.txt diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index fda4f5d534..33242f1c5d 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -517,7 +517,8 @@ $$(gtest_rule): $$(gtest_exe) $$(gtest_deps) valgrind-$$(gtest_rule): $$(gtest_exe) $$(gtest_deps) $(ART_VALGRIND_DEPENDENCIES) $(hide) $$(call ART_TEST_SKIP,$$@) && \ VALGRIND_LIB=$(HOST_OUT)/lib64/valgrind \ - $(HOST_OUT_EXECUTABLES)/valgrind --leak-check=full --error-exitcode=1 $$< && \ + $(HOST_OUT_EXECUTABLES)/valgrind --leak-check=full --error-exitcode=1 \ + --suppressions=art/test/valgrind-suppressions.txt $$< && \ $$(call ART_TEST_PASSED,$$@) || $$(call ART_TEST_FAILED,$$@) ART_TEST_HOST_VALGRIND_GTEST$$($(2)ART_PHONY_TEST_HOST_SUFFIX)_RULES += valgrind-$$(gtest_rule) diff --git a/test/valgrind-suppressions.txt b/test/valgrind-suppressions.txt new file mode 100644 index 0000000000..acab6e5135 --- /dev/null +++ b/test/valgrind-suppressions.txt @@ -0,0 +1,15 @@ +{ + b/27596582 + Memcheck:Cond + fun:index + fun:expand_dynamic_string_token + fun:_dl_map_object + fun:map_doit + fun:_dl_catch_error + fun:do_preload + fun:dl_main + fun:_dl_sysdep_start + fun:_dl_start_final + fun:_dl_start + obj:/lib/x86_64-linux-gnu/ld-2.19.so +} -- GitLab From 6832fbe366dd4fc79daa8976f8edbe78d4591649 Mon Sep 17 00:00:00 2001 From: David Srbecky Date: Fri, 11 Mar 2016 18:48:55 +0000 Subject: [PATCH 202/204] Fix DCHECK in OatQuickMethodHeader::GetFrameSizeInBytes() We should check whether the frame is divisible by kStackAlignment. Change-Id: I5936a6e90b38c09775b4c3a8212641e639c4181c --- runtime/oat_quick_method_header.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/oat_quick_method_header.h b/runtime/oat_quick_method_header.h index 59c37e4915..daabc6ee09 100644 --- a/runtime/oat_quick_method_header.h +++ b/runtime/oat_quick_method_header.h @@ -119,7 +119,7 @@ class PACKED(4) OatQuickMethodHeader { uint32_t GetFrameSizeInBytes() const { uint32_t result = frame_info_.FrameSizeInBytes(); if (kCheckFrameSize) { - DCHECK_LE(static_cast(kStackAlignment), result); + DCHECK_ALIGNED(result, kStackAlignment); } return result; } -- GitLab From 10d4c08c0ea9df0a85a11e1c77974df24078c0ec Mon Sep 17 00:00:00 2001 From: Hiroshi Yamauchi Date: Wed, 24 Feb 2016 12:51:18 -0800 Subject: [PATCH 203/204] Assembly region TLAB allocation fast path for arm. This is for the CC collector. Share the common fast path code with the tlab fast path code. Speedup (on N5): BinaryTrees: 2291 -> 902 ms (-60%) MemAllocTest: 2137 -> 1845 ms (-14%) Bug: 9986565 Bug: 12687968 Change-Id: Ica63094ec2f85eaa4fd04d202a20090399275d85 --- runtime/arch/arm/quick_entrypoints_arm.S | 200 +++++++++++------- runtime/arch/arm64/quick_entrypoints_arm64.S | 5 +- runtime/arch/mips/quick_entrypoints_mips.S | 5 +- .../arch/mips64/quick_entrypoints_mips64.S | 5 +- runtime/arch/quick_alloc_entrypoints.S | 3 +- runtime/arch/x86/quick_entrypoints_x86.S | 2 +- .../arch/x86_64/quick_entrypoints_x86_64.S | 3 + runtime/asm_support.h | 5 + 8 files changed, 144 insertions(+), 84 deletions(-) diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S index cfcef49084..92714930c3 100644 --- a/runtime/arch/arm/quick_entrypoints_arm.S +++ b/runtime/arch/arm/quick_entrypoints_arm.S @@ -943,85 +943,6 @@ ONE_ARG_DOWNCALL art_quick_resolve_string, artResolveStringFromCode, RETURN_IF_R // Generate the allocation entrypoints for each allocator. GENERATE_ALLOC_ENTRYPOINTS_FOR_EACH_ALLOCATOR -// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_tlab, TLAB). -ENTRY art_quick_alloc_object_tlab - // Fast path tlab allocation. - // r0: type_idx/return value, r1: ArtMethod*, r9: Thread::Current - // r2, r3, r12: free. -#if defined(USE_READ_BARRIER) - eor r0, r0, r0 // Read barrier not supported here. - sub r0, r0, #1 // Return -1. - bx lr -#endif - ldr r2, [r1, #ART_METHOD_DEX_CACHE_TYPES_OFFSET_32] // Load dex cache resolved types array - // Load the class (r2) - ldr r2, [r2, r0, lsl #COMPRESSED_REFERENCE_SIZE_SHIFT] - cbz r2, .Lart_quick_alloc_object_tlab_slow_path // Check null class - // Check class status. - ldr r3, [r2, #MIRROR_CLASS_STATUS_OFFSET] - cmp r3, #MIRROR_CLASS_STATUS_INITIALIZED - bne .Lart_quick_alloc_object_tlab_slow_path - // Add a fake dependence from the - // following access flag and size - // loads to the status load. - // This is to prevent those loads - // from being reordered above the - // status load and reading wrong - // values (an alternative is to use - // a load-acquire for the status). - eor r3, r3, r3 - add r2, r2, r3 - // Check access flags has - // kAccClassIsFinalizable. - ldr r3, [r2, #MIRROR_CLASS_ACCESS_FLAGS_OFFSET] - tst r3, #ACCESS_FLAGS_CLASS_IS_FINALIZABLE - bne .Lart_quick_alloc_object_tlab_slow_path - // Load thread_local_pos (r12) and - // thread_local_end (r3) with ldrd. - // Check constraints for ldrd. -#if !((THREAD_LOCAL_POS_OFFSET + 4 == THREAD_LOCAL_END_OFFSET) && (THREAD_LOCAL_POS_OFFSET % 8 == 0)) -#error "Thread::thread_local_pos/end must be consecutive and are 8 byte aligned for performance" -#endif - ldrd r12, r3, [r9, #THREAD_LOCAL_POS_OFFSET] - sub r12, r3, r12 // Compute the remaining buf size. - ldr r3, [r2, #MIRROR_CLASS_OBJECT_SIZE_OFFSET] // Load the object size (r3). - cmp r3, r12 // Check if it fits. OK to do this - // before rounding up the object size - // assuming the buf size alignment. - bhi .Lart_quick_alloc_object_tlab_slow_path - // "Point of no slow path". Won't go to the slow path from here on. OK to clobber r0 and r1. - // Round up the object size by the - // object alignment. (addr + 7) & ~7. - add r3, r3, #OBJECT_ALIGNMENT_MASK - and r3, r3, #OBJECT_ALIGNMENT_MASK_TOGGLED - // Reload old thread_local_pos (r0) - // for the return value. - ldr r0, [r9, #THREAD_LOCAL_POS_OFFSET] - add r1, r0, r3 - str r1, [r9, #THREAD_LOCAL_POS_OFFSET] // Store new thread_local_pos. - ldr r1, [r9, #THREAD_LOCAL_OBJECTS_OFFSET] // Increment thread_local_objects. - add r1, r1, #1 - str r1, [r9, #THREAD_LOCAL_OBJECTS_OFFSET] - POISON_HEAP_REF r2 - str r2, [r0, #MIRROR_OBJECT_CLASS_OFFSET] // Store the class pointer. - // Fence. This is "ish" not "ishst" so - // that the code after this allocation - // site will see the right values in - // the fields of the class. - // Alternatively we could use "ishst" - // if we use load-acquire for the - // class status load.) - dmb ish - bx lr -.Lart_quick_alloc_object_tlab_slow_path: - SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r2, r3 // Save callee saves in case of GC. - mov r2, r9 // Pass Thread::Current. - bl artAllocObjectFromCodeTLAB // (uint32_t type_idx, Method* method, Thread*) - RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME - RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER -END art_quick_alloc_object_tlab - - // A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_rosalloc, RosAlloc). ENTRY art_quick_alloc_object_rosalloc // Fast path rosalloc allocation. @@ -1125,6 +1046,127 @@ ENTRY art_quick_alloc_object_rosalloc RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER END art_quick_alloc_object_rosalloc +// The common fast path code for art_quick_alloc_object_tlab and art_quick_alloc_object_region_tlab. +// +// r0: type_idx/return value, r1: ArtMethod*, r2: class, r9: Thread::Current, r3, r12: free. +// Need to preserve r0 and r1 to the slow path. +.macro ALLOC_OBJECT_TLAB_FAST_PATH slowPathLabel + cbz r2, \slowPathLabel // Check null class + // Check class status. + ldr r3, [r2, #MIRROR_CLASS_STATUS_OFFSET] + cmp r3, #MIRROR_CLASS_STATUS_INITIALIZED + bne \slowPathLabel + // Add a fake dependence from the + // following access flag and size + // loads to the status load. + // This is to prevent those loads + // from being reordered above the + // status load and reading wrong + // values (an alternative is to use + // a load-acquire for the status). + eor r3, r3, r3 + add r2, r2, r3 + // Check access flags has + // kAccClassIsFinalizable. + ldr r3, [r2, #MIRROR_CLASS_ACCESS_FLAGS_OFFSET] + tst r3, #ACCESS_FLAGS_CLASS_IS_FINALIZABLE + bne \slowPathLabel + // Load thread_local_pos (r12) and + // thread_local_end (r3) with ldrd. + // Check constraints for ldrd. +#if !((THREAD_LOCAL_POS_OFFSET + 4 == THREAD_LOCAL_END_OFFSET) && (THREAD_LOCAL_POS_OFFSET % 8 == 0)) +#error "Thread::thread_local_pos/end must be consecutive and are 8 byte aligned for performance" +#endif + ldrd r12, r3, [r9, #THREAD_LOCAL_POS_OFFSET] + sub r12, r3, r12 // Compute the remaining buf size. + ldr r3, [r2, #MIRROR_CLASS_OBJECT_SIZE_OFFSET] // Load the object size (r3). + cmp r3, r12 // Check if it fits. OK to do this + // before rounding up the object size + // assuming the buf size alignment. + bhi \slowPathLabel + // "Point of no slow path". Won't go to the slow path from here on. OK to clobber r0 and r1. + // Round up the object size by the + // object alignment. (addr + 7) & ~7. + add r3, r3, #OBJECT_ALIGNMENT_MASK + and r3, r3, #OBJECT_ALIGNMENT_MASK_TOGGLED + // Reload old thread_local_pos (r0) + // for the return value. + ldr r0, [r9, #THREAD_LOCAL_POS_OFFSET] + add r1, r0, r3 + str r1, [r9, #THREAD_LOCAL_POS_OFFSET] // Store new thread_local_pos. + ldr r1, [r9, #THREAD_LOCAL_OBJECTS_OFFSET] // Increment thread_local_objects. + add r1, r1, #1 + str r1, [r9, #THREAD_LOCAL_OBJECTS_OFFSET] + POISON_HEAP_REF r2 + str r2, [r0, #MIRROR_OBJECT_CLASS_OFFSET] // Store the class pointer. + // Fence. This is "ish" not "ishst" so + // that the code after this allocation + // site will see the right values in + // the fields of the class. + // Alternatively we could use "ishst" + // if we use load-acquire for the + // class status load.) + dmb ish + bx lr +.endm + +// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_tlab, TLAB). +ENTRY art_quick_alloc_object_tlab + // Fast path tlab allocation. + // r0: type_idx/return value, r1: ArtMethod*, r9: Thread::Current + // r2, r3, r12: free. +#if defined(USE_READ_BARRIER) + eor r0, r0, r0 // Read barrier not supported here. + sub r0, r0, #1 // Return -1. + bx lr +#endif + ldr r2, [r1, #ART_METHOD_DEX_CACHE_TYPES_OFFSET_32] // Load dex cache resolved types array + // Load the class (r2) + ldr r2, [r2, r0, lsl #COMPRESSED_REFERENCE_SIZE_SHIFT] + ALLOC_OBJECT_TLAB_FAST_PATH .Lart_quick_alloc_object_tlab_slow_path +.Lart_quick_alloc_object_tlab_slow_path: + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r2, r3 // Save callee saves in case of GC. + mov r2, r9 // Pass Thread::Current. + bl artAllocObjectFromCodeTLAB // (uint32_t type_idx, Method* method, Thread*) + RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME + RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER +END art_quick_alloc_object_tlab + +// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_region_tlab, RegionTLAB) +ENTRY art_quick_alloc_object_region_tlab + // Fast path tlab allocation. + // r0: type_idx/return value, r1: ArtMethod*, r9: Thread::Current, r2, r3, r12: free. +#if !defined(USE_READ_BARRIER) + eor r0, r0, r0 // Read barrier must be enabled here. + sub r0, r0, #1 // Return -1. + bx lr +#endif + ldr r2, [r1, #ART_METHOD_DEX_CACHE_TYPES_OFFSET_32] // Load dex cache resolved types array + // Load the class (r2) + ldr r2, [r2, r0, lsl #COMPRESSED_REFERENCE_SIZE_SHIFT] + // Read barrier for class load. + ldr r3, [r9, #THREAD_IS_GC_MARKING_OFFSET] + cbnz r3, .Lart_quick_alloc_object_region_tlab_class_load_read_barrier_slow_path +.Lart_quick_alloc_object_region_tlab_class_load_read_barrier_slow_path_exit: + ALLOC_OBJECT_TLAB_FAST_PATH .Lart_quick_alloc_object_region_tlab_slow_path +.Lart_quick_alloc_object_region_tlab_class_load_read_barrier_slow_path: + // The read barrier slow path. Mark + // the class. + push {r0, r1, r3, lr} // Save registers. r3 is pushed only + // to align sp by 16 bytes. + mov r0, r2 // Pass the class as the first param. + bl artReadBarrierMark + mov r2, r0 // Get the (marked) class back. + pop {r0, r1, r3, lr} + b .Lart_quick_alloc_object_region_tlab_class_load_read_barrier_slow_path_exit +.Lart_quick_alloc_object_region_tlab_slow_path: + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r2, r3 // Save callee saves in case of GC. + mov r2, r9 // Pass Thread::Current. + bl artAllocObjectFromCodeRegionTLAB // (uint32_t type_idx, Method* method, Thread*) + RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME + RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER +END art_quick_alloc_object_region_tlab + /* * Called by managed code when the value in rSUSPEND has been decremented to 0. */ diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S index e8480087a7..e4c255809b 100644 --- a/runtime/arch/arm64/quick_entrypoints_arm64.S +++ b/runtime/arch/arm64/quick_entrypoints_arm64.S @@ -1537,7 +1537,7 @@ ONE_ARG_DOWNCALL art_quick_resolve_string, artResolveStringFromCode, RETURN_IF_R // Generate the allocation entrypoints for each allocator. GENERATE_ALLOC_ENTRYPOINTS_FOR_EACH_ALLOCATOR -GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_tlab, TLAB) + // A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_rosalloc, RosAlloc). ENTRY art_quick_alloc_object_rosalloc // Fast path rosalloc allocation. @@ -1638,6 +1638,9 @@ ENTRY art_quick_alloc_object_rosalloc RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER END art_quick_alloc_object_rosalloc +GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_tlab, TLAB) +GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_region_tlab, RegionTLAB) + /* * Called by managed code when the thread has been asked to suspend. */ diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S index 699ab3e65a..882a16938f 100644 --- a/runtime/arch/mips/quick_entrypoints_mips.S +++ b/runtime/arch/mips/quick_entrypoints_mips.S @@ -1313,7 +1313,7 @@ END \name // Generate the allocation entrypoints for each allocator. GENERATE_ALLOC_ENTRYPOINTS_FOR_EACH_ALLOCATOR -GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_tlab, TLAB) + // A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_rosalloc, RosAlloc). ENTRY art_quick_alloc_object_rosalloc @@ -1421,6 +1421,9 @@ ENTRY art_quick_alloc_object_rosalloc END art_quick_alloc_object_rosalloc +GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_tlab, TLAB) +GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_region_tlab, RegionTLAB) + /* * Entry from managed code to resolve a string, this stub will allocate a String and deliver an * exception on error. On success the String is returned. A0 holds the string index. The fast diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S index d264c9baaf..2f48b888b2 100644 --- a/runtime/arch/mips64/quick_entrypoints_mips64.S +++ b/runtime/arch/mips64/quick_entrypoints_mips64.S @@ -1367,7 +1367,7 @@ END \name // Generate the allocation entrypoints for each allocator. GENERATE_ALLOC_ENTRYPOINTS_FOR_EACH_ALLOCATOR -GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_tlab, TLAB) + // A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_rosalloc, RosAlloc). ENTRY art_quick_alloc_object_rosalloc @@ -1467,6 +1467,9 @@ ENTRY art_quick_alloc_object_rosalloc END art_quick_alloc_object_rosalloc +GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_tlab, TLAB) +GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_region_tlab, RegionTLAB) + /* * Entry from managed code to resolve a string, this stub will allocate a String and deliver an * exception on error. On success the String is returned. A0 holds the string index. The fast diff --git a/runtime/arch/quick_alloc_entrypoints.S b/runtime/arch/quick_alloc_entrypoints.S index fbacdbc930..290769b365 100644 --- a/runtime/arch/quick_alloc_entrypoints.S +++ b/runtime/arch/quick_alloc_entrypoints.S @@ -219,7 +219,8 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_region_instrumented, RegionI GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_region_instrumented, RegionInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region_instrumented, RegionInstrumented) -GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_region_tlab, RegionTLAB) +// This is to be separately defined for each architecture to allow a hand-written assembly fast path. +// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_region_tlab, RegionTLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_tlab, RegionTLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_region_tlab, RegionTLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB) diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S index fbee5d7724..f83ad05c65 100644 --- a/runtime/arch/x86/quick_entrypoints_x86.S +++ b/runtime/arch/x86/quick_entrypoints_x86.S @@ -887,8 +887,8 @@ DEFINE_FUNCTION art_quick_alloc_object_rosalloc RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER // return or deliver exception END_FUNCTION art_quick_alloc_object_rosalloc - GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_tlab, TLAB) +GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_region_tlab, RegionTLAB) ONE_ARG_DOWNCALL art_quick_resolve_string, artResolveStringFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER ONE_ARG_DOWNCALL art_quick_initialize_static_storage, artInitializeStaticStorageFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S index 69caec88f0..dee8d3c6f3 100644 --- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S +++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S @@ -809,6 +809,7 @@ END_MACRO // Generate the allocation entrypoints for each allocator. GENERATE_ALLOC_ENTRYPOINTS_FOR_EACH_ALLOCATOR + // A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_rosalloc, RosAlloc). DEFINE_FUNCTION art_quick_alloc_object_rosalloc // Fast path rosalloc allocation. @@ -943,6 +944,8 @@ DEFINE_FUNCTION art_quick_alloc_object_tlab RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER // return or deliver exception END_FUNCTION art_quick_alloc_object_tlab +GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_region_tlab, RegionTLAB) + ONE_ARG_DOWNCALL art_quick_resolve_string, artResolveStringFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER ONE_ARG_DOWNCALL art_quick_initialize_static_storage, artInitializeStaticStorageFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER ONE_ARG_DOWNCALL art_quick_initialize_type, artInitializeTypeFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER diff --git a/runtime/asm_support.h b/runtime/asm_support.h index 879364e6d2..d5f0dffc38 100644 --- a/runtime/asm_support.h +++ b/runtime/asm_support.h @@ -101,6 +101,11 @@ ADD_TEST_EQ(THREAD_FLAGS_OFFSET, ADD_TEST_EQ(THREAD_ID_OFFSET, art::Thread::ThinLockIdOffset<__SIZEOF_POINTER__>().Int32Value()) +// Offset of field Thread::tls32_.is_gc_marking. +#define THREAD_IS_GC_MARKING_OFFSET 52 +ADD_TEST_EQ(THREAD_IS_GC_MARKING_OFFSET, + art::Thread::IsGcMarkingOffset<__SIZEOF_POINTER__>().Int32Value()) + // Offset of field Thread::tlsPtr_.card_table. #define THREAD_CARD_TABLE_OFFSET 128 ADD_TEST_EQ(THREAD_CARD_TABLE_OFFSET, -- GitLab From ace690f5e440930d7bbad97fdbfdc3eb65e230be Mon Sep 17 00:00:00 2001 From: buzbee Date: Fri, 11 Mar 2016 09:51:11 -0800 Subject: [PATCH 204/204] ART: mterp arm/arm64 cleanup Assembly code cleanup in response to comments from already-submitted CL: https://android-review.googlesource.com/#/c/188977/ Change-Id: I0ea85c5759a08cb50ef3e97dc5cf79b3ba041640 --- runtime/interpreter/mterp/arm/binopLit8.S | 2 +- runtime/interpreter/mterp/arm/binopWide.S | 6 +- .../interpreter/mterp/arm/binopWide2addr.S | 4 +- runtime/interpreter/mterp/arm/entry.S | 4 +- runtime/interpreter/mterp/arm/fbinop2addr.S | 6 +- .../interpreter/mterp/arm/fbinopWide2addr.S | 5 +- runtime/interpreter/mterp/arm/funop.S | 3 +- runtime/interpreter/mterp/arm/funopNarrower.S | 3 +- runtime/interpreter/mterp/arm/funopWider.S | 3 +- runtime/interpreter/mterp/arm/op_aget_wide.S | 2 +- runtime/interpreter/mterp/arm/op_aput_wide.S | 2 +- runtime/interpreter/mterp/arm/op_cmp_long.S | 4 +- runtime/interpreter/mterp/arm/op_const.S | 4 +- runtime/interpreter/mterp/arm/op_const_16.S | 2 +- runtime/interpreter/mterp/arm/op_const_4.S | 3 +- .../interpreter/mterp/arm/op_const_high16.S | 2 +- .../mterp/arm/op_const_string_jumbo.S | 4 +- runtime/interpreter/mterp/arm/op_const_wide.S | 2 +- .../interpreter/mterp/arm/op_const_wide_16.S | 4 +- .../interpreter/mterp/arm/op_const_wide_32.S | 2 +- .../mterp/arm/op_const_wide_high16.S | 2 +- runtime/interpreter/mterp/arm/op_iget_wide.S | 2 +- .../mterp/arm/op_iget_wide_quick.S | 2 +- .../interpreter/mterp/arm/op_instance_of.S | 3 +- runtime/interpreter/mterp/arm/op_iput_wide.S | 2 +- .../mterp/arm/op_iput_wide_quick.S | 2 +- .../interpreter/mterp/arm/op_long_to_double.S | 4 +- .../mterp/arm/op_move_result_wide.S | 2 +- runtime/interpreter/mterp/arm/op_move_wide.S | 4 +- .../interpreter/mterp/arm/op_move_wide_16.S | 4 +- .../mterp/arm/op_move_wide_from16.S | 4 +- runtime/interpreter/mterp/arm/op_mul_long.S | 6 +- .../interpreter/mterp/arm/op_mul_long_2addr.S | 4 +- .../interpreter/mterp/arm/op_return_wide.S | 2 +- runtime/interpreter/mterp/arm/op_sget_wide.S | 2 +- runtime/interpreter/mterp/arm/op_shl_long.S | 4 +- .../interpreter/mterp/arm/op_shl_long_2addr.S | 2 +- runtime/interpreter/mterp/arm/op_shr_long.S | 4 +- .../interpreter/mterp/arm/op_shr_long_2addr.S | 2 +- runtime/interpreter/mterp/arm/op_sput_wide.S | 2 +- runtime/interpreter/mterp/arm/op_ushr_long.S | 4 +- .../mterp/arm/op_ushr_long_2addr.S | 2 +- runtime/interpreter/mterp/arm/unopNarrower.S | 2 +- runtime/interpreter/mterp/arm/unopWide.S | 4 +- runtime/interpreter/mterp/arm/unopWider.S | 2 +- runtime/interpreter/mterp/arm64/entry.S | 2 +- runtime/interpreter/mterp/arm64/header.S | 2 +- .../interpreter/mterp/arm64/op_iput_wide.S | 2 +- .../interpreter/mterp/arm64/op_sput_wide.S | 2 +- runtime/interpreter/mterp/mips/entry.S | 2 +- runtime/interpreter/mterp/out/mterp_arm.S | 282 ++++++++---------- runtime/interpreter/mterp/out/mterp_arm64.S | 8 +- runtime/interpreter/mterp/out/mterp_mips.S | 54 ++-- 53 files changed, 233 insertions(+), 261 deletions(-) diff --git a/runtime/interpreter/mterp/arm/binopLit8.S b/runtime/interpreter/mterp/arm/binopLit8.S index ec0b3c445d..b8f0d925cd 100644 --- a/runtime/interpreter/mterp/arm/binopLit8.S +++ b/runtime/interpreter/mterp/arm/binopLit8.S @@ -13,7 +13,7 @@ * shl-int/lit8, shr-int/lit8, ushr-int/lit8 */ /* binop/lit8 vAA, vBB, #+CC */ - FETCH_S r3, 1 @ r3<- ssssCCBB (sign-extended for CC + FETCH_S r3, 1 @ r3<- ssssCCBB (sign-extended for CC) mov r9, rINST, lsr #8 @ r9<- AA and r2, r3, #255 @ r2<- BB GET_VREG r0, r2 @ r0<- vBB diff --git a/runtime/interpreter/mterp/arm/binopWide.S b/runtime/interpreter/mterp/arm/binopWide.S index 1d511ecfb0..4d880015c8 100644 --- a/runtime/interpreter/mterp/arm/binopWide.S +++ b/runtime/interpreter/mterp/arm/binopWide.S @@ -19,9 +19,9 @@ mov rINST, rINST, lsr #8 @ rINST<- AA and r2, r0, #255 @ r2<- BB mov r3, r0, lsr #8 @ r3<- CC - add r9, rFP, rINST, lsl #2 @ r9<- &fp[AA] - add r2, rFP, r2, lsl #2 @ r2<- &fp[BB] - add r3, rFP, r3, lsl #2 @ r3<- &fp[CC] + VREG_INDEX_TO_ADDR r9, rINST @ r9<- &fp[AA] + VREG_INDEX_TO_ADDR r2, r2 @ r2<- &fp[BB] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[CC] ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1 ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1 .if $chkzero diff --git a/runtime/interpreter/mterp/arm/binopWide2addr.S b/runtime/interpreter/mterp/arm/binopWide2addr.S index 81db48bade..bb16335c34 100644 --- a/runtime/interpreter/mterp/arm/binopWide2addr.S +++ b/runtime/interpreter/mterp/arm/binopWide2addr.S @@ -16,8 +16,8 @@ /* binop/2addr vA, vB */ mov r1, rINST, lsr #12 @ r1<- B ubfx rINST, rINST, #8, #4 @ rINST<- A - add r1, rFP, r1, lsl #2 @ r1<- &fp[B] - add r9, rFP, rINST, lsl #2 @ r9<- &fp[A] + VREG_INDEX_TO_ADDR r1, r1 @ r1<- &fp[B] + VREG_INDEX_TO_ADDR r9, rINST @ r9<- &fp[A] ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1 ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1 .if $chkzero diff --git a/runtime/interpreter/mterp/arm/entry.S b/runtime/interpreter/mterp/arm/entry.S index 4c5ffc5b4e..981c03659f 100644 --- a/runtime/interpreter/mterp/arm/entry.S +++ b/runtime/interpreter/mterp/arm/entry.S @@ -47,8 +47,8 @@ ExecuteMterpImpl: /* set up "named" registers */ mov rSELF, r0 ldr r0, [r2, #SHADOWFRAME_NUMBER_OF_VREGS_OFFSET] - add rFP, r2, #SHADOWFRAME_VREGS_OFFSET @ point to insns[] (i.e. - the dalivk byte code). - add rREFS, rFP, r0, lsl #2 @ point to reference array in shadow frame + add rFP, r2, #SHADOWFRAME_VREGS_OFFSET @ point to vregs. + VREG_INDEX_TO_ADDR rREFS, r0 @ point to reference array in shadow frame ldr r0, [r2, #SHADOWFRAME_DEX_PC_OFFSET] @ Get starting dex_pc. add rPC, r1, #CODEITEM_INSNS_OFFSET @ Point to base of insns[] add rPC, rPC, r0, lsl #1 @ Create direct pointer to 1st dex opcode diff --git a/runtime/interpreter/mterp/arm/fbinop2addr.S b/runtime/interpreter/mterp/arm/fbinop2addr.S index b052a29a88..53c87a08f3 100644 --- a/runtime/interpreter/mterp/arm/fbinop2addr.S +++ b/runtime/interpreter/mterp/arm/fbinop2addr.S @@ -7,14 +7,12 @@ */ /* binop/2addr vA, vB */ mov r3, rINST, lsr #12 @ r3<- B - mov r9, rINST, lsr #8 @ r9<- A+ + ubfx r9, rINST, #8, #4 @ r9<- A VREG_INDEX_TO_ADDR r3, r3 @ r3<- &vB - and r9, r9, #15 @ r9<- A - flds s1, [r3] @ s1<- vB VREG_INDEX_TO_ADDR r9, r9 @ r9<- &vA + flds s1, [r3] @ s1<- vB FETCH_ADVANCE_INST 1 @ advance rPC, load rINST flds s0, [r9] @ s0<- vA - $instr @ s2<- op GET_INST_OPCODE ip @ extract opcode from rINST fsts s2, [r9] @ vAA<- s2 diff --git a/runtime/interpreter/mterp/arm/fbinopWide2addr.S b/runtime/interpreter/mterp/arm/fbinopWide2addr.S index 4e7401dae3..9766e2c0c4 100644 --- a/runtime/interpreter/mterp/arm/fbinopWide2addr.S +++ b/runtime/interpreter/mterp/arm/fbinopWide2addr.S @@ -8,11 +8,10 @@ */ /* binop/2addr vA, vB */ mov r3, rINST, lsr #12 @ r3<- B - mov r9, rINST, lsr #8 @ r9<- A+ + ubfx r9, rINST, #8, #4 @ r9<- A VREG_INDEX_TO_ADDR r3, r3 @ r3<- &vB - and r9, r9, #15 @ r9<- A - fldd d1, [r3] @ d1<- vB CLEAR_SHADOW_PAIR r9, ip, r0 @ Zero out shadow regs + fldd d1, [r3] @ d1<- vB VREG_INDEX_TO_ADDR r9, r9 @ r9<- &vA FETCH_ADVANCE_INST 1 @ advance rPC, load rINST fldd d0, [r9] @ d0<- vA diff --git a/runtime/interpreter/mterp/arm/funop.S b/runtime/interpreter/mterp/arm/funop.S index d7a0859cdc..1b8bb8bac6 100644 --- a/runtime/interpreter/mterp/arm/funop.S +++ b/runtime/interpreter/mterp/arm/funop.S @@ -6,11 +6,10 @@ */ /* unop vA, vB */ mov r3, rINST, lsr #12 @ r3<- B - mov r9, rINST, lsr #8 @ r9<- A+ VREG_INDEX_TO_ADDR r3, r3 @ r3<- &vB flds s0, [r3] @ s0<- vB + ubfx r9, rINST, #8, #4 @ r9<- A FETCH_ADVANCE_INST 1 @ advance rPC, load rINST - and r9, r9, #15 @ r9<- A $instr @ s1<- op GET_INST_OPCODE ip @ extract opcode from rINST VREG_INDEX_TO_ADDR r9, r9 @ r9<- &vA diff --git a/runtime/interpreter/mterp/arm/funopNarrower.S b/runtime/interpreter/mterp/arm/funopNarrower.S index 9daec28556..b9f758ba86 100644 --- a/runtime/interpreter/mterp/arm/funopNarrower.S +++ b/runtime/interpreter/mterp/arm/funopNarrower.S @@ -6,11 +6,10 @@ */ /* unop vA, vB */ mov r3, rINST, lsr #12 @ r3<- B - mov r9, rINST, lsr #8 @ r9<- A+ VREG_INDEX_TO_ADDR r3, r3 @ r3<- &vB fldd d0, [r3] @ d0<- vB + ubfx r9, rINST, #8, #4 @ r9<- A FETCH_ADVANCE_INST 1 @ advance rPC, load rINST - and r9, r9, #15 @ r9<- A $instr @ s0<- op GET_INST_OPCODE ip @ extract opcode from rINST VREG_INDEX_TO_ADDR r9, r9 @ r9<- &vA diff --git a/runtime/interpreter/mterp/arm/funopWider.S b/runtime/interpreter/mterp/arm/funopWider.S index 450ba3a157..854cdc9b66 100644 --- a/runtime/interpreter/mterp/arm/funopWider.S +++ b/runtime/interpreter/mterp/arm/funopWider.S @@ -6,11 +6,10 @@ */ /* unop vA, vB */ mov r3, rINST, lsr #12 @ r3<- B - mov r9, rINST, lsr #8 @ r9<- A+ VREG_INDEX_TO_ADDR r3, r3 @ r3<- &vB flds s0, [r3] @ s0<- vB + ubfx r9, rINST, #8, #4 @ r9<- A FETCH_ADVANCE_INST 1 @ advance rPC, load rINST - and r9, r9, #15 @ r9<- A $instr @ d0<- op CLEAR_SHADOW_PAIR r9, ip, lr @ Zero shadow regs GET_INST_OPCODE ip @ extract opcode from rINST diff --git a/runtime/interpreter/mterp/arm/op_aget_wide.S b/runtime/interpreter/mterp/arm/op_aget_wide.S index e1430b44f2..853a7a4e79 100644 --- a/runtime/interpreter/mterp/arm/op_aget_wide.S +++ b/runtime/interpreter/mterp/arm/op_aget_wide.S @@ -19,7 +19,7 @@ bcs common_errArrayIndex @ index >= length, bail FETCH_ADVANCE_INST 2 @ advance rPC, load rINST ldrd r2, [r0, #MIRROR_WIDE_ARRAY_DATA_OFFSET] @ r2/r3<- vBB[vCC] - add r9, rFP, r9, lsl #2 @ r9<- &fp[AA] + VREG_INDEX_TO_ADDR r9, r9 @ r9<- &fp[AA] GET_INST_OPCODE ip @ extract opcode from rINST stmia r9, {r2-r3} @ vAA/vAA+1<- r2/r3 GOTO_OPCODE ip @ jump to next instruction diff --git a/runtime/interpreter/mterp/arm/op_aput_wide.S b/runtime/interpreter/mterp/arm/op_aput_wide.S index 49839d1b24..005750752f 100644 --- a/runtime/interpreter/mterp/arm/op_aput_wide.S +++ b/runtime/interpreter/mterp/arm/op_aput_wide.S @@ -15,7 +15,7 @@ ldr r3, [r0, #MIRROR_ARRAY_LENGTH_OFFSET] @ r3<- arrayObj->length add r0, r0, r1, lsl #3 @ r0<- arrayObj + index*width cmp r1, r3 @ compare unsigned index, length - add r9, rFP, r9, lsl #2 @ r9<- &fp[AA] + VREG_INDEX_TO_ADDR r9, r9 @ r9<- &fp[AA] bcs common_errArrayIndex @ index >= length, bail FETCH_ADVANCE_INST 2 @ advance rPC, load rINST ldmia r9, {r2-r3} @ r2/r3<- vAA/vAA+1 diff --git a/runtime/interpreter/mterp/arm/op_cmp_long.S b/runtime/interpreter/mterp/arm/op_cmp_long.S index 2b4c0ea5bb..e57b19c5cc 100644 --- a/runtime/interpreter/mterp/arm/op_cmp_long.S +++ b/runtime/interpreter/mterp/arm/op_cmp_long.S @@ -23,8 +23,8 @@ mov r9, rINST, lsr #8 @ r9<- AA and r2, r0, #255 @ r2<- BB mov r3, r0, lsr #8 @ r3<- CC - add r2, rFP, r2, lsl #2 @ r2<- &fp[BB] - add r3, rFP, r3, lsl #2 @ r3<- &fp[CC] + VREG_INDEX_TO_ADDR r2, r2 @ r2<- &fp[BB] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[CC] ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1 ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1 cmp r1, r3 @ compare (vBB+1, vCC+1) diff --git a/runtime/interpreter/mterp/arm/op_const.S b/runtime/interpreter/mterp/arm/op_const.S index de3e3c3c88..39890a085a 100644 --- a/runtime/interpreter/mterp/arm/op_const.S +++ b/runtime/interpreter/mterp/arm/op_const.S @@ -1,7 +1,7 @@ /* const vAA, #+BBBBbbbb */ mov r3, rINST, lsr #8 @ r3<- AA - FETCH r0, 1 @ r0<- bbbb (low - FETCH r1, 2 @ r1<- BBBB (high + FETCH r0, 1 @ r0<- bbbb (low) + FETCH r1, 2 @ r1<- BBBB (high) FETCH_ADVANCE_INST 3 @ advance rPC, load rINST orr r0, r0, r1, lsl #16 @ r0<- BBBBbbbb GET_INST_OPCODE ip @ extract opcode from rINST diff --git a/runtime/interpreter/mterp/arm/op_const_16.S b/runtime/interpreter/mterp/arm/op_const_16.S index 59c6dac10a..a30cf3a0db 100644 --- a/runtime/interpreter/mterp/arm/op_const_16.S +++ b/runtime/interpreter/mterp/arm/op_const_16.S @@ -1,5 +1,5 @@ /* const/16 vAA, #+BBBB */ - FETCH_S r0, 1 @ r0<- ssssBBBB (sign-extended + FETCH_S r0, 1 @ r0<- ssssBBBB (sign-extended) mov r3, rINST, lsr #8 @ r3<- AA FETCH_ADVANCE_INST 2 @ advance rPC, load rINST SET_VREG r0, r3 @ vAA<- r0 diff --git a/runtime/interpreter/mterp/arm/op_const_4.S b/runtime/interpreter/mterp/arm/op_const_4.S index c177bb9eb3..c97b0e91f5 100644 --- a/runtime/interpreter/mterp/arm/op_const_4.S +++ b/runtime/interpreter/mterp/arm/op_const_4.S @@ -1,8 +1,7 @@ /* const/4 vA, #+B */ - mov r1, rINST, lsl #16 @ r1<- Bxxx0000 + sbfx r1, rINST, #12, #4 @ r1<- sssssssB (sign-extended) ubfx r0, rINST, #8, #4 @ r0<- A FETCH_ADVANCE_INST 1 @ advance rPC, load rINST - mov r1, r1, asr #28 @ r1<- sssssssB (sign-extended) GET_INST_OPCODE ip @ ip<- opcode from rINST SET_VREG r1, r0 @ fp[A]<- r1 GOTO_OPCODE ip @ execute next instruction diff --git a/runtime/interpreter/mterp/arm/op_const_high16.S b/runtime/interpreter/mterp/arm/op_const_high16.S index 460d546f3b..536276d52d 100644 --- a/runtime/interpreter/mterp/arm/op_const_high16.S +++ b/runtime/interpreter/mterp/arm/op_const_high16.S @@ -1,5 +1,5 @@ /* const/high16 vAA, #+BBBB0000 */ - FETCH r0, 1 @ r0<- 0000BBBB (zero-extended + FETCH r0, 1 @ r0<- 0000BBBB (zero-extended) mov r3, rINST, lsr #8 @ r3<- AA mov r0, r0, lsl #16 @ r0<- BBBB0000 FETCH_ADVANCE_INST 2 @ advance rPC, load rINST diff --git a/runtime/interpreter/mterp/arm/op_const_string_jumbo.S b/runtime/interpreter/mterp/arm/op_const_string_jumbo.S index 1a3d0b2542..1255c0768d 100644 --- a/runtime/interpreter/mterp/arm/op_const_string_jumbo.S +++ b/runtime/interpreter/mterp/arm/op_const_string_jumbo.S @@ -1,7 +1,7 @@ /* const/string vAA, String@BBBBBBBB */ EXPORT_PC - FETCH r0, 1 @ r0<- bbbb (low - FETCH r2, 2 @ r2<- BBBB (high + FETCH r0, 1 @ r0<- bbbb (low) + FETCH r2, 2 @ r2<- BBBB (high) mov r1, rINST, lsr #8 @ r1<- AA orr r0, r0, r2, lsl #16 @ r1<- BBBBbbbb add r2, rFP, #OFF_FP_SHADOWFRAME diff --git a/runtime/interpreter/mterp/arm/op_const_wide.S b/runtime/interpreter/mterp/arm/op_const_wide.S index 12394b6cbe..8310a4c129 100644 --- a/runtime/interpreter/mterp/arm/op_const_wide.S +++ b/runtime/interpreter/mterp/arm/op_const_wide.S @@ -8,7 +8,7 @@ orr r1, r2, r3, lsl #16 @ r1<- HHHHhhhh (high word) CLEAR_SHADOW_PAIR r9, r2, r3 @ Zero out the shadow regs FETCH_ADVANCE_INST 5 @ advance rPC, load rINST - add r9, rFP, r9, lsl #2 @ r9<- &fp[AA] + VREG_INDEX_TO_ADDR r9, r9 @ r9<- &fp[AA] GET_INST_OPCODE ip @ extract opcode from rINST stmia r9, {r0-r1} @ vAA<- r0/r1 GOTO_OPCODE ip @ jump to next instruction diff --git a/runtime/interpreter/mterp/arm/op_const_wide_16.S b/runtime/interpreter/mterp/arm/op_const_wide_16.S index 3811d8641b..28abb512f0 100644 --- a/runtime/interpreter/mterp/arm/op_const_wide_16.S +++ b/runtime/interpreter/mterp/arm/op_const_wide_16.S @@ -1,10 +1,10 @@ /* const-wide/16 vAA, #+BBBB */ - FETCH_S r0, 1 @ r0<- ssssBBBB (sign-extended + FETCH_S r0, 1 @ r0<- ssssBBBB (sign-extended) mov r3, rINST, lsr #8 @ r3<- AA mov r1, r0, asr #31 @ r1<- ssssssss FETCH_ADVANCE_INST 2 @ advance rPC, load rINST CLEAR_SHADOW_PAIR r3, r2, lr @ Zero out the shadow regs - add r3, rFP, r3, lsl #2 @ r3<- &fp[AA] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[AA] GET_INST_OPCODE ip @ extract opcode from rINST stmia r3, {r0-r1} @ vAA<- r0/r1 GOTO_OPCODE ip @ jump to next instruction diff --git a/runtime/interpreter/mterp/arm/op_const_wide_32.S b/runtime/interpreter/mterp/arm/op_const_wide_32.S index 0b6f1cc384..c10bb0461a 100644 --- a/runtime/interpreter/mterp/arm/op_const_wide_32.S +++ b/runtime/interpreter/mterp/arm/op_const_wide_32.S @@ -5,7 +5,7 @@ FETCH_ADVANCE_INST 3 @ advance rPC, load rINST orr r0, r0, r2, lsl #16 @ r0<- BBBBbbbb CLEAR_SHADOW_PAIR r3, r2, lr @ Zero out the shadow regs - add r3, rFP, r3, lsl #2 @ r3<- &fp[AA] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[AA] mov r1, r0, asr #31 @ r1<- ssssssss GET_INST_OPCODE ip @ extract opcode from rINST stmia r3, {r0-r1} @ vAA<- r0/r1 diff --git a/runtime/interpreter/mterp/arm/op_const_wide_high16.S b/runtime/interpreter/mterp/arm/op_const_wide_high16.S index b9796eb561..d7e38ecc20 100644 --- a/runtime/interpreter/mterp/arm/op_const_wide_high16.S +++ b/runtime/interpreter/mterp/arm/op_const_wide_high16.S @@ -5,7 +5,7 @@ mov r1, r1, lsl #16 @ r1<- BBBB0000 FETCH_ADVANCE_INST 2 @ advance rPC, load rINST CLEAR_SHADOW_PAIR r3, r0, r2 @ Zero shadow regs - add r3, rFP, r3, lsl #2 @ r3<- &fp[AA] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[AA] GET_INST_OPCODE ip @ extract opcode from rINST stmia r3, {r0-r1} @ vAA<- r0/r1 GOTO_OPCODE ip @ jump to next instruction diff --git a/runtime/interpreter/mterp/arm/op_iget_wide.S b/runtime/interpreter/mterp/arm/op_iget_wide.S index 859ffac038..e287d519ad 100644 --- a/runtime/interpreter/mterp/arm/op_iget_wide.S +++ b/runtime/interpreter/mterp/arm/op_iget_wide.S @@ -16,7 +16,7 @@ cmp r3, #0 bne MterpException @ bail out CLEAR_SHADOW_PAIR r2, ip, lr @ Zero out the shadow regs - add r3, rFP, r2, lsl #2 @ r3<- &fp[A] + VREG_INDEX_TO_ADDR r3, r2 @ r3<- &fp[A] stmia r3, {r0-r1} @ fp[A]<- r0/r1 ADVANCE 2 GET_INST_OPCODE ip @ extract opcode from rINST diff --git a/runtime/interpreter/mterp/arm/op_iget_wide_quick.S b/runtime/interpreter/mterp/arm/op_iget_wide_quick.S index 07f854adf4..5a7177d8f5 100644 --- a/runtime/interpreter/mterp/arm/op_iget_wide_quick.S +++ b/runtime/interpreter/mterp/arm/op_iget_wide_quick.S @@ -7,7 +7,7 @@ beq common_errNullObject @ object was null ldrd r0, [r3, ip] @ r0<- obj.field (64 bits, aligned) FETCH_ADVANCE_INST 2 @ advance rPC, load rINST - add r3, rFP, r2, lsl #2 @ r3<- &fp[A] + VREG_INDEX_TO_ADDR r3, r2 @ r3<- &fp[A] CLEAR_SHADOW_PAIR r2, ip, lr @ Zero out the shadow regs GET_INST_OPCODE ip @ extract opcode from rINST stmia r3, {r0-r1} @ fp[A]<- r0/r1 diff --git a/runtime/interpreter/mterp/arm/op_instance_of.S b/runtime/interpreter/mterp/arm/op_instance_of.S index d76f0b09fe..019929edf9 100644 --- a/runtime/interpreter/mterp/arm/op_instance_of.S +++ b/runtime/interpreter/mterp/arm/op_instance_of.S @@ -11,10 +11,9 @@ VREG_INDEX_TO_ADDR r1, r1 @ r1<- &object ldr r2, [rFP, #OFF_FP_METHOD] @ r2<- method mov r3, rSELF @ r3<- self - mov r9, rINST, lsr #8 @ r9<- A+ - and r9, r9, #15 @ r9<- A bl MterpInstanceOf @ (index, &obj, method, self) ldr r1, [rSELF, #THREAD_EXCEPTION_OFFSET] + ubfx r9, rINST, #8, #4 @ r9<- A PREFETCH_INST 2 cmp r1, #0 @ exception pending? bne MterpException diff --git a/runtime/interpreter/mterp/arm/op_iput_wide.S b/runtime/interpreter/mterp/arm/op_iput_wide.S index 8bbd63efc9..3dda1877b5 100644 --- a/runtime/interpreter/mterp/arm/op_iput_wide.S +++ b/runtime/interpreter/mterp/arm/op_iput_wide.S @@ -5,7 +5,7 @@ mov r1, rINST, lsr #12 @ r1<- B GET_VREG r1, r1 @ r1<- fp[B], the object pointer ubfx r2, rINST, #8, #4 @ r2<- A - add r2, rFP, r2, lsl #2 @ r2<- &fp[A] + VREG_INDEX_TO_ADDR r2, r2 @ r2<- &fp[A] ldr r3, [rFP, #OFF_FP_METHOD] @ r3<- referrer PREFETCH_INST 2 bl artSet64InstanceFromMterp diff --git a/runtime/interpreter/mterp/arm/op_iput_wide_quick.S b/runtime/interpreter/mterp/arm/op_iput_wide_quick.S index a2fc9e11ed..88e6ea102c 100644 --- a/runtime/interpreter/mterp/arm/op_iput_wide_quick.S +++ b/runtime/interpreter/mterp/arm/op_iput_wide_quick.S @@ -5,7 +5,7 @@ ubfx r0, rINST, #8, #4 @ r0<- A cmp r2, #0 @ check object for null beq common_errNullObject @ object was null - add r0, rFP, r0, lsl #2 @ r0<- &fp[A] + VREG_INDEX_TO_ADDR r0, r0 @ r0<- &fp[A] ldmia r0, {r0-r1} @ r0/r1<- fp[A]/fp[A+1] FETCH_ADVANCE_INST 2 @ advance rPC, load rINST strd r0, [r2, r3] @ obj.field<- r0/r1 diff --git a/runtime/interpreter/mterp/arm/op_long_to_double.S b/runtime/interpreter/mterp/arm/op_long_to_double.S index 1d48a2acf2..cac12d48d4 100644 --- a/runtime/interpreter/mterp/arm/op_long_to_double.S +++ b/runtime/interpreter/mterp/arm/op_long_to_double.S @@ -8,8 +8,8 @@ */ mov r3, rINST, lsr #12 @ r3<- B ubfx r9, rINST, #8, #4 @ r9<- A - add r3, rFP, r3, lsl #2 @ r3<- &fp[B] - add r9, rFP, r9, lsl #2 @ r9<- &fp[A] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[B] + VREG_INDEX_TO_ADDR r9, r9 @ r9<- &fp[A] vldr d0, [r3] @ d0<- vAA FETCH_ADVANCE_INST 1 @ advance rPC, load rINST diff --git a/runtime/interpreter/mterp/arm/op_move_result_wide.S b/runtime/interpreter/mterp/arm/op_move_result_wide.S index 1845ccf69f..87929eaeeb 100644 --- a/runtime/interpreter/mterp/arm/op_move_result_wide.S +++ b/runtime/interpreter/mterp/arm/op_move_result_wide.S @@ -1,7 +1,7 @@ /* move-result-wide vAA */ mov rINST, rINST, lsr #8 @ rINST<- AA ldr r3, [rFP, #OFF_FP_RESULT_REGISTER] - add r2, rFP, rINST, lsl #2 @ r2<- &fp[AA] + VREG_INDEX_TO_ADDR r2, rINST @ r2<- &fp[AA] ldmia r3, {r0-r1} @ r0/r1<- retval.j CLEAR_SHADOW_PAIR rINST, ip, lr @ Zero out the shadow regs FETCH_ADVANCE_INST 1 @ advance rPC, load rINST diff --git a/runtime/interpreter/mterp/arm/op_move_wide.S b/runtime/interpreter/mterp/arm/op_move_wide.S index f5d156d732..ff353ea5d9 100644 --- a/runtime/interpreter/mterp/arm/op_move_wide.S +++ b/runtime/interpreter/mterp/arm/op_move_wide.S @@ -2,8 +2,8 @@ /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */ mov r3, rINST, lsr #12 @ r3<- B ubfx rINST, rINST, #8, #4 @ rINST<- A - add r3, rFP, r3, lsl #2 @ r3<- &fp[B] - add r2, rFP, rINST, lsl #2 @ r2<- &fp[A] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[B] + VREG_INDEX_TO_ADDR r2, rINST @ r2<- &fp[A] ldmia r3, {r0-r1} @ r0/r1<- fp[B] CLEAR_SHADOW_PAIR rINST, ip, lr @ Zero out the shadow regs FETCH_ADVANCE_INST 1 @ advance rPC, load rINST diff --git a/runtime/interpreter/mterp/arm/op_move_wide_16.S b/runtime/interpreter/mterp/arm/op_move_wide_16.S index 8a55c4b13b..9812b66e97 100644 --- a/runtime/interpreter/mterp/arm/op_move_wide_16.S +++ b/runtime/interpreter/mterp/arm/op_move_wide_16.S @@ -2,8 +2,8 @@ /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */ FETCH r3, 2 @ r3<- BBBB FETCH r2, 1 @ r2<- AAAA - add r3, rFP, r3, lsl #2 @ r3<- &fp[BBBB] - add lr, rFP, r2, lsl #2 @ r2<- &fp[AAAA] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[BBBB] + VREG_INDEX_TO_ADDR lr, r2 @ r2<- &fp[AAAA] ldmia r3, {r0-r1} @ r0/r1<- fp[BBBB] FETCH_ADVANCE_INST 3 @ advance rPC, load rINST CLEAR_SHADOW_PAIR r2, r3, ip @ Zero out the shadow regs diff --git a/runtime/interpreter/mterp/arm/op_move_wide_from16.S b/runtime/interpreter/mterp/arm/op_move_wide_from16.S index b65259db50..d2cc60ca9d 100644 --- a/runtime/interpreter/mterp/arm/op_move_wide_from16.S +++ b/runtime/interpreter/mterp/arm/op_move_wide_from16.S @@ -2,8 +2,8 @@ /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */ FETCH r3, 1 @ r3<- BBBB mov rINST, rINST, lsr #8 @ rINST<- AA - add r3, rFP, r3, lsl #2 @ r3<- &fp[BBBB] - add r2, rFP, rINST, lsl #2 @ r2<- &fp[AA] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[BBBB] + VREG_INDEX_TO_ADDR r2, rINST @ r2<- &fp[AA] ldmia r3, {r0-r1} @ r0/r1<- fp[BBBB] CLEAR_SHADOW_PAIR rINST, ip, lr @ Zero out the shadow regs FETCH_ADVANCE_INST 2 @ advance rPC, load rINST diff --git a/runtime/interpreter/mterp/arm/op_mul_long.S b/runtime/interpreter/mterp/arm/op_mul_long.S index 9e83778e2f..8f40f1976b 100644 --- a/runtime/interpreter/mterp/arm/op_mul_long.S +++ b/runtime/interpreter/mterp/arm/op_mul_long.S @@ -20,8 +20,8 @@ FETCH r0, 1 @ r0<- CCBB and r2, r0, #255 @ r2<- BB mov r3, r0, lsr #8 @ r3<- CC - add r2, rFP, r2, lsl #2 @ r2<- &fp[BB] - add r3, rFP, r3, lsl #2 @ r3<- &fp[CC] + VREG_INDEX_TO_ADDR r2, r2 @ r2<- &fp[BB] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[CC] ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1 ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1 mul ip, r2, r1 @ ip<- ZxW @@ -29,7 +29,7 @@ mla r2, r0, r3, ip @ r2<- YxX + (ZxW) mov r0, rINST, lsr #8 @ r0<- AA add r10, r2, r10 @ r10<- r10 + low(ZxW + (YxX)) - add r0, rFP, r0, lsl #2 @ r0<- &fp[AA] + VREG_INDEX_TO_ADDR r0, r0 @ r0<- &fp[AA] FETCH_ADVANCE_INST 2 @ advance rPC, load rINST GET_INST_OPCODE ip @ extract opcode from rINST stmia r0, {r9-r10} @ vAA/vAA+1<- r9/r10 diff --git a/runtime/interpreter/mterp/arm/op_mul_long_2addr.S b/runtime/interpreter/mterp/arm/op_mul_long_2addr.S index 789dbd3025..7ef24c5142 100644 --- a/runtime/interpreter/mterp/arm/op_mul_long_2addr.S +++ b/runtime/interpreter/mterp/arm/op_mul_long_2addr.S @@ -9,8 +9,8 @@ /* mul-long/2addr vA, vB */ mov r1, rINST, lsr #12 @ r1<- B ubfx r9, rINST, #8, #4 @ r9<- A - add r1, rFP, r1, lsl #2 @ r1<- &fp[B] - add rINST, rFP, r9, lsl #2 @ rINST<- &fp[A] + VREG_INDEX_TO_ADDR r1, r1 @ r1<- &fp[B] + VREG_INDEX_TO_ADDR rINST, r9 @ rINST<- &fp[A] ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1 ldmia rINST, {r0-r1} @ r0/r1<- vAA/vAA+1 mul ip, r2, r1 @ ip<- ZxW diff --git a/runtime/interpreter/mterp/arm/op_return_wide.S b/runtime/interpreter/mterp/arm/op_return_wide.S index cfab5301e5..ceae878fa4 100644 --- a/runtime/interpreter/mterp/arm/op_return_wide.S +++ b/runtime/interpreter/mterp/arm/op_return_wide.S @@ -9,6 +9,6 @@ ands lr, #(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) blne MterpSuspendCheck @ (self) mov r2, rINST, lsr #8 @ r2<- AA - add r2, rFP, r2, lsl #2 @ r2<- &fp[AA] + VREG_INDEX_TO_ADDR r2, r2 @ r2<- &fp[AA] ldmia r2, {r0-r1} @ r0/r1 <- vAA/vAA+1 b MterpReturn diff --git a/runtime/interpreter/mterp/arm/op_sget_wide.S b/runtime/interpreter/mterp/arm/op_sget_wide.S index 3a5090866a..4f2f89d6c3 100644 --- a/runtime/interpreter/mterp/arm/op_sget_wide.S +++ b/runtime/interpreter/mterp/arm/op_sget_wide.S @@ -12,7 +12,7 @@ bl artGet64StaticFromCode ldr r3, [rSELF, #THREAD_EXCEPTION_OFFSET] mov r9, rINST, lsr #8 @ r9<- AA - add lr, rFP, r9, lsl #2 @ r9<- &fp[AA] + VREG_INDEX_TO_ADDR lr, r9 @ r9<- &fp[AA] cmp r3, #0 @ Fail to resolve? bne MterpException @ bail out FETCH_ADVANCE_INST 2 @ advance rPC, load rINST diff --git a/runtime/interpreter/mterp/arm/op_shl_long.S b/runtime/interpreter/mterp/arm/op_shl_long.S index 12ea24883f..82ec6ed09f 100644 --- a/runtime/interpreter/mterp/arm/op_shl_long.S +++ b/runtime/interpreter/mterp/arm/op_shl_long.S @@ -9,12 +9,12 @@ mov r9, rINST, lsr #8 @ r9<- AA and r3, r0, #255 @ r3<- BB mov r0, r0, lsr #8 @ r0<- CC - add r3, rFP, r3, lsl #2 @ r3<- &fp[BB] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[BB] GET_VREG r2, r0 @ r2<- vCC ldmia r3, {r0-r1} @ r0/r1<- vBB/vBB+1 CLEAR_SHADOW_PAIR r9, lr, ip @ Zero out the shadow regs and r2, r2, #63 @ r2<- r2 & 0x3f - add r9, rFP, r9, lsl #2 @ r9<- &fp[AA] + VREG_INDEX_TO_ADDR r9, r9 @ r9<- &fp[AA] mov r1, r1, asl r2 @ r1<- r1 << r2 rsb r3, r2, #32 @ r3<- 32 - r2 orr r1, r1, r0, lsr r3 @ r1<- r1 | (r0 << (32-r2)) diff --git a/runtime/interpreter/mterp/arm/op_shl_long_2addr.S b/runtime/interpreter/mterp/arm/op_shl_long_2addr.S index 4799e77213..f361a7d29c 100644 --- a/runtime/interpreter/mterp/arm/op_shl_long_2addr.S +++ b/runtime/interpreter/mterp/arm/op_shl_long_2addr.S @@ -7,7 +7,7 @@ ubfx r9, rINST, #8, #4 @ r9<- A GET_VREG r2, r3 @ r2<- vB CLEAR_SHADOW_PAIR r9, lr, ip @ Zero out the shadow regs - add r9, rFP, r9, lsl #2 @ r9<- &fp[A] + VREG_INDEX_TO_ADDR r9, r9 @ r9<- &fp[A] and r2, r2, #63 @ r2<- r2 & 0x3f ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1 mov r1, r1, asl r2 @ r1<- r1 << r2 diff --git a/runtime/interpreter/mterp/arm/op_shr_long.S b/runtime/interpreter/mterp/arm/op_shr_long.S index 88a13d6072..a0afe5b040 100644 --- a/runtime/interpreter/mterp/arm/op_shr_long.S +++ b/runtime/interpreter/mterp/arm/op_shr_long.S @@ -9,12 +9,12 @@ mov r9, rINST, lsr #8 @ r9<- AA and r3, r0, #255 @ r3<- BB mov r0, r0, lsr #8 @ r0<- CC - add r3, rFP, r3, lsl #2 @ r3<- &fp[BB] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[BB] GET_VREG r2, r0 @ r2<- vCC ldmia r3, {r0-r1} @ r0/r1<- vBB/vBB+1 CLEAR_SHADOW_PAIR r9, lr, ip @ Zero out the shadow regs and r2, r2, #63 @ r0<- r0 & 0x3f - add r9, rFP, r9, lsl #2 @ r9<- &fp[AA] + VREG_INDEX_TO_ADDR r9, r9 @ r9<- &fp[AA] mov r0, r0, lsr r2 @ r0<- r2 >> r2 rsb r3, r2, #32 @ r3<- 32 - r2 orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2)) diff --git a/runtime/interpreter/mterp/arm/op_shr_long_2addr.S b/runtime/interpreter/mterp/arm/op_shr_long_2addr.S index 78d8bb7dba..976110efd4 100644 --- a/runtime/interpreter/mterp/arm/op_shr_long_2addr.S +++ b/runtime/interpreter/mterp/arm/op_shr_long_2addr.S @@ -7,7 +7,7 @@ ubfx r9, rINST, #8, #4 @ r9<- A GET_VREG r2, r3 @ r2<- vB CLEAR_SHADOW_PAIR r9, lr, ip @ Zero out the shadow regs - add r9, rFP, r9, lsl #2 @ r9<- &fp[A] + VREG_INDEX_TO_ADDR r9, r9 @ r9<- &fp[A] and r2, r2, #63 @ r2<- r2 & 0x3f ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1 mov r0, r0, lsr r2 @ r0<- r2 >> r2 diff --git a/runtime/interpreter/mterp/arm/op_sput_wide.S b/runtime/interpreter/mterp/arm/op_sput_wide.S index adbcffa5a9..8d8ed8c4a2 100644 --- a/runtime/interpreter/mterp/arm/op_sput_wide.S +++ b/runtime/interpreter/mterp/arm/op_sput_wide.S @@ -8,7 +8,7 @@ FETCH r0, 1 @ r0<- field ref BBBB ldr r1, [rFP, #OFF_FP_METHOD] mov r2, rINST, lsr #8 @ r3<- AA - add r2, rFP, r2, lsl #2 + VREG_INDEX_TO_ADDR r2, r2 mov r3, rSELF PREFETCH_INST 2 @ Get next inst, but don't advance rPC bl artSet64IndirectStaticFromMterp diff --git a/runtime/interpreter/mterp/arm/op_ushr_long.S b/runtime/interpreter/mterp/arm/op_ushr_long.S index f98ec639fa..c817bc9fb9 100644 --- a/runtime/interpreter/mterp/arm/op_ushr_long.S +++ b/runtime/interpreter/mterp/arm/op_ushr_long.S @@ -9,12 +9,12 @@ mov r9, rINST, lsr #8 @ r9<- AA and r3, r0, #255 @ r3<- BB mov r0, r0, lsr #8 @ r0<- CC - add r3, rFP, r3, lsl #2 @ r3<- &fp[BB] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[BB] GET_VREG r2, r0 @ r2<- vCC ldmia r3, {r0-r1} @ r0/r1<- vBB/vBB+1 CLEAR_SHADOW_PAIR r9, lr, ip @ Zero out the shadow regs and r2, r2, #63 @ r0<- r0 & 0x3f - add r9, rFP, r9, lsl #2 @ r9<- &fp[AA] + VREG_INDEX_TO_ADDR r9, r9 @ r9<- &fp[AA] mov r0, r0, lsr r2 @ r0<- r2 >> r2 rsb r3, r2, #32 @ r3<- 32 - r2 orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2)) diff --git a/runtime/interpreter/mterp/arm/op_ushr_long_2addr.S b/runtime/interpreter/mterp/arm/op_ushr_long_2addr.S index 840283dd58..2735f8733a 100644 --- a/runtime/interpreter/mterp/arm/op_ushr_long_2addr.S +++ b/runtime/interpreter/mterp/arm/op_ushr_long_2addr.S @@ -7,7 +7,7 @@ ubfx r9, rINST, #8, #4 @ r9<- A GET_VREG r2, r3 @ r2<- vB CLEAR_SHADOW_PAIR r9, lr, ip @ Zero out the shadow regs - add r9, rFP, r9, lsl #2 @ r9<- &fp[A] + VREG_INDEX_TO_ADDR r9, r9 @ r9<- &fp[A] and r2, r2, #63 @ r2<- r2 & 0x3f ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1 mov r0, r0, lsr r2 @ r0<- r2 >> r2 diff --git a/runtime/interpreter/mterp/arm/unopNarrower.S b/runtime/interpreter/mterp/arm/unopNarrower.S index a5fc02797d..2d0453aeb1 100644 --- a/runtime/interpreter/mterp/arm/unopNarrower.S +++ b/runtime/interpreter/mterp/arm/unopNarrower.S @@ -12,7 +12,7 @@ /* unop vA, vB */ mov r3, rINST, lsr #12 @ r3<- B ubfx r9, rINST, #8, #4 @ r9<- A - add r3, rFP, r3, lsl #2 @ r3<- &fp[B] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[B] ldmia r3, {r0-r1} @ r0/r1<- vB/vB+1 FETCH_ADVANCE_INST 1 @ advance rPC, load rINST $preinstr @ optional op; may set condition codes diff --git a/runtime/interpreter/mterp/arm/unopWide.S b/runtime/interpreter/mterp/arm/unopWide.S index a07423468d..cd5defd616 100644 --- a/runtime/interpreter/mterp/arm/unopWide.S +++ b/runtime/interpreter/mterp/arm/unopWide.S @@ -9,8 +9,8 @@ /* unop vA, vB */ mov r3, rINST, lsr #12 @ r3<- B ubfx rINST, rINST, #8, #4 @ rINST<- A - add r3, rFP, r3, lsl #2 @ r3<- &fp[B] - add r9, rFP, rINST, lsl #2 @ r9<- &fp[A] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[B] + VREG_INDEX_TO_ADDR r9, rINST @ r9<- &fp[A] ldmia r3, {r0-r1} @ r0/r1<- vAA CLEAR_SHADOW_PAIR rINST, ip, lr @ Zero shadow regs FETCH_ADVANCE_INST 1 @ advance rPC, load rINST diff --git a/runtime/interpreter/mterp/arm/unopWider.S b/runtime/interpreter/mterp/arm/unopWider.S index 23b6b9d2f5..9d504899b8 100644 --- a/runtime/interpreter/mterp/arm/unopWider.S +++ b/runtime/interpreter/mterp/arm/unopWider.S @@ -10,7 +10,7 @@ mov r3, rINST, lsr #12 @ r3<- B ubfx rINST, rINST, #8, #4 @ rINST<- A GET_VREG r0, r3 @ r0<- vB - add r9, rFP, rINST, lsl #2 @ r9<- &fp[A] + VREG_INDEX_TO_ADDR r9, rINST @ r9<- &fp[A] $preinstr @ optional op; may set condition codes CLEAR_SHADOW_PAIR rINST, ip, lr @ Zero shadow regs FETCH_ADVANCE_INST 1 @ advance rPC, load rINST diff --git a/runtime/interpreter/mterp/arm64/entry.S b/runtime/interpreter/mterp/arm64/entry.S index f9073ab5d9..23e656e826 100644 --- a/runtime/interpreter/mterp/arm64/entry.S +++ b/runtime/interpreter/mterp/arm64/entry.S @@ -46,7 +46,7 @@ ExecuteMterpImpl: /* set up "named" registers */ mov xSELF, x0 ldr w0, [x2, #SHADOWFRAME_NUMBER_OF_VREGS_OFFSET] - add xFP, x2, #SHADOWFRAME_VREGS_OFFSET // point to insns[] (i.e. - the dalivk byte code). + add xFP, x2, #SHADOWFRAME_VREGS_OFFSET // point to vregs. add xREFS, xFP, w0, lsl #2 // point to reference array in shadow frame ldr w0, [x2, #SHADOWFRAME_DEX_PC_OFFSET] // Get starting dex_pc. add xPC, x1, #CODEITEM_INSNS_OFFSET // Point to base of insns[] diff --git a/runtime/interpreter/mterp/arm64/header.S b/runtime/interpreter/mterp/arm64/header.S index 722375002b..7101ba972c 100644 --- a/runtime/interpreter/mterp/arm64/header.S +++ b/runtime/interpreter/mterp/arm64/header.S @@ -272,7 +272,7 @@ codes. * Convert a virtual register index into an address. */ .macro VREG_INDEX_TO_ADDR reg, vreg - add \reg, xFP, \vreg, lsl #2 /* WARNING/FIXME: handle shadow frame vreg zero if store */ + add \reg, xFP, \vreg, lsl #2 /* WARNING: handle shadow frame vreg zero if store */ .endm /* diff --git a/runtime/interpreter/mterp/arm64/op_iput_wide.S b/runtime/interpreter/mterp/arm64/op_iput_wide.S index 4ce95251f6..e1ab1271f5 100644 --- a/runtime/interpreter/mterp/arm64/op_iput_wide.S +++ b/runtime/interpreter/mterp/arm64/op_iput_wide.S @@ -5,7 +5,7 @@ lsr w1, wINST, #12 // w1<- B GET_VREG w1, w1 // w1<- fp[B], the object pointer ubfx w2, wINST, #8, #4 // w2<- A - add x2, xFP, x2, lsl #2 // w2<- &fp[A] + VREG_INDEX_TO_ADDR x2, x2 // w2<- &fp[A] ldr x3, [xFP, #OFF_FP_METHOD] // w3<- referrer PREFETCH_INST 2 bl artSet64InstanceFromMterp diff --git a/runtime/interpreter/mterp/arm64/op_sput_wide.S b/runtime/interpreter/mterp/arm64/op_sput_wide.S index 1d034ecf2f..a79b1a6172 100644 --- a/runtime/interpreter/mterp/arm64/op_sput_wide.S +++ b/runtime/interpreter/mterp/arm64/op_sput_wide.S @@ -8,7 +8,7 @@ FETCH w0, 1 // w0<- field ref BBBB ldr x1, [xFP, #OFF_FP_METHOD] lsr w2, wINST, #8 // w3<- AA - add x2, xFP, w2, lsl #2 + VREG_INDEX_TO_ADDR x2, w2 mov x3, xSELF PREFETCH_INST 2 // Get next inst, but don't advance rPC bl artSet64IndirectStaticFromMterp diff --git a/runtime/interpreter/mterp/mips/entry.S b/runtime/interpreter/mterp/mips/entry.S index cef08feaa0..5771a4f402 100644 --- a/runtime/interpreter/mterp/mips/entry.S +++ b/runtime/interpreter/mterp/mips/entry.S @@ -49,7 +49,7 @@ ExecuteMterpImpl: /* set up "named" registers */ move rSELF, a0 lw a0, SHADOWFRAME_NUMBER_OF_VREGS_OFFSET(a2) - addu rFP, a2, SHADOWFRAME_VREGS_OFFSET # point to insns[] (i.e. - the dalivk byte code). + addu rFP, a2, SHADOWFRAME_VREGS_OFFSET # point to vregs. EAS2(rREFS, rFP, a0) # point to reference array in shadow frame lw a0, SHADOWFRAME_DEX_PC_OFFSET(a2) # Get starting dex_pc addu rPC, a1, CODEITEM_INSNS_OFFSET # Point to base of insns[] diff --git a/runtime/interpreter/mterp/out/mterp_arm.S b/runtime/interpreter/mterp/out/mterp_arm.S index b26a63a5ee..092474d544 100644 --- a/runtime/interpreter/mterp/out/mterp_arm.S +++ b/runtime/interpreter/mterp/out/mterp_arm.S @@ -343,8 +343,8 @@ ExecuteMterpImpl: /* set up "named" registers */ mov rSELF, r0 ldr r0, [r2, #SHADOWFRAME_NUMBER_OF_VREGS_OFFSET] - add rFP, r2, #SHADOWFRAME_VREGS_OFFSET @ point to insns[] (i.e. - the dalivk byte code). - add rREFS, rFP, r0, lsl #2 @ point to reference array in shadow frame + add rFP, r2, #SHADOWFRAME_VREGS_OFFSET @ point to vregs. + VREG_INDEX_TO_ADDR rREFS, r0 @ point to reference array in shadow frame ldr r0, [r2, #SHADOWFRAME_DEX_PC_OFFSET] @ Get starting dex_pc. add rPC, r1, #CODEITEM_INSNS_OFFSET @ Point to base of insns[] add rPC, rPC, r0, lsl #1 @ Create direct pointer to 1st dex opcode @@ -435,8 +435,8 @@ artMterpAsmInstructionStart = .L_op_nop /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */ mov r3, rINST, lsr #12 @ r3<- B ubfx rINST, rINST, #8, #4 @ rINST<- A - add r3, rFP, r3, lsl #2 @ r3<- &fp[B] - add r2, rFP, rINST, lsl #2 @ r2<- &fp[A] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[B] + VREG_INDEX_TO_ADDR r2, rINST @ r2<- &fp[A] ldmia r3, {r0-r1} @ r0/r1<- fp[B] CLEAR_SHADOW_PAIR rINST, ip, lr @ Zero out the shadow regs FETCH_ADVANCE_INST 1 @ advance rPC, load rINST @@ -452,8 +452,8 @@ artMterpAsmInstructionStart = .L_op_nop /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */ FETCH r3, 1 @ r3<- BBBB mov rINST, rINST, lsr #8 @ rINST<- AA - add r3, rFP, r3, lsl #2 @ r3<- &fp[BBBB] - add r2, rFP, rINST, lsl #2 @ r2<- &fp[AA] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[BBBB] + VREG_INDEX_TO_ADDR r2, rINST @ r2<- &fp[AA] ldmia r3, {r0-r1} @ r0/r1<- fp[BBBB] CLEAR_SHADOW_PAIR rINST, ip, lr @ Zero out the shadow regs FETCH_ADVANCE_INST 2 @ advance rPC, load rINST @@ -469,8 +469,8 @@ artMterpAsmInstructionStart = .L_op_nop /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */ FETCH r3, 2 @ r3<- BBBB FETCH r2, 1 @ r2<- AAAA - add r3, rFP, r3, lsl #2 @ r3<- &fp[BBBB] - add lr, rFP, r2, lsl #2 @ r2<- &fp[AAAA] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[BBBB] + VREG_INDEX_TO_ADDR lr, r2 @ r2<- &fp[AAAA] ldmia r3, {r0-r1} @ r0/r1<- fp[BBBB] FETCH_ADVANCE_INST 3 @ advance rPC, load rINST CLEAR_SHADOW_PAIR r2, r3, ip @ Zero out the shadow regs @@ -563,7 +563,7 @@ artMterpAsmInstructionStart = .L_op_nop /* move-result-wide vAA */ mov rINST, rINST, lsr #8 @ rINST<- AA ldr r3, [rFP, #OFF_FP_RESULT_REGISTER] - add r2, rFP, rINST, lsl #2 @ r2<- &fp[AA] + VREG_INDEX_TO_ADDR r2, rINST @ r2<- &fp[AA] ldmia r3, {r0-r1} @ r0/r1<- retval.j CLEAR_SHADOW_PAIR rINST, ip, lr @ Zero out the shadow regs FETCH_ADVANCE_INST 1 @ advance rPC, load rINST @@ -655,7 +655,7 @@ artMterpAsmInstructionStart = .L_op_nop ands lr, #(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) blne MterpSuspendCheck @ (self) mov r2, rINST, lsr #8 @ r2<- AA - add r2, rFP, r2, lsl #2 @ r2<- &fp[AA] + VREG_INDEX_TO_ADDR r2, r2 @ r2<- &fp[AA] ldmia r2, {r0-r1} @ r0/r1 <- vAA/vAA+1 b MterpReturn @@ -687,10 +687,9 @@ artMterpAsmInstructionStart = .L_op_nop .L_op_const_4: /* 0x12 */ /* File: arm/op_const_4.S */ /* const/4 vA, #+B */ - mov r1, rINST, lsl #16 @ r1<- Bxxx0000 + sbfx r1, rINST, #12, #4 @ r1<- sssssssB (sign-extended) ubfx r0, rINST, #8, #4 @ r0<- A FETCH_ADVANCE_INST 1 @ advance rPC, load rINST - mov r1, r1, asr #28 @ r1<- sssssssB (sign-extended) GET_INST_OPCODE ip @ ip<- opcode from rINST SET_VREG r1, r0 @ fp[A]<- r1 GOTO_OPCODE ip @ execute next instruction @@ -700,7 +699,7 @@ artMterpAsmInstructionStart = .L_op_nop .L_op_const_16: /* 0x13 */ /* File: arm/op_const_16.S */ /* const/16 vAA, #+BBBB */ - FETCH_S r0, 1 @ r0<- ssssBBBB (sign-extended + FETCH_S r0, 1 @ r0<- ssssBBBB (sign-extended) mov r3, rINST, lsr #8 @ r3<- AA FETCH_ADVANCE_INST 2 @ advance rPC, load rINST SET_VREG r0, r3 @ vAA<- r0 @@ -713,8 +712,8 @@ artMterpAsmInstructionStart = .L_op_nop /* File: arm/op_const.S */ /* const vAA, #+BBBBbbbb */ mov r3, rINST, lsr #8 @ r3<- AA - FETCH r0, 1 @ r0<- bbbb (low - FETCH r1, 2 @ r1<- BBBB (high + FETCH r0, 1 @ r0<- bbbb (low) + FETCH r1, 2 @ r1<- BBBB (high) FETCH_ADVANCE_INST 3 @ advance rPC, load rINST orr r0, r0, r1, lsl #16 @ r0<- BBBBbbbb GET_INST_OPCODE ip @ extract opcode from rINST @@ -726,7 +725,7 @@ artMterpAsmInstructionStart = .L_op_nop .L_op_const_high16: /* 0x15 */ /* File: arm/op_const_high16.S */ /* const/high16 vAA, #+BBBB0000 */ - FETCH r0, 1 @ r0<- 0000BBBB (zero-extended + FETCH r0, 1 @ r0<- 0000BBBB (zero-extended) mov r3, rINST, lsr #8 @ r3<- AA mov r0, r0, lsl #16 @ r0<- BBBB0000 FETCH_ADVANCE_INST 2 @ advance rPC, load rINST @@ -739,12 +738,12 @@ artMterpAsmInstructionStart = .L_op_nop .L_op_const_wide_16: /* 0x16 */ /* File: arm/op_const_wide_16.S */ /* const-wide/16 vAA, #+BBBB */ - FETCH_S r0, 1 @ r0<- ssssBBBB (sign-extended + FETCH_S r0, 1 @ r0<- ssssBBBB (sign-extended) mov r3, rINST, lsr #8 @ r3<- AA mov r1, r0, asr #31 @ r1<- ssssssss FETCH_ADVANCE_INST 2 @ advance rPC, load rINST CLEAR_SHADOW_PAIR r3, r2, lr @ Zero out the shadow regs - add r3, rFP, r3, lsl #2 @ r3<- &fp[AA] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[AA] GET_INST_OPCODE ip @ extract opcode from rINST stmia r3, {r0-r1} @ vAA<- r0/r1 GOTO_OPCODE ip @ jump to next instruction @@ -760,7 +759,7 @@ artMterpAsmInstructionStart = .L_op_nop FETCH_ADVANCE_INST 3 @ advance rPC, load rINST orr r0, r0, r2, lsl #16 @ r0<- BBBBbbbb CLEAR_SHADOW_PAIR r3, r2, lr @ Zero out the shadow regs - add r3, rFP, r3, lsl #2 @ r3<- &fp[AA] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[AA] mov r1, r0, asr #31 @ r1<- ssssssss GET_INST_OPCODE ip @ extract opcode from rINST stmia r3, {r0-r1} @ vAA<- r0/r1 @@ -780,7 +779,7 @@ artMterpAsmInstructionStart = .L_op_nop orr r1, r2, r3, lsl #16 @ r1<- HHHHhhhh (high word) CLEAR_SHADOW_PAIR r9, r2, r3 @ Zero out the shadow regs FETCH_ADVANCE_INST 5 @ advance rPC, load rINST - add r9, rFP, r9, lsl #2 @ r9<- &fp[AA] + VREG_INDEX_TO_ADDR r9, r9 @ r9<- &fp[AA] GET_INST_OPCODE ip @ extract opcode from rINST stmia r9, {r0-r1} @ vAA<- r0/r1 GOTO_OPCODE ip @ jump to next instruction @@ -796,7 +795,7 @@ artMterpAsmInstructionStart = .L_op_nop mov r1, r1, lsl #16 @ r1<- BBBB0000 FETCH_ADVANCE_INST 2 @ advance rPC, load rINST CLEAR_SHADOW_PAIR r3, r0, r2 @ Zero shadow regs - add r3, rFP, r3, lsl #2 @ r3<- &fp[AA] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[AA] GET_INST_OPCODE ip @ extract opcode from rINST stmia r3, {r0-r1} @ vAA<- r0/r1 GOTO_OPCODE ip @ jump to next instruction @@ -825,8 +824,8 @@ artMterpAsmInstructionStart = .L_op_nop /* File: arm/op_const_string_jumbo.S */ /* const/string vAA, String@BBBBBBBB */ EXPORT_PC - FETCH r0, 1 @ r0<- bbbb (low - FETCH r2, 2 @ r2<- BBBB (high + FETCH r0, 1 @ r0<- bbbb (low) + FETCH r2, 2 @ r2<- BBBB (high) mov r1, rINST, lsr #8 @ r1<- AA orr r0, r0, r2, lsl #16 @ r1<- BBBBbbbb add r2, rFP, #OFF_FP_SHADOWFRAME @@ -938,10 +937,9 @@ artMterpAsmInstructionStart = .L_op_nop VREG_INDEX_TO_ADDR r1, r1 @ r1<- &object ldr r2, [rFP, #OFF_FP_METHOD] @ r2<- method mov r3, rSELF @ r3<- self - mov r9, rINST, lsr #8 @ r9<- A+ - and r9, r9, #15 @ r9<- A bl MterpInstanceOf @ (index, &obj, method, self) ldr r1, [rSELF, #THREAD_EXCEPTION_OFFSET] + ubfx r9, rINST, #8, #4 @ r9<- A PREFETCH_INST 2 cmp r1, #0 @ exception pending? bne MterpException @@ -1509,8 +1507,8 @@ artMterpAsmInstructionStart = .L_op_nop mov r9, rINST, lsr #8 @ r9<- AA and r2, r0, #255 @ r2<- BB mov r3, r0, lsr #8 @ r3<- CC - add r2, rFP, r2, lsl #2 @ r2<- &fp[BB] - add r3, rFP, r3, lsl #2 @ r3<- &fp[CC] + VREG_INDEX_TO_ADDR r2, r2 @ r2<- &fp[BB] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[CC] ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1 ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1 cmp r1, r3 @ compare (vBB+1, vCC+1) @@ -2089,7 +2087,7 @@ artMterpAsmInstructionStart = .L_op_nop bcs common_errArrayIndex @ index >= length, bail FETCH_ADVANCE_INST 2 @ advance rPC, load rINST ldrd r2, [r0, #MIRROR_WIDE_ARRAY_DATA_OFFSET] @ r2/r3<- vBB[vCC] - add r9, rFP, r9, lsl #2 @ r9<- &fp[AA] + VREG_INDEX_TO_ADDR r9, r9 @ r9<- &fp[AA] GET_INST_OPCODE ip @ extract opcode from rINST stmia r9, {r2-r3} @ vAA/vAA+1<- r2/r3 GOTO_OPCODE ip @ jump to next instruction @@ -2314,7 +2312,7 @@ artMterpAsmInstructionStart = .L_op_nop ldr r3, [r0, #MIRROR_ARRAY_LENGTH_OFFSET] @ r3<- arrayObj->length add r0, r0, r1, lsl #3 @ r0<- arrayObj + index*width cmp r1, r3 @ compare unsigned index, length - add r9, rFP, r9, lsl #2 @ r9<- &fp[AA] + VREG_INDEX_TO_ADDR r9, r9 @ r9<- &fp[AA] bcs common_errArrayIndex @ index >= length, bail FETCH_ADVANCE_INST 2 @ advance rPC, load rINST ldmia r9, {r2-r3} @ r2/r3<- vAA/vAA+1 @@ -2533,7 +2531,7 @@ artMterpAsmInstructionStart = .L_op_nop cmp r3, #0 bne MterpException @ bail out CLEAR_SHADOW_PAIR r2, ip, lr @ Zero out the shadow regs - add r3, rFP, r2, lsl #2 @ r3<- &fp[A] + VREG_INDEX_TO_ADDR r3, r2 @ r3<- &fp[A] stmia r3, {r0-r1} @ fp[A]<- r0/r1 ADVANCE 2 GET_INST_OPCODE ip @ extract opcode from rINST @@ -2736,7 +2734,7 @@ artMterpAsmInstructionStart = .L_op_nop mov r1, rINST, lsr #12 @ r1<- B GET_VREG r1, r1 @ r1<- fp[B], the object pointer ubfx r2, rINST, #8, #4 @ r2<- A - add r2, rFP, r2, lsl #2 @ r2<- &fp[A] + VREG_INDEX_TO_ADDR r2, r2 @ r2<- &fp[A] ldr r3, [rFP, #OFF_FP_METHOD] @ r3<- referrer PREFETCH_INST 2 bl artSet64InstanceFromMterp @@ -2923,7 +2921,7 @@ artMterpAsmInstructionStart = .L_op_nop bl artGet64StaticFromCode ldr r3, [rSELF, #THREAD_EXCEPTION_OFFSET] mov r9, rINST, lsr #8 @ r9<- AA - add lr, rFP, r9, lsl #2 @ r9<- &fp[AA] + VREG_INDEX_TO_ADDR lr, r9 @ r9<- &fp[AA] cmp r3, #0 @ Fail to resolve? bne MterpException @ bail out FETCH_ADVANCE_INST 2 @ advance rPC, load rINST @@ -3135,7 +3133,7 @@ artMterpAsmInstructionStart = .L_op_nop FETCH r0, 1 @ r0<- field ref BBBB ldr r1, [rFP, #OFF_FP_METHOD] mov r2, rINST, lsr #8 @ r3<- AA - add r2, rFP, r2, lsl #2 + VREG_INDEX_TO_ADDR r2, r2 mov r3, rSELF PREFETCH_INST 2 @ Get next inst, but don't advance rPC bl artSet64IndirectStaticFromMterp @@ -3668,8 +3666,8 @@ artMterpAsmInstructionStart = .L_op_nop /* unop vA, vB */ mov r3, rINST, lsr #12 @ r3<- B ubfx rINST, rINST, #8, #4 @ rINST<- A - add r3, rFP, r3, lsl #2 @ r3<- &fp[B] - add r9, rFP, rINST, lsl #2 @ r9<- &fp[A] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[B] + VREG_INDEX_TO_ADDR r9, rINST @ r9<- &fp[A] ldmia r3, {r0-r1} @ r0/r1<- vAA CLEAR_SHADOW_PAIR rINST, ip, lr @ Zero shadow regs FETCH_ADVANCE_INST 1 @ advance rPC, load rINST @@ -3696,8 +3694,8 @@ artMterpAsmInstructionStart = .L_op_nop /* unop vA, vB */ mov r3, rINST, lsr #12 @ r3<- B ubfx rINST, rINST, #8, #4 @ rINST<- A - add r3, rFP, r3, lsl #2 @ r3<- &fp[B] - add r9, rFP, rINST, lsl #2 @ r9<- &fp[A] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[B] + VREG_INDEX_TO_ADDR r9, rINST @ r9<- &fp[A] ldmia r3, {r0-r1} @ r0/r1<- vAA CLEAR_SHADOW_PAIR rINST, ip, lr @ Zero shadow regs FETCH_ADVANCE_INST 1 @ advance rPC, load rINST @@ -3750,8 +3748,8 @@ artMterpAsmInstructionStart = .L_op_nop /* unop vA, vB */ mov r3, rINST, lsr #12 @ r3<- B ubfx rINST, rINST, #8, #4 @ rINST<- A - add r3, rFP, r3, lsl #2 @ r3<- &fp[B] - add r9, rFP, rINST, lsl #2 @ r9<- &fp[A] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[B] + VREG_INDEX_TO_ADDR r9, rINST @ r9<- &fp[A] ldmia r3, {r0-r1} @ r0/r1<- vAA CLEAR_SHADOW_PAIR rINST, ip, lr @ Zero shadow regs FETCH_ADVANCE_INST 1 @ advance rPC, load rINST @@ -3779,7 +3777,7 @@ artMterpAsmInstructionStart = .L_op_nop mov r3, rINST, lsr #12 @ r3<- B ubfx rINST, rINST, #8, #4 @ rINST<- A GET_VREG r0, r3 @ r0<- vB - add r9, rFP, rINST, lsl #2 @ r9<- &fp[A] + VREG_INDEX_TO_ADDR r9, rINST @ r9<- &fp[A] @ optional op; may set condition codes CLEAR_SHADOW_PAIR rINST, ip, lr @ Zero shadow regs FETCH_ADVANCE_INST 1 @ advance rPC, load rINST @@ -3803,11 +3801,10 @@ artMterpAsmInstructionStart = .L_op_nop */ /* unop vA, vB */ mov r3, rINST, lsr #12 @ r3<- B - mov r9, rINST, lsr #8 @ r9<- A+ VREG_INDEX_TO_ADDR r3, r3 @ r3<- &vB flds s0, [r3] @ s0<- vB + ubfx r9, rINST, #8, #4 @ r9<- A FETCH_ADVANCE_INST 1 @ advance rPC, load rINST - and r9, r9, #15 @ r9<- A fsitos s1, s0 @ s1<- op GET_INST_OPCODE ip @ extract opcode from rINST VREG_INDEX_TO_ADDR r9, r9 @ r9<- &vA @@ -3828,11 +3825,10 @@ artMterpAsmInstructionStart = .L_op_nop */ /* unop vA, vB */ mov r3, rINST, lsr #12 @ r3<- B - mov r9, rINST, lsr #8 @ r9<- A+ VREG_INDEX_TO_ADDR r3, r3 @ r3<- &vB flds s0, [r3] @ s0<- vB + ubfx r9, rINST, #8, #4 @ r9<- A FETCH_ADVANCE_INST 1 @ advance rPC, load rINST - and r9, r9, #15 @ r9<- A fsitod d0, s0 @ d0<- op CLEAR_SHADOW_PAIR r9, ip, lr @ Zero shadow regs GET_INST_OPCODE ip @ extract opcode from rINST @@ -3880,7 +3876,7 @@ artMterpAsmInstructionStart = .L_op_nop /* unop vA, vB */ mov r3, rINST, lsr #12 @ r3<- B ubfx r9, rINST, #8, #4 @ r9<- A - add r3, rFP, r3, lsl #2 @ r3<- &fp[B] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[B] ldmia r3, {r0-r1} @ r0/r1<- vB/vB+1 FETCH_ADVANCE_INST 1 @ advance rPC, load rINST @ optional op; may set condition codes @@ -3904,8 +3900,8 @@ artMterpAsmInstructionStart = .L_op_nop */ mov r3, rINST, lsr #12 @ r3<- B ubfx r9, rINST, #8, #4 @ r9<- A - add r3, rFP, r3, lsl #2 @ r3<- &fp[B] - add r9, rFP, r9, lsl #2 @ r9<- &fp[A] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[B] + VREG_INDEX_TO_ADDR r9, r9 @ r9<- &fp[A] vldr d0, [r3] @ d0<- vAA FETCH_ADVANCE_INST 1 @ advance rPC, load rINST @@ -3935,11 +3931,10 @@ constvalop_long_to_double: */ /* unop vA, vB */ mov r3, rINST, lsr #12 @ r3<- B - mov r9, rINST, lsr #8 @ r9<- A+ VREG_INDEX_TO_ADDR r3, r3 @ r3<- &vB flds s0, [r3] @ s0<- vB + ubfx r9, rINST, #8, #4 @ r9<- A FETCH_ADVANCE_INST 1 @ advance rPC, load rINST - and r9, r9, #15 @ r9<- A ftosizs s1, s0 @ s1<- op GET_INST_OPCODE ip @ extract opcode from rINST VREG_INDEX_TO_ADDR r9, r9 @ r9<- &vA @@ -3964,7 +3959,7 @@ constvalop_long_to_double: mov r3, rINST, lsr #12 @ r3<- B ubfx rINST, rINST, #8, #4 @ rINST<- A GET_VREG r0, r3 @ r0<- vB - add r9, rFP, rINST, lsl #2 @ r9<- &fp[A] + VREG_INDEX_TO_ADDR r9, rINST @ r9<- &fp[A] @ optional op; may set condition codes CLEAR_SHADOW_PAIR rINST, ip, lr @ Zero shadow regs FETCH_ADVANCE_INST 1 @ advance rPC, load rINST @@ -3989,11 +3984,10 @@ constvalop_long_to_double: */ /* unop vA, vB */ mov r3, rINST, lsr #12 @ r3<- B - mov r9, rINST, lsr #8 @ r9<- A+ VREG_INDEX_TO_ADDR r3, r3 @ r3<- &vB flds s0, [r3] @ s0<- vB + ubfx r9, rINST, #8, #4 @ r9<- A FETCH_ADVANCE_INST 1 @ advance rPC, load rINST - and r9, r9, #15 @ r9<- A vcvt.f64.f32 d0, s0 @ d0<- op CLEAR_SHADOW_PAIR r9, ip, lr @ Zero shadow regs GET_INST_OPCODE ip @ extract opcode from rINST @@ -4015,11 +4009,10 @@ constvalop_long_to_double: */ /* unop vA, vB */ mov r3, rINST, lsr #12 @ r3<- B - mov r9, rINST, lsr #8 @ r9<- A+ VREG_INDEX_TO_ADDR r3, r3 @ r3<- &vB fldd d0, [r3] @ d0<- vB + ubfx r9, rINST, #8, #4 @ r9<- A FETCH_ADVANCE_INST 1 @ advance rPC, load rINST - and r9, r9, #15 @ r9<- A ftosizd s0, d0 @ s0<- op GET_INST_OPCODE ip @ extract opcode from rINST VREG_INDEX_TO_ADDR r9, r9 @ r9<- &vA @@ -4043,8 +4036,8 @@ constvalop_long_to_double: /* unop vA, vB */ mov r3, rINST, lsr #12 @ r3<- B ubfx rINST, rINST, #8, #4 @ rINST<- A - add r3, rFP, r3, lsl #2 @ r3<- &fp[B] - add r9, rFP, rINST, lsl #2 @ r9<- &fp[A] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[B] + VREG_INDEX_TO_ADDR r9, rINST @ r9<- &fp[A] ldmia r3, {r0-r1} @ r0/r1<- vAA CLEAR_SHADOW_PAIR rINST, ip, lr @ Zero shadow regs FETCH_ADVANCE_INST 1 @ advance rPC, load rINST @@ -4070,11 +4063,10 @@ constvalop_long_to_double: */ /* unop vA, vB */ mov r3, rINST, lsr #12 @ r3<- B - mov r9, rINST, lsr #8 @ r9<- A+ VREG_INDEX_TO_ADDR r3, r3 @ r3<- &vB fldd d0, [r3] @ d0<- vB + ubfx r9, rINST, #8, #4 @ r9<- A FETCH_ADVANCE_INST 1 @ advance rPC, load rINST - and r9, r9, #15 @ r9<- A vcvt.f32.f64 s0, d0 @ s0<- op GET_INST_OPCODE ip @ extract opcode from rINST VREG_INDEX_TO_ADDR r9, r9 @ r9<- &vA @@ -4626,9 +4618,9 @@ constvalop_long_to_double: mov rINST, rINST, lsr #8 @ rINST<- AA and r2, r0, #255 @ r2<- BB mov r3, r0, lsr #8 @ r3<- CC - add r9, rFP, rINST, lsl #2 @ r9<- &fp[AA] - add r2, rFP, r2, lsl #2 @ r2<- &fp[BB] - add r3, rFP, r3, lsl #2 @ r3<- &fp[CC] + VREG_INDEX_TO_ADDR r9, rINST @ r9<- &fp[AA] + VREG_INDEX_TO_ADDR r2, r2 @ r2<- &fp[BB] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[CC] ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1 ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1 .if 0 @@ -4670,9 +4662,9 @@ constvalop_long_to_double: mov rINST, rINST, lsr #8 @ rINST<- AA and r2, r0, #255 @ r2<- BB mov r3, r0, lsr #8 @ r3<- CC - add r9, rFP, rINST, lsl #2 @ r9<- &fp[AA] - add r2, rFP, r2, lsl #2 @ r2<- &fp[BB] - add r3, rFP, r3, lsl #2 @ r3<- &fp[CC] + VREG_INDEX_TO_ADDR r9, rINST @ r9<- &fp[AA] + VREG_INDEX_TO_ADDR r2, r2 @ r2<- &fp[BB] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[CC] ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1 ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1 .if 0 @@ -4715,8 +4707,8 @@ constvalop_long_to_double: FETCH r0, 1 @ r0<- CCBB and r2, r0, #255 @ r2<- BB mov r3, r0, lsr #8 @ r3<- CC - add r2, rFP, r2, lsl #2 @ r2<- &fp[BB] - add r3, rFP, r3, lsl #2 @ r3<- &fp[CC] + VREG_INDEX_TO_ADDR r2, r2 @ r2<- &fp[BB] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[CC] ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1 ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1 mul ip, r2, r1 @ ip<- ZxW @@ -4724,7 +4716,7 @@ constvalop_long_to_double: mla r2, r0, r3, ip @ r2<- YxX + (ZxW) mov r0, rINST, lsr #8 @ r0<- AA add r10, r2, r10 @ r10<- r10 + low(ZxW + (YxX)) - add r0, rFP, r0, lsl #2 @ r0<- &fp[AA] + VREG_INDEX_TO_ADDR r0, r0 @ r0<- &fp[AA] FETCH_ADVANCE_INST 2 @ advance rPC, load rINST GET_INST_OPCODE ip @ extract opcode from rINST stmia r0, {r9-r10} @ vAA/vAA+1<- r9/r10 @@ -4755,9 +4747,9 @@ constvalop_long_to_double: mov rINST, rINST, lsr #8 @ rINST<- AA and r2, r0, #255 @ r2<- BB mov r3, r0, lsr #8 @ r3<- CC - add r9, rFP, rINST, lsl #2 @ r9<- &fp[AA] - add r2, rFP, r2, lsl #2 @ r2<- &fp[BB] - add r3, rFP, r3, lsl #2 @ r3<- &fp[CC] + VREG_INDEX_TO_ADDR r9, rINST @ r9<- &fp[AA] + VREG_INDEX_TO_ADDR r2, r2 @ r2<- &fp[BB] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[CC] ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1 ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1 .if 1 @@ -4800,9 +4792,9 @@ constvalop_long_to_double: mov rINST, rINST, lsr #8 @ rINST<- AA and r2, r0, #255 @ r2<- BB mov r3, r0, lsr #8 @ r3<- CC - add r9, rFP, rINST, lsl #2 @ r9<- &fp[AA] - add r2, rFP, r2, lsl #2 @ r2<- &fp[BB] - add r3, rFP, r3, lsl #2 @ r3<- &fp[CC] + VREG_INDEX_TO_ADDR r9, rINST @ r9<- &fp[AA] + VREG_INDEX_TO_ADDR r2, r2 @ r2<- &fp[BB] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[CC] ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1 ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1 .if 1 @@ -4844,9 +4836,9 @@ constvalop_long_to_double: mov rINST, rINST, lsr #8 @ rINST<- AA and r2, r0, #255 @ r2<- BB mov r3, r0, lsr #8 @ r3<- CC - add r9, rFP, rINST, lsl #2 @ r9<- &fp[AA] - add r2, rFP, r2, lsl #2 @ r2<- &fp[BB] - add r3, rFP, r3, lsl #2 @ r3<- &fp[CC] + VREG_INDEX_TO_ADDR r9, rINST @ r9<- &fp[AA] + VREG_INDEX_TO_ADDR r2, r2 @ r2<- &fp[BB] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[CC] ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1 ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1 .if 0 @@ -4888,9 +4880,9 @@ constvalop_long_to_double: mov rINST, rINST, lsr #8 @ rINST<- AA and r2, r0, #255 @ r2<- BB mov r3, r0, lsr #8 @ r3<- CC - add r9, rFP, rINST, lsl #2 @ r9<- &fp[AA] - add r2, rFP, r2, lsl #2 @ r2<- &fp[BB] - add r3, rFP, r3, lsl #2 @ r3<- &fp[CC] + VREG_INDEX_TO_ADDR r9, rINST @ r9<- &fp[AA] + VREG_INDEX_TO_ADDR r2, r2 @ r2<- &fp[BB] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[CC] ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1 ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1 .if 0 @@ -4932,9 +4924,9 @@ constvalop_long_to_double: mov rINST, rINST, lsr #8 @ rINST<- AA and r2, r0, #255 @ r2<- BB mov r3, r0, lsr #8 @ r3<- CC - add r9, rFP, rINST, lsl #2 @ r9<- &fp[AA] - add r2, rFP, r2, lsl #2 @ r2<- &fp[BB] - add r3, rFP, r3, lsl #2 @ r3<- &fp[CC] + VREG_INDEX_TO_ADDR r9, rINST @ r9<- &fp[AA] + VREG_INDEX_TO_ADDR r2, r2 @ r2<- &fp[BB] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[CC] ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1 ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1 .if 0 @@ -4966,12 +4958,12 @@ constvalop_long_to_double: mov r9, rINST, lsr #8 @ r9<- AA and r3, r0, #255 @ r3<- BB mov r0, r0, lsr #8 @ r0<- CC - add r3, rFP, r3, lsl #2 @ r3<- &fp[BB] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[BB] GET_VREG r2, r0 @ r2<- vCC ldmia r3, {r0-r1} @ r0/r1<- vBB/vBB+1 CLEAR_SHADOW_PAIR r9, lr, ip @ Zero out the shadow regs and r2, r2, #63 @ r2<- r2 & 0x3f - add r9, rFP, r9, lsl #2 @ r9<- &fp[AA] + VREG_INDEX_TO_ADDR r9, r9 @ r9<- &fp[AA] mov r1, r1, asl r2 @ r1<- r1 << r2 rsb r3, r2, #32 @ r3<- 32 - r2 orr r1, r1, r0, lsr r3 @ r1<- r1 | (r0 << (32-r2)) @@ -4998,12 +4990,12 @@ constvalop_long_to_double: mov r9, rINST, lsr #8 @ r9<- AA and r3, r0, #255 @ r3<- BB mov r0, r0, lsr #8 @ r0<- CC - add r3, rFP, r3, lsl #2 @ r3<- &fp[BB] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[BB] GET_VREG r2, r0 @ r2<- vCC ldmia r3, {r0-r1} @ r0/r1<- vBB/vBB+1 CLEAR_SHADOW_PAIR r9, lr, ip @ Zero out the shadow regs and r2, r2, #63 @ r0<- r0 & 0x3f - add r9, rFP, r9, lsl #2 @ r9<- &fp[AA] + VREG_INDEX_TO_ADDR r9, r9 @ r9<- &fp[AA] mov r0, r0, lsr r2 @ r0<- r2 >> r2 rsb r3, r2, #32 @ r3<- 32 - r2 orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2)) @@ -5030,12 +5022,12 @@ constvalop_long_to_double: mov r9, rINST, lsr #8 @ r9<- AA and r3, r0, #255 @ r3<- BB mov r0, r0, lsr #8 @ r0<- CC - add r3, rFP, r3, lsl #2 @ r3<- &fp[BB] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[BB] GET_VREG r2, r0 @ r2<- vCC ldmia r3, {r0-r1} @ r0/r1<- vBB/vBB+1 CLEAR_SHADOW_PAIR r9, lr, ip @ Zero out the shadow regs and r2, r2, #63 @ r0<- r0 & 0x3f - add r9, rFP, r9, lsl #2 @ r9<- &fp[AA] + VREG_INDEX_TO_ADDR r9, r9 @ r9<- &fp[AA] mov r0, r0, lsr r2 @ r0<- r2 >> r2 rsb r3, r2, #32 @ r3<- 32 - r2 orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2)) @@ -5355,9 +5347,9 @@ constvalop_long_to_double: mov rINST, rINST, lsr #8 @ rINST<- AA and r2, r0, #255 @ r2<- BB mov r3, r0, lsr #8 @ r3<- CC - add r9, rFP, rINST, lsl #2 @ r9<- &fp[AA] - add r2, rFP, r2, lsl #2 @ r2<- &fp[BB] - add r3, rFP, r3, lsl #2 @ r3<- &fp[CC] + VREG_INDEX_TO_ADDR r9, rINST @ r9<- &fp[AA] + VREG_INDEX_TO_ADDR r2, r2 @ r2<- &fp[BB] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[CC] ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1 ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1 .if 0 @@ -5808,8 +5800,8 @@ constvalop_long_to_double: /* binop/2addr vA, vB */ mov r1, rINST, lsr #12 @ r1<- B ubfx rINST, rINST, #8, #4 @ rINST<- A - add r1, rFP, r1, lsl #2 @ r1<- &fp[B] - add r9, rFP, rINST, lsl #2 @ r9<- &fp[A] + VREG_INDEX_TO_ADDR r1, r1 @ r1<- &fp[B] + VREG_INDEX_TO_ADDR r9, rINST @ r9<- &fp[A] ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1 ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1 .if 0 @@ -5848,8 +5840,8 @@ constvalop_long_to_double: /* binop/2addr vA, vB */ mov r1, rINST, lsr #12 @ r1<- B ubfx rINST, rINST, #8, #4 @ rINST<- A - add r1, rFP, r1, lsl #2 @ r1<- &fp[B] - add r9, rFP, rINST, lsl #2 @ r9<- &fp[A] + VREG_INDEX_TO_ADDR r1, r1 @ r1<- &fp[B] + VREG_INDEX_TO_ADDR r9, rINST @ r9<- &fp[A] ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1 ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1 .if 0 @@ -5881,8 +5873,8 @@ constvalop_long_to_double: /* mul-long/2addr vA, vB */ mov r1, rINST, lsr #12 @ r1<- B ubfx r9, rINST, #8, #4 @ r9<- A - add r1, rFP, r1, lsl #2 @ r1<- &fp[B] - add rINST, rFP, r9, lsl #2 @ rINST<- &fp[A] + VREG_INDEX_TO_ADDR r1, r1 @ r1<- &fp[B] + VREG_INDEX_TO_ADDR rINST, r9 @ rINST<- &fp[A] ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1 ldmia rINST, {r0-r1} @ r0/r1<- vAA/vAA+1 mul ip, r2, r1 @ ip<- ZxW @@ -5917,8 +5909,8 @@ constvalop_long_to_double: /* binop/2addr vA, vB */ mov r1, rINST, lsr #12 @ r1<- B ubfx rINST, rINST, #8, #4 @ rINST<- A - add r1, rFP, r1, lsl #2 @ r1<- &fp[B] - add r9, rFP, rINST, lsl #2 @ r9<- &fp[A] + VREG_INDEX_TO_ADDR r1, r1 @ r1<- &fp[B] + VREG_INDEX_TO_ADDR r9, rINST @ r9<- &fp[A] ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1 ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1 .if 1 @@ -5958,8 +5950,8 @@ constvalop_long_to_double: /* binop/2addr vA, vB */ mov r1, rINST, lsr #12 @ r1<- B ubfx rINST, rINST, #8, #4 @ rINST<- A - add r1, rFP, r1, lsl #2 @ r1<- &fp[B] - add r9, rFP, rINST, lsl #2 @ r9<- &fp[A] + VREG_INDEX_TO_ADDR r1, r1 @ r1<- &fp[B] + VREG_INDEX_TO_ADDR r9, rINST @ r9<- &fp[A] ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1 ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1 .if 1 @@ -5998,8 +5990,8 @@ constvalop_long_to_double: /* binop/2addr vA, vB */ mov r1, rINST, lsr #12 @ r1<- B ubfx rINST, rINST, #8, #4 @ rINST<- A - add r1, rFP, r1, lsl #2 @ r1<- &fp[B] - add r9, rFP, rINST, lsl #2 @ r9<- &fp[A] + VREG_INDEX_TO_ADDR r1, r1 @ r1<- &fp[B] + VREG_INDEX_TO_ADDR r9, rINST @ r9<- &fp[A] ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1 ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1 .if 0 @@ -6038,8 +6030,8 @@ constvalop_long_to_double: /* binop/2addr vA, vB */ mov r1, rINST, lsr #12 @ r1<- B ubfx rINST, rINST, #8, #4 @ rINST<- A - add r1, rFP, r1, lsl #2 @ r1<- &fp[B] - add r9, rFP, rINST, lsl #2 @ r9<- &fp[A] + VREG_INDEX_TO_ADDR r1, r1 @ r1<- &fp[B] + VREG_INDEX_TO_ADDR r9, rINST @ r9<- &fp[A] ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1 ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1 .if 0 @@ -6078,8 +6070,8 @@ constvalop_long_to_double: /* binop/2addr vA, vB */ mov r1, rINST, lsr #12 @ r1<- B ubfx rINST, rINST, #8, #4 @ rINST<- A - add r1, rFP, r1, lsl #2 @ r1<- &fp[B] - add r9, rFP, rINST, lsl #2 @ r9<- &fp[A] + VREG_INDEX_TO_ADDR r1, r1 @ r1<- &fp[B] + VREG_INDEX_TO_ADDR r9, rINST @ r9<- &fp[A] ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1 ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1 .if 0 @@ -6109,7 +6101,7 @@ constvalop_long_to_double: ubfx r9, rINST, #8, #4 @ r9<- A GET_VREG r2, r3 @ r2<- vB CLEAR_SHADOW_PAIR r9, lr, ip @ Zero out the shadow regs - add r9, rFP, r9, lsl #2 @ r9<- &fp[A] + VREG_INDEX_TO_ADDR r9, r9 @ r9<- &fp[A] and r2, r2, #63 @ r2<- r2 & 0x3f ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1 mov r1, r1, asl r2 @ r1<- r1 << r2 @@ -6136,7 +6128,7 @@ constvalop_long_to_double: ubfx r9, rINST, #8, #4 @ r9<- A GET_VREG r2, r3 @ r2<- vB CLEAR_SHADOW_PAIR r9, lr, ip @ Zero out the shadow regs - add r9, rFP, r9, lsl #2 @ r9<- &fp[A] + VREG_INDEX_TO_ADDR r9, r9 @ r9<- &fp[A] and r2, r2, #63 @ r2<- r2 & 0x3f ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1 mov r0, r0, lsr r2 @ r0<- r2 >> r2 @@ -6163,7 +6155,7 @@ constvalop_long_to_double: ubfx r9, rINST, #8, #4 @ r9<- A GET_VREG r2, r3 @ r2<- vB CLEAR_SHADOW_PAIR r9, lr, ip @ Zero out the shadow regs - add r9, rFP, r9, lsl #2 @ r9<- &fp[A] + VREG_INDEX_TO_ADDR r9, r9 @ r9<- &fp[A] and r2, r2, #63 @ r2<- r2 & 0x3f ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1 mov r0, r0, lsr r2 @ r0<- r2 >> r2 @@ -6191,14 +6183,12 @@ constvalop_long_to_double: */ /* binop/2addr vA, vB */ mov r3, rINST, lsr #12 @ r3<- B - mov r9, rINST, lsr #8 @ r9<- A+ + ubfx r9, rINST, #8, #4 @ r9<- A VREG_INDEX_TO_ADDR r3, r3 @ r3<- &vB - and r9, r9, #15 @ r9<- A - flds s1, [r3] @ s1<- vB VREG_INDEX_TO_ADDR r9, r9 @ r9<- &vA + flds s1, [r3] @ s1<- vB FETCH_ADVANCE_INST 1 @ advance rPC, load rINST flds s0, [r9] @ s0<- vA - fadds s2, s0, s1 @ s2<- op GET_INST_OPCODE ip @ extract opcode from rINST fsts s2, [r9] @ vAA<- s2 @@ -6219,14 +6209,12 @@ constvalop_long_to_double: */ /* binop/2addr vA, vB */ mov r3, rINST, lsr #12 @ r3<- B - mov r9, rINST, lsr #8 @ r9<- A+ + ubfx r9, rINST, #8, #4 @ r9<- A VREG_INDEX_TO_ADDR r3, r3 @ r3<- &vB - and r9, r9, #15 @ r9<- A - flds s1, [r3] @ s1<- vB VREG_INDEX_TO_ADDR r9, r9 @ r9<- &vA + flds s1, [r3] @ s1<- vB FETCH_ADVANCE_INST 1 @ advance rPC, load rINST flds s0, [r9] @ s0<- vA - fsubs s2, s0, s1 @ s2<- op GET_INST_OPCODE ip @ extract opcode from rINST fsts s2, [r9] @ vAA<- s2 @@ -6247,14 +6235,12 @@ constvalop_long_to_double: */ /* binop/2addr vA, vB */ mov r3, rINST, lsr #12 @ r3<- B - mov r9, rINST, lsr #8 @ r9<- A+ + ubfx r9, rINST, #8, #4 @ r9<- A VREG_INDEX_TO_ADDR r3, r3 @ r3<- &vB - and r9, r9, #15 @ r9<- A - flds s1, [r3] @ s1<- vB VREG_INDEX_TO_ADDR r9, r9 @ r9<- &vA + flds s1, [r3] @ s1<- vB FETCH_ADVANCE_INST 1 @ advance rPC, load rINST flds s0, [r9] @ s0<- vA - fmuls s2, s0, s1 @ s2<- op GET_INST_OPCODE ip @ extract opcode from rINST fsts s2, [r9] @ vAA<- s2 @@ -6275,14 +6261,12 @@ constvalop_long_to_double: */ /* binop/2addr vA, vB */ mov r3, rINST, lsr #12 @ r3<- B - mov r9, rINST, lsr #8 @ r9<- A+ + ubfx r9, rINST, #8, #4 @ r9<- A VREG_INDEX_TO_ADDR r3, r3 @ r3<- &vB - and r9, r9, #15 @ r9<- A - flds s1, [r3] @ s1<- vB VREG_INDEX_TO_ADDR r9, r9 @ r9<- &vA + flds s1, [r3] @ s1<- vB FETCH_ADVANCE_INST 1 @ advance rPC, load rINST flds s0, [r9] @ s0<- vA - fdivs s2, s0, s1 @ s2<- op GET_INST_OPCODE ip @ extract opcode from rINST fsts s2, [r9] @ vAA<- s2 @@ -6343,11 +6327,10 @@ constvalop_long_to_double: */ /* binop/2addr vA, vB */ mov r3, rINST, lsr #12 @ r3<- B - mov r9, rINST, lsr #8 @ r9<- A+ + ubfx r9, rINST, #8, #4 @ r9<- A VREG_INDEX_TO_ADDR r3, r3 @ r3<- &vB - and r9, r9, #15 @ r9<- A - fldd d1, [r3] @ d1<- vB CLEAR_SHADOW_PAIR r9, ip, r0 @ Zero out shadow regs + fldd d1, [r3] @ d1<- vB VREG_INDEX_TO_ADDR r9, r9 @ r9<- &vA FETCH_ADVANCE_INST 1 @ advance rPC, load rINST fldd d0, [r9] @ d0<- vA @@ -6372,11 +6355,10 @@ constvalop_long_to_double: */ /* binop/2addr vA, vB */ mov r3, rINST, lsr #12 @ r3<- B - mov r9, rINST, lsr #8 @ r9<- A+ + ubfx r9, rINST, #8, #4 @ r9<- A VREG_INDEX_TO_ADDR r3, r3 @ r3<- &vB - and r9, r9, #15 @ r9<- A - fldd d1, [r3] @ d1<- vB CLEAR_SHADOW_PAIR r9, ip, r0 @ Zero out shadow regs + fldd d1, [r3] @ d1<- vB VREG_INDEX_TO_ADDR r9, r9 @ r9<- &vA FETCH_ADVANCE_INST 1 @ advance rPC, load rINST fldd d0, [r9] @ d0<- vA @@ -6401,11 +6383,10 @@ constvalop_long_to_double: */ /* binop/2addr vA, vB */ mov r3, rINST, lsr #12 @ r3<- B - mov r9, rINST, lsr #8 @ r9<- A+ + ubfx r9, rINST, #8, #4 @ r9<- A VREG_INDEX_TO_ADDR r3, r3 @ r3<- &vB - and r9, r9, #15 @ r9<- A - fldd d1, [r3] @ d1<- vB CLEAR_SHADOW_PAIR r9, ip, r0 @ Zero out shadow regs + fldd d1, [r3] @ d1<- vB VREG_INDEX_TO_ADDR r9, r9 @ r9<- &vA FETCH_ADVANCE_INST 1 @ advance rPC, load rINST fldd d0, [r9] @ d0<- vA @@ -6430,11 +6411,10 @@ constvalop_long_to_double: */ /* binop/2addr vA, vB */ mov r3, rINST, lsr #12 @ r3<- B - mov r9, rINST, lsr #8 @ r9<- A+ + ubfx r9, rINST, #8, #4 @ r9<- A VREG_INDEX_TO_ADDR r3, r3 @ r3<- &vB - and r9, r9, #15 @ r9<- A - fldd d1, [r3] @ d1<- vB CLEAR_SHADOW_PAIR r9, ip, r0 @ Zero out shadow regs + fldd d1, [r3] @ d1<- vB VREG_INDEX_TO_ADDR r9, r9 @ r9<- &vA FETCH_ADVANCE_INST 1 @ advance rPC, load rINST fldd d0, [r9] @ d0<- vA @@ -6467,8 +6447,8 @@ constvalop_long_to_double: /* binop/2addr vA, vB */ mov r1, rINST, lsr #12 @ r1<- B ubfx rINST, rINST, #8, #4 @ rINST<- A - add r1, rFP, r1, lsl #2 @ r1<- &fp[B] - add r9, rFP, rINST, lsl #2 @ r9<- &fp[A] + VREG_INDEX_TO_ADDR r1, r1 @ r1<- &fp[B] + VREG_INDEX_TO_ADDR r9, rINST @ r9<- &fp[A] ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1 ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1 .if 0 @@ -6783,7 +6763,7 @@ constvalop_long_to_double: * shl-int/lit8, shr-int/lit8, ushr-int/lit8 */ /* binop/lit8 vAA, vBB, #+CC */ - FETCH_S r3, 1 @ r3<- ssssCCBB (sign-extended for CC + FETCH_S r3, 1 @ r3<- ssssCCBB (sign-extended for CC) mov r9, rINST, lsr #8 @ r9<- AA and r2, r3, #255 @ r2<- BB GET_VREG r0, r2 @ r0<- vBB @@ -6821,7 +6801,7 @@ constvalop_long_to_double: * shl-int/lit8, shr-int/lit8, ushr-int/lit8 */ /* binop/lit8 vAA, vBB, #+CC */ - FETCH_S r3, 1 @ r3<- ssssCCBB (sign-extended for CC + FETCH_S r3, 1 @ r3<- ssssCCBB (sign-extended for CC) mov r9, rINST, lsr #8 @ r9<- AA and r2, r3, #255 @ r2<- BB GET_VREG r0, r2 @ r0<- vBB @@ -6860,7 +6840,7 @@ constvalop_long_to_double: * shl-int/lit8, shr-int/lit8, ushr-int/lit8 */ /* binop/lit8 vAA, vBB, #+CC */ - FETCH_S r3, 1 @ r3<- ssssCCBB (sign-extended for CC + FETCH_S r3, 1 @ r3<- ssssCCBB (sign-extended for CC) mov r9, rINST, lsr #8 @ r9<- AA and r2, r3, #255 @ r2<- BB GET_VREG r0, r2 @ r0<- vBB @@ -6967,7 +6947,7 @@ constvalop_long_to_double: * shl-int/lit8, shr-int/lit8, ushr-int/lit8 */ /* binop/lit8 vAA, vBB, #+CC */ - FETCH_S r3, 1 @ r3<- ssssCCBB (sign-extended for CC + FETCH_S r3, 1 @ r3<- ssssCCBB (sign-extended for CC) mov r9, rINST, lsr #8 @ r9<- AA and r2, r3, #255 @ r2<- BB GET_VREG r0, r2 @ r0<- vBB @@ -7005,7 +6985,7 @@ constvalop_long_to_double: * shl-int/lit8, shr-int/lit8, ushr-int/lit8 */ /* binop/lit8 vAA, vBB, #+CC */ - FETCH_S r3, 1 @ r3<- ssssCCBB (sign-extended for CC + FETCH_S r3, 1 @ r3<- ssssCCBB (sign-extended for CC) mov r9, rINST, lsr #8 @ r9<- AA and r2, r3, #255 @ r2<- BB GET_VREG r0, r2 @ r0<- vBB @@ -7043,7 +7023,7 @@ constvalop_long_to_double: * shl-int/lit8, shr-int/lit8, ushr-int/lit8 */ /* binop/lit8 vAA, vBB, #+CC */ - FETCH_S r3, 1 @ r3<- ssssCCBB (sign-extended for CC + FETCH_S r3, 1 @ r3<- ssssCCBB (sign-extended for CC) mov r9, rINST, lsr #8 @ r9<- AA and r2, r3, #255 @ r2<- BB GET_VREG r0, r2 @ r0<- vBB @@ -7081,7 +7061,7 @@ constvalop_long_to_double: * shl-int/lit8, shr-int/lit8, ushr-int/lit8 */ /* binop/lit8 vAA, vBB, #+CC */ - FETCH_S r3, 1 @ r3<- ssssCCBB (sign-extended for CC + FETCH_S r3, 1 @ r3<- ssssCCBB (sign-extended for CC) mov r9, rINST, lsr #8 @ r9<- AA and r2, r3, #255 @ r2<- BB GET_VREG r0, r2 @ r0<- vBB @@ -7119,7 +7099,7 @@ constvalop_long_to_double: * shl-int/lit8, shr-int/lit8, ushr-int/lit8 */ /* binop/lit8 vAA, vBB, #+CC */ - FETCH_S r3, 1 @ r3<- ssssCCBB (sign-extended for CC + FETCH_S r3, 1 @ r3<- ssssCCBB (sign-extended for CC) mov r9, rINST, lsr #8 @ r9<- AA and r2, r3, #255 @ r2<- BB GET_VREG r0, r2 @ r0<- vBB @@ -7157,7 +7137,7 @@ constvalop_long_to_double: * shl-int/lit8, shr-int/lit8, ushr-int/lit8 */ /* binop/lit8 vAA, vBB, #+CC */ - FETCH_S r3, 1 @ r3<- ssssCCBB (sign-extended for CC + FETCH_S r3, 1 @ r3<- ssssCCBB (sign-extended for CC) mov r9, rINST, lsr #8 @ r9<- AA and r2, r3, #255 @ r2<- BB GET_VREG r0, r2 @ r0<- vBB @@ -7207,7 +7187,7 @@ constvalop_long_to_double: beq common_errNullObject @ object was null ldrd r0, [r3, ip] @ r0<- obj.field (64 bits, aligned) FETCH_ADVANCE_INST 2 @ advance rPC, load rINST - add r3, rFP, r2, lsl #2 @ r3<- &fp[A] + VREG_INDEX_TO_ADDR r3, r2 @ r3<- &fp[A] CLEAR_SHADOW_PAIR r2, ip, lr @ Zero out the shadow regs GET_INST_OPCODE ip @ extract opcode from rINST stmia r3, {r0-r1} @ fp[A]<- r0/r1 @@ -7263,7 +7243,7 @@ constvalop_long_to_double: ubfx r0, rINST, #8, #4 @ r0<- A cmp r2, #0 @ check object for null beq common_errNullObject @ object was null - add r0, rFP, r0, lsl #2 @ r0<- &fp[A] + VREG_INDEX_TO_ADDR r0, r0 @ r0<- &fp[A] ldmia r0, {r0-r1} @ r0/r1<- fp[A]/fp[A+1] FETCH_ADVANCE_INST 2 @ advance rPC, load rINST strd r0, [r2, r3] @ obj.field<- r0/r1 diff --git a/runtime/interpreter/mterp/out/mterp_arm64.S b/runtime/interpreter/mterp/out/mterp_arm64.S index c7c0fb5b79..cdb27e89e6 100644 --- a/runtime/interpreter/mterp/out/mterp_arm64.S +++ b/runtime/interpreter/mterp/out/mterp_arm64.S @@ -279,7 +279,7 @@ codes. * Convert a virtual register index into an address. */ .macro VREG_INDEX_TO_ADDR reg, vreg - add \reg, xFP, \vreg, lsl #2 /* WARNING/FIXME: handle shadow frame vreg zero if store */ + add \reg, xFP, \vreg, lsl #2 /* WARNING: handle shadow frame vreg zero if store */ .endm /* @@ -338,7 +338,7 @@ ExecuteMterpImpl: /* set up "named" registers */ mov xSELF, x0 ldr w0, [x2, #SHADOWFRAME_NUMBER_OF_VREGS_OFFSET] - add xFP, x2, #SHADOWFRAME_VREGS_OFFSET // point to insns[] (i.e. - the dalivk byte code). + add xFP, x2, #SHADOWFRAME_VREGS_OFFSET // point to vregs. add xREFS, xFP, w0, lsl #2 // point to reference array in shadow frame ldr w0, [x2, #SHADOWFRAME_DEX_PC_OFFSET] // Get starting dex_pc. add xPC, x1, #CODEITEM_INSNS_OFFSET // Point to base of insns[] @@ -2552,7 +2552,7 @@ artMterpAsmInstructionStart = .L_op_nop lsr w1, wINST, #12 // w1<- B GET_VREG w1, w1 // w1<- fp[B], the object pointer ubfx w2, wINST, #8, #4 // w2<- A - add x2, xFP, x2, lsl #2 // w2<- &fp[A] + VREG_INDEX_TO_ADDR x2, x2 // w2<- &fp[A] ldr x3, [xFP, #OFF_FP_METHOD] // w3<- referrer PREFETCH_INST 2 bl artSet64InstanceFromMterp @@ -2941,7 +2941,7 @@ artMterpAsmInstructionStart = .L_op_nop FETCH w0, 1 // w0<- field ref BBBB ldr x1, [xFP, #OFF_FP_METHOD] lsr w2, wINST, #8 // w3<- AA - add x2, xFP, w2, lsl #2 + VREG_INDEX_TO_ADDR x2, w2 mov x3, xSELF PREFETCH_INST 2 // Get next inst, but don't advance rPC bl artSet64IndirectStaticFromMterp diff --git a/runtime/interpreter/mterp/out/mterp_mips.S b/runtime/interpreter/mterp/out/mterp_mips.S index 7ae1ab110d..b134129a5a 100644 --- a/runtime/interpreter/mterp/out/mterp_mips.S +++ b/runtime/interpreter/mterp/out/mterp_mips.S @@ -542,7 +542,7 @@ ExecuteMterpImpl: /* set up "named" registers */ move rSELF, a0 lw a0, SHADOWFRAME_NUMBER_OF_VREGS_OFFSET(a2) - addu rFP, a2, SHADOWFRAME_VREGS_OFFSET # point to insns[] (i.e. - the dalivk byte code). + addu rFP, a2, SHADOWFRAME_VREGS_OFFSET # point to vregs. EAS2(rREFS, rFP, a0) # point to reference array in shadow frame lw a0, SHADOWFRAME_DEX_PC_OFFSET(a2) # Get starting dex_pc addu rPC, a1, CODEITEM_INSNS_OFFSET # Point to base of insns[] @@ -4373,8 +4373,8 @@ artMterpAsmInstructionStart = .L_op_nop * * If "chkzero" is set to 1, we perform a divide-by-zero check on * vCC (a1). Useful for integer division and modulus. Note that we - * *don't* check for (INT_MIN / -1) here, because the ARM math lib - * handles it correctly. + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. * * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int, * xor-int, shl-int, shr-int, ushr-int @@ -4412,8 +4412,8 @@ artMterpAsmInstructionStart = .L_op_nop * * If "chkzero" is set to 1, we perform a divide-by-zero check on * vCC (a1). Useful for integer division and modulus. Note that we - * *don't* check for (INT_MIN / -1) here, because the ARM math lib - * handles it correctly. + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. * * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int, * xor-int, shl-int, shr-int, ushr-int @@ -4451,8 +4451,8 @@ artMterpAsmInstructionStart = .L_op_nop * * If "chkzero" is set to 1, we perform a divide-by-zero check on * vCC (a1). Useful for integer division and modulus. Note that we - * *don't* check for (INT_MIN / -1) here, because the ARM math lib - * handles it correctly. + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. * * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int, * xor-int, shl-int, shr-int, ushr-int @@ -4491,8 +4491,8 @@ artMterpAsmInstructionStart = .L_op_nop * * If "chkzero" is set to 1, we perform a divide-by-zero check on * vCC (a1). Useful for integer division and modulus. Note that we - * *don't* check for (INT_MIN / -1) here, because the ARM math lib - * handles it correctly. + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. * * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int, * xor-int, shl-int, shr-int, ushr-int @@ -4526,8 +4526,8 @@ artMterpAsmInstructionStart = .L_op_nop * * If "chkzero" is set to 1, we perform a divide-by-zero check on * vCC (a1). Useful for integer division and modulus. Note that we - * *don't* check for (INT_MIN / -1) here, because the ARM math lib - * handles it correctly. + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. * * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int, * xor-int, shl-int, shr-int, ushr-int @@ -4567,8 +4567,8 @@ artMterpAsmInstructionStart = .L_op_nop * * If "chkzero" is set to 1, we perform a divide-by-zero check on * vCC (a1). Useful for integer division and modulus. Note that we - * *don't* check for (INT_MIN / -1) here, because the ARM math lib - * handles it correctly. + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. * * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int, * xor-int, shl-int, shr-int, ushr-int @@ -4602,8 +4602,8 @@ artMterpAsmInstructionStart = .L_op_nop * * If "chkzero" is set to 1, we perform a divide-by-zero check on * vCC (a1). Useful for integer division and modulus. Note that we - * *don't* check for (INT_MIN / -1) here, because the ARM math lib - * handles it correctly. + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. * * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int, * xor-int, shl-int, shr-int, ushr-int @@ -4642,8 +4642,8 @@ artMterpAsmInstructionStart = .L_op_nop * * If "chkzero" is set to 1, we perform a divide-by-zero check on * vCC (a1). Useful for integer division and modulus. Note that we - * *don't* check for (INT_MIN / -1) here, because the ARM math lib - * handles it correctly. + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. * * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int, * xor-int, shl-int, shr-int, ushr-int @@ -4681,8 +4681,8 @@ artMterpAsmInstructionStart = .L_op_nop * * If "chkzero" is set to 1, we perform a divide-by-zero check on * vCC (a1). Useful for integer division and modulus. Note that we - * *don't* check for (INT_MIN / -1) here, because the ARM math lib - * handles it correctly. + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. * * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int, * xor-int, shl-int, shr-int, ushr-int @@ -4720,8 +4720,8 @@ artMterpAsmInstructionStart = .L_op_nop * * If "chkzero" is set to 1, we perform a divide-by-zero check on * vCC (a1). Useful for integer division and modulus. Note that we - * *don't* check for (INT_MIN / -1) here, because the ARM math lib - * handles it correctly. + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. * * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int, * xor-int, shl-int, shr-int, ushr-int @@ -4759,8 +4759,8 @@ artMterpAsmInstructionStart = .L_op_nop * * If "chkzero" is set to 1, we perform a divide-by-zero check on * vCC (a1). Useful for integer division and modulus. Note that we - * *don't* check for (INT_MIN / -1) here, because the ARM math lib - * handles it correctly. + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. * * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int, * xor-int, shl-int, shr-int, ushr-int @@ -4798,8 +4798,8 @@ artMterpAsmInstructionStart = .L_op_nop * * If "chkzero" is set to 1, we perform a divide-by-zero check on * vCC (a1). Useful for integer division and modulus. Note that we - * *don't* check for (INT_MIN / -1) here, because the ARM math lib - * handles it correctly. + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. * * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int, * xor-int, shl-int, shr-int, ushr-int @@ -4837,8 +4837,8 @@ artMterpAsmInstructionStart = .L_op_nop * * If "chkzero" is set to 1, we perform a divide-by-zero check on * vCC (a1). Useful for integer division and modulus. Note that we - * *don't* check for (INT_MIN / -1) here, because the ARM math lib - * handles it correctly. + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. * * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int, * xor-int, shl-int, shr-int, ushr-int -- GitLab