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