1 /* 2 * Copyright (c) 2020, 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 */ 26 package jdk.internal.foreign.abi.x64.windows; 27 28 import jdk.incubator.foreign.CSupport; 29 import jdk.incubator.foreign.CSupport.VaList; 30 import jdk.incubator.foreign.MemoryAddress; 31 import jdk.incubator.foreign.MemoryHandles; 32 import jdk.incubator.foreign.MemoryLayout; 33 import jdk.incubator.foreign.MemorySegment; 34 import jdk.internal.foreign.abi.SharedUtils; 35 import jdk.internal.foreign.abi.SharedUtils.SimpleVaArg; 36 37 import java.lang.invoke.VarHandle; 38 import java.util.ArrayList; 39 import java.util.List; 40 41 import static jdk.incubator.foreign.CSupport.Win64.C_POINTER; 42 import static jdk.incubator.foreign.MemorySegment.CLOSE; 43 import static jdk.incubator.foreign.MemorySegment.READ; 44 45 // see vadefs.h (VC header) 46 // 47 // in short 48 // -> va_list is just a pointer to a buffer with 64 bit entries. 49 // -> non-power-of-two-sized, or larger than 64 bit types passed by reference. 50 // -> other types passed in 64 bit slots by normal function calling convention. 51 // 52 // X64 va_arg impl: 53 // 54 // typedef char* va_list; 55 // 56 // #define __crt_va_arg(ap, t) \ 57 // ((sizeof(t) > sizeof(__int64) || (sizeof(t) & (sizeof(t) - 1)) != 0) \ 58 // ? **(t**)((ap += sizeof(__int64)) - sizeof(__int64)) \ 59 // : *(t* )((ap += sizeof(__int64)) - sizeof(__int64))) 60 // 61 class WinVaList implements VaList { 62 public static final Class<?> CARRIER = MemoryAddress.class; 63 private static final long VA_SLOT_SIZE_BYTES = 8; 64 private static final VarHandle VH_address = MemoryHandles.asAddressVarHandle(C_POINTER.varHandle(long.class)); 65 66 private static final VaList EMPTY = new SharedUtils.EmptyVaList(MemoryAddress.NULL); 67 68 private final MemorySegment segment; 69 private MemoryAddress ptr; 70 private final List<MemorySegment> copies; 71 72 WinVaList(MemorySegment segment) { 73 this(segment, new ArrayList<>()); 74 } 75 76 WinVaList(MemorySegment segment, List<MemorySegment> copies) { 77 this.segment = segment; 78 this.ptr = segment.baseAddress(); 79 this.copies = copies; 80 } 81 82 public static final VaList empty() { 83 return EMPTY; 84 } 85 86 @Override 87 public int vargAsInt(MemoryLayout layout) { 88 return (int) read(int.class, layout); 89 } 90 91 @Override 92 public long vargAsLong(MemoryLayout layout) { 93 return (long) read(long.class, layout); 94 } 95 96 @Override 97 public double vargAsDouble(MemoryLayout layout) { 98 return (double) read(double.class, layout); 99 } 100 101 @Override 102 public MemoryAddress vargAsAddress(MemoryLayout layout) { 103 return (MemoryAddress) read(MemoryAddress.class, layout); 104 } 105 106 @Override 107 public MemorySegment vargAsSegment(MemoryLayout layout) { 108 return (MemorySegment) read(MemorySegment.class, layout); 109 } 110 111 private Object read(Class<?> carrier, MemoryLayout layout) { 112 SharedUtils.checkCompatibleType(carrier, layout, Windowsx64Linker.ADDRESS_SIZE); 113 Object res; 114 if (carrier == MemorySegment.class) { 115 TypeClass typeClass = TypeClass.typeClassFor(layout); 116 res = switch (typeClass) { 117 case STRUCT_REFERENCE -> { 118 MemoryAddress structAddr = (MemoryAddress) VH_address.get(ptr); 119 try (MemorySegment struct = MemorySegment.ofNativeRestricted(structAddr, layout.byteSize(), 120 segment.ownerThread(), null, null)) { 121 MemorySegment seg = MemorySegment.allocateNative(layout.byteSize()); 122 seg.copyFrom(struct); 123 yield seg; 124 } 125 } 126 case STRUCT_REGISTER -> { 127 MemorySegment struct = MemorySegment.allocateNative(layout); 128 struct.copyFrom(segment.asSlice(ptr.segmentOffset(), layout.byteSize())); 129 yield struct; 130 } 131 default -> throw new IllegalStateException("Unexpected TypeClass: " + typeClass); 132 }; 133 } else { 134 VarHandle reader = SharedUtils.vhPrimitiveOrAddress(carrier, layout); 135 res = reader.get(ptr); 136 } 137 ptr = ptr.addOffset(VA_SLOT_SIZE_BYTES); 138 return res; 139 } 140 141 @Override 142 public void skip(MemoryLayout... layouts) { 143 ptr = ptr.addOffset(layouts.length * VA_SLOT_SIZE_BYTES); 144 } 145 146 static WinVaList ofAddress(MemoryAddress addr) { 147 return new WinVaList(MemorySegment.ofNativeRestricted(addr, Long.MAX_VALUE, Thread.currentThread(), null, null)); 148 } 149 150 static Builder builder() { 151 return new Builder(); 152 } 153 154 @Override 155 public void close() { 156 segment.close(); 157 copies.forEach(MemorySegment::close); 158 } 159 160 @Override 161 public VaList copy() { 162 return WinVaList.ofAddress(ptr); 163 } 164 165 @Override 166 public MemoryAddress address() { 167 return ptr; 168 } 169 170 @Override 171 public boolean isAlive() { 172 return segment.isAlive(); 173 } 174 175 static class Builder implements VaList.Builder { 176 177 private final List<SimpleVaArg> args = new ArrayList<>(); 178 179 private Builder arg(Class<?> carrier, MemoryLayout layout, Object value) { 180 SharedUtils.checkCompatibleType(carrier, layout, Windowsx64Linker.ADDRESS_SIZE); 181 args.add(new SimpleVaArg(carrier, layout, value)); 182 return this; 183 } 184 185 @Override 186 public Builder vargFromInt(MemoryLayout layout, int value) { 187 return arg(int.class, layout, value); 188 } 189 190 @Override 191 public Builder vargFromLong(MemoryLayout layout, long value) { 192 return arg(long.class, layout, value); 193 } 194 195 @Override 196 public Builder vargFromDouble(MemoryLayout layout, double value) { 197 return arg(double.class, layout, value); 198 } 199 200 @Override 201 public Builder vargFromAddress(MemoryLayout layout, MemoryAddress value) { 202 return arg(MemoryAddress.class, layout, value); 203 } 204 205 @Override 206 public Builder vargFromSegment(MemoryLayout layout, MemorySegment value) { 207 return arg(MemorySegment.class, layout, value); 208 } 209 210 public VaList build() { 211 if (args.isEmpty()) { 212 return EMPTY; 213 } 214 MemorySegment ms = MemorySegment.allocateNative(VA_SLOT_SIZE_BYTES * args.size()); 215 List<MemorySegment> copies = new ArrayList<>(); 216 217 MemoryAddress addr = ms.baseAddress(); 218 for (SimpleVaArg arg : args) { 219 if (arg.carrier == MemorySegment.class) { 220 MemorySegment msArg = ((MemorySegment) arg.value); 221 TypeClass typeClass = TypeClass.typeClassFor(arg.layout); 222 switch (typeClass) { 223 case STRUCT_REFERENCE -> { 224 MemorySegment copy = MemorySegment.allocateNative(arg.layout); 225 copy.copyFrom(msArg); // by-value 226 copies.add(copy); 227 VH_address.set(addr, copy.baseAddress()); 228 } 229 case STRUCT_REGISTER -> { 230 MemorySegment slice = ms.asSlice(addr.segmentOffset(), VA_SLOT_SIZE_BYTES); 231 slice.copyFrom(msArg); 232 } 233 default -> throw new IllegalStateException("Unexpected TypeClass: " + typeClass); 234 } 235 } else { 236 VarHandle writer = arg.varHandle(); 237 writer.set(addr, arg.value); 238 } 239 addr = addr.addOffset(VA_SLOT_SIZE_BYTES); 240 } 241 242 return new WinVaList(ms.withAccessModes(CLOSE | READ), copies); 243 } 244 } 245 }