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.MemoryLayout;
 29 import jdk.incubator.foreign.SequenceLayout;
 30 import jdk.incubator.foreign.ValueLayout;
 31 import jdk.internal.foreign.PlatformLayouts;
 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         return switch (PlatformLayouts.getKind(layout)) {
111             case CHAR, SHORT, INT, LONG, LONG_LONG -> ArgumentClassImpl.INTEGER;
112             case FLOAT, DOUBLE -> ArgumentClassImpl.SSE;
113             case POINTER -> ArgumentClassImpl.POINTER;
114         };
115     }
116 
117     // TODO: handle zero length arrays
118     private static List<ArgumentClassImpl> classifyStructType(GroupLayout type) {
119         List<ArgumentClassImpl>[] eightbytes = groupByEightBytes(type);
120         long nWords = eightbytes.length;
121         if (nWords > MAX_AGGREGATE_REGS_SIZE) {
122             return createMemoryClassArray(nWords);
123         }
124 
125         ArrayList<ArgumentClassImpl> classes = new ArrayList<>();
126 
127         for (int idx = 0; idx < nWords; idx++) {
128             List<ArgumentClassImpl> subclasses = eightbytes[idx];
129             ArgumentClassImpl result = subclasses.stream()
130                     .reduce(ArgumentClassImpl.NO_CLASS, ArgumentClassImpl::merge);
131             classes.add(result);
132         }
133 
134         for (int i = 0; i < classes.size(); i++) {
135             ArgumentClassImpl c = classes.get(i);
136 
137             if (c == ArgumentClassImpl.MEMORY) {
138                 // if any of the eightbytes was passed in memory, pass the whole thing in memory
139                 return createMemoryClassArray(classes.size());
140             }
141 
142             if (c == ArgumentClassImpl.X87UP) {
143                 if (i == 0) {
144                     throw new IllegalArgumentException("Unexpected leading X87UP class");
145                 }
146 
147                 if (classes.get(i - 1) != ArgumentClassImpl.X87) {
148                     return createMemoryClassArray(classes.size());
149                 }
150             }
151         }
152 
153         if (classes.size() > 2) {
154             if (classes.get(0) != ArgumentClassImpl.SSE) {
155                 return createMemoryClassArray(classes.size());
156             }
157 
158             for (int i = 1; i < classes.size(); i++) {
159                 if (classes.get(i) != ArgumentClassImpl.SSEUP) {
160                     return createMemoryClassArray(classes.size());
161                 }
162             }
163         }
164 
165         return classes;
166     }
167 
168     static TypeClass classifyLayout(MemoryLayout type) {
169         try {
170             if (type instanceof ValueLayout) {
171                 return ofValue((ValueLayout)type);
172             } else if (type instanceof GroupLayout) {
173                 return ofStruct((GroupLayout)type);
174             } else {
175                 throw new IllegalArgumentException("Unhandled type " + type);
176             }
177         } catch (UnsupportedOperationException e) {
178             System.err.println("Failed to classify layout: " + type);
179             throw e;
180         }
181     }
182 
183     private static List<ArgumentClassImpl>[] groupByEightBytes(GroupLayout group) {
184         long offset = 0L;
185         int nEightbytes = (int) Utils.alignUp(group.byteSize(), 8) / 8;
186         @SuppressWarnings({"unchecked", "rawtypes"})
187         List<ArgumentClassImpl>[] groups = new List[nEightbytes];
188         for (MemoryLayout l : group.memberLayouts()) {
189             groupByEightBytes(l, offset, groups);
190             if (group.isStruct()) {
191                 offset += l.byteSize();
192             }
193         }
194         return groups;
195     }
196 
197     private static void groupByEightBytes(MemoryLayout l, long offset, List<ArgumentClassImpl>[] groups) {
198         if (l instanceof GroupLayout) {
199             GroupLayout group = (GroupLayout)l;
200             for (MemoryLayout m : group.memberLayouts()) {
201                 groupByEightBytes(m, offset, groups);
202                 if (group.isStruct()) {
203                     offset += m.byteSize();
204                 }
205             }
206         } else if (l.isPadding()) {
207             return;
208         } else if (l instanceof SequenceLayout) {
209             SequenceLayout seq = (SequenceLayout)l;
210             MemoryLayout elem = seq.elementLayout();
211             for (long i = 0 ; i < seq.elementCount().getAsLong() ; i++) {
212                 groupByEightBytes(elem, offset, groups);
213                 offset += elem.byteSize();
214             }
215         } else if (l instanceof ValueLayout) {
216             List<ArgumentClassImpl> layouts = groups[(int)offset / 8];
217             if (layouts == null) {
218                 layouts = new ArrayList<>();
219                 groups[(int)offset / 8] = layouts;
220             }
221             // if the aggregate contains unaligned fields, it has class MEMORY
222             ArgumentClassImpl argumentClass = (offset % l.byteAlignment()) == 0 ?
223                     argumentClassFor(l) :
224                     ArgumentClassImpl.MEMORY;
225             layouts.add(argumentClass);
226         } else {
227             throw new IllegalStateException("Unexpected layout: " + l);
228         }
229     }
230 }