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 --enable-preview -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 }