1 /*
2  * Copyright (c) 2014, 2018, 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 
24 
25 package org.graalvm.compiler.nodes.calc;
26 
27 import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_1;
28 
29 import org.graalvm.compiler.core.common.calc.CanonicalCondition;
30 import org.graalvm.compiler.core.common.type.ArithmeticOpTable;
31 import org.graalvm.compiler.core.common.type.ArithmeticOpTable.IntegerConvertOp;
32 import org.graalvm.compiler.core.common.type.ArithmeticOpTable.IntegerConvertOp.Narrow;
33 import org.graalvm.compiler.core.common.type.ArithmeticOpTable.IntegerConvertOp.ZeroExtend;
34 import org.graalvm.compiler.core.common.type.IntegerStamp;
35 import org.graalvm.compiler.core.common.type.PrimitiveStamp;
36 import org.graalvm.compiler.core.common.type.Stamp;
37 import org.graalvm.compiler.graph.NodeClass;
38 import org.graalvm.compiler.graph.spi.CanonicalizerTool;
39 import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool;
40 import org.graalvm.compiler.nodeinfo.NodeInfo;
41 import org.graalvm.compiler.nodes.NodeView;
42 import org.graalvm.compiler.nodes.ValueNode;
43 import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool;
44 
45 import jdk.vm.ci.code.CodeUtil;
46 
47 /**
48  * The {@code ZeroExtendNode} converts an integer to a wider integer using zero extension.
49  */
50 @NodeInfo(cycles = CYCLES_1)
51 public final class ZeroExtendNode extends IntegerConvertNode<ZeroExtend, Narrow> {
52 
53     public static final NodeClass<ZeroExtendNode> TYPE = NodeClass.create(ZeroExtendNode.class);
54 
55     private final boolean inputAlwaysPositive;
56 
ZeroExtendNode(ValueNode input, int resultBits)57     public ZeroExtendNode(ValueNode input, int resultBits) {
58         this(input, PrimitiveStamp.getBits(input.stamp(NodeView.DEFAULT)), resultBits, false);
59         assert 0 < PrimitiveStamp.getBits(input.stamp(NodeView.DEFAULT)) && PrimitiveStamp.getBits(input.stamp(NodeView.DEFAULT)) <= resultBits;
60     }
61 
62     public ZeroExtendNode(ValueNode input, int inputBits, int resultBits, boolean inputAlwaysPositive) {
63         super(TYPE, ArithmeticOpTable::getZeroExtend, ArithmeticOpTable::getNarrow, inputBits, resultBits, input);
64         this.inputAlwaysPositive = inputAlwaysPositive;
65     }
66 
67     public static ValueNode create(ValueNode input, int resultBits, NodeView view) {
68         return create(input, PrimitiveStamp.getBits(input.stamp(view)), resultBits, view, false);
69     }
70 
71     public static ValueNode create(ValueNode input, int inputBits, int resultBits, NodeView view) {
72         return create(input, inputBits, resultBits, view, false);
73     }
74 
75     public static ValueNode create(ValueNode input, int inputBits, int resultBits, NodeView view, boolean alwaysPositive) {
76         IntegerConvertOp<ZeroExtend> signExtend = ArithmeticOpTable.forStamp(input.stamp(view)).getZeroExtend();
77         ValueNode synonym = findSynonym(signExtend, input, inputBits, resultBits, signExtend.foldStamp(inputBits, resultBits, input.stamp(view)));
78         if (synonym != null) {
79             return synonym;
80         }
81         return canonical(null, input, inputBits, resultBits, view, alwaysPositive);
82     }
83 
84     @Override
85     public boolean isLossless() {
86         return true;
87     }
88 
89     public boolean isInputAlwaysPositive() {
90         return inputAlwaysPositive;
91     }
92 
93     @Override
94     public boolean preservesOrder(CanonicalCondition cond) {
95         switch (cond) {
96             case LT:
97                 return false;
98             default:
99                 return true;
100         }
101     }
102 
103     @Override
104     public ValueNode canonical(CanonicalizerTool tool, ValueNode forValue) {
105         NodeView view = NodeView.from(tool);
106         ValueNode ret = super.canonical(tool, forValue);
107         if (ret != this) {
108             return ret;
109         }
110 
111         return canonical(this, forValue, getInputBits(), getResultBits(), view, inputAlwaysPositive);
112     }
113 
114     private static ValueNode canonical(ZeroExtendNode zeroExtendNode, ValueNode forValue, int inputBits, int resultBits, NodeView view, boolean alwaysPositive) {
115         ZeroExtendNode self = zeroExtendNode;
116         if (forValue instanceof ZeroExtendNode) {
117             // xxxx -(zero-extend)-> 0000 xxxx -(zero-extend)-> 00000000 0000xxxx
118             // ==> xxxx -(zero-extend)-> 00000000 0000xxxx
119             ZeroExtendNode other = (ZeroExtendNode) forValue;
120             return new ZeroExtendNode(other.getValue(), other.getInputBits(), resultBits, other.isInputAlwaysPositive());
121         }
122         if (forValue instanceof NarrowNode) {
123             NarrowNode narrow = (NarrowNode) forValue;
124             Stamp inputStamp = narrow.getValue().stamp(view);
125             if (inputStamp instanceof IntegerStamp) {
126                 IntegerStamp istamp = (IntegerStamp) inputStamp;
127                 long mask = CodeUtil.mask(PrimitiveStamp.getBits(narrow.stamp(view)));
128 
129                 if ((istamp.upMask() & ~mask) == 0) {
130                     // The original value cannot change because of the narrow and zero extend.
131 
132                     if (istamp.getBits() < resultBits) {
133                         // Need to keep the zero extend, skip the narrow.
134                         return create(narrow.getValue(), resultBits, view);
135                     } else if (istamp.getBits() > resultBits) {
136                         // Need to keep the narrow, skip the zero extend.
137                         return NarrowNode.create(narrow.getValue(), resultBits, view);
138                     } else {
139                         assert istamp.getBits() == resultBits;
140                         // Just return the original value.
141                         return narrow.getValue();
142                     }
143                 }
144             }
145         }
146 
147         if (self == null) {
148             self = new ZeroExtendNode(forValue, inputBits, resultBits, alwaysPositive);
149         }
150         return self;
151     }
152 
153     @Override
154     public void generate(NodeLIRBuilderTool nodeValueMap, ArithmeticLIRGeneratorTool gen) {
155         nodeValueMap.setResult(this, gen.emitZeroExtend(nodeValueMap.operand(getValue()), getInputBits(), getResultBits()));
156     }
157 
158     @Override
159     public boolean mayNullCheckSkipConversion() {
160         return true;
161     }
162 }
163