1 /*
   2  * Copyright (c) 2019, 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.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 package jdk.internal.foreign.abi.x64.windows;
  24 
  25 import jdk.internal.foreign.Util;
  26 import jdk.internal.foreign.abi.*;
  27 import jdk.internal.foreign.abi.x64.ArgumentClass;
  28 import jdk.internal.foreign.abi.x64.SharedUtils;
  29 
  30 import java.foreign.layout.*;
  31 import java.util.ArrayList;
  32 import java.util.List;
  33 import java.util.function.BiConsumer;
  34 
  35 import static sun.security.action.GetBooleanAction.privilegedGetProperty;
  36 
  37 class CallingSequenceBuilderImpl extends CallingSequenceBuilder {
  38 
  39     private static final SharedUtils.StorageDebugHelper storageDbgHelper = new SharedUtils.StorageDebugHelper(
  40             new String[] { "rcx", "rdx", "r8", "r9" },
  41             new String[] { "rax" },
  42             new String[0],
  43             Windowsx64ABI.MAX_VECTOR_ARGUMENT_REGISTERS,
  44             Windowsx64ABI.MAX_VECTOR_RETURN_REGISTERS
  45     );
  46 
  47     private static final boolean DEBUG =
  48             privilegedGetProperty("jdk.internal.foreign.abi.windows.x64.DEBUG");
  49 
  50     public CallingSequenceBuilderImpl(Layout layout) {
  51         this(layout, new StorageCalculator(false), new StorageCalculator(true));
  52     }
  53 
  54     private CallingSequenceBuilderImpl(Layout layout, StorageCalculator retCalculator, StorageCalculator argCalculator) {
  55         super(layout,
  56                 (a, c) -> retCalculator.addBindings(a, c, false),
  57                 (a, c) -> argCalculator.addBindings(a, c, false),
  58                 (a, c) -> argCalculator.addBindings(a, c, true));
  59     }
  60 
  61     @Override
  62     protected ArgumentInfo makeArgument(Layout layout, int pos, String name) {
  63         return new ArgumentInfo(layout, pos, name);
  64     }
  65 
  66     static class ArgumentInfo extends Argument {
  67         private final List<ArgumentClass> classes;
  68         
  69         ArgumentInfo(Layout layout, int argumentIndex, String debugName) {
  70             super(layout, argumentIndex, debugName);
  71             this.classes = classifyType(layout, argumentIndex == -1);
  72         }
  73 
  74         public int getRegs() {
  75             return (int)classes.stream()
  76                     .filter(this::isRegisterClass)
  77                     .count();
  78         }
  79 
  80         @Override
  81         public boolean inMemory() {
  82             return classes.stream().allMatch(this::isMemoryClass);
  83         }
  84 
  85         private boolean isMemoryClass(ArgumentClass cl) {
  86             return cl == ArgumentClass.MEMORY ||
  87                     cl == ArgumentClass.X87 ||
  88                     cl == ArgumentClass.X87UP;
  89         }
  90 
  91         private boolean isRegisterClass(ArgumentClass cl) {
  92             return cl == ArgumentClass.INTEGER ||
  93                     cl == ArgumentClass.SSE;
  94         }
  95 
  96         public List<ArgumentClass> getClasses() {
  97             return classes;
  98         }
  99     }
 100 
 101     static List<ArgumentClass> classifyValueType(Value type) {
 102         ArrayList<ArgumentClass> classes = new ArrayList<>();
 103 
 104         switch (type.kind()) {
 105             case INTEGRAL_SIGNED: case INTEGRAL_UNSIGNED:
 106                 classes.add(ArgumentClass.INTEGER);
 107                 // int128
 108                 long left = (type.bitsSize() / 8) - 8;
 109                 while (left > 0) {
 110                     classes.add(ArgumentClass.INTEGER);
 111                     left -= 8;
 112                 }
 113                 return classes;
 114             case FLOATING_POINT:
 115                 if ((type.bitsSize() / 8) > 8) {
 116                     classes.add(ArgumentClass.X87);
 117                     classes.add(ArgumentClass.X87UP);
 118                     return classes;
 119                 } else {
 120                     classes.add(ArgumentClass.SSE);
 121                     return classes;
 122                 }
 123             default:
 124                 throw new IllegalArgumentException("Type " + type + " is not yet supported");
 125         }
 126     }
 127 
 128     static boolean isRegisterAggregate(Layout type) {
 129         // FIXME handle bit size 1, 2, 4
 130         long size = type.bitsSize() / 8;
 131         return size == 1
 132             || size == 2
 133             || size == 4
 134             || size == 8;
 135     }
 136     
 137     private static List<ArgumentClass> createMemoryClassArray(long n) {
 138         ArrayList<ArgumentClass> classes = new ArrayList<>();
 139         for (int i = 0; i < n; i++) {
 140             classes.add(ArgumentClass.MEMORY);
 141         }
 142 
 143         return classes;
 144     }
 145 
 146     private static List<ArgumentClass> classifyStructType(Group type, boolean isReturn) {
 147         ArrayList<ArgumentClass> classes = new ArrayList<>();
 148         
 149         if(isRegisterAggregate(type)) {
 150             classes.add(ArgumentClass.INTEGER);
 151         } else {
 152             if(isReturn) {
 153                 return createMemoryClassArray(Util.alignUp((type.bitsSize() / 8), 8));
 154             } else {
 155                 classes.add(ArgumentClass.INTEGER);
 156             }
 157         }
 158 
 159         return classes;
 160     }
 161 
 162     private static List<ArgumentClass> classifyType(Layout type, boolean isReturn) {
 163         if (type instanceof Value) {
 164             return classifyValueType((Value) type);
 165         } else if (type instanceof Address) {
 166             ArrayList<ArgumentClass> classes = new ArrayList<>();
 167             classes.add(ArgumentClass.INTEGER);
 168             return classes;
 169         } else if (type instanceof Sequence) {
 170             ArrayList<ArgumentClass> classes = new ArrayList<>();
 171             classes.add(ArgumentClass.INTEGER); // arrrays are always passed as pointers
 172             return classes;
 173         } else if (type instanceof Group) {
 174             return classifyStructType((Group) type, isReturn);
 175         } else {
 176             throw new IllegalArgumentException("Unhandled type " + type);
 177         }
 178     }
 179 
 180     static class StorageCalculator {
 181         private final boolean forArguments;
 182 
 183         private int nRegs = 0;
 184         private long stackOffset = 0;
 185 
 186         StorageCalculator(boolean forArguments) {
 187             this.forArguments = forArguments;
 188         }
 189 
 190         void addBindings(Argument arg, BiConsumer<StorageClass, ArgumentBinding> bindingConsumer, boolean forVarargs) {
 191             ArgumentInfo info = (ArgumentInfo)arg;
 192             if (info.inMemory() ||
 193                 nRegs + info.getRegs() > (forArguments ? Windowsx64ABI.MAX_REGISTER_ARGUMENTS : Windowsx64ABI.MAX_REGISTER_RETURNS)) {
 194                 // stack
 195 
 196                 long alignment = Math.max(SharedUtils.alignment(info.layout(), true), 8);
 197 
 198                 long newStackOffset = Util.alignUp(stackOffset, alignment);
 199                 stackOffset = newStackOffset;
 200 
 201                 long tmpStackOffset = stackOffset;
 202                 for (int i = 0; i < info.getClasses().size(); i++) {
 203                     Storage storage = new Storage(StorageClass.STACK_ARGUMENT_SLOT, tmpStackOffset / 8, 8);
 204                     bindingConsumer.accept(StorageClass.STACK_ARGUMENT_SLOT, new ArgumentBinding(storage, info, i * 8));
 205 
 206                     if (DEBUG) {
 207                         System.out.println("Argument " + info.name() + " will be passed on stack at offset " + tmpStackOffset);
 208                     }
 209 
 210                     tmpStackOffset += 8;
 211                 }
 212 
 213                 stackOffset += info.layout().bitsSize() / 8;
 214             } else {
 215                 // regs
 216                 for (int i = 0; i < info.getClasses().size(); i++) {
 217                     Storage storage;
 218 
 219                     ArgumentClass c = info.getClasses().get(i);
 220 
 221                     switch (c) {
 222                     case INTEGER:
 223                         storage = new Storage(forArguments ? StorageClass.INTEGER_ARGUMENT_REGISTER : StorageClass.INTEGER_RETURN_REGISTER, nRegs++, SharedUtils.INTEGER_REGISTER_SIZE);
 224                         bindingConsumer.accept(storage.getStorageClass(), new ArgumentBinding(storage, info, i * 8));
 225 
 226                         if (DEBUG) {
 227                             System.out.println("Argument " + info.name() + " will be passed in register " +
 228                                     storageDbgHelper.getStorageName(storage));
 229                         }
 230                         break;
 231 
 232                     case SSE:
 233                         int width = 8;
 234 
 235                         for (int j = i + 1; j < info.getClasses().size(); j++) {
 236                             if (info.getClasses().get(j) == ArgumentClass.SSEUP) {
 237                                 width += 8;
 238                             }
 239                         }
 240 
 241                         if (width > 64) {
 242                             throw new IllegalArgumentException((width * 8) + "-bit vector arguments not supported");
 243                         }
 244 
 245                         storage = new Storage(forArguments ? StorageClass.VECTOR_ARGUMENT_REGISTER : StorageClass.VECTOR_RETURN_REGISTER,
 246                                 nRegs, width, SharedUtils.VECTOR_REGISTER_SIZE);
 247                         bindingConsumer.accept(storage.getStorageClass(), new ArgumentBinding(storage, info, i * 8));
 248 
 249                         if (DEBUG) {
 250                             System.out.println("Argument " + info.name() + " will be passed in register " +
 251                                     storageDbgHelper.getStorageName(storage));
 252                         }
 253 
 254                         if(width == 8 && storage.getStorageClass() == StorageClass.VECTOR_ARGUMENT_REGISTER && forVarargs) {
 255                             Storage extraStorage = new Storage(StorageClass.INTEGER_ARGUMENT_REGISTER, nRegs, SharedUtils.INTEGER_REGISTER_SIZE);
 256                             bindingConsumer.accept(storage.getStorageClass(), new ArgumentBinding(extraStorage, info, i * 8));
 257 
 258                             if (DEBUG) {
 259                                 System.out.println("Argument " + info.name() + " will be passed in register " +
 260                                         storageDbgHelper.getStorageName(extraStorage));
 261                             }
 262                         }
 263 
 264                         nRegs++;
 265                         break;
 266 
 267                     case SSEUP:
 268                         break;
 269 
 270                     default:
 271                         throw new UnsupportedOperationException("Unhandled class " + c);
 272                     }
 273                 }
 274             }
 275         }
 276     }
 277 }