--- old/test/compiler/valhalla/valuetypes/ValueTypeTestBench.java 2016-11-04 16:24:21.421205298 +0100 +++ new/test/compiler/valhalla/valuetypes/ValueTypeTestBench.java 2016-11-04 16:24:21.297205304 +0100 @@ -73,6 +73,16 @@ public static MyValue createInline(int x, long y, double z) { return __Make MyValue(x, y, z); } + + @DontInline + public String toStringDontInline() { + return "MyValue: x=" + x + " y=" + y + " z=" + z; + } + + @ForceInline + public String toStringInline() { + return "MyValue: x=" + x + " y=" + y + " z=" + z; + } } public class ValueTypeTestBench { @@ -315,13 +325,73 @@ Asserts.assertEQ(result, warmup ? 42 + (1000*(double)rI) : (rI + rL + rD)); } + // Create a value type in a non-inlined method and then call a + // non-inlined method on that value type. + @Test(failOn = (ALLOC + STORE)) + public String test14() { + MyValue v = MyValue.createDontInline(32, 64L, 128.0); + String s = v.toStringDontInline(); + return s; + } + + @DontCompile + public void test14_verifier(boolean b) { + String s = test14(); + System.out.println("Result is: " + s); + } + + // Create a value type in an inlined method and then call a + // non-inlined method on that value type. + @Test(match = {ALLOC}, matchCount = {1}) + public String test15() { + MyValue v = MyValue.createInline(65, 129L, 257.0); + String s = v.toStringDontInline(); + return s; + } + + @DontCompile + public void test15_verifier(boolean b) { + String s = test15(); + System.out.println("Result is: " + s); + } + + // Create a value type in a non-inlined method and then call an + // inlined method on that value type. Allocations are due to building + // String objects and not due to allocating value types. + @Test(match = {ALLOC}, matchCount = {2}) + public String test16() { + MyValue v = MyValue.createDontInline(130, 258L, 514.0); + String s = v.toStringInline(); + return s; + } + + @DontCompile + public void test16_verifier(boolean b) { + String s = test16(); + System.out.println("Result is: " + s); + } + + // Create a value type in an inlined method and then call an + // inlined method on that value type. + @Test(match = {ALLOC}, matchCount = {2}) + public String test17() { + MyValue v = MyValue.createInline(259, 515L, 1027.0); + String s = v.toStringInline(); + return s; + } + + @DontCompile + public void test17_verifier(boolean b) { + String s = test17(); + System.out.println("Result is: " + s); + } // ========== Helper methods ========== @DontCompile public double sumValue(MyValue v) { return v.x + v.y + v.z; - } + } // ========== Test infrastructure ========== @@ -362,12 +432,12 @@ if (args.length == 0) { // Run tests in own process and verify output OutputAnalyzer oa = ProcessTools.executeTestJvm("-noverify", - "-XX:+UnlockDiagnosticVMOptions", "-Xbootclasspath/a:.", "-XX:+WhiteBoxAPI", - "-XX:-TieredCompilation", "-XX:-BackgroundCompilation", "-XX:-UseOnStackReplacement", - "-XX:CompileCommand=quiet", "-XX:+PrintCompilation", "-XX:+PrintIdeal", "-XX:+PrintOptoAssembly", - "-XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.ValueTypeTestBench::*", - "-XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.MyValue::*", - ValueTypeTestBench.class.getName(), "run"); + "-XX:+UnlockDiagnosticVMOptions", "-Xbootclasspath/a:.", "-XX:+WhiteBoxAPI", + "-XX:-TieredCompilation", "-XX:-BackgroundCompilation", "-XX:-UseOnStackReplacement", + "-XX:CompileCommand=quiet", "-XX:+PrintCompilation", "-XX:+PrintIdeal", "-XX:+PrintOptoAssembly", + "-XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.ValueTypeTestBench::*", + "-XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.MyValue::*", + ValueTypeTestBench.class.getName(), "run"); String output = oa.getOutput(); oa.shouldHaveExitValue(0); parseOutput(output); @@ -498,4 +568,4 @@ // Prevent method compilation @Retention(RetentionPolicy.RUNTIME) -@interface DontCompile { } \ No newline at end of file +@interface DontCompile { } --- old/src/share/vm/opto/graphKit.cpp 2016-11-04 16:24:21.401205299 +0100 +++ new/src/share/vm/opto/graphKit.cpp 2016-11-04 16:24:21.277205305 +0100 @@ -3497,7 +3497,7 @@ // Now generate allocation code // The entire memory state is needed for slow path of the allocation - // since GC and deoptimization can happened. + // since GC and deoptimization can happen. Node *mem = reset_memory(); set_all_memory(mem); // Create new memory state --- old/src/share/vm/opto/parse1.cpp 2016-11-04 16:24:21.421205298 +0100 +++ new/src/share/vm/opto/parse1.cpp 2016-11-04 16:24:21.285205304 +0100 @@ -1114,7 +1114,9 @@ // If this is an inlined method, we may have to do a receiver null check. if (_caller->has_method() && is_normal_parse() && !method()->is_static()) { GraphKit kit(_caller); - kit.null_check_receiver_before_call(method()); + if (!kit.argument(0)->is_ValueType()) { + kit.null_check_receiver_before_call(method()); + } _caller = kit.transfer_exceptions_into_jvms(); if (kit.stopped()) { _exits.add_exception_states_from(_caller); --- old/src/share/vm/interpreter/interpreterRuntime.cpp 2016-11-04 16:24:21.449205297 +0100 +++ new/src/share/vm/interpreter/interpreterRuntime.cpp 2016-11-04 16:24:21.281205304 +0100 @@ -964,6 +964,9 @@ } } // end JvmtiHideSingleStepping + assert(!(bytecode == Bytecodes::_invokedirect && info.call_kind() != CallInfo::direct_call), + "the target of a invokedirect bytecode must be a direct call"); + // check if link resolution caused cpCache to be updated ConstantPoolCacheEntry* cp_cache_entry = cache_entry(thread); if (cp_cache_entry->is_resolved(bytecode)) return; --- old/src/cpu/x86/vm/templateTable_x86.cpp 2016-11-04 16:24:21.505205294 +0100 +++ new/src/cpu/x86/vm/templateTable_x86.cpp 2016-11-04 16:24:21.353205301 +0100 @@ -3616,7 +3616,7 @@ const bool load_receiver = (recv != noreg); const bool save_flags = (flags != noreg); assert(load_receiver == (code != Bytecodes::_invokestatic && code != Bytecodes::_invokedynamic), ""); - assert(save_flags == (is_invokeinterface || is_invokevirtual), "need flags for vfinal"); + assert(save_flags == (is_invokeinterface || is_invokevirtual || is_invokedirect), "need flags for vfinal"); assert(flags == noreg || flags == rdx, ""); assert(recv == noreg || recv == rcx, ""); @@ -3751,21 +3751,33 @@ } /* - * Dummy implemented basically as invokevirtual (blindly uses resolve_invokevirtual), - * but assumes it picks out a final call (unsure about interfaces, default - * methods and where "Value.equals(QValue;)Z" should live at this point in time) + * The invokedirect bytecode is implemented as an invokevirtual bytecode: + * The implementation blindly uses resolve_invokevirtual() and assumes + * that a final call will be picked at the end. (Currently unsure about interfaces, + * default methods, and about where "Value.equals(QValue;)Z" should live.) */ void TemplateTable::invokedirect(int byte_no) { transition(vtos, vtos); assert(byte_no == f2_byte, "use this argument"); - prepare_invoke(byte_no, rbx, noreg, rcx); // recv, flags + prepare_invoke(byte_no, + rbx, // method (and not vtable index, as the method to be invoked should be final) + noreg, + rcx, rdx); // recv, flags + + // Check if the method is final + Label notFinal; + __ movl(rax, rdx); + __ andl(rax, (1 << ConstantPoolCacheEntry::is_vfinal_shift)); + __ jcc(Assembler::zero, notFinal); __ verify_oop(rcx); __ null_check(rcx); - __ profile_final_call(rax); __ profile_arguments_type(rax, rbx, rbcp, true); __ jump_from_interpreted(rbx, rax); + + __ bind(notFinal); + __ stop("Interpreter observed a non-final method as a target of an invokedirect instruction"); } void TemplateTable::invokespecial(int byte_no) { --- old/src/share/vm/opto/valuetypenode.cpp 2016-11-04 16:24:21.517205293 +0100 +++ new/src/share/vm/opto/valuetypenode.cpp 2016-11-04 16:24:21.385205300 +0100 @@ -36,7 +36,7 @@ Node* ValueTypeNode::make(PhaseGVN& gvn, Node* mem, Node* oop) { // Create and initialize a ValueTypeNode by loading all field - // values from memory and also save the oop to the heap allocated version. + // values from memory and also save the oop to the heap-allocated version. const TypeValueTypePtr* vtptr = gvn.type(oop)->is_valuetypeptr(); ValueTypeNode* vt = new ValueTypeNode(vtptr->value_type(), oop); for (uint index = 0; index < vt->field_count(); ++index) { --- old/src/share/vm/opto/callGenerator.cpp 2016-11-04 16:24:21.565205291 +0100 +++ new/src/share/vm/opto/callGenerator.cpp 2016-11-04 16:24:21.433205297 +0100 @@ -153,9 +153,14 @@ } _call_node = call; // Save the call node in case we need it later if (!is_static) { - // Make an explicit receiver null_check as part of this call. - // Since we share a map with the caller, his JVMS gets adjusted. - kit.null_check_receiver_before_call(method()); + if (kit.argument(0)->is_ValueType()) { + ValueTypeNode* vt = kit.argument(0)->as_ValueType(); + vt->store_to_memory(&kit); + } else { + // Make an explicit receiver null_check as part of this call. + // Since we share a map with the caller, his JVMS gets adjusted. + kit.null_check_receiver_before_call(method()); + } if (kit.stopped()) { // And dump it back to the caller, decorated with any exceptions: return kit.transfer_exceptions_into_jvms();