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