< prev index next >

src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp

Print this page
@@ -31,15 +31,18 @@
  #include "c1/c1_LIRAssembler.hpp"
  #include "c1/c1_MacroAssembler.hpp"
  #include "c1/c1_Runtime1.hpp"
  #include "c1/c1_ValueStack.hpp"
  #include "ci/ciArrayKlass.hpp"
+ #include "ci/ciInlineKlass.hpp"
  #include "ci/ciInstance.hpp"
+ #include "ci/ciObjArrayKlass.hpp"
  #include "code/compiledIC.hpp"
  #include "gc/shared/collectedHeap.hpp"
  #include "nativeInst_riscv.hpp"
  #include "oops/objArrayKlass.hpp"
+ #include "oops/oop.inline.hpp"
  #include "runtime/frame.inline.hpp"
  #include "runtime/sharedRuntime.hpp"
  #include "utilities/powerOfTwo.hpp"
  #include "vmreg_riscv.inline.hpp"
  

@@ -339,11 +342,11 @@
      __ mv(x10, x9);   // Restore the exception
    }
  
    // remove the activation and dispatch to the unwind handler
    __ block_comment("remove_frame and dispatch to the unwind handler");
-   __ remove_frame(initial_frame_size_in_bytes());
+   __ remove_frame(initial_frame_size_in_bytes(), needs_stack_repair());
    __ far_jump(RuntimeAddress(Runtime1::entry_for(StubId::c1_unwind_exception_id)));
  
    // Emit the slow path assembly
    if (stub != nullptr) {
      stub->emit_code(this);

@@ -380,12 +383,14 @@
  }
  
  void LIR_Assembler::return_op(LIR_Opr result, C1SafepointPollStub* code_stub) {
    assert(result->is_illegal() || !result->is_single_cpu() || result->as_register() == x10, "word returns are in x10");
  
+   assert(!InlineTypeReturnedAsFields, "unimplemented");
+ 
    // Pop the stack before the safepoint code
-   __ remove_frame(initial_frame_size_in_bytes());
+   __ remove_frame(initial_frame_size_in_bytes(), needs_stack_repair());
  
    if (StackReservedPages > 0 && compilation()->has_reserved_stack_access()) {
      __ reserved_stack_check();
    }
  

@@ -393,10 +398,15 @@
    __ relocate(relocInfo::poll_return_type);
    __ safepoint_poll(*code_stub->entry(), true /* at_return */, true /* in_nmethod */);
    __ ret();
  }
  
+ int LIR_Assembler::store_inline_type_fields_to_buf(ciInlineKlass* vk) {
+   Unimplemented();
+   return 0;
+ }
+ 
  int LIR_Assembler::safepoint_poll(LIR_Opr tmp, CodeEmitInfo* info) {
    guarantee(info != nullptr, "Shouldn't be null");
    __ get_polling_page(t0, relocInfo::poll_type);
    add_debug_info_for_branch(info);  // This isn't just debug info:
                                      // it's the oop map

@@ -434,14 +444,14 @@
        __ mv(dest->as_register_lo(), (intptr_t)c->as_jlong());
        break;
  
      case T_OBJECT:
      case T_ARRAY:
-       if (patch_code == lir_patch_none) {
-         jobject2reg(c->as_jobject(), dest->as_register());
-       } else {
+       if (patch_code != lir_patch_none) {
          jobject2reg_with_patching(dest->as_register(), info);
+       } else {
+         jobject2reg(c->as_jobject(), dest->as_register());
        }
        break;
  
      case T_METADATA:
        if (patch_code != lir_patch_none) {

@@ -841,10 +851,25 @@
  
      __ verify_oop(dest->as_register());
    }
  }
  
+ void LIR_Assembler::move(LIR_Opr src, LIR_Opr dst) {
+   assert(dst->is_cpu_register(), "must be");
+   assert(dst->type() == src->type(), "must be");
+ 
+   if (src->is_cpu_register()) {
+     reg2reg(src, dst);
+   } else if (src->is_stack()) {
+     stack2reg(src, dst, dst->type());
+   } else if (src->is_constant()) {
+     const2reg(src, dst, lir_patch_none, nullptr);
+   } else {
+     ShouldNotReachHere();
+   }
+ }
+ 
  void LIR_Assembler::emit_op3(LIR_Op3* op) {
    switch (op->code()) {
      case lir_idiv: // fall through
      case lir_irem:
        arithmetic_idiv(op->code(),

@@ -995,11 +1020,11 @@
  }
  
  void LIR_Assembler::emit_alloc_array(LIR_OpAllocArray* op) {
    Register len = op->len()->as_register();
  
-   if (UseSlowPath ||
+   if (UseSlowPath || op->always_slow_path() ||
        (!UseFastNewObjectArray && is_reference_type(op->type())) ||
        (!UseFastNewTypeArray   && !is_reference_type(op->type()))) {
      __ j(*op->stub()->entry());
    } else {
      Register tmp1 = op->tmp1()->as_register();

@@ -1058,12 +1083,22 @@
        // successful cast, fall through to profile or jump
      } else {
        // See if we get an immediate positive hit
        __ beq(k_RInfo, t0, *success_target);
        // check for self
-       __ beq(klass_RInfo, k_RInfo, *success_target);
- 
+       if (k->is_loaded() && k->is_obj_array_klass()) {
+         // For a direct pointer comparison, we need the refined array klass pointer
+         ciKlass* k_refined = ciObjArrayKlass::make(k->as_obj_array_klass()->element_klass());
+         if (!k_refined->is_loaded()) {
+           bailout("encountered unloaded_ciobjarrayklass due to out of memory error");
+           return;
+         }
+         __ mov_metadata(t0, k_refined->constant_encoding());
+         __ beq(klass_RInfo, t0, *success_target);
+       } else {
+         __ beq(klass_RInfo, k_RInfo, *success_target);
+       }
        __ subi(sp, sp, 2 * wordSize); // 2: store k_RInfo and klass_RInfo
        __ sd(k_RInfo, Address(sp, 0));             // sub klass
        __ sd(klass_RInfo, Address(sp, wordSize));  // super klass
        __ far_call(RuntimeAddress(Runtime1::entry_for(StubId::c1_slow_subtype_check_id)));
        // load result to k_RInfo

@@ -1144,20 +1179,23 @@
    Rtmp1 = op->tmp3()->as_register();
    select_different_registers(obj, dst, k_RInfo, klass_RInfo, Rtmp1);
  
    assert_different_registers(obj, k_RInfo, klass_RInfo);
  
-   if (should_profile) {
-     profile_object(md, data, obj, k_RInfo, klass_RInfo, obj_is_null);
-   } else {
-     __ beqz(obj, *obj_is_null);
+   if (op->need_null_check()) {
+     if (should_profile) {
+       profile_object(md, data, obj, k_RInfo, klass_RInfo, obj_is_null);
+     } else {
+       __ beqz(obj, *obj_is_null);
+     }
    }
  
    typecheck_loaded(op, k, k_RInfo);
    __ verify_oop(obj);
  
    if (op->fast_check()) {
+     assert(!k->is_loaded() || !k->is_obj_array_klass(), "Use refined array for a direct pointer comparison");
      // get object class
      // not a safepoint as obj null check happens earlier
      __ load_klass(t0, obj, t1);
      __ bne(t0, k_RInfo, *failure_target, /* is_far */ true);
      // successful cast, fall through to profile or jump

@@ -1197,10 +1235,126 @@
    } else {
      ShouldNotReachHere();
    }
  }
  
+ void LIR_Assembler::emit_opFlattenedArrayCheck(LIR_OpFlattenedArrayCheck* op) {
+   // We are loading/storing from/to an array that *may* be a flat array (the
+   // declared type is Object[], abstract[], interface[] or VT.ref[]).
+   // If this array is a flat array, take the slow path.
+   __ test_flat_array_oop(op->array()->as_register(), op->tmp()->as_register(), *op->stub()->entry());
+ }
+ 
+ void LIR_Assembler::emit_opNullFreeArrayCheck(LIR_OpNullFreeArrayCheck* op) {
+   // We are storing into an array that *may* be null-free (the declared type is
+   // Object[], abstract[], interface[] or VT.ref[]).
+   Label test_mark_word;
+   Register tmp = op->tmp()->as_register();
+   __ ld(tmp, Address(op->array()->as_register(), oopDesc::mark_offset_in_bytes()));
+   __ test_bit(t0, tmp, exact_log2(markWord::unlocked_value));
+   __ bnez(t0, test_mark_word);
+   __ load_prototype_header(tmp, op->array()->as_register());
+   __ bind(test_mark_word);
+   __ test_bit(tmp, tmp, exact_log2(markWord::null_free_array_bit_in_place));
+ }
+ 
+ void LIR_Assembler::emit_opSubstitutabilityCheck(LIR_OpSubstitutabilityCheck* op) {
+   Label L_oops_equal;
+   Label L_oops_not_equal;
+   Label L_end;
+ 
+   Register left  = op->left()->as_register();
+   Register right = op->right()->as_register();
+ 
+   __ beq(left, right, L_oops_equal);
+ 
+   // (1) Null check -- if one of the operands is null, the other must not be null (because
+   //     the two references are not equal), so they are not substitutable,
+   __ beqz(left, L_oops_not_equal);
+   __ beqz(right, L_oops_not_equal);
+ 
+   ciKlass* left_klass = op->left_klass();
+   ciKlass* right_klass = op->right_klass();
+ 
+   // (2) Inline type check -- if either of the operands is not a inline type,
+   //     they are not substitutable. We do this only if we are not sure that the
+   //     operands are inline type
+   if ((left_klass == nullptr || right_klass == nullptr) ||// The klass is still unloaded, or came from a Phi node.
+       !left_klass->is_inlinetype() || !right_klass->is_inlinetype()) {
+     Register tmp1 = op->tmp1()->as_register();
+     Register tmp2 = op->tmp2()->as_register();
+     __ mv(tmp1, markWord::inline_type_pattern);
+     __ ld(tmp2, Address(left, oopDesc::mark_offset_in_bytes()));
+     __ andr(tmp1, tmp1, tmp2);
+     __ ld(tmp2, Address(right, oopDesc::mark_offset_in_bytes()));
+     __ andr(tmp1, tmp1, tmp2);
+     __ mv(tmp2, (u1)markWord::inline_type_pattern);
+     __ bne(tmp1, tmp2, L_oops_not_equal);
+   }
+ 
+   // (3) Same klass check: if the operands are of different klasses, they are not substitutable.
+   if (left_klass != nullptr && left_klass->is_inlinetype() && left_klass == right_klass) {
+     // No need to load klass -- the operands are statically known to be the same inline klass.
+     __ j(*op->stub()->entry());
+   } else {
+     Register left_klass_op = op->tmp1()->as_register();
+     Register right_klass_op = op->tmp2()->as_register();
+     if (UseCompactObjectHeaders) {
+       __ load_narrow_klass_compact(left_klass_op, left);
+       __ load_narrow_klass_compact(right_klass_op, right);
+     } else {
+       __ lwu(left_klass_op,  Address(left,  oopDesc::klass_offset_in_bytes()));
+       __ lwu(right_klass_op, Address(right, oopDesc::klass_offset_in_bytes()));
+     }
+     __ beq(left_klass_op, right_klass_op, *op->stub()->entry(), /* is_far */ true); // same klass -> do slow check
+     // fall through to L_oops_not_equal
+   }
+ 
+   __ bind(L_oops_not_equal);
+   move(op->not_equal_result(), op->result_opr());
+   __ j(L_end);
+ 
+   // We've returned from the stub. X10 contains 0x0 IFF the two
+   // operands are not substitutable. (Don't compare against 0x1 in case the
+   // C compiler is naughty)
+   __ bind(*op->stub()->continuation());
+   __ beqz(x10, L_oops_not_equal); // (call_stub() == 0x0) -> not_equal
+ 
+   __ bind(L_oops_equal);
+   move(op->equal_result(), op->result_opr()); // (call_stub() != 0x0) -> equal
+   // fall-through
+   __ bind(L_end);
+ }
+ 
+ void LIR_Assembler::emit_profile_inline_type(LIR_OpProfileInlineType* op) {
+   Register obj = op->obj()->as_register();
+   Register tmp = op->tmp()->as_pointer_register();
+   bool not_null = op->not_null();
+   int flag = op->flag();
+ 
+   assert_different_registers(tmp, t0, t1);
+ 
+   Label not_inline_type;
+   if (!not_null) {
+     __ beqz(obj, not_inline_type);
+   }
+ 
+   __ test_oop_is_not_inline_type(obj, tmp, not_inline_type);
+ 
+   Address mdo_addr = as_Address(op->mdp()->as_address_ptr(), t1);
+   __ lbu(tmp, mdo_addr);
+   __ mv(t0, flag);
+   __ orr(tmp, tmp, t0);
+   __ sb(tmp, mdo_addr);
+ 
+   __ bind(not_inline_type);
+ }
+ 
+ void LIR_Assembler::check_orig_pc() {
+   Unimplemented();
+ }
+ 
  void LIR_Assembler::emit_compare_and_swap(LIR_OpCompareAndSwap* op) {
    Register addr;
    if (op->addr()->is_register()) {
      addr = as_reg(op->addr());
    } else {

@@ -1318,22 +1472,22 @@
    address call = __ reloc_call(Address(op->addr(), rtype));
    if (call == nullptr) {
      bailout("reloc call address stub overflow");
      return;
    }
-   add_call_info(code_offset(), op->info());
+   add_call_info(code_offset(), op->info(), op->maybe_return_as_fields());
    __ post_call_nop();
  }
  
  void LIR_Assembler::ic_call(LIR_OpJavaCall* op) {
    Assembler::IncompressibleScope scope(_masm);
    address call = __ ic_call(op->addr());
    if (call == nullptr) {
      bailout("reloc call address stub overflow");
      return;
    }
-   add_call_info(code_offset(), op->info());
+   add_call_info(code_offset(), op->info(), op->maybe_return_as_fields());
    __ post_call_nop();
  }
  
  void LIR_Assembler::emit_static_call_stub() {
    address call_pc = __ pc();
< prev index next >