1 /*
   2  * Copyright (c) 2014, 2016, 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.hotspot.nodes;
  24 
  25 import static org.graalvm.compiler.core.common.GraalOptions.GeneratePIC;
  26 import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_2;
  27 import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_2;
  28 
  29 import org.graalvm.compiler.core.common.type.AbstractObjectStamp;
  30 import org.graalvm.compiler.core.common.type.ObjectStamp;
  31 import org.graalvm.compiler.core.common.type.Stamp;
  32 import org.graalvm.compiler.debug.GraalError;
  33 import org.graalvm.compiler.graph.NodeClass;
  34 import org.graalvm.compiler.graph.spi.CanonicalizerTool;
  35 import org.graalvm.compiler.hotspot.CompressEncoding;
  36 import org.graalvm.compiler.hotspot.HotSpotLIRGenerator;
  37 import org.graalvm.compiler.hotspot.nodes.type.KlassPointerStamp;
  38 import org.graalvm.compiler.hotspot.nodes.type.NarrowOopStamp;
  39 import org.graalvm.compiler.nodeinfo.NodeInfo;
  40 import org.graalvm.compiler.nodes.ConstantNode;
  41 import org.graalvm.compiler.nodes.ValueNode;
  42 import org.graalvm.compiler.nodes.calc.ConvertNode;
  43 import org.graalvm.compiler.nodes.calc.UnaryNode;
  44 import org.graalvm.compiler.nodes.spi.LIRLowerable;
  45 import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool;
  46 import org.graalvm.compiler.nodes.type.StampTool;
  47 
  48 import jdk.vm.ci.hotspot.HotSpotCompressedNullConstant;
  49 import jdk.vm.ci.hotspot.HotSpotConstant;
  50 import jdk.vm.ci.meta.Constant;
  51 import jdk.vm.ci.meta.ConstantReflectionProvider;
  52 import jdk.vm.ci.meta.JavaConstant;
  53 import jdk.vm.ci.meta.Value;
  54 
  55 /**
  56  * Compress or uncompress an oop or metaspace pointer.
  57  */
  58 @NodeInfo(nameTemplate = "{p#op/s}", cycles = CYCLES_2, size = SIZE_2)
  59 public final class CompressionNode extends UnaryNode implements ConvertNode, LIRLowerable {
  60 
  61     public static final NodeClass<CompressionNode> TYPE = NodeClass.create(CompressionNode.class);
  62 
  63     public enum CompressionOp {
  64         Compress,
  65         Uncompress
  66     }
  67 
  68     protected final CompressionOp op;
  69     protected final CompressEncoding encoding;
  70 
  71     public CompressionNode(CompressionOp op, ValueNode input, CompressEncoding encoding) {
  72         super(TYPE, mkStamp(op, input.stamp(), encoding), input);
  73         this.op = op;
  74         this.encoding = encoding;
  75     }
  76 
  77     @Override
  78     public Stamp foldStamp(Stamp newStamp) {
  79         assert newStamp.isCompatible(getValue().stamp());
  80         return mkStamp(op, newStamp, encoding);
  81     }
  82 
  83     public static CompressionNode compress(ValueNode input, CompressEncoding encoding) {
  84         return input.graph().unique(new CompressionNode(CompressionOp.Compress, input, encoding));
  85     }
  86 
  87     public static CompressionNode compressNoUnique(ValueNode input, CompressEncoding encoding) {
  88         return new CompressionNode(CompressionOp.Compress, input, encoding);
  89     }
  90 
  91     public static CompressionNode uncompress(ValueNode input, CompressEncoding encoding) {
  92         return input.graph().unique(new CompressionNode(CompressionOp.Uncompress, input, encoding));
  93     }
  94 
  95     private static Constant compress(Constant c) {
  96         if (JavaConstant.NULL_POINTER.equals(c)) {
  97             return HotSpotCompressedNullConstant.COMPRESSED_NULL;
  98         } else if (c instanceof HotSpotConstant) {
  99             return ((HotSpotConstant) c).compress();
 100         } else {
 101             throw GraalError.shouldNotReachHere("invalid constant input for compress op: " + c);
 102         }
 103     }
 104 
 105     private static Constant uncompress(Constant c) {
 106         if (c instanceof HotSpotConstant) {
 107             return ((HotSpotConstant) c).uncompress();
 108         } else {
 109             throw GraalError.shouldNotReachHere("invalid constant input for uncompress op: " + c);
 110         }
 111     }
 112 
 113     @Override
 114     public Constant convert(Constant c, ConstantReflectionProvider constantReflection) {
 115         switch (op) {
 116             case Compress:
 117                 return compress(c);
 118             case Uncompress:
 119                 return uncompress(c);
 120             default:
 121                 throw GraalError.shouldNotReachHere();
 122         }
 123     }
 124 
 125     @Override
 126     public Constant reverse(Constant c, ConstantReflectionProvider constantReflection) {
 127         switch (op) {
 128             case Compress:
 129                 return uncompress(c);
 130             case Uncompress:
 131                 return compress(c);
 132             default:
 133                 throw GraalError.shouldNotReachHere();
 134         }
 135     }
 136 
 137     @Override
 138     public boolean isLossless() {
 139         return true;
 140     }
 141 
 142     private static Stamp mkStamp(CompressionOp op, Stamp input, CompressEncoding encoding) {
 143         switch (op) {
 144             case Compress:
 145                 if (input instanceof ObjectStamp) {
 146                     // compressed oop
 147                     return NarrowOopStamp.compressed((ObjectStamp) input, encoding);
 148                 } else if (input instanceof KlassPointerStamp) {
 149                     // compressed klass pointer
 150                     return ((KlassPointerStamp) input).compressed(encoding);
 151                 }
 152                 break;
 153             case Uncompress:
 154                 if (input instanceof NarrowOopStamp) {
 155                     // oop
 156                     assert encoding.equals(((NarrowOopStamp) input).getEncoding());
 157                     return ((NarrowOopStamp) input).uncompressed();
 158                 } else if (input instanceof KlassPointerStamp) {
 159                     // metaspace pointer
 160                     assert encoding.equals(((KlassPointerStamp) input).getEncoding());
 161                     return ((KlassPointerStamp) input).uncompressed();
 162                 }
 163                 break;
 164         }
 165         throw GraalError.shouldNotReachHere(String.format("Unexpected input stamp %s", input));
 166     }
 167 
 168     public CompressionOp getOp() {
 169         return op;
 170     }
 171 
 172     public CompressEncoding getEncoding() {
 173         return encoding;
 174     }
 175 
 176     @Override
 177     public ValueNode canonical(CanonicalizerTool tool, ValueNode forValue) {
 178         if (forValue.isConstant()) {
 179             if (GeneratePIC.getValue()) {
 180                 // We always want uncompressed constants
 181                 return this;
 182             }
 183             int stableDimension = ((ConstantNode) forValue).getStableDimension();
 184             boolean isDefaultStable = ((ConstantNode) forValue).isDefaultStable();
 185             return ConstantNode.forConstant(stamp(), convert(forValue.asConstant(), tool.getConstantReflection()), stableDimension, isDefaultStable, tool.getMetaAccess());
 186         } else if (forValue instanceof CompressionNode) {
 187             CompressionNode other = (CompressionNode) forValue;
 188             if (op != other.op && encoding.equals(other.encoding)) {
 189                 return other.getValue();
 190             }
 191         }
 192         return this;
 193     }
 194 
 195     @Override
 196     public void generate(NodeLIRBuilderTool gen) {
 197         HotSpotLIRGenerator hsGen = (HotSpotLIRGenerator) gen.getLIRGeneratorTool();
 198         boolean nonNull;
 199         if (getValue().stamp() instanceof AbstractObjectStamp) {
 200             nonNull = StampTool.isPointerNonNull(getValue().stamp());
 201         } else {
 202             // metaspace pointers are never null
 203             nonNull = true;
 204         }
 205 
 206         Value result;
 207         switch (op) {
 208             case Compress:
 209                 result = hsGen.emitCompress(gen.operand(getValue()), encoding, nonNull);
 210                 break;
 211             case Uncompress:
 212                 result = hsGen.emitUncompress(gen.operand(getValue()), encoding, nonNull);
 213                 break;
 214             default:
 215                 throw GraalError.shouldNotReachHere();
 216         }
 217         gen.setResult(this, result);
 218     }
 219 
 220     @NodeIntrinsic
 221     public static native Object compression(@ConstantNodeParameter CompressionOp op, Object object, @ConstantNodeParameter CompressEncoding encoding);
 222 }