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 package jdk.internal.foreign.abi.x64.sysv;
 26 
 27 import java.lang.foreign.GroupLayout;
 28 import java.lang.foreign.MemoryLayout;
 29 import java.lang.foreign.MemorySegment;
 30 import java.lang.foreign.PaddingLayout;
 31 import java.lang.foreign.SequenceLayout;
 32 import java.lang.foreign.StructLayout;
 33 import java.lang.foreign.ValueLayout;
 34 import jdk.internal.foreign.Utils;
 35 
 36 import java.util.ArrayList;
 37 import java.util.List;
 38 import java.util.stream.Collectors;
 39 import java.util.stream.IntStream;
 40 
 41 class TypeClass {
 42     enum Kind {
 43         STRUCT,
 44         POINTER,
 45         INTEGER,
 46         FLOAT
 47     }
 48 
 49     private final Kind kind;
 50     final List<ArgumentClassImpl> classes;
 51 
 52     private TypeClass(Kind kind, List<ArgumentClassImpl> classes) {
 53         this.kind = kind;
 54         this.classes = classes;
 55     }
 56 
 57     public static TypeClass ofValue(ValueLayout layout) {
 58         final Kind kind;
 59         ArgumentClassImpl argClass = argumentClassFor(layout);
 60         kind = switch (argClass) {
 61             case POINTER -> Kind.POINTER;
 62             case INTEGER -> Kind.INTEGER;
 63             case SSE -> Kind.FLOAT;
 64             default -> throw new IllegalStateException("Unexpected argument class: " + argClass);
 65         };
 66         return new TypeClass(kind, List.of(argClass));
 67     }
 68 
 69     public static TypeClass ofStruct(GroupLayout layout) {
 70         return new TypeClass(Kind.STRUCT, classifyStructType(layout));
 71     }
 72 
 73     boolean inMemory() {
 74         return classes.stream().anyMatch(c -> c == ArgumentClassImpl.MEMORY);
 75     }
 76 
 77     private long numClasses(ArgumentClassImpl clazz) {
 78         return classes.stream().filter(c -> c == clazz).count();
 79     }
 80 
 81     public long nIntegerRegs() {
 82         return numClasses(ArgumentClassImpl.INTEGER) + numClasses(ArgumentClassImpl.POINTER);
 83     }
 84 
 85     public long nVectorRegs() {
 86         return numClasses(ArgumentClassImpl.SSE);
 87     }
 88 
 89     public Kind kind() {
 90         return kind;
 91     }
 92 
 93     // layout classification
 94 
 95     // The AVX 512 enlightened ABI says "eight eightbytes"
 96     // Although AMD64 0.99.6 states 4 eightbytes
 97     private static final int MAX_AGGREGATE_REGS_SIZE = 8;
 98     static final List<ArgumentClassImpl> COMPLEX_X87_CLASSES = List.of(
 99          ArgumentClassImpl.X87,
100          ArgumentClassImpl.X87UP,
101          ArgumentClassImpl.X87,
102          ArgumentClassImpl.X87UP
103     );
104 
105     private static List<ArgumentClassImpl> createMemoryClassArray(long size) {
106         return IntStream.range(0, (int)size)
107                 .mapToObj(i -> ArgumentClassImpl.MEMORY)
108                 .collect(Collectors.toCollection(ArrayList::new));
109     }
110 
111     private static ArgumentClassImpl argumentClassFor(ValueLayout layout) {
112         Class<?> carrier = layout.carrier();
113         if (carrier == boolean.class || carrier == byte.class || carrier == char.class ||
114                 carrier == short.class || carrier == int.class || carrier == long.class) {
115             return ArgumentClassImpl.INTEGER;
116         } else if (carrier == float.class || carrier == double.class) {
117             return ArgumentClassImpl.SSE;
118         } else if (carrier == MemorySegment.class) {
119             return ArgumentClassImpl.POINTER;
120         } else {
121             throw new IllegalStateException("Cannot get here: " + carrier.getName());
122         }
123     }
124 
125     // TODO: handle zero length arrays
126     private static List<ArgumentClassImpl> classifyStructType(GroupLayout type) {
127         List<ArgumentClassImpl>[] eightbytes = groupByEightBytes(type);
128         long nWords = eightbytes.length;
129         if (nWords > MAX_AGGREGATE_REGS_SIZE) {
130             return createMemoryClassArray(nWords);
131         }
132 
133         ArrayList<ArgumentClassImpl> classes = new ArrayList<>();
134 
135         for (int idx = 0; idx < nWords; idx++) {
136             List<ArgumentClassImpl> subclasses = eightbytes[idx];
137             ArgumentClassImpl result = subclasses.stream()
138                     .reduce(ArgumentClassImpl.NO_CLASS, ArgumentClassImpl::merge);
139             classes.add(result);
140         }
141 
142         for (int i = 0; i < classes.size(); i++) {
143             ArgumentClassImpl c = classes.get(i);
144 
145             if (c == ArgumentClassImpl.MEMORY) {
146                 // if any of the eightbytes was passed in memory, pass the whole thing in memory
147                 return createMemoryClassArray(classes.size());
148             }
149 
150             if (c == ArgumentClassImpl.X87UP) {
151                 if (i == 0) {
152                     throw new IllegalArgumentException("Unexpected leading X87UP class");
153                 }
154 
155                 if (classes.get(i - 1) != ArgumentClassImpl.X87) {
156                     return createMemoryClassArray(classes.size());
157                 }
158             }
159         }
160 
161         if (classes.size() > 2) {
162             if (classes.get(0) != ArgumentClassImpl.SSE) {
163                 return createMemoryClassArray(classes.size());
164             }
165 
166             for (int i = 1; i < classes.size(); i++) {
167                 if (classes.get(i) != ArgumentClassImpl.SSEUP) {
168                     return createMemoryClassArray(classes.size());
169                 }
170             }
171         }
172 
173         return classes;
174     }
175 
176     static TypeClass classifyLayout(MemoryLayout type) {
177         try {
178             if (type instanceof ValueLayout valueLayout) {
179                 return ofValue(valueLayout);
180             } else if (type instanceof GroupLayout groupLayout) {
181                 return ofStruct(groupLayout);
182             } else {
183                 throw new IllegalArgumentException("Unsupported layout: " + type);
184             }
185         } catch (UnsupportedOperationException e) {
186             System.err.println("Failed to classify layout: " + type);
187             throw e;
188         }
189     }
190 
191     private static List<ArgumentClassImpl>[] groupByEightBytes(GroupLayout group) {
192         long offset = 0L;
193         int nEightbytes;
194         try {
195             // alignUp can overflow the value, but it's okay since toIntExact still catches it
196             nEightbytes = Math.toIntExact(Utils.alignUp(group.byteSize(), 8) / 8);
197         } catch (ArithmeticException e) {
198             throw new IllegalArgumentException("GroupLayout is too large: " + group, e);
199         }
200         @SuppressWarnings({"unchecked", "rawtypes"})
201         List<ArgumentClassImpl>[] groups = new List[nEightbytes];
202         for (MemoryLayout l : group.memberLayouts()) {
203             groupByEightBytes(l, offset, groups);
204             if (group instanceof StructLayout) {
205                 offset += l.byteSize();
206             }
207         }
208         return groups;
209     }
210 
211     private static void groupByEightBytes(MemoryLayout layout,
212                                           long offset,
213                                           List<ArgumentClassImpl>[] groups) {
214         switch (layout) {
215             case GroupLayout group -> {
216                 for (MemoryLayout m : group.memberLayouts()) {
217                     groupByEightBytes(m, offset, groups);
218                     if (group instanceof StructLayout) {
219                         offset += m.byteSize();
220                     }
221                 }
222             }
223             case PaddingLayout  _ -> {
224             }
225             case SequenceLayout seq -> {
226                 MemoryLayout elem = seq.elementLayout();
227                 for (long i = 0; i < seq.elementCount(); i++) {
228                     groupByEightBytes(elem, offset, groups);
229                     offset += elem.byteSize();
230                 }
231             }
232             case ValueLayout vl -> {
233                 List<ArgumentClassImpl> layouts = groups[(int) offset / 8];
234                 if (layouts == null) {
235                     layouts = new ArrayList<>();
236                     groups[(int) offset / 8] = layouts;
237                 }
238                 // if the aggregate contains unaligned fields, it has class MEMORY
239                 ArgumentClassImpl argumentClass = (offset % vl.byteAlignment()) == 0 ?
240                         argumentClassFor(vl) :
241                         ArgumentClassImpl.MEMORY;
242                 layouts.add(argumentClass);
243             }
244             case null, default -> throw new IllegalStateException("Unexpected layout: " + layout);
245         }
246     }
247 }