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 }