]> git.ipfire.org Git - thirdparty/gcc.git/blob - gcc/convert.c
Merge basic-improvements-branch to trunk
[thirdparty/gcc.git] / gcc / convert.c
1 /* Utility routines for data type conversion for GNU C.
2 Copyright (C) 1987, 1988, 1991, 1992, 1993, 1994, 1995, 1997,
3 1998 Free Software Foundation, Inc.
4
5 This file is part of GCC.
6
7 GCC is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 2, or (at your option) any later
10 version.
11
12 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GCC; see the file COPYING. If not, write to the Free
19 Software Foundation, 59 Temple Place - Suite 330, Boston, MA
20 02111-1307, USA. */
21
22
23 /* These routines are somewhat language-independent utility function
24 intended to be called by the language-specific convert () functions. */
25
26 #include "config.h"
27 #include "system.h"
28 #include "coretypes.h"
29 #include "tm.h"
30 #include "tree.h"
31 #include "flags.h"
32 #include "convert.h"
33 #include "toplev.h"
34 #include "langhooks.h"
35 static tree strip_float_extensions PARAMS ((tree));
36
37 /* Convert EXPR to some pointer or reference type TYPE.
38
39 EXPR must be pointer, reference, integer, enumeral, or literal zero;
40 in other cases error is called. */
41
42 tree
43 convert_to_pointer (type, expr)
44 tree type, expr;
45 {
46 if (integer_zerop (expr))
47 {
48 expr = build_int_2 (0, 0);
49 TREE_TYPE (expr) = type;
50 return expr;
51 }
52
53 switch (TREE_CODE (TREE_TYPE (expr)))
54 {
55 case POINTER_TYPE:
56 case REFERENCE_TYPE:
57 return build1 (NOP_EXPR, type, expr);
58
59 case INTEGER_TYPE:
60 case ENUMERAL_TYPE:
61 case BOOLEAN_TYPE:
62 case CHAR_TYPE:
63 if (TYPE_PRECISION (TREE_TYPE (expr)) == POINTER_SIZE)
64 return build1 (CONVERT_EXPR, type, expr);
65
66 return
67 convert_to_pointer (type,
68 convert ((*lang_hooks.types.type_for_size)
69 (POINTER_SIZE, 0), expr));
70
71 default:
72 error ("cannot convert to a pointer type");
73 return convert_to_pointer (type, integer_zero_node);
74 }
75 }
76
77 /* Avoid any floating point extensions from EXP. */
78 static tree
79 strip_float_extensions (exp)
80 tree exp;
81 {
82 tree sub, expt, subt;
83
84 if (TREE_CODE (exp) != NOP_EXPR)
85 return exp;
86
87 sub = TREE_OPERAND (exp, 0);
88 subt = TREE_TYPE (sub);
89 expt = TREE_TYPE (exp);
90
91 if (!FLOAT_TYPE_P (subt))
92 return exp;
93
94 if (TYPE_PRECISION (subt) > TYPE_PRECISION (expt))
95 return exp;
96
97 return strip_float_extensions (sub);
98 }
99
100
101 /* Convert EXPR to some floating-point type TYPE.
102
103 EXPR must be float, integer, or enumeral;
104 in other cases error is called. */
105
106 tree
107 convert_to_real (type, expr)
108 tree type, expr;
109 {
110 enum built_in_function fcode = builtin_mathfn_code (expr);
111 tree itype = TREE_TYPE (expr);
112
113 /* Convert (float)sqrt((double)x) where x is float into sqrtf(x) */
114 if ((fcode == BUILT_IN_SQRT
115 || fcode == BUILT_IN_SQRTL
116 || fcode == BUILT_IN_SIN
117 || fcode == BUILT_IN_SINL
118 || fcode == BUILT_IN_COS
119 || fcode == BUILT_IN_COSL
120 || fcode == BUILT_IN_EXP
121 || fcode == BUILT_IN_EXPL)
122 && optimize
123 && (TYPE_MODE (type) == TYPE_MODE (double_type_node)
124 || TYPE_MODE (type) == TYPE_MODE (float_type_node)))
125 {
126 tree arg0 = strip_float_extensions (TREE_VALUE (TREE_OPERAND (expr, 1)));
127 tree newtype = type;
128
129 /* We have (outertype)sqrt((innertype)x). Choose the wider mode from
130 the both as the safe type for operation. */
131 if (TYPE_PRECISION (TREE_TYPE (arg0)) > TYPE_PRECISION (type))
132 newtype = TREE_TYPE (arg0);
133
134 /* Be curefull about integer to fp conversions.
135 These may overflow still. */
136 if (FLOAT_TYPE_P (TREE_TYPE (arg0))
137 && TYPE_PRECISION (newtype) <= TYPE_PRECISION (itype)
138 && (TYPE_MODE (newtype) == TYPE_MODE (double_type_node)
139 || TYPE_MODE (newtype) == TYPE_MODE (float_type_node)))
140 {
141 tree arglist;
142 if (TYPE_MODE (type) == TYPE_MODE (float_type_node))
143 switch (fcode)
144 {
145 case BUILT_IN_SQRT:
146 case BUILT_IN_SQRTL:
147 fcode = BUILT_IN_SQRTF;
148 break;
149 case BUILT_IN_SIN:
150 case BUILT_IN_SINL:
151 fcode = BUILT_IN_SINF;
152 break;
153 case BUILT_IN_COS:
154 case BUILT_IN_COSL:
155 fcode = BUILT_IN_COSF;
156 break;
157 case BUILT_IN_EXP:
158 case BUILT_IN_EXPL:
159 fcode = BUILT_IN_EXPF;
160 break;
161 default:
162 abort ();
163 }
164 else
165 switch (fcode)
166 {
167 case BUILT_IN_SQRT:
168 case BUILT_IN_SQRTL:
169 fcode = BUILT_IN_SQRT;
170 break;
171 case BUILT_IN_SIN:
172 case BUILT_IN_SINL:
173 fcode = BUILT_IN_SIN;
174 break;
175 case BUILT_IN_COS:
176 case BUILT_IN_COSL:
177 fcode = BUILT_IN_COS;
178 break;
179 case BUILT_IN_EXP:
180 case BUILT_IN_EXPL:
181 fcode = BUILT_IN_EXP;
182 break;
183 default:
184 abort ();
185 }
186
187 /* ??? Fortran frontend does not initialize built_in_decls.
188 For some reason creating the decl using builtin_function does not
189 work as it should. */
190 if (built_in_decls [fcode])
191 {
192 arglist = build_tree_list (NULL_TREE, fold (convert_to_real (newtype, arg0)));
193 expr = build_function_call_expr (built_in_decls [fcode], arglist);
194 if (newtype == type)
195 return expr;
196 }
197 }
198 }
199
200 /* Propagate the cast into the operation. */
201 if (itype != type && FLOAT_TYPE_P (type))
202 switch (TREE_CODE (expr))
203 {
204 /* convert (float)-x into -(float)x. This is always safe. */
205 case ABS_EXPR:
206 case NEGATE_EXPR:
207 return build1 (TREE_CODE (expr), type,
208 fold (convert_to_real (type,
209 TREE_OPERAND (expr, 0))));
210 /* convert (outertype)((innertype0)a+(innertype1)b)
211 into ((newtype)a+(newtype)b) where newtype
212 is the widest mode from all of these. */
213 case PLUS_EXPR:
214 case MINUS_EXPR:
215 case MULT_EXPR:
216 case RDIV_EXPR:
217 {
218 tree arg0 = strip_float_extensions (TREE_OPERAND (expr, 0));
219 tree arg1 = strip_float_extensions (TREE_OPERAND (expr, 1));
220
221 if (FLOAT_TYPE_P (TREE_TYPE (arg0))
222 && FLOAT_TYPE_P (TREE_TYPE (arg1)))
223 {
224 tree newtype = type;
225 if (TYPE_PRECISION (TREE_TYPE (arg0)) > TYPE_PRECISION (newtype))
226 newtype = TREE_TYPE (arg0);
227 if (TYPE_PRECISION (TREE_TYPE (arg1)) > TYPE_PRECISION (newtype))
228 newtype = TREE_TYPE (arg1);
229 if (TYPE_PRECISION (newtype) < TYPE_PRECISION (itype))
230 {
231 expr = build (TREE_CODE (expr), newtype,
232 fold (convert_to_real (newtype, arg0)),
233 fold (convert_to_real (newtype, arg1)));
234 if (newtype == type)
235 return expr;
236 }
237 }
238 }
239 break;
240 default:
241 break;
242 }
243
244 switch (TREE_CODE (TREE_TYPE (expr)))
245 {
246 case REAL_TYPE:
247 return build1 (flag_float_store ? CONVERT_EXPR : NOP_EXPR,
248 type, expr);
249
250 case INTEGER_TYPE:
251 case ENUMERAL_TYPE:
252 case BOOLEAN_TYPE:
253 case CHAR_TYPE:
254 return build1 (FLOAT_EXPR, type, expr);
255
256 case COMPLEX_TYPE:
257 return convert (type,
258 fold (build1 (REALPART_EXPR,
259 TREE_TYPE (TREE_TYPE (expr)), expr)));
260
261 case POINTER_TYPE:
262 case REFERENCE_TYPE:
263 error ("pointer value used where a floating point value was expected");
264 return convert_to_real (type, integer_zero_node);
265
266 default:
267 error ("aggregate value used where a float was expected");
268 return convert_to_real (type, integer_zero_node);
269 }
270 }
271
272 /* Convert EXPR to some integer (or enum) type TYPE.
273
274 EXPR must be pointer, integer, discrete (enum, char, or bool), float, or
275 vector; in other cases error is called.
276
277 The result of this is always supposed to be a newly created tree node
278 not in use in any existing structure. */
279
280 tree
281 convert_to_integer (type, expr)
282 tree type, expr;
283 {
284 enum tree_code ex_form = TREE_CODE (expr);
285 tree intype = TREE_TYPE (expr);
286 unsigned int inprec = TYPE_PRECISION (intype);
287 unsigned int outprec = TYPE_PRECISION (type);
288
289 /* An INTEGER_TYPE cannot be incomplete, but an ENUMERAL_TYPE can
290 be. Consider `enum E = { a, b = (enum E) 3 };'. */
291 if (!COMPLETE_TYPE_P (type))
292 {
293 error ("conversion to incomplete type");
294 return error_mark_node;
295 }
296
297 switch (TREE_CODE (intype))
298 {
299 case POINTER_TYPE:
300 case REFERENCE_TYPE:
301 if (integer_zerop (expr))
302 expr = integer_zero_node;
303 else
304 expr = fold (build1 (CONVERT_EXPR, (*lang_hooks.types.type_for_size)
305 (POINTER_SIZE, 0), expr));
306
307 return convert_to_integer (type, expr);
308
309 case INTEGER_TYPE:
310 case ENUMERAL_TYPE:
311 case BOOLEAN_TYPE:
312 case CHAR_TYPE:
313 /* If this is a logical operation, which just returns 0 or 1, we can
314 change the type of the expression. For some logical operations,
315 we must also change the types of the operands to maintain type
316 correctness. */
317
318 if (TREE_CODE_CLASS (ex_form) == '<')
319 {
320 TREE_TYPE (expr) = type;
321 return expr;
322 }
323
324 else if (ex_form == TRUTH_AND_EXPR || ex_form == TRUTH_ANDIF_EXPR
325 || ex_form == TRUTH_OR_EXPR || ex_form == TRUTH_ORIF_EXPR
326 || ex_form == TRUTH_XOR_EXPR)
327 {
328 TREE_OPERAND (expr, 0) = convert (type, TREE_OPERAND (expr, 0));
329 TREE_OPERAND (expr, 1) = convert (type, TREE_OPERAND (expr, 1));
330 TREE_TYPE (expr) = type;
331 return expr;
332 }
333
334 else if (ex_form == TRUTH_NOT_EXPR)
335 {
336 TREE_OPERAND (expr, 0) = convert (type, TREE_OPERAND (expr, 0));
337 TREE_TYPE (expr) = type;
338 return expr;
339 }
340
341 /* If we are widening the type, put in an explicit conversion.
342 Similarly if we are not changing the width. After this, we know
343 we are truncating EXPR. */
344
345 else if (outprec >= inprec)
346 return build1 (NOP_EXPR, type, expr);
347
348 /* If TYPE is an enumeral type or a type with a precision less
349 than the number of bits in its mode, do the conversion to the
350 type corresponding to its mode, then do a nop conversion
351 to TYPE. */
352 else if (TREE_CODE (type) == ENUMERAL_TYPE
353 || outprec != GET_MODE_BITSIZE (TYPE_MODE (type)))
354 return build1 (NOP_EXPR, type,
355 convert ((*lang_hooks.types.type_for_mode)
356 (TYPE_MODE (type), TREE_UNSIGNED (type)),
357 expr));
358
359 /* Here detect when we can distribute the truncation down past some
360 arithmetic. For example, if adding two longs and converting to an
361 int, we can equally well convert both to ints and then add.
362 For the operations handled here, such truncation distribution
363 is always safe.
364 It is desirable in these cases:
365 1) when truncating down to full-word from a larger size
366 2) when truncating takes no work.
367 3) when at least one operand of the arithmetic has been extended
368 (as by C's default conversions). In this case we need two conversions
369 if we do the arithmetic as already requested, so we might as well
370 truncate both and then combine. Perhaps that way we need only one.
371
372 Note that in general we cannot do the arithmetic in a type
373 shorter than the desired result of conversion, even if the operands
374 are both extended from a shorter type, because they might overflow
375 if combined in that type. The exceptions to this--the times when
376 two narrow values can be combined in their narrow type even to
377 make a wider result--are handled by "shorten" in build_binary_op. */
378
379 switch (ex_form)
380 {
381 case RSHIFT_EXPR:
382 /* We can pass truncation down through right shifting
383 when the shift count is a nonpositive constant. */
384 if (TREE_CODE (TREE_OPERAND (expr, 1)) == INTEGER_CST
385 && tree_int_cst_lt (TREE_OPERAND (expr, 1),
386 convert (TREE_TYPE (TREE_OPERAND (expr, 1)),
387 integer_one_node)))
388 goto trunc1;
389 break;
390
391 case LSHIFT_EXPR:
392 /* We can pass truncation down through left shifting
393 when the shift count is a nonnegative constant and
394 the target type is unsigned. */
395 if (TREE_CODE (TREE_OPERAND (expr, 1)) == INTEGER_CST
396 && tree_int_cst_sgn (TREE_OPERAND (expr, 1)) >= 0
397 && TREE_UNSIGNED (type)
398 && TREE_CODE (TYPE_SIZE (type)) == INTEGER_CST)
399 {
400 /* If shift count is less than the width of the truncated type,
401 really shift. */
402 if (tree_int_cst_lt (TREE_OPERAND (expr, 1), TYPE_SIZE (type)))
403 /* In this case, shifting is like multiplication. */
404 goto trunc1;
405 else
406 {
407 /* If it is >= that width, result is zero.
408 Handling this with trunc1 would give the wrong result:
409 (int) ((long long) a << 32) is well defined (as 0)
410 but (int) a << 32 is undefined and would get a
411 warning. */
412
413 tree t = convert_to_integer (type, integer_zero_node);
414
415 /* If the original expression had side-effects, we must
416 preserve it. */
417 if (TREE_SIDE_EFFECTS (expr))
418 return build (COMPOUND_EXPR, type, expr, t);
419 else
420 return t;
421 }
422 }
423 break;
424
425 case MAX_EXPR:
426 case MIN_EXPR:
427 case MULT_EXPR:
428 {
429 tree arg0 = get_unwidened (TREE_OPERAND (expr, 0), type);
430 tree arg1 = get_unwidened (TREE_OPERAND (expr, 1), type);
431
432 /* Don't distribute unless the output precision is at least as big
433 as the actual inputs. Otherwise, the comparison of the
434 truncated values will be wrong. */
435 if (outprec >= TYPE_PRECISION (TREE_TYPE (arg0))
436 && outprec >= TYPE_PRECISION (TREE_TYPE (arg1))
437 /* If signedness of arg0 and arg1 don't match,
438 we can't necessarily find a type to compare them in. */
439 && (TREE_UNSIGNED (TREE_TYPE (arg0))
440 == TREE_UNSIGNED (TREE_TYPE (arg1))))
441 goto trunc1;
442 break;
443 }
444
445 case PLUS_EXPR:
446 case MINUS_EXPR:
447 case BIT_AND_EXPR:
448 case BIT_IOR_EXPR:
449 case BIT_XOR_EXPR:
450 case BIT_ANDTC_EXPR:
451 trunc1:
452 {
453 tree arg0 = get_unwidened (TREE_OPERAND (expr, 0), type);
454 tree arg1 = get_unwidened (TREE_OPERAND (expr, 1), type);
455
456 if (outprec >= BITS_PER_WORD
457 || TRULY_NOOP_TRUNCATION (outprec, inprec)
458 || inprec > TYPE_PRECISION (TREE_TYPE (arg0))
459 || inprec > TYPE_PRECISION (TREE_TYPE (arg1)))
460 {
461 /* Do the arithmetic in type TYPEX,
462 then convert result to TYPE. */
463 tree typex = type;
464
465 /* Can't do arithmetic in enumeral types
466 so use an integer type that will hold the values. */
467 if (TREE_CODE (typex) == ENUMERAL_TYPE)
468 typex = (*lang_hooks.types.type_for_size)
469 (TYPE_PRECISION (typex), TREE_UNSIGNED (typex));
470
471 /* But now perhaps TYPEX is as wide as INPREC.
472 In that case, do nothing special here.
473 (Otherwise would recurse infinitely in convert. */
474 if (TYPE_PRECISION (typex) != inprec)
475 {
476 /* Don't do unsigned arithmetic where signed was wanted,
477 or vice versa.
478 Exception: if both of the original operands were
479 unsigned then we can safely do the work as unsigned.
480 Exception: shift operations take their type solely
481 from the first argument.
482 Exception: the LSHIFT_EXPR case above requires that
483 we perform this operation unsigned lest we produce
484 signed-overflow undefinedness.
485 And we may need to do it as unsigned
486 if we truncate to the original size. */
487 if (TREE_UNSIGNED (TREE_TYPE (expr))
488 || (TREE_UNSIGNED (TREE_TYPE (arg0))
489 && (TREE_UNSIGNED (TREE_TYPE (arg1))
490 || ex_form == LSHIFT_EXPR
491 || ex_form == RSHIFT_EXPR
492 || ex_form == LROTATE_EXPR
493 || ex_form == RROTATE_EXPR))
494 || ex_form == LSHIFT_EXPR)
495 typex = (*lang_hooks.types.unsigned_type) (typex);
496 else
497 typex = (*lang_hooks.types.signed_type) (typex);
498 return convert (type,
499 fold (build (ex_form, typex,
500 convert (typex, arg0),
501 convert (typex, arg1),
502 0)));
503 }
504 }
505 }
506 break;
507
508 case NEGATE_EXPR:
509 case BIT_NOT_EXPR:
510 /* This is not correct for ABS_EXPR,
511 since we must test the sign before truncation. */
512 {
513 tree typex = type;
514
515 /* Can't do arithmetic in enumeral types
516 so use an integer type that will hold the values. */
517 if (TREE_CODE (typex) == ENUMERAL_TYPE)
518 typex = (*lang_hooks.types.type_for_size)
519 (TYPE_PRECISION (typex), TREE_UNSIGNED (typex));
520
521 /* But now perhaps TYPEX is as wide as INPREC.
522 In that case, do nothing special here.
523 (Otherwise would recurse infinitely in convert. */
524 if (TYPE_PRECISION (typex) != inprec)
525 {
526 /* Don't do unsigned arithmetic where signed was wanted,
527 or vice versa. */
528 if (TREE_UNSIGNED (TREE_TYPE (expr)))
529 typex = (*lang_hooks.types.unsigned_type) (typex);
530 else
531 typex = (*lang_hooks.types.signed_type) (typex);
532 return convert (type,
533 fold (build1 (ex_form, typex,
534 convert (typex,
535 TREE_OPERAND (expr, 0)))));
536 }
537 }
538
539 case NOP_EXPR:
540 /* Don't introduce a
541 "can't convert between vector values of different size" error. */
542 if (TREE_CODE (TREE_TYPE (TREE_OPERAND (expr, 0))) == VECTOR_TYPE
543 && (GET_MODE_SIZE (TYPE_MODE (TREE_TYPE (TREE_OPERAND (expr, 0))))
544 != GET_MODE_SIZE (TYPE_MODE (type))))
545 break;
546 /* If truncating after truncating, might as well do all at once.
547 If truncating after extending, we may get rid of wasted work. */
548 return convert (type, get_unwidened (TREE_OPERAND (expr, 0), type));
549
550 case COND_EXPR:
551 /* It is sometimes worthwhile to push the narrowing down through
552 the conditional and never loses. */
553 return fold (build (COND_EXPR, type, TREE_OPERAND (expr, 0),
554 convert (type, TREE_OPERAND (expr, 1)),
555 convert (type, TREE_OPERAND (expr, 2))));
556
557 default:
558 break;
559 }
560
561 return build1 (NOP_EXPR, type, expr);
562
563 case REAL_TYPE:
564 return build1 (FIX_TRUNC_EXPR, type, expr);
565
566 case COMPLEX_TYPE:
567 return convert (type,
568 fold (build1 (REALPART_EXPR,
569 TREE_TYPE (TREE_TYPE (expr)), expr)));
570
571 case VECTOR_TYPE:
572 if (GET_MODE_SIZE (TYPE_MODE (type))
573 != GET_MODE_SIZE (TYPE_MODE (TREE_TYPE (expr))))
574 {
575 error ("can't convert between vector values of different size");
576 return error_mark_node;
577 }
578 return build1 (NOP_EXPR, type, expr);
579
580 default:
581 error ("aggregate value used where an integer was expected");
582 return convert (type, integer_zero_node);
583 }
584 }
585
586 /* Convert EXPR to the complex type TYPE in the usual ways. */
587
588 tree
589 convert_to_complex (type, expr)
590 tree type, expr;
591 {
592 tree subtype = TREE_TYPE (type);
593
594 switch (TREE_CODE (TREE_TYPE (expr)))
595 {
596 case REAL_TYPE:
597 case INTEGER_TYPE:
598 case ENUMERAL_TYPE:
599 case BOOLEAN_TYPE:
600 case CHAR_TYPE:
601 return build (COMPLEX_EXPR, type, convert (subtype, expr),
602 convert (subtype, integer_zero_node));
603
604 case COMPLEX_TYPE:
605 {
606 tree elt_type = TREE_TYPE (TREE_TYPE (expr));
607
608 if (TYPE_MAIN_VARIANT (elt_type) == TYPE_MAIN_VARIANT (subtype))
609 return expr;
610 else if (TREE_CODE (expr) == COMPLEX_EXPR)
611 return fold (build (COMPLEX_EXPR,
612 type,
613 convert (subtype, TREE_OPERAND (expr, 0)),
614 convert (subtype, TREE_OPERAND (expr, 1))));
615 else
616 {
617 expr = save_expr (expr);
618 return
619 fold (build (COMPLEX_EXPR,
620 type, convert (subtype,
621 fold (build1 (REALPART_EXPR,
622 TREE_TYPE (TREE_TYPE (expr)),
623 expr))),
624 convert (subtype,
625 fold (build1 (IMAGPART_EXPR,
626 TREE_TYPE (TREE_TYPE (expr)),
627 expr)))));
628 }
629 }
630
631 case POINTER_TYPE:
632 case REFERENCE_TYPE:
633 error ("pointer value used where a complex was expected");
634 return convert_to_complex (type, integer_zero_node);
635
636 default:
637 error ("aggregate value used where a complex was expected");
638 return convert_to_complex (type, integer_zero_node);
639 }
640 }
641
642 /* Convert EXPR to the vector type TYPE in the usual ways. */
643
644 tree
645 convert_to_vector (type, expr)
646 tree type, expr;
647 {
648 switch (TREE_CODE (TREE_TYPE (expr)))
649 {
650 case INTEGER_TYPE:
651 case VECTOR_TYPE:
652 if (GET_MODE_SIZE (TYPE_MODE (type))
653 != GET_MODE_SIZE (TYPE_MODE (TREE_TYPE (expr))))
654 {
655 error ("can't convert between vector values of different size");
656 return error_mark_node;
657 }
658 return build1 (NOP_EXPR, type, expr);
659
660 default:
661 error ("can't convert value to a vector");
662 return convert_to_vector (type, integer_zero_node);
663 }
664 }