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