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