1 /* 2 * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 package org.graalvm.compiler.nodes.test; 24 25 import java.util.EnumSet; 26 import java.util.HashSet; 27 28 import org.graalvm.compiler.core.common.calc.FloatConvert; 29 import org.graalvm.compiler.core.common.calc.FloatConvertCategory; 30 import org.graalvm.compiler.core.common.type.ArithmeticOpTable; 31 import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp; 32 import org.graalvm.compiler.core.common.type.ArithmeticOpTable.IntegerConvertOp; 33 import org.graalvm.compiler.core.common.type.ArithmeticOpTable.ShiftOp; 34 import org.graalvm.compiler.core.common.type.FloatStamp; 35 import org.graalvm.compiler.core.common.type.IntegerStamp; 36 import org.graalvm.compiler.core.common.type.PrimitiveStamp; 37 import org.graalvm.compiler.core.common.type.Stamp; 38 import org.graalvm.compiler.test.GraalTest; 39 import org.junit.Test; 40 41 import jdk.vm.ci.meta.Constant; 42 import jdk.vm.ci.meta.JavaConstant; 43 import jdk.vm.ci.meta.JavaKind; 44 45 /** 46 * Exercise the various stamp folding operations by generating ranges from a set of boundary values 47 * and then ensuring that the values that produced those ranges are in the resulting stamp. 48 */ 49 public class PrimitiveStampBoundaryTest extends GraalTest { 50 51 static long[] longBoundaryValues = {Long.MIN_VALUE, Long.MIN_VALUE + 1, Integer.MIN_VALUE, Integer.MIN_VALUE + 1, -1, 0, 1, Integer.MAX_VALUE - 1, Integer.MAX_VALUE, Long.MAX_VALUE - 1, 52 Long.MAX_VALUE}; 53 54 static int[] shiftBoundaryValues = {-128, -1, 0, 1, 4, 8, 16, 31, 63, 128}; 55 56 static HashSet<IntegerStamp> shiftStamps; 57 static HashSet<PrimitiveStamp> integerTestStamps; 58 static HashSet<PrimitiveStamp> floatTestStamps; 59 60 static { 61 shiftStamps = new HashSet<>(); 62 for (long v1 : shiftBoundaryValues) { 63 for (long v2 : shiftBoundaryValues) { 64 shiftStamps.add(IntegerStamp.create(32, Math.min(v1, v2), Math.max(v1, v2))); 65 } 66 } 67 68 integerTestStamps = new HashSet<>(); 69 for (long v1 : longBoundaryValues) { 70 for (long v2 : longBoundaryValues) { 71 if (v2 == (int) v2 && v1 == (int) v1) { 72 integerTestStamps.add(IntegerStamp.create(32, Math.min(v1, v2), Math.max(v1, v2))); 73 } 74 integerTestStamps.add(IntegerStamp.create(64, Math.min(v1, v2), Math.max(v1, v2))); 75 } 76 } 77 } 78 79 static double[] doubleBoundaryValues = {Double.NEGATIVE_INFINITY, Double.MIN_VALUE, Float.NEGATIVE_INFINITY, Float.MIN_VALUE, 80 Long.MIN_VALUE, Long.MIN_VALUE + 1, Integer.MIN_VALUE, Integer.MIN_VALUE + 1, -1, 0, 1, 81 Integer.MAX_VALUE - 1, Integer.MAX_VALUE, Long.MAX_VALUE - 1, Long.MAX_VALUE, 82 Float.MAX_VALUE, Float.POSITIVE_INFINITY, Double.MAX_VALUE, Double.POSITIVE_INFINITY}; 83 84 static double[] doubleSpecialValues = {Double.NaN, -0.0, -0.0F, Float.NaN}; 85 86 static { 87 floatTestStamps = new HashSet<>(); 88 89 for (double d1 : doubleBoundaryValues) { 90 for (double d2 : doubleBoundaryValues) { 91 float f1 = (float) d2; 92 float f2 = (float) d1; 93 if (d2 == f1 && d1 == f2) { 94 generateFloatingStamps(new FloatStamp(32, Math.min(f2, f1), Math.max(f2, f1), true)); 95 generateFloatingStamps(new FloatStamp(32, Math.min(f2, f1), Math.max(f2, f1), false)); 96 } 97 generateFloatingStamps(new FloatStamp(64, Math.min(d1, d2), Math.max(d1, d2), true)); 98 generateFloatingStamps(new FloatStamp(64, Math.min(d1, d2), Math.max(d1, d2), false)); 99 } 100 } 101 } 102 103 private static void generateFloatingStamps(FloatStamp floatStamp) { 104 floatTestStamps.add(floatStamp); 105 for (double d : doubleSpecialValues) { 106 FloatStamp newStamp = (FloatStamp) floatStamp.meet(floatStampForConstant(d, floatStamp.getBits())); 107 if (!newStamp.isUnrestricted()) { 108 floatTestStamps.add(newStamp); 109 } 110 } 111 } 112 113 @Test 114 public void testConvertBoundaryValues() { 115 testConvertBoundaryValues(IntegerStamp.OPS.getSignExtend(), 32, 64, integerTestStamps); 116 testConvertBoundaryValues(IntegerStamp.OPS.getZeroExtend(), 32, 64, integerTestStamps); 117 testConvertBoundaryValues(IntegerStamp.OPS.getNarrow(), 64, 32, integerTestStamps); 118 } 119 120 private static void testConvertBoundaryValues(IntegerConvertOp<?> op, int inputBits, int resultBits, HashSet<PrimitiveStamp> stamps) { 121 for (PrimitiveStamp stamp : stamps) { 122 if (inputBits == stamp.getBits()) { 123 Stamp lower = boundaryStamp(stamp, false); 124 Stamp upper = boundaryStamp(stamp, true); 125 checkConvertOperation(op, inputBits, resultBits, op.foldStamp(inputBits, resultBits, stamp), lower); 126 checkConvertOperation(op, inputBits, resultBits, op.foldStamp(inputBits, resultBits, stamp), upper); 127 } 128 } 129 } 130 131 private static void checkConvertOperation(IntegerConvertOp<?> op, int inputBits, int resultBits, Stamp result, Stamp v1stamp) { 132 Stamp folded = op.foldStamp(inputBits, resultBits, v1stamp); 133 assertTrue(folded.asConstant() != null, "should constant fold %s %s %s", op, v1stamp, folded); 134 assertTrue(result.meet(folded).equals(result), "result out of range %s %s %s %s %s", op, v1stamp, folded, result, result.meet(folded)); 135 } 136 137 @Test 138 public void testFloatConvertBoundaryValues() { 139 for (FloatConvert op : EnumSet.allOf(FloatConvert.class)) { 140 ArithmeticOpTable.FloatConvertOp floatConvert = IntegerStamp.OPS.getFloatConvert(op); 141 if (floatConvert == null) { 142 continue; 143 } 144 assert op.getCategory() == FloatConvertCategory.IntegerToFloatingPoint : op; 145 testConvertBoundaryValues(floatConvert, op.getInputBits(), integerTestStamps); 146 } 147 for (FloatConvert op : EnumSet.allOf(FloatConvert.class)) { 148 ArithmeticOpTable.FloatConvertOp floatConvert = FloatStamp.OPS.getFloatConvert(op); 149 if (floatConvert == null) { 150 continue; 151 } 152 assert op.getCategory() == FloatConvertCategory.FloatingPointToInteger || op.getCategory() == FloatConvertCategory.FloatingPointToFloatingPoint : op; 153 testConvertBoundaryValues(floatConvert, op.getInputBits(), floatTestStamps); 154 } 155 } 156 157 private static void testConvertBoundaryValues(ArithmeticOpTable.FloatConvertOp op, int bits, HashSet<PrimitiveStamp> stamps) { 158 for (PrimitiveStamp stamp : stamps) { 159 if (bits == stamp.getBits()) { 160 Stamp lower = boundaryStamp(stamp, false); 161 Stamp upper = boundaryStamp(stamp, true); 162 checkConvertOperation(op, op.foldStamp(stamp), lower); 163 checkConvertOperation(op, op.foldStamp(stamp), upper); 164 } 165 } 166 } 167 168 private static void checkConvertOperation(ArithmeticOpTable.FloatConvertOp op, Stamp result, Stamp v1stamp) { 169 Stamp folded = op.foldStamp(v1stamp); 170 assertTrue(folded.asConstant() != null, "should constant fold %s %s %s", op, v1stamp, folded); 171 assertTrue(result.meet(folded).equals(result), "result out of range %s %s %s %s %s", op, v1stamp, folded, result, result.meet(folded)); 172 } 173 174 @Test 175 public void testShiftBoundaryValues() { 176 for (ShiftOp<?> op : IntegerStamp.OPS.getShiftOps()) { 177 testShiftBoundaryValues(op, integerTestStamps, shiftStamps); 178 } 179 } 180 181 private static void testShiftBoundaryValues(ShiftOp<?> shiftOp, HashSet<PrimitiveStamp> stamps, HashSet<IntegerStamp> shifts) { 182 for (PrimitiveStamp testStamp : stamps) { 183 if (testStamp instanceof IntegerStamp) { 184 IntegerStamp stamp = (IntegerStamp) testStamp; 185 for (IntegerStamp shiftStamp : shifts) { 186 IntegerStamp foldedStamp = (IntegerStamp) shiftOp.foldStamp(stamp, shiftStamp); 187 checkShiftOperation(stamp.getBits(), shiftOp, foldedStamp, stamp.lowerBound(), shiftStamp.lowerBound()); 188 checkShiftOperation(stamp.getBits(), shiftOp, foldedStamp, stamp.lowerBound(), shiftStamp.upperBound()); 189 checkShiftOperation(stamp.getBits(), shiftOp, foldedStamp, stamp.upperBound(), shiftStamp.lowerBound()); 190 checkShiftOperation(stamp.getBits(), shiftOp, foldedStamp, stamp.upperBound(), shiftStamp.upperBound()); 191 } 192 } 193 } 194 } 195 196 private static void checkShiftOperation(int bits, ShiftOp<?> op, IntegerStamp result, long v1, long v2) { 197 IntegerStamp v1stamp = IntegerStamp.create(bits, v1, v1); 198 IntegerStamp v2stamp = IntegerStamp.create(32, v2, v2); 199 IntegerStamp folded = (IntegerStamp) op.foldStamp(v1stamp, v2stamp); 200 Constant constant = op.foldConstant(JavaConstant.forPrimitiveInt(bits, v1), (int) v2); 201 assertTrue(constant != null); 202 assertTrue(folded.asConstant() != null, "should constant fold %s %s %s %s", op, v1stamp, v2stamp, folded); 203 assertTrue(result.meet(folded).equals(result), "result out of range %s %s %s %s %s %s", op, v1stamp, v2stamp, folded, result, result.meet(folded)); 204 } 205 206 private static void checkBinaryOperation(ArithmeticOpTable.BinaryOp<?> op, Stamp result, Stamp v1stamp, Stamp v2stamp) { 207 Stamp folded = op.foldStamp(v1stamp, v2stamp); 208 Constant constant = op.foldConstant(v1stamp.asConstant(), v2stamp.asConstant()); 209 if (constant != null) { 210 Constant constant2 = folded.asConstant(); 211 if (constant2 == null && v1stamp instanceof FloatStamp) { 212 JavaConstant c = (JavaConstant) constant; 213 assertTrue((c.getJavaKind() == JavaKind.Double && Double.isNaN(c.asDouble())) || 214 (c.getJavaKind() == JavaKind.Float && Float.isNaN(c.asFloat()))); 215 } else { 216 assertTrue(constant2 != null, "should constant fold %s %s %s %s", op, v1stamp, v2stamp, folded); 217 if (!constant.equals(constant2)) { 218 op.foldConstant(v1stamp.asConstant(), v2stamp.asConstant()); 219 op.foldStamp(v1stamp, v2stamp); 220 } 221 assertTrue(constant.equals(constant2), "should produce same constant %s %s %s %s %s", op, v1stamp, v2stamp, constant, constant2); 222 } 223 assertTrue(result.meet(folded).equals(result), "result out of range %s %s %s %s %s %s", op, v1stamp, v2stamp, folded, result, result.meet(folded)); 224 } 225 } 226 227 @Test 228 public void testBinaryBoundaryValues() { 229 for (BinaryOp<?> op : IntegerStamp.OPS.getBinaryOps()) { 230 if (op != null) { 231 testBinaryBoundaryValues(op, integerTestStamps); 232 } 233 } 234 for (BinaryOp<?> op : FloatStamp.OPS.getBinaryOps()) { 235 if (op != null) { 236 testBinaryBoundaryValues(op, floatTestStamps); 237 } 238 } 239 } 240 241 private static Stamp boundaryStamp(Stamp v1, boolean upper) { 242 if (v1 instanceof IntegerStamp) { 243 IntegerStamp istamp = (IntegerStamp) v1; 244 long bound = upper ? istamp.upperBound() : istamp.lowerBound(); 245 return IntegerStamp.create(istamp.getBits(), bound, bound); 246 } else if (v1 instanceof FloatStamp) { 247 FloatStamp floatStamp = (FloatStamp) v1; 248 double bound = upper ? floatStamp.upperBound() : floatStamp.lowerBound(); 249 int bits = floatStamp.getBits(); 250 return floatStampForConstant(bound, bits); 251 } else { 252 throw new InternalError("unexpected stamp type " + v1); 253 } 254 } 255 256 private static FloatStamp floatStampForConstant(double bound, int bits) { 257 if (bits == 32) { 258 float fbound = (float) bound; 259 return new FloatStamp(bits, fbound, fbound, !Float.isNaN(fbound)); 260 } else { 261 return new FloatStamp(bits, bound, bound, !Double.isNaN(bound)); 262 } 263 } 264 265 private static void testBinaryBoundaryValues(ArithmeticOpTable.BinaryOp<?> op, HashSet<PrimitiveStamp> stamps) { 266 for (PrimitiveStamp v1 : stamps) { 267 for (PrimitiveStamp v2 : stamps) { 268 if (v1.getBits() == v2.getBits() && v1.getClass() == v2.getClass()) { 269 Stamp result = op.foldStamp(v1, v2); 270 Stamp v1lower = boundaryStamp(v1, false); 271 Stamp v1upper = boundaryStamp(v1, true); 272 Stamp v2lower = boundaryStamp(v2, false); 273 Stamp v2upper = boundaryStamp(v2, true); 274 checkBinaryOperation(op, result, v1lower, v2lower); 275 checkBinaryOperation(op, result, v1lower, v2upper); 276 checkBinaryOperation(op, result, v1upper, v2lower); 277 checkBinaryOperation(op, result, v1upper, v2upper); 278 } 279 } 280 } 281 } 282 283 @Test 284 public void testUnaryBoundaryValues() { 285 for (ArithmeticOpTable.UnaryOp<?> op : IntegerStamp.OPS.getUnaryOps()) { 286 if (op != null) { 287 testUnaryBoundaryValues(op, integerTestStamps); 288 } 289 } 290 for (ArithmeticOpTable.UnaryOp<?> op : FloatStamp.OPS.getUnaryOps()) { 291 if (op != null) { 292 testUnaryBoundaryValues(op, floatTestStamps); 293 } 294 } 295 } 296 297 private static void testUnaryBoundaryValues(ArithmeticOpTable.UnaryOp<?> op, HashSet<PrimitiveStamp> stamps) { 298 for (PrimitiveStamp v1 : stamps) { 299 Stamp result = op.foldStamp(v1); 300 checkUnaryOperation(op, result, boundaryStamp(v1, false)); 301 checkUnaryOperation(op, result, boundaryStamp(v1, true)); 302 } 303 } 304 305 private static void checkUnaryOperation(ArithmeticOpTable.UnaryOp<?> op, Stamp result, Stamp v1stamp) { 306 Stamp folded = op.foldStamp(v1stamp); 307 Constant v1constant = v1stamp.asConstant(); 308 if (v1constant != null) { 309 Constant constant = op.foldConstant(v1constant); 310 if (constant != null) { 311 Constant constant2 = folded.asConstant(); 312 if (constant2 == null && v1stamp instanceof FloatStamp) { 313 JavaConstant c = (JavaConstant) constant; 314 assertTrue((c.getJavaKind() == JavaKind.Double && Double.isNaN(c.asDouble())) || 315 (c.getJavaKind() == JavaKind.Float && Float.isNaN(c.asFloat()))); 316 } else { 317 assertTrue(constant2 != null, "should constant fold %s %s %s", op, v1stamp, folded); 318 assertTrue(constant.equals(constant2), "should produce same constant %s %s %s %s", op, v1stamp, constant, constant2); 319 } 320 } 321 } else { 322 assert v1stamp instanceof FloatStamp; 323 } 324 assertTrue(result.meet(folded).equals(result), "result out of range %s %s %s %s %s", op, v1stamp, folded, result, result.meet(folded)); 325 } 326 }