1 /*
  2  * Copyright (c) 2020, 2023, 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 jdk.internal.foreign.abi;
 26 
 27 import java.lang.foreign.FunctionDescriptor;
 28 
 29 import java.lang.invoke.MethodType;
 30 import java.util.List;
 31 import java.util.stream.Stream;
 32 
 33 public class CallingSequence {
 34     private final boolean forUpcall;
 35     private final MethodType callerMethodType;
 36     private final MethodType calleeMethodType;
 37     private final FunctionDescriptor desc;
 38     private final boolean needsReturnBuffer;
 39     private final long returnBufferSize;
 40     private final long allocationSize;
 41 
 42     private final List<Binding> returnBindings;
 43     private final List<List<Binding>> argumentBindings;
 44 
 45     private final LinkerOptions linkerOptions;
 46 
 47     public CallingSequence(boolean forUpcall, MethodType callerMethodType, MethodType calleeMethodType, FunctionDescriptor desc,
 48                            boolean needsReturnBuffer, long returnBufferSize, long allocationSize,
 49                            List<List<Binding>> argumentBindings, List<Binding> returnBindings,
 50                            LinkerOptions linkerOptions) {
 51         this.forUpcall = forUpcall;
 52         this.callerMethodType = callerMethodType;
 53         this.calleeMethodType = calleeMethodType;
 54         this.desc = desc;
 55         this.needsReturnBuffer = needsReturnBuffer;
 56         this.returnBufferSize = returnBufferSize;
 57         this.allocationSize = allocationSize;
 58         this.returnBindings = returnBindings;
 59         this.argumentBindings = argumentBindings;
 60         this.linkerOptions = linkerOptions;
 61     }
 62 
 63     /**
 64      * An important distinction is that downcalls have 1 recipe per caller parameter and
 65      * each callee parameter corresponds to a VM_STORE. Upcalls have 1 recipe per callee parameter and
 66      * each caller parameter corresponds to a VM_LOAD.
 67      *
 68      * The VM_STOREs are then implemented by the leaf handle for downcalls, and vice versa, the wrapper
 69      * stub that wraps an upcall handle implements the VM_LOADS. In both cases the register values are
 70      * communicated through Java primitives.
 71      *
 72      * The 'argumentBindingsCount' below corresponds to the number of recipes, so it is the
 73      * caller parameter count for downcalls, and the callee parameter count for upcalls.
 74      *
 75      * @return the number of binding recipes in this calling sequence
 76      */
 77     public int argumentBindingsCount() {
 78         return argumentBindings.size();
 79     }
 80 
 81     public List<Binding> argumentBindings(int i) {
 82         return argumentBindings.get(i);
 83     }
 84 
 85     public Stream<Binding> argumentBindings() {
 86         return argumentBindings.stream().flatMap(List::stream);
 87     }
 88 
 89     public List<Binding> returnBindings() {
 90         return returnBindings;
 91     }
 92 
 93     public boolean forUpcall() {
 94         return forUpcall;
 95     }
 96 
 97     public boolean forDowncall() {
 98         return !forUpcall;
 99     }
100 
101     /**
102      * Returns the caller method type, which is the high-level method type
103      * for downcalls (the type of the downcall method handle)
104      * and the low-level method type (all primitives, VM facing) for upcalls.
105      *
106      * Note that for downcalls a single parameter in this method type corresponds
107      * to a single argument binding recipe in this calling sequence, but it may
108      * correspond to multiple parameters in the callee method type (for instance
109      * if a struct is split into multiple register values).
110      *
111      * @return the caller method type.
112      */
113     public MethodType callerMethodType() {
114         return callerMethodType;
115     }
116 
117     /**
118      * Returns the callee method type, which is the low-level method type
119      * (all primitives, VM facing) for downcalls and the high-level method type
120      * for upcalls (also the method type of the user-supplied target MH).
121      *
122      * Note that for upcalls a single parameter in this method type corresponds
123      * to a single argument binding recipe in this calling sequence, but it may
124      * correspond to multiple parameters in the caller method type (for instance
125      * if a struct is reconstructed from multiple register values).
126      *
127      * @return the callee method type.
128      */
129     public MethodType calleeMethodType() {
130         return calleeMethodType;
131     }
132 
133     public FunctionDescriptor functionDesc() {
134         return desc;
135     }
136 
137     /**
138      * Whether this calling sequence needs a return buffer.
139      *
140      * A return buffer is used to support functions that  return values
141      * in multiple registers, which is not possible to do just with Java primitives
142      * (we can only return 1 value in Java, meaning only 1 register value).
143      *
144      * To emulate these multi-register returns, we instead use a pre-allocated buffer
145      * (the return buffer) from/into which the return values are loaded/stored.
146      *
147      * For downcalls, we allocate the buffer in Java code, and pass the address down
148      * to the VM stub, which stores the returned register values into this buffer.
149      * VM_LOADs in the binding recipe for the return value then load the value from this buffer.
150      *
151      * For upcalls, the VM stub allocates a buffer (on the stack), and passes the address
152      * to the Java method handle it calls. VM_STOREs in the return binding recipe then
153      * store values into this buffer, after which the VM stub moves the values from the buffer
154      * into the right register.
155      *
156      * @return whether this calling sequence needs a return buffer.
157      */
158     public boolean needsReturnBuffer() {
159         return needsReturnBuffer;
160     }
161 
162     /**
163      * The size of the return buffer, if one is needed.
164      *
165      * @see #needsReturnBuffer
166      *
167      * @return the return buffer size
168      */
169     public long returnBufferSize() {
170         return returnBufferSize;
171     }
172 
173     /**
174      * The amount of bytes this calling sequence needs to allocate during an invocation.
175      *
176      * Includes the return buffer size as well as space for any buffer copies in the recipes.
177      *
178      * @return the allocation size
179      */
180     public long allocationSize() {
181         return allocationSize;
182     }
183 
184     public boolean hasReturnBindings() {
185         return !returnBindings.isEmpty();
186     }
187 
188     public int capturedStateMask() {
189         return linkerOptions.capturedCallState()
190                 .mapToInt(CapturableState::mask)
191                 .reduce(0, (a, b) -> a | b);
192     }
193 
194     public boolean needsTransition() {
195         return !linkerOptions.isTrivial();
196     }
197 
198     public int numLeadingParams() {
199         return 2 + (linkerOptions.hasCapturedCallState() ? 1 : 0); // 2 for addr, allocator
200     }
201 
202     public String asString() {
203         StringBuilder sb = new StringBuilder();
204 
205         sb.append("CallingSequence: {\n");
206         sb.append("  callerMethodType: ").append(callerMethodType);
207         sb.append("  calleeMethodType: ").append(calleeMethodType);
208         sb.append("  FunctionDescriptor: ").append(desc);
209         sb.append("  Argument Bindings:\n");
210         for (int i = 0; i < argumentBindingsCount(); i++) {
211             sb.append("    ").append(i).append(": ").append(argumentBindings.get(i)).append("\n");
212         }
213         if (!returnBindings.isEmpty()) {
214             sb.append("    ").append("Return: ").append(returnBindings).append("\n");
215         }
216         sb.append("}\n");
217 
218         return sb.toString();
219     }
220 }