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 }