diff --git a/src/hotspot/cpu/aarch64/aarch64.ad b/src/hotspot/cpu/aarch64/aarch64.ad index 1b8b9801f3c..190bfdd1f67 100644 --- a/src/hotspot/cpu/aarch64/aarch64.ad +++ b/src/hotspot/cpu/aarch64/aarch64.ad @@ -7780,7 +7780,7 @@ instruct membar_acquire() %{ ins_cost(VOLATILE_REF_COST); format %{ "membar_acquire\n\t" - "dmb ish" %} + "dmb ishld" %} ins_encode %{ __ block_comment("membar_acquire"); @@ -7834,11 +7834,13 @@ instruct membar_release() %{ ins_cost(VOLATILE_REF_COST); format %{ "membar_release\n\t" - "dmb ish" %} + "dmb ishst\n\tdmb ishld" %} ins_encode %{ __ block_comment("membar_release"); - __ membar(Assembler::LoadStore|Assembler::StoreStore); + // These will be merged if AlwaysMergeDMB is enabled. + __ membar(Assembler::StoreStore); + __ membar(Assembler::LoadStore); %} ins_pipe(pipe_serial); %} diff --git a/src/hotspot/cpu/aarch64/globals_aarch64.hpp b/src/hotspot/cpu/aarch64/globals_aarch64.hpp index 2f83838fc0f..9c20e3737c8 100644 --- a/src/hotspot/cpu/aarch64/globals_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/globals_aarch64.hpp @@ -124,6 +124,8 @@ define_pd_global(intx, InlineSmallCode, 1000); range(1, 99) \ product(ccstr, UseBranchProtection, "none", \ "Branch Protection to use: none, standard, pac-ret") \ + product(bool, AlwaysMergeDMB, true, DIAGNOSTIC, \ + "Always merge DMB instructions in code emission") \ // end of ARCH_FLAGS diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp index 1929077fa16..f90aefc8fd3 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp @@ -2350,14 +2350,36 @@ void MacroAssembler::membar(Membar_mask_bits order_constraint) { address last = code()->last_insn(); if (last != nullptr && nativeInstruction_at(last)->is_Membar() && prev == last) { NativeMembar *bar = NativeMembar_at(prev); - // We are merging two memory barrier instructions. On AArch64 we - // can do this simply by ORing them together. - bar->set_kind(bar->get_kind() | order_constraint); - BLOCK_COMMENT("merged membar"); - } else { - code()->set_last_insn(pc()); - dmb(Assembler::barrier(order_constraint)); + if (AlwaysMergeDMB) { + bar->set_kind(bar->get_kind() | order_constraint); + BLOCK_COMMENT("merged membar(always)"); + return; + } + // Don't promote DMB ST|DMB LD to DMB (a full barrier) because + // doing so would introduce a StoreLoad which the caller did not + // intend + if (bar->get_kind() == order_constraint + || bar->get_kind() == AnyAny + || order_constraint == AnyAny) { + // We are merging two memory barrier instructions. On AArch64 we + // can do this simply by ORing them together. + bar->set_kind(bar->get_kind() | order_constraint); + BLOCK_COMMENT("merged membar"); + return; + } else { + // A special case like "DMB ST;DMB LD;DMB ST", the last DMB can be skipped + // We need check the last 2 instructions + address prev2 = prev - NativeMembar::instruction_size; + if (last != code()->last_label() && nativeInstruction_at(prev2)->is_Membar()) { + NativeMembar *bar2 = NativeMembar_at(prev2); + assert(bar2->get_kind() == order_constraint, "it should be merged before"); + BLOCK_COMMENT("merged membar(elided)"); + return; + } + } } + code()->set_last_insn(pc()); + dmb(Assembler::barrier(order_constraint)); } bool MacroAssembler::try_merge_ldst(Register rt, const Address &adr, size_t size_in_bytes, bool is_store) { diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp index 7b7ecf49c8c..3bfd6e70872 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp @@ -150,6 +150,7 @@ class MacroAssembler: public Assembler { void bind(Label& L) { Assembler::bind(L); code()->clear_last_insn(); + code()->set_last_label(pc()); } void membar(Membar_mask_bits order_constraint); diff --git a/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp b/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp index 18f310c746c..aa64f411dbf 100644 --- a/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2015, 2020, Red Hat Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -230,6 +230,9 @@ void VM_Version::initialize() { if (FLAG_IS_DEFAULT(OnSpinWaitInstCount)) { FLAG_SET_DEFAULT(OnSpinWaitInstCount, 1); } + if (FLAG_IS_DEFAULT(AlwaysMergeDMB)) { + FLAG_SET_DEFAULT(AlwaysMergeDMB, false); + } } if (_cpu == CPU_ARM) { diff --git a/src/hotspot/share/asm/codeBuffer.cpp b/src/hotspot/share/asm/codeBuffer.cpp index d0c8feda24a..ea47e435c0b 100644 --- a/src/hotspot/share/asm/codeBuffer.cpp +++ b/src/hotspot/share/asm/codeBuffer.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -928,6 +928,10 @@ void CodeBuffer::expand(CodeSection* which_cs, csize_t amount) { // Move all the code and relocations to the new blob: relocate_code_to(&cb); + // some internal addresses, _last_insn _last_label, are used during code emission, + // adjust them in expansion + adjust_internal_address(insts_begin(), cb.insts_begin()); + // Copy the temporary code buffer into the current code buffer. // Basically, do {*this = cb}, except for some control information. this->take_over_code_from(&cb); @@ -949,6 +953,15 @@ void CodeBuffer::expand(CodeSection* which_cs, csize_t amount) { #endif //PRODUCT } +void CodeBuffer::adjust_internal_address(address from, address to) { + if (_last_insn != nullptr) { + _last_insn += to - from; + } + if (_last_label != nullptr) { + _last_label += to - from; + } +} + void CodeBuffer::take_over_code_from(CodeBuffer* cb) { // Must already have disposed of the old blob somehow. assert(blob() == nullptr, "must be empty"); diff --git a/src/hotspot/share/asm/codeBuffer.hpp b/src/hotspot/share/asm/codeBuffer.hpp index 48476bedfe2..343981e1a7b 100644 --- a/src/hotspot/share/asm/codeBuffer.hpp +++ b/src/hotspot/share/asm/codeBuffer.hpp @@ -433,6 +433,7 @@ class CodeBuffer: public StackObj DEBUG_ONLY(COMMA private Scrubber) { Arena* _overflow_arena; address _last_insn; // used to merge consecutive memory barriers, loads or stores. + address _last_label; // record last bind label address, it's also the start of current bb. SharedStubToInterpRequests* _shared_stub_to_interp_requests; // used to collect requests for shared iterpreter stubs SharedTrampolineRequests* _shared_trampoline_requests; // used to collect requests for shared trampolines @@ -457,6 +458,7 @@ class CodeBuffer: public StackObj DEBUG_ONLY(COMMA private Scrubber) { _oop_recorder = nullptr; _overflow_arena = nullptr; _last_insn = nullptr; + _last_label = nullptr; _finalize_stubs = false; _shared_stub_to_interp_requests = nullptr; _shared_trampoline_requests = nullptr; @@ -510,6 +512,9 @@ class CodeBuffer: public StackObj DEBUG_ONLY(COMMA private Scrubber) { // moves code sections to new buffer (assumes relocs are already in there) void relocate_code_to(CodeBuffer* cb) const; + // adjust some internal address during expand + void adjust_internal_address(address from, address to); + // set up a model of the final layout of my contents void compute_final_layout(CodeBuffer* dest) const; @@ -679,6 +684,9 @@ class CodeBuffer: public StackObj DEBUG_ONLY(COMMA private Scrubber) { void set_last_insn(address a) { _last_insn = a; } void clear_last_insn() { set_last_insn(nullptr); } + address last_label() const { return _last_label; } + void set_last_label(address a) { _last_label = a; } + #ifndef PRODUCT AsmRemarks &asm_remarks() { return _asm_remarks; } DbgStrings &dbg_strings() { return _dbg_strings; } diff --git a/test/hotspot/gtest/aarch64/test_assembler_aarch64.cpp b/test/hotspot/gtest/aarch64/test_assembler_aarch64.cpp index 1d756854755..60999f2bf34 100644 --- a/test/hotspot/gtest/aarch64/test_assembler_aarch64.cpp +++ b/test/hotspot/gtest/aarch64/test_assembler_aarch64.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020, Red Hat Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -28,8 +28,10 @@ #include "asm/assembler.hpp" #include "asm/assembler.inline.hpp" +#include "asm/macroAssembler.hpp" #include "compiler/disassembler.hpp" #include "memory/resourceArea.hpp" +#include "nativeInst_aarch64.hpp" #include "unittest.hpp" #define __ _masm. @@ -81,4 +83,376 @@ TEST_VM(AssemblerAArch64, validate) { BufferBlob::free(b); } +constexpr uint32_t test_encode_dmb_ld = 0xd50339bf; +constexpr uint32_t test_encode_dmb_st = 0xd5033abf; +constexpr uint32_t test_encode_dmb = 0xd5033bbf; +constexpr uint32_t test_encode_nop = 0xd503201f; + +static void asm_dump(address start, address end) { + ResourceMark rm; + stringStream ss; + ss.print_cr("Insns:"); + Disassembler::decode(start, end, &ss); + printf("%s\n", ss.as_string()); +} + +void test_merge_dmb() { + BufferBlob* b = BufferBlob::create("aarch64Test", 400); + CodeBuffer code(b); + MacroAssembler _masm(&code); + + { + // merge with same type + __ membar(Assembler::Membar_mask_bits::StoreStore); + __ membar(Assembler::Membar_mask_bits::StoreStore); + __ membar(Assembler::Membar_mask_bits::StoreStore); + __ nop(); + __ membar(Assembler::Membar_mask_bits::LoadStore); + __ membar(Assembler::Membar_mask_bits::LoadStore); + __ membar(Assembler::Membar_mask_bits::LoadStore); + __ membar(Assembler::Membar_mask_bits::LoadStore); + __ nop(); + // merge with high rank + __ membar(Assembler::Membar_mask_bits::LoadStore); + __ membar(Assembler::Membar_mask_bits::LoadStore); + __ membar(Assembler::Membar_mask_bits::AnyAny); + __ membar(Assembler::Membar_mask_bits::StoreStore); + __ membar(Assembler::Membar_mask_bits::StoreStore); + __ nop(); + // merge with different type + __ membar(Assembler::Membar_mask_bits::LoadStore); + __ membar(Assembler::Membar_mask_bits::StoreStore); + __ membar(Assembler::Membar_mask_bits::LoadStore); + __ membar(Assembler::Membar_mask_bits::StoreStore); + } + asm_dump(code.insts()->start(), code.insts()->end()); + // AlwaysMergeDMB + static const unsigned int insns1[] = { + test_encode_dmb_st, + test_encode_nop, + test_encode_dmb_ld, + test_encode_nop, + test_encode_dmb, + test_encode_nop, + test_encode_dmb, + }; + // !AlwaysMergeDMB + static const unsigned int insns2[] = { + test_encode_dmb_st, + test_encode_nop, + test_encode_dmb_ld, + test_encode_nop, + test_encode_dmb, + test_encode_nop, + test_encode_dmb_ld, + test_encode_dmb_st, + }; + if (AlwaysMergeDMB) { + EXPECT_EQ(code.insts()->size(), (CodeSection::csize_t)(sizeof insns1)); + asm_check((const unsigned int *)code.insts()->start(), insns1, sizeof insns1 / sizeof insns1[0]); + } else { + EXPECT_EQ(code.insts()->size(), (CodeSection::csize_t)(sizeof insns2)); + asm_check((const unsigned int *)code.insts()->start(), insns2, sizeof insns2 / sizeof insns2[0]); + } + + BufferBlob::free(b); +} + +TEST_VM(AssemblerAArch64, merge_dmb_1) { + FlagSetting fs(AlwaysMergeDMB, true); + test_merge_dmb(); +} + +TEST_VM(AssemblerAArch64, merge_dmb_2) { + FlagSetting fs(AlwaysMergeDMB, false); + test_merge_dmb(); +} + +TEST_VM(AssemblerAArch64, merge_dmb_block_by_label) { + BufferBlob* b = BufferBlob::create("aarch64Test", 400); + CodeBuffer code(b); + MacroAssembler _masm(&code); + + { + Label l; + // merge can not cross the label + __ membar(Assembler::Membar_mask_bits::StoreStore); + __ bind(l); + __ membar(Assembler::Membar_mask_bits::StoreStore); + } + asm_dump(code.insts()->start(), code.insts()->end()); + static const unsigned int insns[] = { + 0xd5033abf, // dmb.ishst + 0xd5033abf, // dmb.ishst + }; + EXPECT_EQ(code.insts()->size(), (CodeSection::csize_t)(sizeof insns)); + asm_check((const unsigned int *)code.insts()->start(), insns, sizeof insns / sizeof insns[0]); + + BufferBlob::free(b); +} + +TEST_VM(AssemblerAArch64, merge_dmb_after_expand) { + ResourceMark rm; + BufferBlob* b = BufferBlob::create("aarch64Test", 400); + CodeBuffer code(b); + code.set_blob(b); + MacroAssembler _masm(&code); + + { + __ membar(Assembler::Membar_mask_bits::StoreStore); + code.insts()->maybe_expand_to_ensure_remaining(50000); + __ membar(Assembler::Membar_mask_bits::StoreStore); + } + asm_dump(code.insts()->start(), code.insts()->end()); + static const unsigned int insns[] = { + 0xd5033abf, // dmb.ishst + }; + EXPECT_EQ(code.insts()->size(), (CodeSection::csize_t)(sizeof insns)); + asm_check((const unsigned int *)code.insts()->start(), insns, sizeof insns / sizeof insns[0]); +} + +void expect_dmbld(void* addr) { + if (*((uint32_t *) addr) != test_encode_dmb_ld) { + tty->print_cr("Expected dmb.ld"); + FAIL(); + } +} + +void expect_dmbst(void* addr) { + if (*((uint32_t *) addr) != test_encode_dmb_st) { + tty->print_cr("Expected dmb.st"); + FAIL(); + } +} + +void expect_dmb(void* addr) { + if (*((uint32_t *) addr) != test_encode_dmb) { + tty->print_cr("Expected dmb"); + FAIL(); + } +} + +void expect_any_dmb(void* addr) { + uint32_t encode = *((uint32_t *) addr); + if (encode != test_encode_dmb && encode != test_encode_dmb_ld && encode != test_encode_dmb_st) { + tty->print_cr("Expected a dmb.* instruction"); + FAIL(); + } +} + +void expect_different_dmb_kind(void* addr) { + uint32_t pos1 = *((uint32_t *) addr); + uint32_t pos2 = *(((uint32_t *) addr) + 1); + if (pos1 == pos2) { + tty->print_cr("Expected different dmb kind"); + FAIL(); + } +} + +void expect_dmb_at_least_one(void* addr) { + uint32_t pos1 = *((uint32_t *) addr); + uint32_t pos2 = *(((uint32_t *) addr) + 1); + if (pos1 != test_encode_dmb && pos2 != test_encode_dmb) { + tty->print_cr("Expected at least one dmb"); + FAIL(); + } +} + +void expect_dmb_none(void* addr) { + uint32_t pos1 = *((uint32_t *) addr); + uint32_t pos2 = *(((uint32_t *) addr) + 1); + if (pos1 == test_encode_dmb || pos2 == test_encode_dmb) { + tty->print_cr("Expected no dmb"); + FAIL(); + } +} + +void test_merge_dmb_all_kinds() { + BufferBlob* b = BufferBlob::create("aarch64Test", 20000); + CodeBuffer code(b); + MacroAssembler _masm(&code); + + constexpr int count = 5; + struct { + const char* label; + Assembler::Membar_mask_bits flavor; + // Two groups of two bits describing the ordering, can be OR-ed to figure out composite semantics. + // First group describes ops before the barrier. Second group describes ops after the barrier. + // "01" means "load", "10" means "store", "100" means "any". + int mask; + } kind[count] = { + {"storestore", Assembler::StoreStore, 0b010010}, + {"loadstore", Assembler::LoadStore, 0b001010}, + {"loadload", Assembler::LoadLoad, 0b001001}, + {"storeload", Assembler::StoreLoad, 0b100100}, // quirk: StoreLoad is as powerful as AnyAny + {"anyany", Assembler::AnyAny, 0b100100}, + }; + + for (int b1 = 0; b1 < count; b1++) { + for (int b2 = 0; b2 < count; b2++) { + for (int b3 = 0; b3 < count; b3++) { + for (int b4 = 0; b4 < count; b4++) { + // tty->print_cr("%s + %s + %s + %s", kind[b1].label, kind[b2].label, kind[b3].label, kind[b4].label); + + address start = __ pc(); + __ membar(kind[b1].flavor); + __ membar(kind[b2].flavor); + __ membar(kind[b3].flavor); + __ membar(kind[b4].flavor); + address end = __ pc(); + __ nop(); + + size_t size = pointer_delta(end, start, 1); + if (AlwaysMergeDMB) { + // Expect only a single barrier. + EXPECT_EQ(size, (size_t) NativeMembar::instruction_size); + } else { + EXPECT_LE(size, (size_t) NativeMembar::instruction_size * 2); + } + + // Composite ordering for this group of barriers. + int composite_mask = kind[b1].mask | kind[b2].mask | kind[b3].mask | kind[b4].mask; + + if (size == NativeMembar::instruction_size) { + // If there is a single barrier, we can easily test its type. + switch (composite_mask) { + case 0b001001: + case 0b001010: + case 0b001011: + case 0b001101: + case 0b001110: + case 0b001111: + // Any combination of Load(Load|Store|Any) gets dmb.ld + expect_dmbld(start); + break; + case 0b010010: + // Only StoreStore gets dmb.st + expect_dmbst(start); + break; + default: + // Everything else gets folded into full dmb + expect_dmb(start); + break; + } + } else if (size == 2 * NativeMembar::instruction_size) { + // There are two barriers. Make a few sanity checks. + // They must be different kind + expect_any_dmb(start); + expect_any_dmb(start + NativeMembar::instruction_size); + expect_different_dmb_kind(start); + if ((composite_mask & 0b100100) != 0) { + // There was "any" barrier in the group, a full dmb is expected + expect_dmb_at_least_one(start); + } else { + // Otherwise expect no full dmb + expect_dmb_none(start); + } + } else { + // Merging code does not produce this result. + FAIL(); + } + } + } + } + } + + BufferBlob::free(b); +} + +TEST_VM(AssemblerAArch64, merge_dmb_all_kinds_1) { + FlagSetting fs(AlwaysMergeDMB, true); + test_merge_dmb_all_kinds(); +} + +TEST_VM(AssemblerAArch64, merge_dmb_all_kinds_2) { + FlagSetting fs(AlwaysMergeDMB, false); + test_merge_dmb_all_kinds(); +} + +TEST_VM(AssemblerAArch64, merge_ldst) { + BufferBlob* b = BufferBlob::create("aarch64Test", 400); + CodeBuffer code(b); + MacroAssembler _masm(&code); + + { + Label l; + // merge ld/st into ldp/stp + __ ldr(r0, Address(sp, 8)); + __ ldr(r1, Address(sp, 0)); + __ nop(); + __ str(r0, Address(sp, 0)); + __ str(r1, Address(sp, 8)); + __ nop(); + __ ldrw(r0, Address(sp, 0)); + __ ldrw(r1, Address(sp, 4)); + __ nop(); + __ strw(r0, Address(sp, 4)); + __ strw(r1, Address(sp, 0)); + __ nop(); + // can not merge + __ ldrw(r0, Address(sp, 4)); + __ ldr(r1, Address(sp, 8)); + __ nop(); + __ ldrw(r0, Address(sp, 0)); + __ ldrw(r1, Address(sp, 8)); + __ nop(); + __ str(r0, Address(sp, 0)); + __ bind(l); // block by label + __ str(r1, Address(sp, 8)); + __ nop(); + } + asm_dump(code.insts()->start(), code.insts()->end()); + static const unsigned int insns1[] = { + 0xa94003e1, // ldp x1, x0, [sp] + 0xd503201f, // nop + 0xa90007e0, // stp x0, x1, [sp] + 0xd503201f, // nop + 0x294007e0, // ldp w0, w1, [sp] + 0xd503201f, // nop + 0x290003e1, // stp w1, w0, [sp] + 0xd503201f, // nop + 0xb94007e0, // ldr w0, [sp, 4] + 0xf94007e1, // ldr x1, [sp, 8] + 0xd503201f, // nop + 0xb94003e0, // ldr w0, [sp] + 0xb9400be1, // ldr w1, [sp, 8] + 0xd503201f, // nop + 0xf90003e0, // str x0, [sp] + 0xf90007e1, // str x1, [sp, 8] + 0xd503201f, // nop + }; + EXPECT_EQ(code.insts()->size(), (CodeSection::csize_t)(sizeof insns1)); + asm_check((const unsigned int *)code.insts()->start(), insns1, sizeof insns1 / sizeof insns1[0]); + + BufferBlob::free(b); +} + +TEST_VM(AssemblerAArch64, merge_ldst_after_expand) { + ResourceMark rm; + BufferBlob* b = BufferBlob::create("aarch64Test", 400); + CodeBuffer code(b); + code.set_blob(b); + MacroAssembler _masm(&code); + + { + __ ldr(r0, Address(sp, 8)); + code.insts()->maybe_expand_to_ensure_remaining(10000); + __ ldr(r1, Address(sp, 0)); + __ nop(); + __ str(r0, Address(sp, 0)); + code.insts()->maybe_expand_to_ensure_remaining(100000); + __ str(r1, Address(sp, 8)); + __ nop(); + } + asm_dump(code.insts()->start(), code.insts()->end()); + static const unsigned int insns[] = { + 0xa94003e1, // ldp x1, x0, [sp] + 0xd503201f, // nop + 0xa90007e0, // stp x0, x1, [sp] + 0xd503201f, // nop + }; + EXPECT_EQ(code.insts()->size(), (CodeSection::csize_t)(sizeof insns)); + asm_check((const unsigned int *)code.insts()->start(), insns, sizeof insns / sizeof insns[0]); +} + #endif // AARCH64 diff --git a/test/micro/org/openjdk/bench/vm/compiler/FinalFieldInitialize.java b/test/micro/org/openjdk/bench/vm/compiler/FinalFieldInitialize.java new file mode 100644 index 00000000000..ee0779faecf --- /dev/null +++ b/test/micro/org/openjdk/bench/vm/compiler/FinalFieldInitialize.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2024, Alibaba Group Co., Ltd. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.bench.vm.compiler; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.*; + +import java.util.concurrent.TimeUnit; +import org.openjdk.jmh.infra.Blackhole; + +/* test allocation speed of object with final field */ +@BenchmarkMode(Mode.Throughput) +@OutputTimeUnit(TimeUnit.SECONDS) +@State(Scope.Benchmark) +@Warmup(iterations = 5, time = 3, timeUnit = TimeUnit.SECONDS) +@Measurement(iterations = 3, time = 3, timeUnit = TimeUnit.SECONDS) +@Fork(value = 3) +public class FinalFieldInitialize { + final static int LEN = 100_000; + Object arr[] = null; + @Setup + public void setup(){ + arr = new Object[LEN]; + } + + @Benchmark + public void testAlloc(Blackhole bh) { + for (int i=0; i