1 /*
  2  * Copyright (c) 2024, 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 import jdk.incubator.code.*;
 25 import jdk.incubator.code.dialect.java.JavaOp;
 26 
 27 import java.util.HashMap;
 28 import java.util.function.BiConsumer;
 29 
 30 import static jdk.incubator.code.dialect.java.JavaOp.sub;
 31 import static jdk.incubator.code.analysis.Patterns.*;
 32 
 33 public final class ExpressionElimination {
 34     private ExpressionElimination() {
 35     }
 36 
 37     static OpPattern negP(Pattern operand) {
 38         return opP(JavaOp.NegOp.class, operand);
 39     }
 40 
 41     static OpPattern addP(Pattern lhs, Pattern rhs) {
 42         return opP(JavaOp.AddOp.class, lhs, rhs);
 43     }
 44 
 45     static OpPattern mulP(Pattern lhs, Pattern rhs) {
 46         return opP(JavaOp.MulOp.class, lhs, rhs);
 47     }
 48 
 49     public static <T extends Op> T eliminate(T f) {
 50         // Note expression elimination and other forms of analysis is simplified if first of all expressions
 51         // are normalized e.g. when they have an operand that is a constant expression
 52         // and the operation is associative such as add(0, x) -> add(x, 0)
 53 
 54         var actions = multiMatch(new HashMap<Op.Result, BiConsumer<Block.Builder, Op>>(), f)
 55                 .pattern(mulP(_P(), valueP(constantP(0.0d))))
 56                 .pattern(mulP(valueP(constantP(0.0d)), _P()))
 57                 .pattern(addP(valueP(), constantP(0.0d)))
 58                 .pattern(addP(constantP(0.0d), valueP()))
 59                 .pattern(mulP(constantP(1.0d), valueP()))
 60                 .pattern(mulP(valueP(), constantP(1.0d)))
 61                 .target((ms, as) -> {
 62                     Value a = ms.matchedOperands().get(0);
 63                     as.put(ms.op().result(), (block, op) -> {
 64                         CopyContext cc = block.context();
 65                         cc.mapValue(ms.op().result(), cc.getValue(a));
 66                     });
 67                     return as;
 68                 })
 69                 // add(neg(x), y) -> sub(y, x)
 70                 .pattern(addP(negP(valueP()), valueP()))
 71                 .target((ms, as) -> {
 72                     Value x = ms.matchedOperands().get(0);
 73                     Value y = ms.matchedOperands().get(1);
 74 
 75                     as.put(ms.op().result(), (block, op) -> {
 76                         CopyContext cc = block.context();
 77                         Op.Result r = block.op(sub(cc.getValue(y), cc.getValue(x)));
 78                         cc.mapValue(ms.op().result(), r);
 79                     });
 80                     return as;
 81                 })
 82                 .matchThenApply();
 83 
 84         // Eliminate
 85         Op ef = f.transform(CopyContext.create(), (block, op) -> {
 86             BiConsumer<Block.Builder, Op> a = actions.get(op.result());
 87             if (a != null) {
 88                 a.accept(block, op);
 89             } else {
 90                 block.op(op);
 91             }
 92             return block;
 93         });
 94 
 95         // Remove dead ops
 96         ef = ef.transform(CopyContext.create(), (block, op) -> {
 97             if (!(op instanceof Op.Pure) ||
 98                     !op.result().uses().isEmpty()) {
 99                 block.op(op);
100             }
101             return block;
102         });
103 
104         @SuppressWarnings("unchecked")
105         T t = (T) ef;
106         return t;
107     }
108 }