Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
5 : * This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
11 : * Licensed to the Apache Software Foundation (ASF) under one or more
12 : * contributor license agreements. See the NOTICE file distributed
13 : * with this work for additional information regarding copyright
14 : * ownership. The ASF licenses this file to you under the Apache
15 : * License, Version 2.0 (the "License"); you may not use this file
16 : * except in compliance with the License. You may obtain a copy of
17 : * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 : */
19 :
20 :
21 : // This is an implementation of the x86-64 ABI as described in 'System V
22 : // Application Binary Interface, AMD64 Architecture Processor Supplement'
23 : // (http://www.x86-64.org/documentation/abi-0.95.pdf)
24 : //
25 : // The code in this file is a modification of src/x86/ffi64.c from libffi
26 : // (http://sources.redhat.com/libffi/) which is under the following license:
27 :
28 : /* -----------------------------------------------------------------------
29 : ffi.c - Copyright (c) 2002 Bo Thorsen <bo@suse.de>
30 :
31 : x86-64 Foreign Function Interface
32 :
33 : Permission is hereby granted, free of charge, to any person obtaining
34 : a copy of this software and associated documentation files (the
35 : ``Software''), to deal in the Software without restriction, including
36 : without limitation the rights to use, copy, modify, merge, publish,
37 : distribute, sublicense, and/or sell copies of the Software, and to
38 : permit persons to whom the Software is furnished to do so, subject to
39 : the following conditions:
40 :
41 : The above copyright notice and this permission notice shall be included
42 : in all copies or substantial portions of the Software.
43 :
44 : THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS
45 : OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
46 : MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
47 : IN NO EVENT SHALL CYGNUS SOLUTIONS BE LIABLE FOR ANY CLAIM, DAMAGES OR
48 : OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
49 : ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
50 : OTHER DEALINGS IN THE SOFTWARE.
51 : ----------------------------------------------------------------------- */
52 :
53 : #include "sal/config.h"
54 :
55 : #include "abi.hxx"
56 :
57 : #include <sal/log.hxx>
58 :
59 : using namespace x86_64;
60 :
61 : /* Register class used for passing given 64bit part of the argument.
62 : These represent classes as documented by the PS ABI, with the exception
63 : of SSESF, SSEDF classes, that are basically SSE class, just gcc will
64 : use SF or DFmode move instead of DImode to avoid reformating penalties.
65 :
66 : Similary we play games with INTEGERSI_CLASS to use cheaper SImode moves
67 : whenever possible (upper half does contain padding).
68 : */
69 : enum x86_64_reg_class
70 : {
71 : X86_64_NO_CLASS,
72 : X86_64_INTEGER_CLASS,
73 : X86_64_INTEGERSI_CLASS,
74 : X86_64_SSE_CLASS,
75 : X86_64_SSESF_CLASS,
76 : X86_64_SSEDF_CLASS,
77 : X86_64_SSEUP_CLASS,
78 : X86_64_X87_CLASS,
79 : X86_64_X87UP_CLASS,
80 : X86_64_MEMORY_CLASS
81 : };
82 :
83 : #define MAX_CLASSES 4
84 :
85 : /* x86-64 register passing implementation. See x86-64 ABI for details. Goal
86 : of this code is to classify each 8bytes of incoming argument by the register
87 : class and assign registers accordingly. */
88 :
89 : /* Return the union class of CLASS1 and CLASS2.
90 : See the x86-64 PS ABI for details. */
91 :
92 : static enum x86_64_reg_class
93 3420 : merge_classes (enum x86_64_reg_class class1, enum x86_64_reg_class class2)
94 : throw ()
95 : {
96 : /* Rule #1: If both classes are equal, this is the resulting class. */
97 3420 : if (class1 == class2)
98 252 : return class1;
99 :
100 : /* Rule #2: If one of the classes is NO_CLASS, the resulting class is
101 : the other class. */
102 3168 : if (class1 == X86_64_NO_CLASS)
103 0 : return class2;
104 3168 : if (class2 == X86_64_NO_CLASS)
105 1900 : return class1;
106 :
107 : /* Rule #3: If one of the classes is MEMORY, the result is MEMORY. */
108 1268 : if (class1 == X86_64_MEMORY_CLASS || class2 == X86_64_MEMORY_CLASS)
109 0 : return X86_64_MEMORY_CLASS;
110 :
111 : /* Rule #4: If one of the classes is INTEGER, the result is INTEGER. */
112 1268 : if ((class1 == X86_64_INTEGERSI_CLASS && class2 == X86_64_SSESF_CLASS)
113 1268 : || (class2 == X86_64_INTEGERSI_CLASS && class1 == X86_64_SSESF_CLASS))
114 0 : return X86_64_INTEGERSI_CLASS;
115 1268 : if (class1 == X86_64_INTEGER_CLASS || class1 == X86_64_INTEGERSI_CLASS
116 18 : || class2 == X86_64_INTEGER_CLASS || class2 == X86_64_INTEGERSI_CLASS)
117 1250 : return X86_64_INTEGER_CLASS;
118 :
119 : /* Rule #5: If one of the classes is X87 or X87UP class, MEMORY is used. */
120 18 : if (class1 == X86_64_X87_CLASS || class1 == X86_64_X87UP_CLASS
121 18 : || class2 == X86_64_X87_CLASS || class2 == X86_64_X87UP_CLASS)
122 0 : return X86_64_MEMORY_CLASS;
123 :
124 : /* Rule #6: Otherwise class SSE is used. */
125 18 : return X86_64_SSE_CLASS;
126 : }
127 :
128 : /* Classify the argument of type TYPE and mode MODE.
129 : CLASSES will be filled by the register class used to pass each word
130 : of the operand. The number of words is returned. In case the parameter
131 : should be passed in memory, 0 is returned. As a special case for zero
132 : sized containers, classes[0] will be NO_CLASS and 1 is returned.
133 :
134 : See the x86-64 PS ABI for details.
135 : */
136 : static int
137 1234802 : classify_argument( typelib_TypeDescriptionReference *pTypeRef, enum x86_64_reg_class classes[], int byteOffset ) throw ()
138 : {
139 1234802 : switch ( pTypeRef->eTypeClass )
140 : {
141 : case typelib_TypeClass_VOID:
142 153728 : classes[0] = X86_64_NO_CLASS;
143 153728 : return 1;
144 : case typelib_TypeClass_CHAR:
145 : case typelib_TypeClass_BOOLEAN:
146 : case typelib_TypeClass_BYTE:
147 : case typelib_TypeClass_SHORT:
148 : case typelib_TypeClass_UNSIGNED_SHORT:
149 : case typelib_TypeClass_LONG:
150 : case typelib_TypeClass_UNSIGNED_LONG:
151 : case typelib_TypeClass_HYPER:
152 : case typelib_TypeClass_UNSIGNED_HYPER:
153 : case typelib_TypeClass_ENUM:
154 441647 : if ( ( byteOffset % 8 + pTypeRef->pType->nSize ) <= 4 )
155 440261 : classes[0] = X86_64_INTEGERSI_CLASS;
156 : else
157 1386 : classes[0] = X86_64_INTEGER_CLASS;
158 441647 : return 1;
159 : case typelib_TypeClass_FLOAT:
160 56 : if ( ( byteOffset % 8 ) == 0 )
161 38 : classes[0] = X86_64_SSESF_CLASS;
162 : else
163 18 : classes[0] = X86_64_SSE_CLASS;
164 56 : return 1;
165 : case typelib_TypeClass_DOUBLE:
166 432 : classes[0] = X86_64_SSEDF_CLASS;
167 432 : return 1;
168 : /*case LONGDOUBLE:
169 : classes[0] = X86_64_X87_CLASS;
170 : classes[1] = X86_64_X87UP_CLASS;
171 : return 2;*/
172 : case typelib_TypeClass_STRING:
173 : case typelib_TypeClass_TYPE:
174 : case typelib_TypeClass_ANY:
175 : case typelib_TypeClass_TYPEDEF:
176 : case typelib_TypeClass_SEQUENCE:
177 : case typelib_TypeClass_INTERFACE:
178 631771 : return 0;
179 : case typelib_TypeClass_STRUCT:
180 : case typelib_TypeClass_EXCEPTION:
181 : {
182 7168 : typelib_TypeDescription * pTypeDescr = 0;
183 7168 : TYPELIB_DANGER_GET( &pTypeDescr, pTypeRef );
184 :
185 7168 : const int UNITS_PER_WORD = 8;
186 7168 : int words = ( pTypeDescr->nSize + UNITS_PER_WORD - 1 ) / UNITS_PER_WORD;
187 : enum x86_64_reg_class subclasses[MAX_CLASSES];
188 :
189 : /* If the struct is larger than 16 bytes, pass it on the stack. */
190 7168 : if ( pTypeDescr->nSize > 16 )
191 : {
192 4714 : TYPELIB_DANGER_RELEASE( pTypeDescr );
193 4714 : return 0;
194 : }
195 :
196 6178 : for ( int i = 0; i < words; i++ )
197 3724 : classes[i] = X86_64_NO_CLASS;
198 :
199 2454 : const typelib_CompoundTypeDescription *pStruct = reinterpret_cast<const typelib_CompoundTypeDescription*>( pTypeDescr );
200 :
201 : /* Merge the fields of structure. */
202 5874 : for ( sal_Int32 nMember = 0; nMember < pStruct->nMembers; ++nMember )
203 : {
204 4363 : typelib_TypeDescriptionReference *pTypeInStruct = pStruct->ppTypeRefs[ nMember ];
205 4363 : int offset = byteOffset + pStruct->pMemberOffsets[ nMember ];
206 :
207 4363 : int num = classify_argument( pTypeInStruct, subclasses, offset );
208 :
209 4363 : if ( num == 0 )
210 : {
211 943 : TYPELIB_DANGER_RELEASE( pTypeDescr );
212 943 : return 0;
213 : }
214 :
215 6840 : for ( int i = 0; i < num; i++ )
216 : {
217 3420 : int pos = offset / 8;
218 3420 : classes[i + pos] = merge_classes( subclasses[i], classes[i + pos] );
219 : }
220 : }
221 :
222 1511 : TYPELIB_DANGER_RELEASE( pTypeDescr );
223 :
224 : /* Final merger cleanup. */
225 3874 : for ( int i = 0; i < words; i++ )
226 : {
227 : /* If one class is MEMORY, everything should be passed in
228 : memory. */
229 2363 : if ( classes[i] == X86_64_MEMORY_CLASS )
230 0 : return 0;
231 :
232 : /* The X86_64_SSEUP_CLASS should be always preceded by
233 : X86_64_SSE_CLASS. */
234 2363 : if ( classes[i] == X86_64_SSEUP_CLASS
235 0 : && ( i == 0 || classes[i - 1] != X86_64_SSE_CLASS ) )
236 0 : classes[i] = X86_64_SSE_CLASS;
237 :
238 : /* X86_64_X87UP_CLASS should be preceded by X86_64_X87_CLASS. */
239 2363 : if ( classes[i] == X86_64_X87UP_CLASS
240 0 : && ( i == 0 || classes[i - 1] != X86_64_X87_CLASS ) )
241 0 : classes[i] = X86_64_SSE_CLASS;
242 : }
243 1511 : return words;
244 : }
245 :
246 : default:
247 : SAL_WARN("bridges", "Unhandled case: pType->eTypeClass == "
248 : << pTypeRef->eTypeClass);
249 : assert(false);
250 : }
251 0 : return 0; /* Never reached. */
252 : }
253 :
254 : /* Examine the argument and return set number of register required in each
255 : class. Return 0 iff parameter should be passed in memory. */
256 1229969 : bool x86_64::examine_argument( typelib_TypeDescriptionReference *pTypeRef, bool bInReturn, int &nUsedGPR, int &nUsedSSE ) throw ()
257 : {
258 : enum x86_64_reg_class classes[MAX_CLASSES];
259 : int n;
260 :
261 1229969 : n = classify_argument( pTypeRef, classes, 0 );
262 :
263 1229971 : if ( n == 0 )
264 636488 : return false;
265 :
266 593483 : nUsedGPR = 0;
267 593483 : nUsedSSE = 0;
268 1187639 : for ( n--; n >= 0; n-- )
269 594153 : switch ( classes[n] )
270 : {
271 : case X86_64_INTEGER_CLASS:
272 : case X86_64_INTEGERSI_CLASS:
273 439504 : nUsedGPR++;
274 439504 : break;
275 : case X86_64_SSE_CLASS:
276 : case X86_64_SSESF_CLASS:
277 : case X86_64_SSEDF_CLASS:
278 455 : nUsedSSE++;
279 455 : break;
280 : case X86_64_NO_CLASS:
281 : case X86_64_SSEUP_CLASS:
282 154197 : break;
283 : case X86_64_X87_CLASS:
284 : case X86_64_X87UP_CLASS:
285 0 : if ( !bInReturn )
286 0 : return false;
287 0 : break;
288 : default:
289 : SAL_WARN("bridges", "Unhandled case: classes[n] == " << classes[n]);
290 : assert(false);
291 : }
292 593486 : return true;
293 : }
294 :
295 946999 : bool x86_64::return_in_hidden_param( typelib_TypeDescriptionReference *pTypeRef ) throw ()
296 : {
297 : int g, s;
298 :
299 946999 : return !examine_argument( pTypeRef, true, g, s );
300 : }
301 :
302 473 : void x86_64::fill_struct( typelib_TypeDescriptionReference *pTypeRef, const sal_uInt64 *pGPR, const double *pSSE, void *pStruct ) throw ()
303 : {
304 : enum x86_64_reg_class classes[MAX_CLASSES];
305 : int n;
306 :
307 473 : n = classify_argument( pTypeRef, classes, 0 );
308 :
309 473 : sal_uInt64 *pStructAlign = static_cast<sal_uInt64 *>( pStruct );
310 1128 : for ( n--; n >= 0; n-- )
311 655 : switch ( classes[n] )
312 : {
313 : case X86_64_INTEGER_CLASS:
314 : case X86_64_INTEGERSI_CLASS:
315 646 : *pStructAlign++ = *pGPR++;
316 646 : break;
317 : case X86_64_SSE_CLASS:
318 : case X86_64_SSESF_CLASS:
319 : case X86_64_SSEDF_CLASS:
320 9 : *pStructAlign++ = *reinterpret_cast<const sal_uInt64 *>( pSSE++ );
321 9 : break;
322 : default:
323 0 : break;
324 : }
325 473 : }
326 :
327 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|