001/*
002 * Copyright (c) 2015, 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.replacements;
024
025import static com.oracle.graal.nodes.ConstantNode.*;
026import static jdk.internal.jvmci.meta.LocationIdentity.*;
027
028import java.lang.reflect.*;
029
030import jdk.internal.jvmci.common.*;
031import jdk.internal.jvmci.meta.*;
032
033import com.oracle.graal.api.replacements.*;
034import com.oracle.graal.compiler.common.calc.*;
035import com.oracle.graal.compiler.common.type.*;
036import com.oracle.graal.graphbuilderconf.*;
037import com.oracle.graal.nodes.*;
038import com.oracle.graal.nodes.calc.*;
039import com.oracle.graal.nodes.extended.*;
040import com.oracle.graal.nodes.java.*;
041import com.oracle.graal.nodes.memory.HeapAccess.BarrierType;
042import com.oracle.graal.nodes.memory.address.*;
043import com.oracle.graal.nodes.type.*;
044import com.oracle.graal.word.*;
045import com.oracle.graal.word.Word.Opcode;
046import com.oracle.graal.word.Word.Operation;
047import com.oracle.graal.word.nodes.*;
048
049/**
050 * A plugin for calls to {@linkplain Operation word operations}, as well as all other nodes that
051 * need special handling for {@link Word} types.
052 */
053public class WordOperationPlugin implements NodePlugin, ParameterPlugin, InlineInvokePlugin {
054    protected final WordTypes wordTypes;
055    protected final Kind wordKind;
056    protected final SnippetReflectionProvider snippetReflection;
057
058    public WordOperationPlugin(SnippetReflectionProvider snippetReflection, WordTypes wordTypes) {
059        this.snippetReflection = snippetReflection;
060        this.wordTypes = wordTypes;
061        this.wordKind = wordTypes.getWordKind();
062    }
063
064    @Override
065    public boolean canChangeStackKind(GraphBuilderContext b) {
066        return true;
067    }
068
069    /**
070     * Processes a call to a method if it is annotated with {@link Operation} by adding nodes to the
071     * graph being built that implement the denoted operation.
072     *
073     * @return {@code true} iff {@code method} is annotated with {@link Operation} (and was thus
074     *         processed by this method)
075     */
076    @Override
077    public boolean handleInvoke(GraphBuilderContext b, ResolvedJavaMethod method, ValueNode[] args) {
078        if (!wordTypes.isWordOperation(method)) {
079            return false;
080        }
081        processWordOperation(b, args, wordTypes.getWordOperation(method, b.getMethod().getDeclaringClass()));
082        return true;
083    }
084
085    @Override
086    public FloatingNode interceptParameter(GraphBuilderContext b, int index, Stamp stamp) {
087        ResolvedJavaType type = StampTool.typeOrNull(stamp);
088        if (wordTypes.isWord(type)) {
089            return new ParameterNode(index, wordTypes.getWordStamp(type));
090        }
091        return null;
092    }
093
094    @Override
095    public void notifyNotInlined(GraphBuilderContext b, ResolvedJavaMethod method, Invoke invoke) {
096        if (wordTypes.isWord(invoke.asNode())) {
097            invoke.asNode().setStamp(wordTypes.getWordStamp(StampTool.typeOrNull(invoke.asNode())));
098        }
099    }
100
101    @Override
102    public boolean handleLoadField(GraphBuilderContext b, ValueNode receiver, ResolvedJavaField field) {
103        if (field.getType() instanceof ResolvedJavaType && wordTypes.isWord((ResolvedJavaType) field.getType())) {
104            LoadFieldNode loadFieldNode = new LoadFieldNode(receiver, field);
105            loadFieldNode.setStamp(wordTypes.getWordStamp((ResolvedJavaType) field.getType()));
106            b.addPush(field.getKind(), loadFieldNode);
107            return true;
108        }
109        return false;
110    }
111
112    @Override
113    public boolean handleLoadStaticField(GraphBuilderContext b, ResolvedJavaField staticField) {
114        return handleLoadField(b, null, staticField);
115    }
116
117    @Override
118    public boolean handleLoadIndexed(GraphBuilderContext b, ValueNode array, ValueNode index, Kind elementKind) {
119        ResolvedJavaType arrayType = StampTool.typeOrNull(array);
120        /*
121         * There are cases where the array does not have a known type yet, i.e., the type is null.
122         * In that case we assume it is not a word type.
123         */
124        if (arrayType != null && wordTypes.isWord(arrayType.getComponentType())) {
125            assert elementKind == Kind.Object;
126            b.addPush(elementKind, createLoadIndexedNode(array, index));
127            return true;
128        }
129        return false;
130    }
131
132    protected LoadIndexedNode createLoadIndexedNode(ValueNode array, ValueNode index) {
133        return new LoadIndexedNode(array, index, wordTypes.getWordKind());
134    }
135
136    @Override
137    public boolean handleStoreIndexed(GraphBuilderContext b, ValueNode array, ValueNode index, Kind elementKind, ValueNode value) {
138        ResolvedJavaType arrayType = StampTool.typeOrNull(array);
139        if (arrayType != null && wordTypes.isWord(arrayType.getComponentType())) {
140            assert elementKind == Kind.Object;
141            if (value.getKind() != wordTypes.getWordKind()) {
142                throw b.bailout("Cannot store a non-word value into a word array: " + arrayType.toJavaName(true));
143            }
144            b.add(createStoreIndexedNode(array, index, value));
145            return true;
146        }
147        if (elementKind == Kind.Object && value.getKind() == wordTypes.getWordKind()) {
148            throw b.bailout("Cannot store a word value into a non-word array: " + arrayType.toJavaName(true));
149        }
150        return false;
151    }
152
153    protected StoreIndexedNode createStoreIndexedNode(ValueNode array, ValueNode index, ValueNode value) {
154        return new StoreIndexedNode(array, index, wordTypes.getWordKind(), value);
155    }
156
157    @Override
158    public boolean handleCheckCast(GraphBuilderContext b, ValueNode object, ResolvedJavaType type, JavaTypeProfile profile) {
159        if (!wordTypes.isWord(type)) {
160            if (object.getKind() != Kind.Object) {
161                throw b.bailout("Cannot cast a word value to a non-word type: " + type.toJavaName(true));
162            }
163            return false;
164        }
165
166        if (object.getKind() != wordTypes.getWordKind()) {
167            throw b.bailout("Cannot cast a non-word value to a word type: " + type.toJavaName(true));
168        }
169        b.push(Kind.Object, object);
170        return true;
171    }
172
173    @Override
174    public boolean handleInstanceOf(GraphBuilderContext b, ValueNode object, ResolvedJavaType type, JavaTypeProfile profile) {
175        if (wordTypes.isWord(type)) {
176            throw b.bailout("Cannot use instanceof for word a type: " + type.toJavaName(true));
177        } else if (object.getKind() != Kind.Object) {
178            throw b.bailout("Cannot use instanceof on a word value: " + type.toJavaName(true));
179        }
180        return false;
181    }
182
183    protected void processWordOperation(GraphBuilderContext b, ValueNode[] args, ResolvedJavaMethod wordMethod) throws JVMCIError {
184        Operation operation = wordMethod.getAnnotation(Word.Operation.class);
185        Kind returnKind = wordMethod.getSignature().getReturnKind();
186        switch (operation.opcode()) {
187            case NODE_CLASS:
188                assert args.length == 2;
189                ValueNode left = args[0];
190                ValueNode right = operation.rightOperandIsInt() ? toUnsigned(b, args[1], Kind.Int) : fromSigned(b, args[1]);
191
192                b.addPush(returnKind, createBinaryNodeInstance(operation.node(), left, right));
193                break;
194
195            case COMPARISON:
196                assert args.length == 2;
197                b.push(returnKind, comparisonOp(b, operation.condition(), args[0], fromSigned(b, args[1])));
198                break;
199
200            case NOT:
201                assert args.length == 1;
202                b.addPush(returnKind, new XorNode(args[0], b.add(forIntegerKind(wordKind, -1))));
203                break;
204
205            case READ_POINTER:
206            case READ_OBJECT:
207            case READ_BARRIERED: {
208                assert args.length == 2 || args.length == 3;
209                Kind readKind = wordTypes.asKind(wordMethod.getSignature().getReturnType(wordMethod.getDeclaringClass()));
210                AddressNode address = makeAddress(b, args[0], args[1]);
211                LocationIdentity location;
212                if (args.length == 2) {
213                    location = any();
214                } else {
215                    assert args[2].isConstant();
216                    location = snippetReflection.asObject(LocationIdentity.class, args[2].asJavaConstant());
217                }
218                b.push(returnKind, readOp(b, readKind, address, location, operation.opcode()));
219                break;
220            }
221            case READ_HEAP: {
222                assert args.length == 3;
223                Kind readKind = wordTypes.asKind(wordMethod.getSignature().getReturnType(wordMethod.getDeclaringClass()));
224                AddressNode address = makeAddress(b, args[0], args[1]);
225                BarrierType barrierType = snippetReflection.asObject(BarrierType.class, args[2].asJavaConstant());
226                b.push(returnKind, readOp(b, readKind, address, any(), barrierType, true));
227                break;
228            }
229            case WRITE_POINTER:
230            case WRITE_OBJECT:
231            case WRITE_BARRIERED:
232            case INITIALIZE: {
233                assert args.length == 3 || args.length == 4;
234                Kind writeKind = wordTypes.asKind(wordMethod.getSignature().getParameterType(wordMethod.isStatic() ? 2 : 1, wordMethod.getDeclaringClass()));
235                AddressNode address = makeAddress(b, args[0], args[1]);
236                LocationIdentity location;
237                if (args.length == 3) {
238                    location = any();
239                } else {
240                    assert args[3].isConstant();
241                    location = snippetReflection.asObject(LocationIdentity.class, args[3].asJavaConstant());
242                }
243                writeOp(b, writeKind, address, location, args[2], operation.opcode());
244                break;
245            }
246            case ZERO:
247                assert args.length == 0;
248                b.addPush(returnKind, forIntegerKind(wordKind, 0L));
249                break;
250
251            case FROM_UNSIGNED:
252                assert args.length == 1;
253                b.push(returnKind, fromUnsigned(b, args[0]));
254                break;
255
256            case FROM_SIGNED:
257                assert args.length == 1;
258                b.push(returnKind, fromSigned(b, args[0]));
259                break;
260
261            case TO_RAW_VALUE:
262                assert args.length == 1;
263                b.push(returnKind, toUnsigned(b, args[0], Kind.Long));
264                break;
265
266            case FROM_WORDBASE:
267                assert args.length == 1;
268                b.push(returnKind, args[0]);
269                break;
270
271            case FROM_OBJECT:
272                assert args.length == 1;
273                WordCastNode objectToWord = b.add(WordCastNode.objectToWord(args[0], wordKind));
274                b.push(returnKind, objectToWord);
275                break;
276
277            case FROM_ADDRESS:
278                assert args.length == 1;
279                WordCastNode addressToWord = b.add(WordCastNode.addressToWord(args[0], wordKind));
280                b.push(returnKind, addressToWord);
281                break;
282
283            case TO_OBJECT:
284                assert args.length == 1;
285                WordCastNode wordToObject = b.add(WordCastNode.wordToObject(args[0], wordKind));
286                b.push(returnKind, wordToObject);
287                break;
288
289            default:
290                throw new JVMCIError("Unknown opcode: %s", operation.opcode());
291        }
292    }
293
294    /**
295     * Create an instance of a binary node which is used to lower {@link Word} operations. This
296     * method is called for all {@link Word} operations which are annotated with @Operation(node =
297     * ...) and encapsulates the reflective allocation of the node.
298     */
299    private static ValueNode createBinaryNodeInstance(Class<? extends ValueNode> nodeClass, ValueNode left, ValueNode right) {
300        try {
301            Constructor<?> cons = nodeClass.getDeclaredConstructor(ValueNode.class, ValueNode.class);
302            return (ValueNode) cons.newInstance(left, right);
303        } catch (Throwable ex) {
304            throw new JVMCIError(ex).addContext(nodeClass.getName());
305        }
306    }
307
308    private ValueNode comparisonOp(GraphBuilderContext graph, Condition condition, ValueNode left, ValueNode right) {
309        assert left.getKind() == wordKind && right.getKind() == wordKind;
310
311        // mirroring gets the condition into canonical form
312        boolean mirror = condition.canonicalMirror();
313
314        ValueNode a = mirror ? right : left;
315        ValueNode b = mirror ? left : right;
316
317        CompareNode comparison;
318        if (condition == Condition.EQ || condition == Condition.NE) {
319            comparison = new IntegerEqualsNode(a, b);
320        } else if (condition.isUnsigned()) {
321            comparison = new IntegerBelowNode(a, b);
322        } else {
323            comparison = new IntegerLessThanNode(a, b);
324        }
325
326        ConstantNode trueValue = graph.add(forInt(1));
327        ConstantNode falseValue = graph.add(forInt(0));
328
329        if (condition.canonicalNegate()) {
330            ConstantNode temp = trueValue;
331            trueValue = falseValue;
332            falseValue = temp;
333        }
334        ConditionalNode materialize = graph.add(new ConditionalNode(graph.add(comparison), trueValue, falseValue));
335        return materialize;
336    }
337
338    protected ValueNode readOp(GraphBuilderContext b, Kind readKind, AddressNode address, LocationIdentity location, Opcode op) {
339        assert op == Opcode.READ_POINTER || op == Opcode.READ_OBJECT || op == Opcode.READ_BARRIERED;
340        final BarrierType barrier = (op == Opcode.READ_BARRIERED ? BarrierType.PRECISE : BarrierType.NONE);
341        final boolean compressible = (op == Opcode.READ_OBJECT || op == Opcode.READ_BARRIERED);
342
343        return readOp(b, readKind, address, location, barrier, compressible);
344    }
345
346    public static ValueNode readOp(GraphBuilderContext b, Kind readKind, AddressNode address, LocationIdentity location, BarrierType barrierType, boolean compressible) {
347        /*
348         * A JavaReadNode lowered to a ReadNode that will not float. This means it cannot float
349         * above an explicit zero check on its base address or any other test that ensures the read
350         * is safe.
351         */
352        JavaReadNode read = b.add(new JavaReadNode(readKind, address, location, barrierType, compressible));
353        return read;
354    }
355
356    protected void writeOp(GraphBuilderContext b, Kind writeKind, AddressNode address, LocationIdentity location, ValueNode value, Opcode op) {
357        assert op == Opcode.WRITE_POINTER || op == Opcode.WRITE_OBJECT || op == Opcode.WRITE_BARRIERED || op == Opcode.INITIALIZE;
358        final BarrierType barrier = (op == Opcode.WRITE_BARRIERED ? BarrierType.PRECISE : BarrierType.NONE);
359        final boolean compressible = (op == Opcode.WRITE_OBJECT || op == Opcode.WRITE_BARRIERED);
360        final boolean initialize = (op == Opcode.INITIALIZE);
361        b.add(new JavaWriteNode(writeKind, address, location, value, barrier, compressible, initialize));
362    }
363
364    public AddressNode makeAddress(GraphBuilderContext b, ValueNode base, ValueNode offset) {
365        return b.add(new OffsetAddressNode(base, fromSigned(b, offset)));
366    }
367
368    public ValueNode fromUnsigned(GraphBuilderContext b, ValueNode value) {
369        return convert(b, value, wordKind, true);
370    }
371
372    public ValueNode fromSigned(GraphBuilderContext b, ValueNode value) {
373        return convert(b, value, wordKind, false);
374    }
375
376    public ValueNode toUnsigned(GraphBuilderContext b, ValueNode value, Kind toKind) {
377        return convert(b, value, toKind, true);
378    }
379
380    public ValueNode convert(GraphBuilderContext b, ValueNode value, Kind toKind, boolean unsigned) {
381        if (value.getKind() == toKind) {
382            return value;
383        }
384
385        if (toKind == Kind.Int) {
386            assert value.getKind() == Kind.Long;
387            return b.add(new NarrowNode(value, 32));
388        } else {
389            assert toKind == Kind.Long;
390            assert value.getKind().getStackKind() == Kind.Int;
391            if (unsigned) {
392                return b.add(new ZeroExtendNode(value, 64));
393            } else {
394                return b.add(new SignExtendNode(value, 64));
395            }
396        }
397    }
398
399    public WordTypes getWordTypes() {
400        return wordTypes;
401    }
402}