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.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.CompressEncoding;
  30 import org.graalvm.compiler.core.common.type.AbstractObjectStamp;
  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.lir.gen.LIRGeneratorTool;
  36 import org.graalvm.compiler.nodeinfo.NodeInfo;
  37 import org.graalvm.compiler.nodes.calc.ConvertNode;
  38 import org.graalvm.compiler.nodes.calc.UnaryNode;
  39 import org.graalvm.compiler.nodes.spi.LIRLowerable;
  40 import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool;
  41 import org.graalvm.compiler.nodes.type.StampTool;
  42 
  43 import jdk.vm.ci.meta.Constant;
  44 import jdk.vm.ci.meta.ConstantReflectionProvider;
  45 import jdk.vm.ci.meta.Value;
  46 
  47 /**
  48  * Compress or uncompress an oop or metaspace pointer.
  49  */
  50 @NodeInfo(nameTemplate = "{p#op/s}", cycles = CYCLES_2, size = SIZE_2)
  51 public abstract class CompressionNode extends UnaryNode implements ConvertNode, LIRLowerable {
  52 
  53     public static final NodeClass<CompressionNode> TYPE = NodeClass.create(CompressionNode.class);
  54 
  55     public enum CompressionOp {
  56         Compress,
  57         Uncompress
  58     }
  59 
  60     protected final CompressionOp op;
  61     protected final CompressEncoding encoding;
  62 
  63     public CompressionNode(NodeClass<? extends UnaryNode> c, CompressionOp op, ValueNode input, Stamp stamp, CompressEncoding encoding) {
  64         super(c, stamp, input);
  65         this.op = op;
  66         this.encoding = encoding;
  67     }
  68 
  69     @Override
  70     public Stamp foldStamp(Stamp newStamp) {
  71         assert newStamp.isCompatible(getValue().stamp());
  72         return mkStamp(newStamp);
  73     }
  74 
  75     protected abstract Constant compress(Constant c);
  76 
  77     protected abstract Constant uncompress(Constant c);
  78 
  79     @Override
  80     public Constant convert(Constant c, ConstantReflectionProvider constantReflection) {
  81         switch (op) {
  82             case Compress:
  83                 return compress(c);
  84             case Uncompress:
  85                 return uncompress(c);
  86             default:
  87                 throw GraalError.shouldNotReachHere();
  88         }
  89     }
  90 
  91     @Override
  92     public Constant reverse(Constant c, ConstantReflectionProvider constantReflection) {
  93         switch (op) {
  94             case Compress:
  95                 return uncompress(c);
  96             case Uncompress:
  97                 return compress(c);
  98             default:
  99                 throw GraalError.shouldNotReachHere();
 100         }
 101     }
 102 
 103     @Override
 104     public boolean isLossless() {
 105         return true;
 106     }
 107 
 108     protected abstract Stamp mkStamp(Stamp input);
 109 
 110     public CompressionOp getOp() {
 111         return op;
 112     }
 113 
 114     public CompressEncoding getEncoding() {
 115         return encoding;
 116     }
 117 
 118     @Override
 119     public ValueNode canonical(CanonicalizerTool tool, ValueNode forValue) {
 120         if (forValue.isConstant()) {
 121             if (GeneratePIC.getValue(tool.getOptions())) {
 122                 // We always want uncompressed constants
 123                 return this;
 124             }
 125             int stableDimension = ((ConstantNode) forValue).getStableDimension();
 126             boolean isDefaultStable = ((ConstantNode) forValue).isDefaultStable();
 127             return ConstantNode.forConstant(stamp(), convert(forValue.asConstant(), tool.getConstantReflection()), stableDimension, isDefaultStable, tool.getMetaAccess());
 128         } else if (forValue instanceof CompressionNode) {
 129             CompressionNode other = (CompressionNode) forValue;
 130             if (op != other.op && encoding.equals(other.encoding)) {
 131                 return other.getValue();
 132             }
 133         }
 134         return this;
 135     }
 136 
 137     @Override
 138     public void generate(NodeLIRBuilderTool gen) {
 139         LIRGeneratorTool hsGen = gen.getLIRGeneratorTool();
 140         boolean nonNull;
 141         if (getValue().stamp() instanceof AbstractObjectStamp) {
 142             nonNull = StampTool.isPointerNonNull(getValue().stamp());
 143         } else {
 144             // metaspace pointers are never null
 145             nonNull = true;
 146         }
 147 
 148         Value result;
 149         switch (op) {
 150             case Compress:
 151                 result = hsGen.emitCompress(gen.operand(getValue()), encoding, nonNull);
 152                 break;
 153             case Uncompress:
 154                 result = hsGen.emitUncompress(gen.operand(getValue()), encoding, nonNull);
 155                 break;
 156             default:
 157                 throw GraalError.shouldNotReachHere();
 158         }
 159 
 160         gen.setResult(this, result);
 161     }
 162 
 163     @Override
 164     public boolean mayNullCheckSkipConversion() {
 165         return true;
 166     }
 167 }