1 /*
2    +----------------------------------------------------------------------+
3    | Zend OPcache                                                         |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1998-2018 The PHP Group                                |
6    +----------------------------------------------------------------------+
7    | This source file is subject to version 3.01 of the PHP license,      |
8    | that is bundled with this package in the file LICENSE, and is        |
9    | available through the world-wide-web at the following url:           |
10    | http://www.php.net/license/3_01.txt                                  |
11    | If you did not receive a copy of the PHP license and are unable to   |
12    | obtain it through the world-wide-web, please send a note to          |
13    | license@php.net so we can mail you a copy immediately.               |
14    +----------------------------------------------------------------------+
15    | Authors: Dmitry Stogov <dmitry@zend.com>                             |
16    |          Xinchen Hui <laruence@php.net>                              |
17    +----------------------------------------------------------------------+
18 */
19 
20 /* pass 11
21  * - compact literals table
22  */
23 
24 #include "php.h"
25 #include "Optimizer/zend_optimizer.h"
26 #include "Optimizer/zend_optimizer_internal.h"
27 #include "zend_API.h"
28 #include "zend_constants.h"
29 #include "zend_execute.h"
30 #include "zend_vm.h"
31 
32 #define DEBUG_COMPACT_LITERALS 0
33 
34 #define LITERAL_VALUE                        0x0100
35 #define LITERAL_FUNC                         0x0200
36 #define LITERAL_CLASS                        0x0300
37 #define LITERAL_CONST                        0x0400
38 #define LITERAL_CLASS_CONST                  0x0500
39 #define LITERAL_STATIC_METHOD                0x0600
40 #define LITERAL_STATIC_PROPERTY              0x0700
41 #define LITERAL_METHOD                       0x0800
42 #define LITERAL_PROPERTY                     0x0900
43 #define LITERAL_GLOBAL                       0x0A00
44 
45 #define LITERAL_EX_CLASS                     0x4000
46 #define LITERAL_EX_OBJ                       0x2000
47 #define LITERAL_MAY_MERGE                    0x1000
48 #define LITERAL_KIND_MASK                    0x0f00
49 #define LITERAL_NUM_RELATED_MASK             0x000f
50 #define LITERAL_NUM_SLOTS_MASK               0x00f0
51 #define LITERAL_NUM_SLOTS_SHIFT              4
52 
53 #define LITERAL_NUM_RELATED(info) (info & LITERAL_NUM_RELATED_MASK)
54 #define LITERAL_NUM_SLOTS(info)   ((info & LITERAL_NUM_SLOTS_MASK) >> LITERAL_NUM_SLOTS_SHIFT)
55 
56 typedef struct _literal_info {
57 	uint32_t  flags; /* bitmask (see defines above) */
58 	union {
59 		int    num;   /* variable number or class name literal number */
60 	} u;
61 } literal_info;
62 
63 #define LITERAL_FLAGS(kind, slots, related) \
64 	((kind) | ((slots) << LITERAL_NUM_SLOTS_SHIFT) | (related))
65 
66 #define LITERAL_INFO(n, kind, merge, slots, related) do { \
67 		info[n].flags = (((merge) ? LITERAL_MAY_MERGE : 0) | LITERAL_FLAGS(kind, slots, related)); \
68 	} while (0)
69 
70 #define LITERAL_INFO_CLASS(n, kind, merge, slots, related, _num) do { \
71 		info[n].flags = (LITERAL_EX_CLASS | ((merge) ? LITERAL_MAY_MERGE : 0) | LITERAL_FLAGS(kind, slots, related)); \
72 		info[n].u.num = (_num); \
73 	} while (0)
74 
75 #define LITERAL_INFO_OBJ(n, kind, merge, slots, related) do { \
76 		info[n].flags = (LITERAL_EX_OBJ | ((merge) ? LITERAL_MAY_MERGE : 0) | LITERAL_FLAGS(kind, slots, related)); \
77 		info[n].u.num = (uint32_t)-1; \
78 	} while (0)
79 
optimizer_literal_obj_info(literal_info * info,zend_uchar op_type,znode_op op,int constant,uint32_t kind,uint32_t slots,uint32_t related,zend_op_array * op_array)80 static void optimizer_literal_obj_info(literal_info   *info,
81                                        zend_uchar      op_type,
82                                        znode_op        op,
83                                        int             constant,
84                                        uint32_t       kind,
85                                        uint32_t       slots,
86                                        uint32_t       related,
87                                        zend_op_array  *op_array)
88 {
89 	/* For now we merge only $this object properties and methods.
90 	 * In general it's also possible to do it for any CV variable as well,
91 	 * but it would require complex dataflow and/or type analysis.
92 	 */
93 	if (Z_TYPE(op_array->literals[constant]) == IS_STRING &&
94 	    op_type == IS_UNUSED) {
95 		LITERAL_INFO_OBJ(constant, kind, 1, slots, related);
96 	} else {
97 		LITERAL_INFO(constant, kind, 0, slots, related);
98 	}
99 }
100 
optimizer_literal_class_info(literal_info * info,zend_uchar op_type,znode_op op,int constant,uint32_t kind,uint32_t slots,uint32_t related,zend_op_array * op_array)101 static void optimizer_literal_class_info(literal_info   *info,
102                                          zend_uchar      op_type,
103                                          znode_op        op,
104                                          int             constant,
105                                          uint32_t       kind,
106                                          uint32_t       slots,
107                                          uint32_t       related,
108                                          zend_op_array  *op_array)
109 {
110 	if (op_type == IS_CONST) {
111 		LITERAL_INFO_CLASS(constant, kind, 1, slots, related, op.constant);
112 	} else {
113 		LITERAL_INFO(constant, kind, 0, slots, related);
114 	}
115 }
116 
zend_optimizer_compact_literals(zend_op_array * op_array,zend_optimizer_ctx * ctx)117 void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx *ctx)
118 {
119 	zend_op *opline, *end;
120 	int i, j, n, *map, cache_size;
121 	zval zv, *pos;
122 	literal_info *info;
123 	int l_null = -1;
124 	int l_false = -1;
125 	int l_true = -1;
126 	int l_empty_arr = -1;
127 	HashTable hash;
128 	zend_string *key = NULL;
129 	void *checkpoint = zend_arena_checkpoint(ctx->arena);
130 
131 	if (op_array->last_literal) {
132 		cache_size = 0;
133 		info = (literal_info*)zend_arena_calloc(&ctx->arena, op_array->last_literal, sizeof(literal_info));
134 
135 	    /* Mark literals of specific types */
136 		opline = op_array->opcodes;
137 		end = opline + op_array->last;
138 		while (opline < end) {
139 			switch (opline->opcode) {
140 				case ZEND_INIT_FCALL:
141 					LITERAL_INFO(opline->op2.constant, LITERAL_FUNC, 1, 1, 1);
142 					break;
143 				case ZEND_INIT_FCALL_BY_NAME:
144 					LITERAL_INFO(opline->op2.constant, LITERAL_FUNC, 1, 1, 2);
145 					break;
146 				case ZEND_INIT_NS_FCALL_BY_NAME:
147 					LITERAL_INFO(opline->op2.constant, LITERAL_FUNC, 1, 1, 3);
148 					break;
149 				case ZEND_INIT_METHOD_CALL:
150 					if (opline->op1_type == IS_CONST) {
151 						LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 1, 0, 1);
152 					}
153 					if (opline->op2_type == IS_CONST) {
154 						optimizer_literal_obj_info(
155 							info,
156 							opline->op1_type,
157 							opline->op1,
158 							opline->op2.constant,
159 							LITERAL_METHOD, 2, 2,
160 							op_array);
161 					}
162 					break;
163 				case ZEND_INIT_STATIC_METHOD_CALL:
164 					if (opline->op1_type == IS_CONST) {
165 						LITERAL_INFO(opline->op1.constant, LITERAL_CLASS, 1, 1, 2);
166 					}
167 					if (opline->op2_type == IS_CONST) {
168 						optimizer_literal_class_info(
169 							info,
170 							opline->op1_type,
171 							opline->op1,
172 							opline->op2.constant,
173 							LITERAL_STATIC_METHOD, (opline->op1_type == IS_CONST) ? 1 : 2, 2,
174 							op_array);
175 					}
176 					break;
177 				case ZEND_CATCH:
178 					LITERAL_INFO(opline->op1.constant, LITERAL_CLASS, 1, 1, 2);
179 					break;
180 				case ZEND_DEFINED:
181 					LITERAL_INFO(opline->op1.constant, LITERAL_CONST, 1, 1, 2);
182 					break;
183 				case ZEND_FETCH_CONSTANT:
184 					if ((opline->extended_value & (IS_CONSTANT_IN_NAMESPACE|IS_CONSTANT_UNQUALIFIED)) == (IS_CONSTANT_IN_NAMESPACE|IS_CONSTANT_UNQUALIFIED)) {
185 						LITERAL_INFO(opline->op2.constant, LITERAL_CONST, 1, 1, 5);
186 					} else {
187 						LITERAL_INFO(opline->op2.constant, LITERAL_CONST, 1, 1, 3);
188 					}
189 					break;
190 				case ZEND_FETCH_CLASS_CONSTANT:
191 					if (opline->op1_type == IS_CONST) {
192 						LITERAL_INFO(opline->op1.constant, LITERAL_CLASS, 1, 1, 2);
193 					}
194 					optimizer_literal_class_info(
195 						info,
196 						opline->op1_type,
197 						opline->op1,
198 						opline->op2.constant,
199 						LITERAL_CLASS_CONST, (opline->op1_type == IS_CONST) ? 1 : 2, 1,
200 						op_array);
201 					break;
202 				case ZEND_FETCH_STATIC_PROP_R:
203 				case ZEND_FETCH_STATIC_PROP_W:
204 				case ZEND_FETCH_STATIC_PROP_RW:
205 				case ZEND_FETCH_STATIC_PROP_IS:
206 				case ZEND_FETCH_STATIC_PROP_UNSET:
207 				case ZEND_FETCH_STATIC_PROP_FUNC_ARG:
208 				case ZEND_UNSET_STATIC_PROP:
209 				case ZEND_ISSET_ISEMPTY_STATIC_PROP:
210 					if (opline->op2_type == IS_CONST) {
211 						LITERAL_INFO(opline->op2.constant, LITERAL_CLASS, 1, 1, 2);
212 					}
213 					if (opline->op1_type == IS_CONST) {
214 						optimizer_literal_class_info(
215 							info,
216 							opline->op2_type,
217 							opline->op2,
218 							opline->op1.constant,
219 							LITERAL_STATIC_PROPERTY, 2, 1,
220 							op_array);
221 					}
222 					break;
223 				case ZEND_FETCH_CLASS:
224 				case ZEND_ADD_INTERFACE:
225 				case ZEND_ADD_TRAIT:
226 				case ZEND_INSTANCEOF:
227 					if (opline->op2_type == IS_CONST) {
228 						LITERAL_INFO(opline->op2.constant, LITERAL_CLASS, 1, 1, 2);
229 					}
230 					break;
231 				case ZEND_NEW:
232 					if (opline->op1_type == IS_CONST) {
233 						LITERAL_INFO(opline->op1.constant, LITERAL_CLASS, 1, 1, 2);
234 					}
235 					break;
236 				case ZEND_ASSIGN_OBJ:
237 				case ZEND_FETCH_OBJ_R:
238 				case ZEND_FETCH_OBJ_W:
239 				case ZEND_FETCH_OBJ_RW:
240 				case ZEND_FETCH_OBJ_IS:
241 				case ZEND_FETCH_OBJ_UNSET:
242 				case ZEND_FETCH_OBJ_FUNC_ARG:
243 				case ZEND_UNSET_OBJ:
244 				case ZEND_PRE_INC_OBJ:
245 				case ZEND_PRE_DEC_OBJ:
246 				case ZEND_POST_INC_OBJ:
247 				case ZEND_POST_DEC_OBJ:
248 				case ZEND_ISSET_ISEMPTY_PROP_OBJ:
249 					if (opline->op2_type == IS_CONST) {
250 						optimizer_literal_obj_info(
251 							info,
252 							opline->op1_type,
253 							opline->op1,
254 							opline->op2.constant,
255 							LITERAL_PROPERTY, 2, 1,
256 							op_array);
257 					}
258 					break;
259 				case ZEND_ASSIGN_ADD:
260 				case ZEND_ASSIGN_SUB:
261 				case ZEND_ASSIGN_MUL:
262 				case ZEND_ASSIGN_DIV:
263 				case ZEND_ASSIGN_POW:
264 				case ZEND_ASSIGN_MOD:
265 				case ZEND_ASSIGN_SL:
266 				case ZEND_ASSIGN_SR:
267 				case ZEND_ASSIGN_CONCAT:
268 				case ZEND_ASSIGN_BW_OR:
269 				case ZEND_ASSIGN_BW_AND:
270 				case ZEND_ASSIGN_BW_XOR:
271 					if (opline->op2_type == IS_CONST) {
272 						if (opline->extended_value == ZEND_ASSIGN_OBJ) {
273 							optimizer_literal_obj_info(
274 								info,
275 								opline->op1_type,
276 								opline->op1,
277 								opline->op2.constant,
278 								LITERAL_PROPERTY, 2, 1,
279 								op_array);
280 						} else {
281 							LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 1, 0, 1);
282 						}
283 					}
284 					break;
285 				case ZEND_BIND_GLOBAL:
286 					LITERAL_INFO(opline->op2.constant, LITERAL_GLOBAL, 0, 1, 1);
287 					break;
288 				case ZEND_RECV_INIT:
289 					LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 0, 0, 1);
290 					if (Z_CACHE_SLOT(op_array->literals[opline->op2.constant]) != (uint32_t)-1) {
291 						Z_CACHE_SLOT(op_array->literals[opline->op2.constant]) = cache_size;
292 						cache_size += sizeof(void *);
293 					}
294 					break;
295 				case ZEND_DECLARE_FUNCTION:
296 				case ZEND_DECLARE_CLASS:
297 				case ZEND_DECLARE_INHERITED_CLASS:
298 				case ZEND_DECLARE_INHERITED_CLASS_DELAYED:
299 					LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 0, 0, 2);
300 					break;
301 				case ZEND_RECV:
302 				case ZEND_RECV_VARIADIC:
303 				case ZEND_VERIFY_RETURN_TYPE:
304 					if (opline->op2.num != (uint32_t)-1) {
305 						opline->op2.num = cache_size;
306 						cache_size += sizeof(void *);
307 					}
308 				default:
309 					if (opline->op1_type == IS_CONST) {
310 						LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 1, 0, 1);
311 					}
312 					if (opline->op2_type == IS_CONST) {
313 						LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 1, 0, 1);
314 					}
315 					break;
316 			}
317 			opline++;
318 		}
319 
320 #if DEBUG_COMPACT_LITERALS
321 		{
322 			int i, use_copy;
323 			fprintf(stderr, "File %s func %s\n", op_array->filename->val,
324 					op_array->function_name ? op_array->function_name->val : "main");
325 			fprintf(stderr, "Literlas table size %d\n", op_array->last_literal);
326 
327 			for (i = 0; i < op_array->last_literal; i++) {
328 				zval zv;
329 				ZVAL_COPY_VALUE(&zv, op_array->literals + i);
330 				use_copy = zend_make_printable_zval(op_array->literals + i, &zv);
331 				fprintf(stderr, "Literal %d, val (%d):%s\n", i, Z_STRLEN(zv), Z_STRVAL(zv));
332 				if (use_copy) {
333 					zval_ptr_dtor_nogc(&zv);
334 				}
335 			}
336 			fflush(stderr);
337 		}
338 #endif
339 
340 		/* Merge equal constants */
341 		j = 0;
342 		zend_hash_init(&hash, op_array->last_literal, NULL, NULL, 0);
343 		map = (int*)zend_arena_alloc(&ctx->arena, op_array->last_literal * sizeof(int));
344 		memset(map, 0, op_array->last_literal * sizeof(int));
345 		for (i = 0; i < op_array->last_literal; i++) {
346 			if (!info[i].flags) {
347 				/* unsed literal */
348 				zval_ptr_dtor_nogc(&op_array->literals[i]);
349 				continue;
350 			}
351 			switch (Z_TYPE(op_array->literals[i])) {
352 				case IS_NULL:
353 					/* Only checking MAY_MERGE for IS_NULL here
354 					 * is because only IS_NULL can be default value for class type hinting(RECV_INIT). */
355 					if ((info[i].flags & LITERAL_MAY_MERGE)) {
356 						if (l_null < 0) {
357 							l_null = j;
358 							if (i != j) {
359 								op_array->literals[j] = op_array->literals[i];
360 								info[j] = info[i];
361 							}
362 							j++;
363 						}
364 						map[i] = l_null;
365 					} else {
366 						map[i] = j;
367 						if (i != j) {
368 							op_array->literals[j] = op_array->literals[i];
369 							info[j] = info[i];
370 						}
371 						j++;
372 					}
373 					break;
374 				case IS_FALSE:
375 					if (l_false < 0) {
376 						l_false = j;
377 						if (i != j) {
378 							op_array->literals[j] = op_array->literals[i];
379 							info[j] = info[i];
380 						}
381 						j++;
382 					}
383 					map[i] = l_false;
384 					break;
385 				case IS_TRUE:
386 					if (l_true < 0) {
387 						l_true = j;
388 						if (i != j) {
389 							op_array->literals[j] = op_array->literals[i];
390 							info[j] = info[i];
391 						}
392 						j++;
393 					}
394 					map[i] = l_true;
395 					break;
396 				case IS_LONG:
397 					if ((pos = zend_hash_index_find(&hash, Z_LVAL(op_array->literals[i]))) != NULL) {
398 						map[i] = Z_LVAL_P(pos);
399 					} else {
400 						map[i] = j;
401 						ZVAL_LONG(&zv, j);
402 						zend_hash_index_add_new(&hash, Z_LVAL(op_array->literals[i]), &zv);
403 						if (i != j) {
404 							op_array->literals[j] = op_array->literals[i];
405 							info[j] = info[i];
406 						}
407 						j++;
408 					}
409 					break;
410 				case IS_DOUBLE:
411 					if ((pos = zend_hash_str_find(&hash, (char*)&Z_DVAL(op_array->literals[i]), sizeof(double))) != NULL) {
412 						map[i] = Z_LVAL_P(pos);
413 					} else {
414 						map[i] = j;
415 						ZVAL_LONG(&zv, j);
416 						zend_hash_str_add(&hash, (char*)&Z_DVAL(op_array->literals[i]), sizeof(double), &zv);
417 						if (i != j) {
418 							op_array->literals[j] = op_array->literals[i];
419 							info[j] = info[i];
420 						}
421 						j++;
422 					}
423 					break;
424 				case IS_STRING:
425 				case IS_CONSTANT:
426 					if (info[i].flags & LITERAL_MAY_MERGE) {
427 						if (info[i].flags & LITERAL_EX_OBJ) {
428 							int key_len = sizeof("$this->") - 1 + Z_STRLEN(op_array->literals[i]);
429 							key = zend_string_alloc(key_len, 0);
430 							memcpy(ZSTR_VAL(key), "$this->", sizeof("$this->") - 1);
431 							memcpy(ZSTR_VAL(key) + sizeof("$this->") - 1, Z_STRVAL(op_array->literals[i]), Z_STRLEN(op_array->literals[i]) + 1);
432 							ZSTR_LEN(key) = key_len;
433 						} else if (info[i].flags & LITERAL_EX_CLASS) {
434 							int key_len;
435 							zval *class_name = &op_array->literals[(info[i].u.num < i) ? map[info[i].u.num] : info[i].u.num];
436 							key_len = Z_STRLEN_P(class_name) + sizeof("::") - 1 + Z_STRLEN(op_array->literals[i]);
437 							key = zend_string_alloc(key_len, 0);
438 							memcpy(ZSTR_VAL(key), Z_STRVAL_P(class_name), Z_STRLEN_P(class_name));
439 							memcpy(ZSTR_VAL(key) + Z_STRLEN_P(class_name), "::", sizeof("::") - 1);
440 							memcpy(ZSTR_VAL(key) + Z_STRLEN_P(class_name) + sizeof("::") - 1,
441 								Z_STRVAL(op_array->literals[i]),
442 								Z_STRLEN(op_array->literals[i]) + 1);
443 						} else {
444 							key = zend_string_init(Z_STRVAL(op_array->literals[i]), Z_STRLEN(op_array->literals[i]), 0);
445 						}
446 						ZSTR_H(key) = zend_hash_func(ZSTR_VAL(key), ZSTR_LEN(key));
447 						ZSTR_H(key) += info[i].flags;
448 					}
449 					if ((info[i].flags & LITERAL_MAY_MERGE) &&
450 						(pos = zend_hash_find(&hash, key)) != NULL &&
451 					   	Z_TYPE(op_array->literals[i]) == Z_TYPE(op_array->literals[Z_LVAL_P(pos)]) &&
452 						info[i].flags == info[Z_LVAL_P(pos)].flags) {
453 
454 						zend_string_release(key);
455 						map[i] = Z_LVAL_P(pos);
456 						zval_ptr_dtor_nogc(&op_array->literals[i]);
457 						n = LITERAL_NUM_RELATED(info[i].flags);
458 						while (n > 1) {
459 							i++;
460 							zval_ptr_dtor_nogc(&op_array->literals[i]);
461 							n--;
462 						}
463 					} else {
464 						map[i] = j;
465 						if (info[i].flags & LITERAL_MAY_MERGE) {
466 							ZVAL_LONG(&zv, j);
467 							zend_hash_add_new(&hash, key, &zv);
468 							zend_string_release(key);
469 						}
470 						if (i != j) {
471 							op_array->literals[j] = op_array->literals[i];
472 							info[j] = info[i];
473 						}
474 						if (LITERAL_NUM_SLOTS(info[i].flags)) {
475 							Z_CACHE_SLOT(op_array->literals[j]) = cache_size;
476 							cache_size += LITERAL_NUM_SLOTS(info[i].flags) * sizeof(void*);
477 						}
478 						j++;
479 						n = LITERAL_NUM_RELATED(info[i].flags);
480 						while (n > 1) {
481 							i++;
482 							if (i != j) op_array->literals[j] = op_array->literals[i];
483 							j++;
484 							n--;
485 						}
486 					}
487 					break;
488 				case IS_ARRAY:
489 					if (zend_hash_num_elements(Z_ARRVAL(op_array->literals[i])) == 0) {
490 						if (l_empty_arr < 0) {
491 							l_empty_arr = j;
492 							if (i != j) {
493 								op_array->literals[j] = op_array->literals[i];
494 								info[j] = info[i];
495 							}
496 							j++;
497 						} else {
498 							zval_ptr_dtor_nogc(&op_array->literals[i]);
499 						}
500 						map[i] = l_empty_arr;
501 						break;
502 					}
503 					/* break missing intentionally */
504 				default:
505 					/* don't merge other types */
506 					map[i] = j;
507 					if (i != j) {
508 						op_array->literals[j] = op_array->literals[i];
509 						info[j] = info[i];
510 					}
511 					j++;
512 					break;
513 			}
514 		}
515 		zend_hash_destroy(&hash);
516 		op_array->last_literal = j;
517 		op_array->cache_size = cache_size;
518 
519 	    /* Update opcodes to use new literals table */
520 		opline = op_array->opcodes;
521 		end = opline + op_array->last;
522 		while (opline < end) {
523 			if (opline->op1_type == IS_CONST) {
524 				opline->op1.constant = map[opline->op1.constant];
525 			}
526 			if (opline->op2_type == IS_CONST) {
527 				opline->op2.constant = map[opline->op2.constant];
528 			}
529 			opline++;
530 		}
531 		zend_arena_release(&ctx->arena, checkpoint);
532 
533 #if DEBUG_COMPACT_LITERALS
534 		{
535 			int i, use_copy;
536 			fprintf(stderr, "Optimized literlas table size %d\n", op_array->last_literal);
537 
538 			for (i = 0; i < op_array->last_literal; i++) {
539 				zval zv;
540 				ZVAL_COPY_VALUE(&zv, op_array->literals + i);
541 				use_copy = zend_make_printable_zval(op_array->literals + i, &zv);
542 				fprintf(stderr, "Literal %d, val (%d):%s\n", i, Z_STRLEN(zv), Z_STRVAL(zv));
543 				if (use_copy) {
544 					zval_ptr_dtor_nogc(&zv);
545 				}
546 			}
547 			fflush(stderr);
548 		}
549 #endif
550 	}
551 }
552