irprofile.c 17 KB
Newer Older
Christian Würdig's avatar
Christian Würdig committed
1
2
/*
 * This file is part of libFirm.
3
 * Copyright (C) 2012 University of Karlsruhe.
Christian Würdig's avatar
Christian Würdig committed
4
5
 */

Christian Würdig's avatar
Christian Würdig committed
6
7
8
/**
 * @file
 * @brief       Code instrumentation and execution count profiling.
9
10
 * @author      Adam M. Szalkowski, Steven Schaefer
 * @date        06.04.2006, 11.11.2010
11
12
 */
#include <math.h>
13
#include <stdio.h>
14
15
16
17

#include "hashptr.h"
#include "debug.h"
#include "obst.h"
18
#include "xmalloc.h"
19
20
21
22
23
#include "set.h"
#include "irgwalk.h"
#include "irdump_t.h"
#include "irnode_t.h"
#include "ircons_t.h"
24
#include "execfreq_t.h"
Michael Beck's avatar
Michael Beck committed
25
#include "irprofile.h"
26
#include "typerep.h"
27

28
/* Instrument blocks walker. */
29
typedef struct block_id_walker_data_t {
30
31
	unsigned int  id;       /**< current block id number */
	ir_node      *counters; /**< the node representing the counter array */
32
33
} block_id_walker_data_t;

34
35
36
37
38
39
40
41
42
43
44
45
46
/* Associate counters with blocks. */
typedef struct block_assoc_t {
	unsigned int i;          /**< current block id number */
	unsigned int *counters;  /**< block execution counts */
} block_assoc_t;

/* minimal execution frequency (an execfreq of 0 confuses algos) */
#define MIN_EXECFREQ 0.00001

/* keep the execcounts here because they are only read once per compiler run */
static set *profile = NULL;

/* Hook for vcg output. */
47
static hook_entry_t *hook;
48
49
50
51
52
53
54
55

/* The debug module handle. */
DEBUG_ONLY(static firm_dbg_module_t *dbg;)

/* Since the backend creates a new firm graph we cannot associate counts with
 * blocks directly. Instead we associate them with the block ids, which are
 * maintained.
 */
56
typedef struct execcount_t {
57
	unsigned long block; /**< block id */
58
	uint32_t      count; /**< execution count */
59
60
} execcount_t;

Michael Beck's avatar
Michael Beck committed
61
62
63
/**
 * Compare two execcount_t entries.
 */
64
65
static int cmp_execcount(const void *a, const void *b, size_t size)
{
66
67
	const execcount_t *ea = (const execcount_t*)a;
	const execcount_t *eb = (const execcount_t*)b;
68
	(void) size;
Michael Beck's avatar
Michael Beck committed
69
	return ea->block != eb->block;
70
}
71

72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
uint32_t ir_profile_get_block_execcount(const ir_node *block)
{
	execcount_t *ec, query;

	query.block = get_irn_node_nr(block);
	ec = set_find(execcount_t, profile, &query, sizeof(query), query.block);

	if (ec != NULL) {
		return ec->count;
	} else {
		DBG((dbg, LEVEL_3,
			"Warning: Profile contains no data for %+F\n", block));
		return 0;
	}
}

Michael Beck's avatar
Michael Beck committed
88
89
90
/**
 * Block walker, count number of blocks.
 */
91
static void block_counter(ir_node *bb, void *data)
92
{
93
	unsigned *count = (unsigned*) data;
Michael Beck's avatar
Michael Beck committed
94
95
	(void) bb;
	++(*count);
96
97
}

Michael Beck's avatar
Michael Beck committed
98
/**
99
 * Returns the number of blocks the given graph.
Michael Beck's avatar
Michael Beck committed
100
 */
101
static unsigned int get_irg_n_blocks(ir_graph *irg)
102
{
103
104
105
106
107
108
	unsigned int count = 0;
	irg_block_walk_graph(irg, block_counter, NULL, &count);
	return count;
}

/**
109
 * Returns the number of basic blocks in the current ir program.
110
 */
111
static unsigned int get_irp_n_blocks(void)
112
{
113
114
	int i, n = get_irp_n_irgs();
	unsigned int count = 0;
Matthias Braun's avatar
fixes    
Matthias Braun committed
115

116
117
118
119
	for (i = 0; i < n; i++) {
		ir_graph *irg = get_irp_irg(i);
		count += get_irg_n_blocks(irg);
	}
Matthias Braun's avatar
fixes    
Matthias Braun committed
120

121
	return count;
122
123
}

124
125
/* vcg helper */
static void dump_profile_node_info(void *ctx, FILE *f, const ir_node *irn)
126
{
127
128
129
130
	(void) ctx;
	if (is_Block(irn)) {
		unsigned int execcount = ir_profile_get_block_execcount(irn);
		fprintf(f, "profiled execution count: %u\n", execcount);
131
132
133
	}
}

134
135
136
/**
 * Add the given method entity as a constructor.
 */
137
138
139
static void add_constructor(ir_entity *method)
{
    ir_type   *method_type  = get_entity_type(method);
140
    ir_type   *ptr_type     = new_type_pointer(method_type);
141

142
    ir_type   *constructors = get_segment_type(IR_SEGMENT_CONSTRUCTORS);
143
144
145
	ident     *ide = id_unique("constructor_ptr.%u");
    ir_entity *ptr = new_entity(constructors, ide, ptr_type);
    ir_graph  *irg = get_const_code_irg();
146
    ir_node   *val = new_r_Address(irg, method);
147
148

	set_entity_ld_ident(ptr, new_id_from_chars("", 0));
149
    set_entity_compiler_generated(ptr, 1);
150
151
    set_entity_linkage(ptr, IR_LINKAGE_CONSTANT | IR_LINKAGE_HIDDEN_USER);
    set_entity_visibility(ptr, ir_visibility_private);
152
153
    set_atomic_ent_value(ptr, val);
}
154

155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
/**
 * Returns an entity representing the __init_firmprof function from libfirmprof
 * This is the equivalent of:
 * extern void __init_firmprof(char *filename, uint *counters, uint size)
 */
static ir_entity *get_init_firmprof_ref(void)
{
	ident   *init_name = new_id_from_str("__init_firmprof");
	ir_type *init_type = new_type_method(3, 0);
	ir_type *uint      = new_type_primitive(mode_Iu);
	ir_type *uintptr   = new_type_pointer(uint);
	ir_type *string    = new_type_pointer(new_type_primitive(mode_Bs));
	ir_entity *result;

	set_method_param_type(init_type, 0, string);
	set_method_param_type(init_type, 1, uintptr);
	set_method_param_type(init_type, 2, uint);

	result = new_entity(get_glob_type(), init_name, init_type);
	set_entity_visibility(result, ir_visibility_external);

	return result;
}

179
180
/**
 * Generates a new irg which calls the initializer
Michael Beck's avatar
BugFix:    
Michael Beck committed
181
182
 *
 * Pseudocode:
183
184
185
186
 *    static void __firmprof_initializer(void) __attribute__ ((constructor))
 *    {
 *        __init_firmprof(ent_filename, bblock_counts, n_blocks);
 *    }
187
 */
188
189
static ir_graph *gen_initializer_irg(ir_entity *ent_filename,
                                     ir_entity *bblock_counts, int n_blocks)
190
{
191
192
	ir_graph *irg;
	ir_node  *ins[3];
193
	ir_node  *bb, *ret, *call;
194
	ir_type  *empty_frame_type;
195

196
	ir_entity *init_ent = get_init_firmprof_ref();
Adam Szalkowski's avatar
Adam Szalkowski committed
197

198
199
200
	ident     *name = new_id_from_str("__firmprof_initializer");
	ir_entity *ent  = new_entity(get_glob_type(), name, new_type_method(0, 0));
	set_entity_visibility(ent, ir_visibility_local);
Christian Würdig's avatar
Christian Würdig committed
201
202
	set_entity_ld_ident(ent, name);

203
	/* create the new ir_graph */
Christian Würdig's avatar
Christian Würdig committed
204
	irg = new_ir_graph(ent, 0);
Christian Würdig's avatar
Christian Würdig committed
205
206
	empty_frame_type = get_irg_frame_type(irg);
	set_type_size_bytes(empty_frame_type, 0);
207
	set_type_state(empty_frame_type, layout_fixed);
208

209
	bb = get_r_cur_block(irg);
Adam Szalkowski's avatar
Adam Szalkowski committed
210

211
	ir_node *const callee = new_r_Address(irg, init_ent);
Adam Szalkowski's avatar
Adam Szalkowski committed
212

213
214
	ins[0] = new_r_Address(irg, ent_filename);
	ins[1] = new_r_Address(irg, bblock_counts);
215
	ins[2] = new_r_Const_long(irg, mode_Iu, n_blocks);
216

217
	call = new_r_Call(bb, get_irg_initial_mem(irg), callee, 3, ins,
218
219
	        get_entity_type(init_ent));
	ret  = new_r_Return(bb, new_r_Proj(call, mode_M, pn_Call_M), 0, NULL);
220
221
222
223
224
	mature_immBlock(bb);

	add_immBlock_pred(get_irg_end_block(irg), ret);
	mature_immBlock(get_irg_end_block(irg));

Matthias Braun's avatar
fixes    
Matthias Braun committed
225
226
	irg_finalize_cons(irg);

227
	/* add a pointer to the new function in the constructor section */
228
229
	add_constructor(ent);

230
231
232
	return irg;
}

233
/**
234
235
236
 * Instrument a block with code needed for profiling.
 * This just inserts the instruction nodes, it doesn't connect the memory
 * nodes in a meaningful way.
237
 */
238
static void instrument_block(ir_node *bb, ir_node *address, unsigned int id)
239
{
240
241
242
243
244
245
246
247
248
249
250
251
252
	ir_graph *irg = get_irn_irg(bb);
	ir_node  *load, *store, *offset, *add, *projm, *proji, *unknown, *cnst;

	/* We can't instrument the end block */
	if (bb == get_irg_end_block(irg))
		return;

	unknown = new_r_Unknown(irg, mode_M);
	cnst    = new_r_Const_long(irg, mode_Iu, get_mode_size_bytes(mode_Iu) * id);
	offset  = new_r_Add(bb, address, cnst, get_modeP_data());
	load    = new_r_Load(bb, unknown, offset, mode_Iu, cons_none);
	projm   = new_r_Proj(load, mode_M, pn_Load_M);
	proji   = new_r_Proj(load, mode_Iu, pn_Load_res);
253
	cnst    = new_r_Const_one(irg, mode_Iu);
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
	add     = new_r_Add(bb, proji, cnst, mode_Iu);
	store   = new_r_Store(bb, projm, offset, add, cons_none);
	projm   = new_r_Proj(store, mode_M, pn_Store_M);

	set_irn_link(bb, projm);
	set_irn_link(projm, load);
}

/**
 * SSA Construction for instrumentation code memory.
 *
 * This introduces a new memory node and connects it to the instrumentation
 * codes, inserting phiM nodes as necessary. Note that afterwards, the new
 * memory is not connected to any return nodes and thus still dead.
 */
static void fix_ssa(ir_node *bb, void *data)
{
	ir_graph *irg = get_irn_irg(bb);
	ir_node *mem, *proj, *load;
	int n, arity = get_Block_n_cfgpreds(bb);

	(void) data;

	/* end blocks are not instrumented, skip! */
	if (bb == get_irg_end_block(irg))
		return;

	if (bb == get_irg_start_block(irg)) {
		mem = get_irg_initial_mem(irg);
	} else if (arity == 1) {
		ir_node *pred = get_Block_cfgpred_block(bb, 0);
285
		if (pred != NULL)
286
287
288
			mem = (ir_node*) get_irn_link(pred);
		else
			mem = new_r_NoMem(irg);
289
	} else {
290
291
292
		ir_node **ins = ALLOCAN(ir_node*, arity);
		for (n = arity - 1; n >= 0; --n) {
			ir_node *pred = get_Block_cfgpred_block(bb, n);
293
			if (pred != NULL)
294
295
296
297
298
				ins[n] = (ir_node*) get_irn_link(pred);
			else
				ins[n] = new_r_NoMem(irg);
		}
		mem = new_r_Phi(bb, arity, ins, mode_M);
299
	}
300
301
302
303
304
305
306

	/* The block link fields point to the projm from the instrumentation code,
	 * the projm in turn links to the initial load which lacks a memory
	 * argument at this point. */
	proj = (ir_node*) get_irn_link(bb);
	load = (ir_node*) get_irn_link(proj);
	set_Load_mem(load, mem);
307
308
309
}

/**
310
 * Instrument a single block.
311
 */
312
static void block_instrument_walker(ir_node *bb, void *data)
313
{
314
	block_id_walker_data_t *wd = (block_id_walker_data_t*)data;
315
	instrument_block(bb, wd->counters, wd->id);
316
317
318
	++wd->id;
}

319
320
321
322
323
324
325
326
327
328
329
/**
 * Synchronize the original memory input of node with the additional operand
 * from the profiling code.
 */
static ir_node *sync_mem(ir_node *bb, ir_node *mem)
{
	ir_node *ins[2];
	ins[0] = (ir_node*) get_irn_link(bb);
	ins[1] = mem;
	return new_r_Sync(bb, 2, ins);
}
330

331
332
333
334
335
336
/**
 * Instrument a single ir_graph, counters should point to the bblock
 * counters array.
 */
static void instrument_irg(ir_graph *irg, ir_entity *counters,
                           block_id_walker_data_t *wd)
337
{
338
339
340
	ir_node *end   = get_irg_end(irg);
	ir_node *endbb = get_irg_end_block(irg);
	int i;
341

342
	/* generate a node pointing to the count array */
343
	wd->counters = new_r_Address(irg, counters);
344
345
346
347
348
349
350
351
352

	/* instrument each block in the current irg */
	irg_block_walk_graph(irg, block_instrument_walker, NULL, wd);
	irg_block_walk_graph(irg, fix_ssa, NULL, NULL);

	/* connect the new memory nodes to the return nodes */
	for (i = get_Block_n_cfgpreds(endbb) - 1; i >= 0; --i) {
		ir_node *node = skip_Proj(get_Block_cfgpred(endbb, i));
		ir_node *bb   = get_Block_cfgpred_block(endbb, i);
353
354
		if (bb == NULL)
			continue;
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
		ir_node *mem;

		switch (get_irn_opcode(node)) {
		case iro_Return:
			mem = get_Return_mem(node);
			set_Return_mem(node, sync_mem(bb, mem));
			break;
		case iro_Raise:
			mem = get_Raise_mem(node);
			set_Raise_mem(node, sync_mem(bb, mem));
			break;
		case iro_Bad:
			break;
		default:
			/* A fragile's op exception. There should be another path to End,
			 * so ignore it.
			 */
			assert(is_fragile_op(node) && \
				"unexpected End control flow predecessor");
		}
	}
376

377
378
379
380
381
382
383
384
	/* as well as calls with attribute noreturn */
	for (i = get_End_n_keepalives(end) - 1; i >= 0; --i) {
		ir_node *node = get_End_keepalive(end, i);
		if (is_Call(node)) {
			ir_node *bb  = get_nodes_block(node);
			ir_node *mem = get_Call_mem(node);
			set_Call_mem(node, sync_mem(bb, mem));
		}
385
	}
386
}
387

388
389
390
391
392
393
394
395
396
397
/**
 * Creates a new entity representing the equivalent of
 * static unsigned int name[size]
 */
static ir_entity *new_array_entity(ident *name, int size)
{
	ir_entity *result;
	ir_type *uint_type, *array_type;

	uint_type = new_type_primitive(mode_Iu);
398
	set_type_alignment_bytes(uint_type, get_type_size_bytes(uint_type));
399

400
401
	array_type = new_type_array(uint_type);
	set_array_size_int(array_type, size);
402
	set_type_size_bytes(array_type, size * get_mode_size_bytes(mode_Iu));
403
404
	set_type_alignment_bytes(array_type, get_mode_size_bytes(mode_Iu));
	set_type_state(array_type, layout_fixed);
Christian Würdig's avatar
Christian Würdig committed
405

406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
	result = new_entity(get_glob_type(), name, array_type);
	set_entity_visibility(result, ir_visibility_local);
	set_entity_compiler_generated(result, 1);

	return result;
}

/**
 * Creates a new entity representing the equivalent of
 * static const char name[strlen(string)+1] = string
 */
static ir_entity *new_static_string_entity(ident *name, const char *string)
{
	ir_entity *result;

	ir_type *char_type   = new_type_primitive(mode_Bs);
422
	ir_type *string_type = new_type_array(char_type);
423
424
425
426
427
428

	ir_initializer_t *contents;

	size_t i, length = strlen(string)+1;

	/* Create the type for a fixed-length string */
429
	set_array_size_int(string_type, length);
430
	set_type_size_bytes(string_type, length);
431
432
	set_type_alignment_bytes(string_type, 1);
	set_type_state(string_type, layout_fixed);
Christian Würdig's avatar
Christian Würdig committed
433

434
435
436
437
438
439
440
441
442
443
444
445
	result = new_entity(get_glob_type(), name, string_type);
	set_entity_visibility(result, ir_visibility_local);
	set_entity_linkage(result, IR_LINKAGE_CONSTANT);
	set_entity_compiler_generated(result, 1);

	/* There seems to be no simpler way to do this. Or at least, cparser
	 * does exactly the same thing... */
	contents = create_initializer_compound(length);
	for (i = 0; i < length; i++) {
		ir_tarval *c = new_tarval_from_long(string[i], mode_Bs);
		ir_initializer_t *init = create_initializer_tarval(c);
		set_initializer_compound_value(contents, i, init);
446
	}
447
	set_entity_initializer(result, contents);
448

449
450
	return result;
}
451

452
ir_graph *ir_profile_instrument(const char *filename)
453
454
455
456
457
458
{
	int n, n_blocks = 0;
	ident *counter_id, *filename_id;
	ir_entity *bblock_counts, *ent_filename;
	block_id_walker_data_t wd;
	FIRM_DBG_REGISTER(dbg, "firm.ir.profile");
459

460
461
462
	/* Don't do anything for modules without code. Else the linker will
	 * complain. */
	if (get_irp_n_irgs() == 0)
463
		return NULL;
464

465
466
467
468
469
470
471
472
473
474
475
476
477
478
	/* count the number of block first */
	n_blocks = get_irp_n_blocks();

	/* create all the necessary types and entities. Note that the
	 * types must have a fixed layout, because we are already running in the
	 * backend */
	counter_id    = new_id_from_str("__FIRMPROF__BLOCK_COUNTS");
	bblock_counts = new_array_entity(counter_id, n_blocks);

	filename_id  = new_id_from_str("__FIRMPROF__FILE_NAME");
	ent_filename = new_static_string_entity(filename_id, filename);

	/* initialize block id array and instrument blocks */
	wd.id  = 0;
Christian Würdig's avatar
Christian Würdig committed
479
	for (n = get_irp_n_irgs() - 1; n >= 0; --n) {
480
481
		ir_graph *irg = get_irp_irg(n);
		instrument_irg(irg, bblock_counts, &wd);
482
	}
483

484
	return gen_initializer_irg(ent_filename, bblock_counts, n_blocks);
485
486
}

487
static unsigned int *parse_profile(const char *filename, unsigned int num_blocks)
488
{
489
490
491
492
	FILE *f = fopen(filename, "rb");
	if (!f) {
		DBG((dbg, LEVEL_2, "Failed to open profile file (%s)\n", filename));
		return NULL;
493
494
	}

495
	/* check header */
496
497
498
	uint32_t *result = NULL;
	char      buf[8];
	size_t    ret = fread(buf, 8, 1, f);
499
500
501
502
	if (ret == 0 || strncmp(buf, "firmprof", 8) != 0) {
		DBG((dbg, LEVEL_2, "Broken fileheader in profile\n"));
		goto end;
	}
503

504
	result = XMALLOCN(unsigned int, num_blocks);
505
506
507

	/* The profiling output format is defined to be a sequence of integer
	 * values stored little endian format. */
508
	for (unsigned i = 0; i < num_blocks; ++i) {
Manuel Mohr's avatar
Manuel Mohr committed
509
		unsigned char bytes[4];
510

Manuel Mohr's avatar
Manuel Mohr committed
511
		if ((ret = fread(bytes, 1, 4, f)) < 1)
512
513
514
515
516
517
518
			break;

		result[i] = (bytes[0] <<  0) | (bytes[1] <<  8)
		          | (bytes[2] << 16) | (bytes[3] << 24);
	}

	if (ret < 1) {
519
		DBG((dbg, LEVEL_4, "Failed to read counters... (size: %u)\n",
520
			sizeof(unsigned int) * num_blocks));
521
		free(result);
522
523
524
525
526
527
		result = NULL;
	}

end:
	fclose(f);
	return result;
528
}
529

530
/**
531
 * Reads the corresponding profile info file if it exists.
532
 */
533
534
535
536
537
538
539
540
541
static void block_associate_walker(ir_node *bb, void *env)
{
	block_assoc_t *b = (block_assoc_t*) env;
	execcount_t query;

	query.block = get_irn_node_nr(bb);
	query.count = b->counters[(b->i)++];
	DBG((dbg, LEVEL_4, "execcount(%+F, %u): %u\n", bb, query.block,
	    query.count));
yb9976's avatar
yb9976 committed
542
	(void)set_insert(execcount_t, profile, &query, sizeof(query), query.block);
543
}
544

545
546
static void irp_associate_blocks(block_assoc_t *env)
{
547
	for (int n = get_irp_n_irgs() - 1; n >= 0; --n) {
548
549
		ir_graph *irg = get_irp_irg(n);
		irg_block_walk_graph(irg, block_associate_walker, NULL, env);
550
	}
551
}
552

553
void ir_profile_free(void)
554
{
555
	if (profile) {
556
		del_set(profile);
557
558
559
560
561
562
		profile = NULL;
	}

	if (hook != NULL) {
		dump_remove_node_info_callback(hook);
		hook = NULL;
563
	}
564
565
}

566
bool ir_profile_read(const char *filename)
Adam Szalkowski's avatar
Adam Szalkowski committed
567
{
568
569
	block_assoc_t env;
	FIRM_DBG_REGISTER(dbg, "firm.ir.profile");
Adam Szalkowski's avatar
Adam Szalkowski committed
570

571
572
573
574
575
	unsigned n_blocks = get_irp_n_blocks();
	env.i        = 0;
	env.counters = parse_profile(filename, n_blocks);
	if (!env.counters)
		return false;
576

577
578
	ir_profile_free();
	profile = new_set(cmp_execcount, 16);
579

580
	irp_associate_blocks(&env);
581
	free(env.counters);
582

583
584
585
	/* register the vcg hook */
	hook = dump_add_node_info_callback(dump_profile_node_info, NULL);
	return 1;
586
}
587

588
589
590
591
typedef struct initialize_execfreq_env_t {
	double freq_factor;
} initialize_execfreq_env_t;

592
593
static void initialize_execfreq(ir_node *block, void *data)
{
594
595
596
	const initialize_execfreq_env_t *env
		= (const initialize_execfreq_env_t*) data;
	ir_graph *irg = get_irn_irg(block);
597
598
	double freq;

599
	if (block == get_irg_start_block(irg) || block == get_irg_end_block(irg)) {
600
601
		freq = 1.0;
	} else {
Michael Beck's avatar
Michael Beck committed
602
		freq = ir_profile_get_block_execcount(block);
603
		freq *= env->freq_factor;
604
		if (freq < MIN_EXECFREQ)
605
			freq = MIN_EXECFREQ;
606
607
	}

608
	set_block_execfreq(block, freq);
609
610
}

611
static void ir_set_execfreqs_from_profile(ir_graph *irg)
612
{
613
	/* Find the first block containing instructions */
614
615
	ir_node *start_block = get_irg_start_block(irg);
	unsigned count       = ir_profile_get_block_execcount(start_block);
616
	if (count == 0) {
617
		/* the function was never executed, so fallback to estimated freqs */
618
619
		ir_estimate_execfreq(irg);
		return;
620
621
	}

622
	initialize_execfreq_env_t env;
623
	env.freq_factor = 1.0 / count;
624
	irg_block_walk_graph(irg, initialize_execfreq, NULL, &env);
625
}
626

627
628
629
630
631
632
void ir_create_execfreqs_from_profile(void)
{
	for (int n = get_irp_n_irgs() - 1; n >= 0; --n) {
		ir_graph *irg = get_irp_irg(n);
		ir_set_execfreqs_from_profile(irg);
	}
633
}