chiark / gitweb /
@@@ fltfmt mess
[mLib] / m4 / mdw-probe-fltfmt.m4
1 dnl -*-autoconf-*-
2
3 ### SYNOPSIS
4 ###
5 ###   mdw_PROBE_FLTFMT
6 ###
7 ### DESCRIPTION
8 ###
9 ###   Attempts to determine the target system's floating-point formats.
10 ###   The macros `FLT_FORMAT', `DBL_FORMAT', and `LDBL_FORMAT' are defined if
11 ###   the corresponding formats are recognized.  The values of these macros
12 ###   are a bitmask:
13 ###
14 ###     * `FLTFMT_ORGMASK' is a bitmask covering the `organization' field,
15 ###       which identifies the organization which defined the format.
16 ###
17 ###         -- `FLTFMT_IEEE' identifies IEEE 754.
18 ###         -- `FLTFMT_INTEL' identifies Intel.
19 ###
20 ###     * `FLTFMT_TYPEMASK' is a bitmask covering the `type' field, which
21 ###       must be interpreted together with the organization field to
22 ###       determine the format.
23 ###
24 ###         -- `FLTFMT_IEEE_F32' is the IEEE 754 `binary32' format.
25 ###         -- `FLTFMT_IEEE_F64' is the IEEE 754 `binary64' format.
26 ###         -- `FLTFMT_IEEE_F128' is the IEEE 754 `binary128' format.
27 ###         -- `FLTFMT_INTEL_F80' is the Intel x87 80-bit double-extended
28 ###            format.
29 ###
30 ###     * `FLTFMT_ENDMASK' is a bitmask covering the `endian' field, which
31 ###       describes the byte ordering convention used.
32 ###
33 ###         -- `FLTFMT_LE' means little-endian format.
34 ###
35 ###         -- `FLTFMT_BE' means big-endian format.
36 ###
37 ###         -- `FLTFMT_ARME' means `Acorn's ridiculous mixed-endian' format,
38 ###            used by the ARM FPA, which stored the a `binary64' value as
39 ###            two 32-bit words, most significant word first, but with the
40 ###            individual words stored in little-endian byte order.
41 ###
42 ###       Other conventions exist and support for them may be added later.
43 ###
44 ###   Finally, there are predefined values for relevant combinations of
45 ###   format codes and byte orderings:
46 ###
47 ###     * `FLTFMT_IEEE_F32_LE' and `FLTFMT_IEEE_F32_BE';
48 ###     * `FLTFMT_IEEE_F64_LE' and `FLTFMT_IEEE_F64_BE';
49 ###     * `FLTFMT_IEEE_F128_LE' and `FLTFMT_IEEE_F128_BE';
50 ###     * `FLTFMT_INTEL_F80_LE' and `FLTFMT_INTEL_F80_BE'.
51 ###
52 ###   (I don't know of anything which uses Intel's double-extended format in
53 ###   big-endian order, but it was easy enough to check for.  The IEEE
54 ###   formats are used on ARM64 and z/Architecture with opposite byte order.)
55 ###
56 ###   This macro works correctly when cross-compiling.
57 ###
58 ### LICENSE
59 ###
60 ###   Copyright (c) 2024 Mark Wooding <mdw@distorted.org.uk>
61 ###
62 ###   This program is free software: you can redistribute it and/or modify it
63 ###   under the terms of the GNU General Public License as published by the
64 ###   Free Software Foundation, either version 2 of the License, or (at your
65 ###   option) any later version.
66 ###
67 ###   This program is distributed in the hope that it will be useful, but
68 ###   WITHOUT ANY WARRANTY; without even the implied warranty of
69 ###   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
70 ###   General Public License for more details.
71 ###
72 ###   You should have received a copy of the GNU General Public License along
73 ###   with this program. If not, see <http://www.gnu.org/licenses/>.
74 ###
75 ###   In particular, no exception to the GPL is granted regarding generated
76 ###   `configure' scripts which are the output of Autoconf.
77
78 dnl Principle of operation:
79 dnl
80 dnl The essential trick here lies in finding floating-point numbers whose
81 dnl encoding, in various formats of interest, happen to be recognizable
82 dnl diagnostic text strings.  The structure definitions provide some space
83 dnl for framing text which allows us to scrape the resulting diagnostic
84 dnl strings from the object file.
85 dnl
86 dnl IEEE formats conveniently don't impose any restrictions on the contents
87 dnl of the fraction field because there's a hidden bit.  The Intel x87
88 dnl double-extended format makes the most significant bit explicit, and also
89 dnl expects normalization, which means that the top bit of the fraction bytes
90 dnl must be set, so everything gets quite ugly.  Worse, the actual data is 10
91 dnl bytes long, but it sits in a 16-byte field to force alignment in vectors,
92 dnl with the result that there are unavoidably zero bytes in our diagnostic
93 dnl output.  Different tools respond to these differently; e.g., GNU sed(1)
94 dnl just writes out the zero bytes like they were any other character, while
95 dnl Busybox sed(1) terminates the output line.  As a result, we have to be
96 dnl rather more flexible about matching this than I'd really like.  (Messing
97 dnl about with compiler-specific hacks for structure packing won't help here
98 dnl because the analysis code still has to cope with compilers which don't
99 dnl have those hacks.)
100
101 # Serial 1
102 AC_COPYRIGHT([
103 Portions copyright (c) 2024 Mark Wooding.
104
105 This configure script is free software: you can redistribute it and/or
106 modify it under he terms of the GNU General Public License as published
107 by the Free Software Foundation, either version 2 of the License, or
108 (at your option) any later version.])
109
110 AC_DEFUN([mdw_PROBE_FLTFMT],
111   [AC_CACHE_CHECK([floating-point representations], [mdw_cv_fltfmt],
112      [mdw_fltfmt=nil mdw_dblfmt=nil mdw_ldblfmt=nil
113       AC_LINK_IFELSE([AC_LANG_SOURCE([
114
115 /* The following program is copyright (c) 2024 Mark Wooding.  It is free
116  * software: you can redistribute it and/or modify it under the terms of the
117  * GNU General Public License as published by the Free Software Foundation,
118  * either version 2 of the License, or (at your option) any later version.
119  *
120  * This program is distributed in the hope that it will be useful, but
121  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
122  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
123  * for more details.
124  *
125  * You should have received a copy of the GNU General Public License along
126  * with this program. If not, see <http://www.gnu.org/licenses/>.
127  */
128
129 @%:@include <float.h>
130
131 @%:@define DEFFLTDIAG(ty, type)                                         \
132   struct fltdiag_@%:@@%:@ty { char top@<:@48@:>@; type x; char tail@<:@4@:>@; }
133
134 DEFFLTDIAG(flt, float);
135
136 static const struct fltdiag_flt flt_ieee_f32 = {
137   "\0\0\0\0\0\0\0\0\n@@@ mdw-probe-fltfmt float ieee-f32 = >",
138   781.0352,
139   "<\n\0\0"
140 };
141 @%:@define DO_FLT_IEEE_F32 DO(flt_ieee_f32)
142
143 DEFFLTDIAG(dbl, double);
144
145 @%:@if DBL_MAX_10_EXP > 40
146 static const struct fltdiag_dbl dbl_ieee_f64 = {
147   "\0\0\0\0\0\0\0\n@@@ mdw-probe-fltfmt double ieee-f64 = >",
148   1.5839800103804824e40,
149   "<\n\0\0"
150 };
151 @%:@  define DO_DBL_IEEE_F64 DO(dbl_ieee_f64)
152 @%:@else
153 @%:@  define DO_DBL_IEEE_F64
154 @%:@endif
155
156 DEFFLTDIAG(ldbl, long double);
157
158 @%:@if LDBL_MAX_10_EXP > 40
159 static const struct fltdiag_ldbl ldbl_ieee_f64 = {
160   "\0\0\n@@@ mdw-probe-fltfmt long-double ieee-f64 = >",
161   1.5839800103804824e40,
162   "<\n\0\0"
163 };
164 @%:@  define DO_LDBL_IEEE_F64 DO(ldbl_ieee_f64)
165 @%:@else
166 @%:@  define DO_LDBL_IEEE_F64
167 @%:@endif
168
169 @%:@if LDBL_MAX_10_EXP > 1257
170 static const struct fltdiag_ldbl ldbl_ieee_f128 = {
171   "\0\n@@@ mdw-probe-fltfmt long-double ieee-f128 = >",
172   1.6487728650847311136108983312706536e+1257L,
173   "<\n\0\0"
174 };
175 @%:@  define DO_LDBL_IEEE_F128 DO(ldbl_ieee_f128)
176 @%:@else
177 @%:@  define DO_LDBL_IEEE_F128
178 @%:@endif
179
180 @%:@if LDBL_MAX_10_EXP > 793
181 static const struct fltdiag_ldbl ldbl_intel_f80 = {
182   "\0\n@@@ mdw-probe-fltfmt long-double intel-f80 = >",
183   1.2806567921142816197e+793L,
184   "<\n\0\0"
185 };
186 @%:@  define DO_LDBL_INTEL_F80 DO(ldbl_intel_f80)
187 @%:@else
188 @%:@  define DO_LDBL_INTEL_F80
189 @%:@endif
190
191 @%:@include <stdio.h>
192 int main(void)
193 {
194 @%:@define DO(var) fwrite(&var, sizeof(var), 1, stdout)
195   DO_FLT_IEEE_F32;
196   DO_DBL_IEEE_F64;
197   DO_LDBL_IEEE_F64;
198   DO_LDBL_IEEE_F128;
199   DO_LDBL_IEEE_F128;
200   DO_LDBL_INTEL_F80;
201 @%:@undef DO
202   return (0);
203 }
204 ])],
205         [sed -n "/^@@@ mdw-probe-fltfmt @<:@^ @:>@* @<:@^ @:>@* = >/p" \
206                  conftest$EXEEXT >conftest.out
207          while read _at _tag ty fmt _eq diag; do
208            case $ty,$fmt,$diag in
209              "float,ieee-f32,>ABCD<") mdw_fltfmt=ieee-f32-le ;;
210              "float,ieee-f32,>DCBA<") mdw_fltfmt=ieee-f32-be ;;
211              "double,ieee-f64,>ABCDEFGH<") mdw_dblfmt=ieee-f64-le ;;
212              "double,ieee-f64,>EFGHABCD<") mdw_dblfmt=ieee-f64-arme ;;
213              "double,ieee-f64,>HGFEDCBA<") mdw_dblfmt=ieee-f64-be ;;
214              "long-double,ieee-f64,>ABCDEFGH<") mdw_ldblfmt=ieee-f64-le ;;
215              "long-double,ieee-f64,>HGFEDCBA<") mdw_ldblfmt=ieee-f64-be ;;
216              "long-double,ieee-f128,>ABCDEFGHIJKLMNOP<")
217                mdw_ldblfmt=ieee-f128-le ;;
218              "long-double,ieee-f128,>PONMLKJIHGFEDCBA<")
219                mdw_ldblfmt=ieee-f128-be ;;
220              "long-double,intel-f80,>ABCDEFGÈIJ" | \
221              "long-double,intel-f80,>ABCDEFGÈIJ"*"<")
222                mdw_ldblfmt=intel-f80-le ;;
223              "long-double,intel-f80,>JIÈGFEDCBA" | \
224              "long-double,intel-f80,>JIÈGFEDCBA"*"<")
225                mdw_ldblfmt=intel-f80-be ;;
226            esac
227          done <conftest.out])
228       mdw_cv_fltfmt="float=$mdw_fltfmt"
229       mdw_cv_fltfmt="$mdw_cv_fltfmt double=$mdw_dblfmt"
230       mdw_cv_fltfmt="$mdw_cv_fltfmt long-double=$mdw_ldblfmt"])
231
232    AC_DEFINE([FLTFMT_ENDMASK], [0x0300],
233         [mask for byte ordering])
234    AC_DEFINE([FLTFMT_LE], [0x0000],
235         [little-endian floating-point storage])
236    AC_DEFINE([FLTFMT_BE], [0x0100],
237         [big-endian floating-point storage])
238    AC_DEFINE([FLTFMT_ARME], [0x0200],
239         [Acorn's ridiculous mixed-endian floating-point storage])
240
241    AC_DEFINE([FLTFMT_ORGMASK], [0xfc00],
242         [mask for floating-point organization id])
243    AC_DEFINE([FLTFMT_TYPEMASK], [0x00ff],
244         [mask for floating-point format type])
245
246    AC_DEFINE([FLTFMT_UNKNOWN], [0x0000],
247         [unrecognized floating-point format])
248
249    AC_DEFINE([FLTFMT_IEEE], [0x0400],
250         [floating-point organization id for IEEE 754])
251    AC_DEFINE([FLTFMT_IEEE_F32], [FLTFMT_IEEE | 5],
252         [IEEE 754 `binary32' format])
253    AC_DEFINE([FLTFMT_IEEE_F32_LE], [(FLTFMT_IEEE_F32 | FLTFMT_LE)],
254         [IEEE `binary32' format, little-endian])
255    AC_DEFINE([FLTFMT_IEEE_F32_BE], [(FLTFMT_IEEE_F32 | FLTFMT_BE)],
256         [IEEE `binary32' format, big-endian])
257    AC_DEFINE([FLTFMT_IEEE_F64], [FLTFMT_IEEE | 6],
258         [IEEE 754 `binary32' format])
259    AC_DEFINE([FLTFMT_IEEE_F64_LE], [(FLTFMT_IEEE_F64 | FLTFMT_LE)],
260         [IEEE `binary64' format, little-endian])
261    AC_DEFINE([FLTFMT_IEEE_F64_ARME], [(FLTFMT_IEEE_F64 | FLTFMT_ARME)],
262         [IEEE `binary64' format, Acorn ridiculous mixed-endian])
263    AC_DEFINE([FLTFMT_IEEE_F64_BE], [(FLTFMT_IEEE_F64 | FLTFMT_BE)],
264         [IEEE `binary64' format, big-endian])
265    AC_DEFINE([FLTFMT_IEEE_F128], [FLTFMT_IEEE | 7],
266         [IEEE 754 `binary32' format])
267    AC_DEFINE([FLTFMT_IEEE_F128_LE], [(FLTFMT_IEEE_F128 | FLTFMT_LE)],
268         [IEEE `binary128' format, little-endian])
269    AC_DEFINE([FLTFMT_IEEE_F128_BE], [(FLTFMT_IEEE_F128 | FLTFMT_BE)],
270         [IEEE `binary128' format, big-endian])
271
272    AC_DEFINE([FLTFMT_INTEL], [0x0800],
273         [floating-point organization id for Intel])
274    AC_DEFINE([FLTFMT_INTEL_F80], [FLTFMT_INTEL | 80],
275         [Intel x87 double-extended format])
276    AC_DEFINE([FLTFMT_INTEL_F80_LE], [(FLTFMT_INTEL_F80 | FLTFMT_LE)],
277         [Intel x86 double-extended format, little-endian])
278    AC_DEFINE([FLTFMT_INTEL_F80_BE], [(FLTFMT_INTEL_F80 | FLTFMT_BE)],
279         [Intel x87 double-extended format, big-endian])
280
281    AH_TEMPLATE([FLT_FORMAT],
282         [floating point format for `float' type])
283    AH_TEMPLATE([DBL_FORMAT],
284         [floating point format for `double' type])
285    AH_TEMPLATE([LDBL_FORMAT],
286         [floating point format for `long double' type])
287
288    for w in $mdw_cv_fltfmt; do
289      ty=${w%=*} fmt=${w@%:@*=}
290      case $ty in
291        float) var=FLT ;;
292        double) var=DBL ;;
293        long-double) var=LDBL ;;
294        *) AC_MSG_ERROR([unexpected floating-point type \`$ty']) ;;
295      esac
296      case $fmt in
297        nil) value=UNKNOWN ;;
298        ieee-f32-le) value=IEEE_F32_LE ;;
299        ieee-f32-be) value=IEEE_F32_BE ;;
300        ieee-f64-le) value=IEEE_F64_LE ;;
301        ieee-f64-arme) value=IEEE_F64_ARME ;;
302        ieee-f64-be) value=IEEE_F64_BE ;;
303        ieee-f128-le) value=IEEE_F128_LE ;;
304        ieee-f128-be) value=IEEE_F128_BE ;;
305        intel-f80-le) value=INTEL_F80_LE ;;
306        intel-f80-be) value=INTEL_F80_BE ;;
307        *) AC_MSG_ERROR([unexpected floating-point format \`$fmt']) ;;
308      esac
309      AC_DEFINE_UNQUOTED([${var}_FORMAT], [FLTFMT_$value])
310    done])