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