001/*
002 * Copyright (c) 2014, 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.nodes;
024
025import jdk.internal.jvmci.common.*;
026import jdk.internal.jvmci.hotspot.*;
027import jdk.internal.jvmci.hotspot.HotSpotVMConfig.*;
028import jdk.internal.jvmci.meta.*;
029
030import com.oracle.graal.compiler.common.type.*;
031import com.oracle.graal.graph.*;
032import com.oracle.graal.graph.spi.*;
033import com.oracle.graal.hotspot.*;
034import com.oracle.graal.hotspot.nodes.type.*;
035import com.oracle.graal.nodeinfo.*;
036import com.oracle.graal.nodes.*;
037import com.oracle.graal.nodes.calc.*;
038import com.oracle.graal.nodes.spi.*;
039import com.oracle.graal.nodes.type.*;
040
041/**
042 * Compress or uncompress an oop or metaspace pointer.
043 */
044@NodeInfo(nameTemplate = "{p#op/s}")
045public final class CompressionNode extends UnaryNode implements ConvertNode, LIRLowerable {
046
047    public static final NodeClass<CompressionNode> TYPE = NodeClass.create(CompressionNode.class);
048
049    public enum CompressionOp {
050        Compress,
051        Uncompress
052    }
053
054    protected final CompressionOp op;
055    protected final CompressEncoding encoding;
056
057    public CompressionNode(CompressionOp op, ValueNode input, CompressEncoding encoding) {
058        super(TYPE, mkStamp(op, input.stamp(), encoding), input);
059        this.op = op;
060        this.encoding = encoding;
061    }
062
063    @Override
064    public boolean inferStamp() {
065        return updateStamp(mkStamp(op, getValue().stamp(), encoding));
066    }
067
068    public static CompressionNode compress(ValueNode input, CompressEncoding encoding) {
069        return input.graph().unique(new CompressionNode(CompressionOp.Compress, input, encoding));
070    }
071
072    public static CompressionNode compressNoUnique(ValueNode input, CompressEncoding encoding) {
073        return new CompressionNode(CompressionOp.Compress, input, encoding);
074    }
075
076    public static CompressionNode uncompress(ValueNode input, CompressEncoding encoding) {
077        return input.graph().unique(new CompressionNode(CompressionOp.Uncompress, input, encoding));
078    }
079
080    private static Constant compress(Constant c, CompressEncoding encoding) {
081        if (JavaConstant.NULL_POINTER.equals(c)) {
082            return HotSpotCompressedNullConstant.COMPRESSED_NULL;
083        } else if (c instanceof HotSpotObjectConstant) {
084            return ((HotSpotObjectConstant) c).compress();
085        } else if (c instanceof HotSpotMetaspaceConstant) {
086            return ((HotSpotMetaspaceConstant) c).compress(encoding);
087        } else {
088            throw JVMCIError.shouldNotReachHere("invalid constant input for compress op: " + c);
089        }
090    }
091
092    private static Constant uncompress(Constant c, CompressEncoding encoding) {
093        if (HotSpotCompressedNullConstant.COMPRESSED_NULL.equals(c)) {
094            return JavaConstant.NULL_POINTER;
095        } else if (c instanceof HotSpotObjectConstant) {
096            return ((HotSpotObjectConstant) c).uncompress();
097        } else if (c instanceof HotSpotMetaspaceConstant) {
098            return ((HotSpotMetaspaceConstant) c).uncompress(encoding);
099        } else {
100            throw JVMCIError.shouldNotReachHere("invalid constant input for uncompress op: " + c);
101        }
102    }
103
104    @Override
105    public Constant convert(Constant c, ConstantReflectionProvider constantReflection) {
106        switch (op) {
107            case Compress:
108                return compress(c, encoding);
109            case Uncompress:
110                return uncompress(c, encoding);
111            default:
112                throw JVMCIError.shouldNotReachHere();
113        }
114    }
115
116    @Override
117    public Constant reverse(Constant c, ConstantReflectionProvider constantReflection) {
118        switch (op) {
119            case Compress:
120                return uncompress(c, encoding);
121            case Uncompress:
122                return compress(c, encoding);
123            default:
124                throw JVMCIError.shouldNotReachHere();
125        }
126    }
127
128    @Override
129    public boolean isLossless() {
130        return true;
131    }
132
133    private static Stamp mkStamp(CompressionOp op, Stamp input, CompressEncoding encoding) {
134        switch (op) {
135            case Compress:
136                if (input instanceof ObjectStamp) {
137                    // compressed oop
138                    return NarrowOopStamp.compressed((ObjectStamp) input, encoding);
139                } else if (input instanceof KlassPointerStamp) {
140                    // compressed klass pointer
141                    return ((KlassPointerStamp) input).compressed(encoding);
142                }
143                break;
144            case Uncompress:
145                if (input instanceof NarrowOopStamp) {
146                    // oop
147                    assert encoding.equals(((NarrowOopStamp) input).getEncoding());
148                    return ((NarrowOopStamp) input).uncompressed();
149                } else if (input instanceof KlassPointerStamp) {
150                    // metaspace pointer
151                    assert encoding.equals(((KlassPointerStamp) input).getEncoding());
152                    return ((KlassPointerStamp) input).uncompressed();
153                }
154                break;
155        }
156        throw JVMCIError.shouldNotReachHere(String.format("Unexpected input stamp %s", input));
157    }
158
159    public CompressionOp getOp() {
160        return op;
161    }
162
163    public CompressEncoding getEncoding() {
164        return encoding;
165    }
166
167    @Override
168    public ValueNode canonical(CanonicalizerTool tool, ValueNode forValue) {
169        if (forValue.isConstant()) {
170            return ConstantNode.forConstant(stamp(), convert(forValue.asConstant(), tool.getConstantReflection()), tool.getMetaAccess());
171        } else if (forValue instanceof CompressionNode) {
172            CompressionNode other = (CompressionNode) forValue;
173            if (op != other.op && encoding.equals(other.encoding)) {
174                return other.getValue();
175            }
176        }
177        return this;
178    }
179
180    @Override
181    public void generate(NodeLIRBuilderTool gen) {
182        HotSpotLIRGenerator hsGen = (HotSpotLIRGenerator) gen.getLIRGeneratorTool();
183        boolean nonNull;
184        if (getValue().stamp() instanceof AbstractObjectStamp) {
185            nonNull = StampTool.isPointerNonNull(getValue().stamp());
186        } else {
187            // metaspace pointers are never null
188            nonNull = true;
189        }
190
191        Value result;
192        switch (op) {
193            case Compress:
194                result = hsGen.emitCompress(gen.operand(getValue()), encoding, nonNull);
195                break;
196            case Uncompress:
197                result = hsGen.emitUncompress(gen.operand(getValue()), encoding, nonNull);
198                break;
199            default:
200                throw JVMCIError.shouldNotReachHere();
201        }
202        gen.setResult(this, result);
203    }
204
205    @NodeIntrinsic
206    public static native Object compression(@ConstantNodeParameter CompressionOp op, Object object, @ConstantNodeParameter CompressEncoding encoding);
207}