1 /*
2 * Copyright (c) 2025, 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. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25 package oracle.code.samples;
26
27 import jdk.incubator.code.Block;
28 import jdk.incubator.code.Op;
29 import jdk.incubator.code.Value;
30 import jdk.incubator.code.bytecode.BytecodeGenerator;
31 import jdk.incubator.code.dialect.core.CoreOp;
32 import jdk.incubator.code.dialect.core.CoreType;
33 import jdk.incubator.code.dialect.java.JavaOp;
34 import jdk.incubator.code.dialect.java.JavaOp.InvokeOp.InvokeKind;
35 import jdk.incubator.code.dialect.java.JavaType;
36 import jdk.incubator.code.dialect.java.MethodRef;
37 import jdk.incubator.code.interpreter.Interpreter;
38
39 import java.lang.invoke.MethodHandle;
40 import java.lang.invoke.MethodHandles;
41 import java.util.ArrayList;
42 import java.util.List;
43
44 /**
45 * Demonstrates how to dynamically build a new function using the code reflection API.
46 * <p>
47 * This example creates an <code>rsqrt</code> function, which computes the inverse of a square root.
48 * The function takes one argument of type <code>double</code> and returns a <code>double</code>.
49 * The implementation uses {@link Math#sqrt(double)} for the square root calculation.
50 * </p>
51 *
52 * <p>
53 * In this example, you will learn how to:
54 * <ol>
55 * <li>Create a function dynamically</li>
56 * <li>Append new Op nodes in the builder</li>
57 * <li>Compose operations in the code tree</li>
58 * <li>Create nodes to call static methods</li>
59 * <li>Evaluate the composed method in the interpreter</li>
60 * </ol>
61 * </p>
62 *
63 * <p>
64 * After building the code model for the function, it will be executed both in the code reflection interpreter and in the bytecode interpreter.
65 * </p>
66 *
67 * <p>
68 * <b>How to run:</b><br>
69 * <code>
70 * java --add-modules jdk.incubator.code -cp target/crsamples-1.0-SNAPSHOT.jar oracle.code.samples.DynamicFunctionBuild
71 * </code>
72 * </p>
73 */
74 public class DynamicFunctionBuild {
75
76 static void main(String[] args) {
77
78 CoreOp.FuncOp myFunction = CoreOp.func("rsqrt",
79 // Define the signature of our new method
80 // The function will be called `rsqrt`:
81 // rsqrt(double):double
82 CoreType.functionType(JavaType.DOUBLE, JavaType.DOUBLE))
83 .body(builder -> {
84
85 // The function to build is as follows:
86 // double: function rsqrt(double input)
87 // ret double: 1 / Math.sqrt(input)
88 //
89
90 // Obtain the first parameter
91 Block.Parameter inputParameter = builder.parameters().get(0);
92
93 // Create an op to represent the constant 1
94 CoreOp.ConstantOp constant1 = CoreOp.constant(JavaType.DOUBLE, 1.0);
95 // Add the Op into the builder
96 Op.Result constantResult = builder.op(constant1);
97
98 // Create a MethodRef to point to Math.sqrt
99 MethodRef sqrtMethodRef = MethodRef.method(Math.class, "sqrt", double.class, double.class);
100
101 // Prepare the list of arguments for the Math.sqrt invoke
102 List<Value> arguments = new ArrayList<>();
103 arguments.add(inputParameter);
104
105 // Create an invoke Op
106 JavaOp.InvokeOp invokeMathOp = JavaOp.invoke(InvokeKind.STATIC, false, JavaType.DOUBLE, sqrtMethodRef, arguments);
107
108 // Add the invoke op into the builder
109 Op.Result invokeResult = builder.op(invokeMathOp);
110
111 // Create a division node and add it to the code builder
112 JavaOp.BinaryOp divOp = JavaOp.div(constantResult, invokeResult);
113 Op.Result divResult = builder.op(divOp);
114
115 // Finally, add a return and add it to the code builder
116 CoreOp.ReturnOp retOp = CoreOp.return_(divResult);
117 builder.op(retOp);
118 });
119
120 // Print the code model for the function we have just created
121 System.out.println(myFunction.toText());
122
123 // Run the new function in the Code Reflection's interpreter
124 Object result = Interpreter.invoke(MethodHandles.lookup(), myFunction, 100);
125 System.out.println("Evaluation in the Code Reflection's Interpreter: 1/sqrt(100) = " + result);
126
127 // Run in the Java Bytecode interpreter
128 MethodHandle generate = BytecodeGenerator.generate(MethodHandles.lookup(), myFunction);
129 try {
130 Object resultFromBCInterpreter = generate.invoke(100);
131 System.out.println("Evaluation in the Java Bytecode Interpreter: 1/sqrt(100) = " + resultFromBCInterpreter);
132 } catch (Throwable e) {
133 throw new RuntimeException(e);
134 }
135 }
136 }