001/* 002 * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. 003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 004 * 005 * This code is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU General Public License version 2 only, as 007 * published by the Free Software Foundation. 008 * 009 * This code is distributed in the hope that it will be useful, but WITHOUT 010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 011 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 012 * version 2 for more details (a copy is included in the LICENSE file that 013 * accompanied this code). 014 * 015 * You should have received a copy of the GNU General Public License version 016 * 2 along with this work; if not, write to the Free Software Foundation, 017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 018 * 019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 020 * or visit www.oracle.com if you need additional information or have any 021 * questions. 022 */ 023package com.oracle.graal.hotspot.test; 024 025import static jdk.internal.jvmci.common.UnsafeAccess.*; 026 027import java.lang.ref.*; 028 029import com.oracle.graal.debug.*; 030import com.oracle.graal.debug.Debug.*; 031import jdk.internal.jvmci.hotspot.*; 032import jdk.internal.jvmci.meta.*; 033 034import org.junit.*; 035 036import sun.misc.*; 037 038import com.oracle.graal.compiler.test.*; 039import com.oracle.graal.hotspot.*; 040import com.oracle.graal.hotspot.nodes.*; 041import com.oracle.graal.hotspot.phases.*; 042import com.oracle.graal.hotspot.replacements.*; 043import com.oracle.graal.nodes.*; 044import com.oracle.graal.nodes.StructuredGraph.AllowAssumptions; 045import com.oracle.graal.nodes.memory.HeapAccess.BarrierType; 046import com.oracle.graal.nodes.memory.*; 047import com.oracle.graal.nodes.memory.address.*; 048import com.oracle.graal.nodes.spi.*; 049import com.oracle.graal.phases.*; 050import com.oracle.graal.phases.common.*; 051import com.oracle.graal.phases.common.inlining.*; 052import com.oracle.graal.phases.common.inlining.policy.*; 053import com.oracle.graal.phases.tiers.*; 054 055/** 056 * The following unit tests assert the presence of write barriers for both Serial and G1 GCs. 057 * Normally, the tests check for compile time inserted barriers. However, there are the cases of 058 * unsafe loads of the java.lang.ref.Reference.referent field where runtime checks have to be 059 * performed also. For those cases, the unit tests check the presence of the compile-time inserted 060 * barriers. Concerning the runtime checks, the results of variable inputs (object types and 061 * offsets) passed as input parameters can be checked against printed output from the G1 write 062 * barrier snippets. The runtime checks have been validated offline. 063 */ 064public class WriteBarrierAdditionTest extends GraalCompilerTest { 065 066 private static final HotSpotVMConfig config = HotSpotGraalRuntime.runtime().getConfig(); 067 private static final long referentOffset = HotSpotReplacementsUtil.referentOffset(); 068 069 public static class Container { 070 071 public Container a; 072 public Container b; 073 } 074 075 /** 076 * Expected 2 barriers for the Serial GC and 4 for G1 (2 pre + 2 post). 077 */ 078 @Test 079 public void test1() throws Exception { 080 testHelper("test1Snippet", (config.useG1GC) ? 4 : 2); 081 } 082 083 public static void test1Snippet() { 084 Container main = new Container(); 085 Container temp1 = new Container(); 086 Container temp2 = new Container(); 087 main.a = temp1; 088 main.b = temp2; 089 } 090 091 /** 092 * Expected 4 barriers for the Serial GC and 8 for G1 (4 pre + 4 post). 093 */ 094 @Test 095 public void test2() throws Exception { 096 testHelper("test2Snippet", config.useG1GC ? 8 : 4); 097 } 098 099 public static void test2Snippet(boolean test) { 100 Container main = new Container(); 101 Container temp1 = new Container(); 102 Container temp2 = new Container(); 103 for (int i = 0; i < 10; i++) { 104 if (test) { 105 main.a = temp1; 106 main.b = temp2; 107 } else { 108 main.a = temp2; 109 main.b = temp1; 110 } 111 } 112 } 113 114 /** 115 * Expected 4 barriers for the Serial GC and 8 for G1 (4 pre + 4 post). 116 */ 117 @Test 118 public void test3() throws Exception { 119 testHelper("test3Snippet", config.useG1GC ? 8 : 4); 120 } 121 122 public static void test3Snippet() { 123 Container[] main = new Container[10]; 124 Container temp1 = new Container(); 125 Container temp2 = new Container(); 126 for (int i = 0; i < 10; i++) { 127 main[i].a = main[i].b = temp1; 128 } 129 130 for (int i = 0; i < 10; i++) { 131 main[i].a = main[i].b = temp2; 132 } 133 } 134 135 /** 136 * Expected 2 barriers for the Serial GC and 5 for G1 (3 pre + 2 post) The (2 or 4) barriers are 137 * emitted while initializing the fields of the WeakReference instance. The extra pre barrier of 138 * G1 concerns the read of the referent field. 139 */ 140 @Test 141 public void test4() throws Exception { 142 testHelper("test4Snippet", config.useG1GC ? 5 : 2); 143 } 144 145 public static Object test4Snippet() { 146 WeakReference<Object> weakRef = new WeakReference<>(new Object()); 147 return weakRef.get(); 148 } 149 150 static WeakReference<Object> wr = new WeakReference<>(new Object()); 151 static Container con = new Container(); 152 153 /** 154 * Expected 4 barriers for the Serial GC and 9 for G1 (1 ref + 4 pre + 4 post). In this test, we 155 * load the correct offset of the WeakReference object so naturally we assert the presence of 156 * the pre barrier. 157 */ 158 @Test 159 public void test5() throws Exception { 160 testHelper("test5Snippet", config.useG1GC ? 1 : 0); 161 } 162 163 public static Object test5Snippet() throws Exception { 164 return unsafe.getObject(wr, config.useCompressedOops ? 12L : 16L); 165 } 166 167 /** 168 * The following test concerns the runtime checks of the unsafe loads. In this test, we unsafely 169 * load the java.lang.ref.Reference.referent field so the pre barier has to be executed. 170 */ 171 @Test 172 public void test6() throws Exception { 173 test2("testUnsafeLoad", unsafe, wr, new Long(referentOffset), null); 174 } 175 176 /** 177 * The following test concerns the runtime checks of the unsafe loads. In this test, we unsafely 178 * load a matching offset of a wrong object so the pre barier must not be executed. 179 */ 180 @Test 181 public void test7() throws Exception { 182 test2("testUnsafeLoad", unsafe, con, new Long(referentOffset), null); 183 } 184 185 /** 186 * The following test concerns the runtime checks of the unsafe loads. In this test, we unsafely 187 * load a non-matching offset field of the java.lang.ref.Reference object so the pre barier must 188 * not be executed. 189 */ 190 @Test 191 public void test8() throws Exception { 192 test2("testUnsafeLoad", unsafe, wr, new Long(config.useCompressedOops ? 20 : 32), null); 193 } 194 195 /** 196 * The following test concerns the runtime checks of the unsafe loads. In this test, we unsafely 197 * load a matching offset+disp field of the java.lang.ref.Reference object so the pre barier 198 * must be executed. 199 */ 200 @Test 201 public void test10() throws Exception { 202 test2("testUnsafeLoad", unsafe, wr, new Long(config.useCompressedOops ? 6 : 8), new Integer(config.useCompressedOops ? 6 : 8)); 203 } 204 205 /** 206 * The following test concerns the runtime checks of the unsafe loads. In this test, we unsafely 207 * load a non-matching offset+disp field of the java.lang.ref.Reference object so the pre barier 208 * must not be executed. 209 */ 210 @Test 211 public void test9() throws Exception { 212 test2("testUnsafeLoad", unsafe, wr, new Long(config.useCompressedOops ? 10 : 16), new Integer(config.useCompressedOops ? 10 : 16)); 213 } 214 215 static Object[] src = new Object[1]; 216 static Object[] dst = new Object[1]; 217 218 static { 219 for (int i = 0; i < src.length; i++) { 220 src[i] = new Object(); 221 } 222 for (int i = 0; i < dst.length; i++) { 223 dst[i] = new Object(); 224 } 225 } 226 227 public static void testArrayCopy(Object a, Object b, Object c) throws Exception { 228 System.arraycopy(a, 0, b, 0, (int) c); 229 } 230 231 @Test 232 public void test11() throws Exception { 233 test2("testArrayCopy", src, dst, dst.length); 234 } 235 236 public static Object testUnsafeLoad(Unsafe theUnsafe, Object a, Object b, Object c) throws Exception { 237 final int offset = (c == null ? 0 : ((Integer) c).intValue()); 238 final long displacement = (b == null ? 0 : ((Long) b).longValue()); 239 return theUnsafe.getObject(a, offset + displacement); 240 } 241 242 private HotSpotInstalledCode getInstalledCode(String name, boolean withUnsafePrefix) throws Exception { 243 final ResolvedJavaMethod javaMethod = withUnsafePrefix ? getResolvedJavaMethod(WriteBarrierAdditionTest.class, name, Unsafe.class, Object.class, Object.class, Object.class) 244 : getResolvedJavaMethod(WriteBarrierAdditionTest.class, name, Object.class, Object.class, Object.class); 245 final HotSpotInstalledCode installedCode = (HotSpotInstalledCode) getCode(javaMethod); 246 return installedCode; 247 } 248 249 private void testHelper(final String snippetName, final int expectedBarriers) throws Exception, SecurityException { 250 ResolvedJavaMethod snippet = getResolvedJavaMethod(snippetName); 251 try (Scope s = Debug.scope("WriteBarrierAdditionTest", snippet)) { 252 StructuredGraph graph = parseEager(snippet, AllowAssumptions.NO); 253 HighTierContext highContext = getDefaultHighTierContext(); 254 MidTierContext midContext = new MidTierContext(getProviders(), getCodeCache().getTarget(), OptimisticOptimizations.ALL, graph.method().getProfilingInfo()); 255 new InliningPhase(new InlineEverythingPolicy(), new CanonicalizerPhase()).apply(graph, highContext); 256 new CanonicalizerPhase().apply(graph, highContext); 257 new LoweringPhase(new CanonicalizerPhase(), LoweringTool.StandardLoweringStage.HIGH_TIER).apply(graph, highContext); 258 new GuardLoweringPhase().apply(graph, midContext); 259 new LoweringPhase(new CanonicalizerPhase(), LoweringTool.StandardLoweringStage.MID_TIER).apply(graph, midContext); 260 new WriteBarrierAdditionPhase(config).apply(graph); 261 Debug.dump(graph, "After Write Barrier Addition"); 262 263 int barriers = 0; 264 if (config.useG1GC) { 265 barriers = graph.getNodes().filter(G1ReferentFieldReadBarrier.class).count() + graph.getNodes().filter(G1PreWriteBarrier.class).count() + 266 graph.getNodes().filter(G1PostWriteBarrier.class).count(); 267 } else { 268 barriers = graph.getNodes().filter(SerialWriteBarrier.class).count(); 269 } 270 Assert.assertEquals(expectedBarriers, barriers); 271 for (WriteNode write : graph.getNodes().filter(WriteNode.class)) { 272 if (config.useG1GC) { 273 if (write.getBarrierType() != BarrierType.NONE) { 274 Assert.assertEquals(1, write.successors().count()); 275 Assert.assertTrue(write.next() instanceof G1PostWriteBarrier); 276 Assert.assertTrue(write.predecessor() instanceof G1PreWriteBarrier); 277 } 278 } else { 279 if (write.getBarrierType() != BarrierType.NONE) { 280 Assert.assertEquals(1, write.successors().count()); 281 Assert.assertTrue(write.next() instanceof SerialWriteBarrier); 282 } 283 } 284 } 285 286 for (ReadNode read : graph.getNodes().filter(ReadNode.class)) { 287 if (read.getBarrierType() != BarrierType.NONE) { 288 Assert.assertTrue(read.getAddress() instanceof OffsetAddressNode); 289 JavaConstant constDisp = ((OffsetAddressNode) read.getAddress()).getOffset().asJavaConstant(); 290 Assert.assertNotNull(constDisp); 291 Assert.assertEquals(referentOffset, constDisp.asLong()); 292 Assert.assertTrue(config.useG1GC); 293 Assert.assertEquals(BarrierType.PRECISE, read.getBarrierType()); 294 Assert.assertTrue(read.next() instanceof G1ReferentFieldReadBarrier); 295 } 296 } 297 } catch (Throwable e) { 298 throw Debug.handle(e); 299 } 300 } 301 302 private void test2(final String snippet, Object... args) throws Exception { 303 HotSpotInstalledCode code = getInstalledCode(snippet, args[0] instanceof Unsafe); 304 code.executeVarargs(args); 305 } 306}