1 /*
2 * Copyright 2015 Advanced Micro Devices, Inc.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 * SOFTWARE.
22 *
23 * Authors:
24 * Nicolai Hähnle <nicolai.haehnle@amd.com>
25 *
26 */
27
28 #include "util/u_memory.h"
29 #include "r600_query.h"
30 #include "r600_pipe_common.h"
31 #include "amd/common/r600d_common.h"
32
33 /* Max counters per HW block */
34 #define R600_QUERY_MAX_COUNTERS 16
35
36 static struct r600_perfcounter_block *
lookup_counter(struct r600_perfcounters * pc,unsigned index,unsigned * base_gid,unsigned * sub_index)37 lookup_counter(struct r600_perfcounters *pc, unsigned index,
38 unsigned *base_gid, unsigned *sub_index)
39 {
40 struct r600_perfcounter_block *block = pc->blocks;
41 unsigned bid;
42
43 *base_gid = 0;
44 for (bid = 0; bid < pc->num_blocks; ++bid, ++block) {
45 unsigned total = block->num_groups * block->num_selectors;
46
47 if (index < total) {
48 *sub_index = index;
49 return block;
50 }
51
52 index -= total;
53 *base_gid += block->num_groups;
54 }
55
56 return NULL;
57 }
58
59 static struct r600_perfcounter_block *
lookup_group(struct r600_perfcounters * pc,unsigned * index)60 lookup_group(struct r600_perfcounters *pc, unsigned *index)
61 {
62 unsigned bid;
63 struct r600_perfcounter_block *block = pc->blocks;
64
65 for (bid = 0; bid < pc->num_blocks; ++bid, ++block) {
66 if (*index < block->num_groups)
67 return block;
68 *index -= block->num_groups;
69 }
70
71 return NULL;
72 }
73
74 struct r600_pc_group {
75 struct r600_pc_group *next;
76 struct r600_perfcounter_block *block;
77 unsigned sub_gid; /* only used during init */
78 unsigned result_base; /* only used during init */
79 int se;
80 int instance;
81 unsigned num_counters;
82 unsigned selectors[R600_QUERY_MAX_COUNTERS];
83 };
84
85 struct r600_pc_counter {
86 unsigned base;
87 unsigned qwords;
88 unsigned stride; /* in uint64s */
89 };
90
91 #define R600_PC_SHADERS_WINDOWING (1 << 31)
92
93 struct r600_query_pc {
94 struct r600_query_hw b;
95
96 unsigned shaders;
97 unsigned num_counters;
98 struct r600_pc_counter *counters;
99 struct r600_pc_group *groups;
100 };
101
r600_pc_query_destroy(struct r600_common_context * ctx,struct r600_query * rquery)102 static void r600_pc_query_destroy(struct r600_common_context *ctx,
103 struct r600_query *rquery)
104 {
105 struct r600_query_pc *query = (struct r600_query_pc *)rquery;
106
107 while (query->groups) {
108 struct r600_pc_group *group = query->groups;
109 query->groups = group->next;
110 FREE(group);
111 }
112
113 FREE(query->counters);
114
115 r600_query_hw_destroy(ctx, rquery);
116 }
117
r600_pc_query_prepare_buffer(struct r600_common_context * ctx,struct r600_query_hw * hwquery,struct r600_resource * buffer)118 static bool r600_pc_query_prepare_buffer(struct r600_common_context *ctx,
119 struct r600_query_hw *hwquery,
120 struct r600_resource *buffer)
121 {
122 /* no-op */
123 return true;
124 }
125
r600_pc_query_emit_start(struct r600_common_context * ctx,struct r600_query_hw * hwquery,struct r600_resource * buffer,uint64_t va)126 static void r600_pc_query_emit_start(struct r600_common_context *ctx,
127 struct r600_query_hw *hwquery,
128 struct r600_resource *buffer, uint64_t va)
129 {
130 struct r600_perfcounters *pc = ctx->screen->perfcounters;
131 struct r600_query_pc *query = (struct r600_query_pc *)hwquery;
132 struct r600_pc_group *group;
133 int current_se = -1;
134 int current_instance = -1;
135
136 if (query->shaders)
137 pc->emit_shaders(ctx, query->shaders);
138
139 for (group = query->groups; group; group = group->next) {
140 struct r600_perfcounter_block *block = group->block;
141
142 if (group->se != current_se || group->instance != current_instance) {
143 current_se = group->se;
144 current_instance = group->instance;
145 pc->emit_instance(ctx, group->se, group->instance);
146 }
147
148 pc->emit_select(ctx, block, group->num_counters, group->selectors);
149 }
150
151 if (current_se != -1 || current_instance != -1)
152 pc->emit_instance(ctx, -1, -1);
153
154 pc->emit_start(ctx, buffer, va);
155 }
156
r600_pc_query_emit_stop(struct r600_common_context * ctx,struct r600_query_hw * hwquery,struct r600_resource * buffer,uint64_t va)157 static void r600_pc_query_emit_stop(struct r600_common_context *ctx,
158 struct r600_query_hw *hwquery,
159 struct r600_resource *buffer, uint64_t va)
160 {
161 struct r600_perfcounters *pc = ctx->screen->perfcounters;
162 struct r600_query_pc *query = (struct r600_query_pc *)hwquery;
163 struct r600_pc_group *group;
164
165 pc->emit_stop(ctx, buffer, va);
166
167 for (group = query->groups; group; group = group->next) {
168 struct r600_perfcounter_block *block = group->block;
169 unsigned se = group->se >= 0 ? group->se : 0;
170 unsigned se_end = se + 1;
171
172 if ((block->flags & R600_PC_BLOCK_SE) && (group->se < 0))
173 se_end = ctx->screen->info.max_se;
174
175 do {
176 unsigned instance = group->instance >= 0 ? group->instance : 0;
177
178 do {
179 pc->emit_instance(ctx, se, instance);
180 pc->emit_read(ctx, block,
181 group->num_counters, group->selectors,
182 buffer, va);
183 va += sizeof(uint64_t) * group->num_counters;
184 } while (group->instance < 0 && ++instance < block->num_instances);
185 } while (++se < se_end);
186 }
187
188 pc->emit_instance(ctx, -1, -1);
189 }
190
r600_pc_query_clear_result(struct r600_query_hw * hwquery,union pipe_query_result * result)191 static void r600_pc_query_clear_result(struct r600_query_hw *hwquery,
192 union pipe_query_result *result)
193 {
194 struct r600_query_pc *query = (struct r600_query_pc *)hwquery;
195
196 memset(result, 0, sizeof(result->batch[0]) * query->num_counters);
197 }
198
r600_pc_query_add_result(struct r600_common_context * ctx,struct r600_query_hw * hwquery,void * buffer,union pipe_query_result * result)199 static void r600_pc_query_add_result(struct r600_common_context *ctx,
200 struct r600_query_hw *hwquery,
201 void *buffer,
202 union pipe_query_result *result)
203 {
204 struct r600_query_pc *query = (struct r600_query_pc *)hwquery;
205 uint64_t *results = buffer;
206 unsigned i, j;
207
208 for (i = 0; i < query->num_counters; ++i) {
209 struct r600_pc_counter *counter = &query->counters[i];
210
211 for (j = 0; j < counter->qwords; ++j) {
212 uint32_t value = results[counter->base + j * counter->stride];
213 result->batch[i].u64 += value;
214 }
215 }
216 }
217
218 static struct r600_query_ops batch_query_ops = {
219 .destroy = r600_pc_query_destroy,
220 .begin = r600_query_hw_begin,
221 .end = r600_query_hw_end,
222 .get_result = r600_query_hw_get_result
223 };
224
225 static struct r600_query_hw_ops batch_query_hw_ops = {
226 .prepare_buffer = r600_pc_query_prepare_buffer,
227 .emit_start = r600_pc_query_emit_start,
228 .emit_stop = r600_pc_query_emit_stop,
229 .clear_result = r600_pc_query_clear_result,
230 .add_result = r600_pc_query_add_result,
231 };
232
get_group_state(struct r600_common_screen * screen,struct r600_query_pc * query,struct r600_perfcounter_block * block,unsigned sub_gid)233 static struct r600_pc_group *get_group_state(struct r600_common_screen *screen,
234 struct r600_query_pc *query,
235 struct r600_perfcounter_block *block,
236 unsigned sub_gid)
237 {
238 struct r600_pc_group *group = query->groups;
239
240 while (group) {
241 if (group->block == block && group->sub_gid == sub_gid)
242 return group;
243 group = group->next;
244 }
245
246 group = CALLOC_STRUCT(r600_pc_group);
247 if (!group)
248 return NULL;
249
250 group->block = block;
251 group->sub_gid = sub_gid;
252
253 if (block->flags & R600_PC_BLOCK_SHADER) {
254 unsigned sub_gids = block->num_instances;
255 unsigned shader_id;
256 unsigned shaders;
257 unsigned query_shaders;
258
259 if (block->flags & R600_PC_BLOCK_SE_GROUPS)
260 sub_gids = sub_gids * screen->info.max_se;
261 shader_id = sub_gid / sub_gids;
262 sub_gid = sub_gid % sub_gids;
263
264 shaders = screen->perfcounters->shader_type_bits[shader_id];
265
266 query_shaders = query->shaders & ~R600_PC_SHADERS_WINDOWING;
267 if (query_shaders && query_shaders != shaders) {
268 fprintf(stderr, "r600_perfcounter: incompatible shader groups\n");
269 FREE(group);
270 return NULL;
271 }
272 query->shaders = shaders;
273 }
274
275 if (block->flags & R600_PC_BLOCK_SHADER_WINDOWED && !query->shaders) {
276 // A non-zero value in query->shaders ensures that the shader
277 // masking is reset unless the user explicitly requests one.
278 query->shaders = R600_PC_SHADERS_WINDOWING;
279 }
280
281 if (block->flags & R600_PC_BLOCK_SE_GROUPS) {
282 group->se = sub_gid / block->num_instances;
283 sub_gid = sub_gid % block->num_instances;
284 } else {
285 group->se = -1;
286 }
287
288 if (block->flags & R600_PC_BLOCK_INSTANCE_GROUPS) {
289 group->instance = sub_gid;
290 } else {
291 group->instance = -1;
292 }
293
294 group->next = query->groups;
295 query->groups = group;
296
297 return group;
298 }
299
r600_create_batch_query(struct pipe_context * ctx,unsigned num_queries,unsigned * query_types)300 struct pipe_query *r600_create_batch_query(struct pipe_context *ctx,
301 unsigned num_queries,
302 unsigned *query_types)
303 {
304 struct r600_common_context *rctx = (struct r600_common_context *)ctx;
305 struct r600_common_screen *screen = rctx->screen;
306 struct r600_perfcounters *pc = screen->perfcounters;
307 struct r600_perfcounter_block *block;
308 struct r600_pc_group *group;
309 struct r600_query_pc *query;
310 unsigned base_gid, sub_gid, sub_index;
311 unsigned i, j;
312
313 if (!pc)
314 return NULL;
315
316 query = CALLOC_STRUCT(r600_query_pc);
317 if (!query)
318 return NULL;
319
320 query->b.b.ops = &batch_query_ops;
321 query->b.ops = &batch_query_hw_ops;
322
323 query->num_counters = num_queries;
324
325 /* Collect selectors per group */
326 for (i = 0; i < num_queries; ++i) {
327 unsigned sub_gid;
328
329 if (query_types[i] < R600_QUERY_FIRST_PERFCOUNTER)
330 goto error;
331
332 block = lookup_counter(pc, query_types[i] - R600_QUERY_FIRST_PERFCOUNTER,
333 &base_gid, &sub_index);
334 if (!block)
335 goto error;
336
337 sub_gid = sub_index / block->num_selectors;
338 sub_index = sub_index % block->num_selectors;
339
340 group = get_group_state(screen, query, block, sub_gid);
341 if (!group)
342 goto error;
343
344 if (group->num_counters >= block->num_counters) {
345 fprintf(stderr,
346 "perfcounter group %s: too many selected\n",
347 block->basename);
348 goto error;
349 }
350 group->selectors[group->num_counters] = sub_index;
351 ++group->num_counters;
352 }
353
354 /* Compute result bases and CS size per group */
355 query->b.num_cs_dw_begin = pc->num_start_cs_dwords;
356 query->b.num_cs_dw_end = pc->num_stop_cs_dwords;
357
358 query->b.num_cs_dw_begin += pc->num_instance_cs_dwords; /* conservative */
359 query->b.num_cs_dw_end += pc->num_instance_cs_dwords;
360
361 i = 0;
362 for (group = query->groups; group; group = group->next) {
363 struct r600_perfcounter_block *block = group->block;
364 unsigned select_dw, read_dw;
365 unsigned instances = 1;
366
367 if ((block->flags & R600_PC_BLOCK_SE) && group->se < 0)
368 instances = rctx->screen->info.max_se;
369 if (group->instance < 0)
370 instances *= block->num_instances;
371
372 group->result_base = i;
373 query->b.result_size += sizeof(uint64_t) * instances * group->num_counters;
374 i += instances * group->num_counters;
375
376 pc->get_size(block, group->num_counters, group->selectors,
377 &select_dw, &read_dw);
378 query->b.num_cs_dw_begin += select_dw;
379 query->b.num_cs_dw_end += instances * read_dw;
380 query->b.num_cs_dw_begin += pc->num_instance_cs_dwords; /* conservative */
381 query->b.num_cs_dw_end += instances * pc->num_instance_cs_dwords;
382 }
383
384 if (query->shaders) {
385 if (query->shaders == R600_PC_SHADERS_WINDOWING)
386 query->shaders = 0xffffffff;
387 query->b.num_cs_dw_begin += pc->num_shaders_cs_dwords;
388 }
389
390 /* Map user-supplied query array to result indices */
391 query->counters = CALLOC(num_queries, sizeof(*query->counters));
392 for (i = 0; i < num_queries; ++i) {
393 struct r600_pc_counter *counter = &query->counters[i];
394 struct r600_perfcounter_block *block;
395
396 block = lookup_counter(pc, query_types[i] - R600_QUERY_FIRST_PERFCOUNTER,
397 &base_gid, &sub_index);
398
399 sub_gid = sub_index / block->num_selectors;
400 sub_index = sub_index % block->num_selectors;
401
402 group = get_group_state(screen, query, block, sub_gid);
403 assert(group != NULL);
404
405 for (j = 0; j < group->num_counters; ++j) {
406 if (group->selectors[j] == sub_index)
407 break;
408 }
409
410 counter->base = group->result_base + j;
411 counter->stride = group->num_counters;
412
413 counter->qwords = 1;
414 if ((block->flags & R600_PC_BLOCK_SE) && group->se < 0)
415 counter->qwords = screen->info.max_se;
416 if (group->instance < 0)
417 counter->qwords *= block->num_instances;
418 }
419
420 if (!r600_query_hw_init(rctx, &query->b))
421 goto error;
422
423 return (struct pipe_query *)query;
424
425 error:
426 r600_pc_query_destroy(rctx, &query->b.b);
427 return NULL;
428 }
429
r600_init_block_names(struct r600_common_screen * screen,struct r600_perfcounter_block * block)430 static bool r600_init_block_names(struct r600_common_screen *screen,
431 struct r600_perfcounter_block *block)
432 {
433 unsigned i, j, k;
434 unsigned groups_shader = 1, groups_se = 1, groups_instance = 1;
435 unsigned namelen;
436 char *groupname;
437 char *p;
438
439 if (block->flags & R600_PC_BLOCK_INSTANCE_GROUPS)
440 groups_instance = block->num_instances;
441 if (block->flags & R600_PC_BLOCK_SE_GROUPS)
442 groups_se = screen->info.max_se;
443 if (block->flags & R600_PC_BLOCK_SHADER)
444 groups_shader = screen->perfcounters->num_shader_types;
445
446 namelen = strlen(block->basename);
447 block->group_name_stride = namelen + 1;
448 if (block->flags & R600_PC_BLOCK_SHADER)
449 block->group_name_stride += 3;
450 if (block->flags & R600_PC_BLOCK_SE_GROUPS) {
451 assert(groups_se <= 10);
452 block->group_name_stride += 1;
453
454 if (block->flags & R600_PC_BLOCK_INSTANCE_GROUPS)
455 block->group_name_stride += 1;
456 }
457 if (block->flags & R600_PC_BLOCK_INSTANCE_GROUPS) {
458 assert(groups_instance <= 100);
459 block->group_name_stride += 2;
460 }
461
462 block->group_names = MALLOC(block->num_groups * block->group_name_stride);
463 if (!block->group_names)
464 return false;
465
466 groupname = block->group_names;
467 for (i = 0; i < groups_shader; ++i) {
468 const char *shader_suffix = screen->perfcounters->shader_type_suffixes[i];
469 unsigned shaderlen = strlen(shader_suffix);
470 for (j = 0; j < groups_se; ++j) {
471 for (k = 0; k < groups_instance; ++k) {
472 strcpy(groupname, block->basename);
473 p = groupname + namelen;
474
475 if (block->flags & R600_PC_BLOCK_SHADER) {
476 strcpy(p, shader_suffix);
477 p += shaderlen;
478 }
479
480 if (block->flags & R600_PC_BLOCK_SE_GROUPS) {
481 p += sprintf(p, "%d", j);
482 if (block->flags & R600_PC_BLOCK_INSTANCE_GROUPS)
483 *p++ = '_';
484 }
485
486 if (block->flags & R600_PC_BLOCK_INSTANCE_GROUPS)
487 p += sprintf(p, "%d", k);
488
489 groupname += block->group_name_stride;
490 }
491 }
492 }
493
494 assert(block->num_selectors <= 1000);
495 block->selector_name_stride = block->group_name_stride + 4;
496 block->selector_names = MALLOC(block->num_groups * block->num_selectors *
497 block->selector_name_stride);
498 if (!block->selector_names)
499 return false;
500
501 groupname = block->group_names;
502 p = block->selector_names;
503 for (i = 0; i < block->num_groups; ++i) {
504 for (j = 0; j < block->num_selectors; ++j) {
505 sprintf(p, "%s_%03d", groupname, j);
506 p += block->selector_name_stride;
507 }
508 groupname += block->group_name_stride;
509 }
510
511 return true;
512 }
513
r600_get_perfcounter_info(struct r600_common_screen * screen,unsigned index,struct pipe_driver_query_info * info)514 int r600_get_perfcounter_info(struct r600_common_screen *screen,
515 unsigned index,
516 struct pipe_driver_query_info *info)
517 {
518 struct r600_perfcounters *pc = screen->perfcounters;
519 struct r600_perfcounter_block *block;
520 unsigned base_gid, sub;
521
522 if (!pc)
523 return 0;
524
525 if (!info) {
526 unsigned bid, num_queries = 0;
527
528 for (bid = 0; bid < pc->num_blocks; ++bid) {
529 num_queries += pc->blocks[bid].num_selectors *
530 pc->blocks[bid].num_groups;
531 }
532
533 return num_queries;
534 }
535
536 block = lookup_counter(pc, index, &base_gid, &sub);
537 if (!block)
538 return 0;
539
540 if (!block->selector_names) {
541 if (!r600_init_block_names(screen, block))
542 return 0;
543 }
544 info->name = block->selector_names + sub * block->selector_name_stride;
545 info->query_type = R600_QUERY_FIRST_PERFCOUNTER + index;
546 info->max_value.u64 = 0;
547 info->type = PIPE_DRIVER_QUERY_TYPE_UINT64;
548 info->result_type = PIPE_DRIVER_QUERY_RESULT_TYPE_AVERAGE;
549 info->group_id = base_gid + sub / block->num_selectors;
550 info->flags = PIPE_DRIVER_QUERY_FLAG_BATCH;
551 if (sub > 0 && sub + 1 < block->num_selectors * block->num_groups)
552 info->flags |= PIPE_DRIVER_QUERY_FLAG_DONT_LIST;
553 return 1;
554 }
555
r600_get_perfcounter_group_info(struct r600_common_screen * screen,unsigned index,struct pipe_driver_query_group_info * info)556 int r600_get_perfcounter_group_info(struct r600_common_screen *screen,
557 unsigned index,
558 struct pipe_driver_query_group_info *info)
559 {
560 struct r600_perfcounters *pc = screen->perfcounters;
561 struct r600_perfcounter_block *block;
562
563 if (!pc)
564 return 0;
565
566 if (!info)
567 return pc->num_groups;
568
569 block = lookup_group(pc, &index);
570 if (!block)
571 return 0;
572
573 if (!block->group_names) {
574 if (!r600_init_block_names(screen, block))
575 return 0;
576 }
577 info->name = block->group_names + index * block->group_name_stride;
578 info->num_queries = block->num_selectors;
579 info->max_active_queries = block->num_counters;
580 return 1;
581 }
582
r600_perfcounters_destroy(struct r600_common_screen * rscreen)583 void r600_perfcounters_destroy(struct r600_common_screen *rscreen)
584 {
585 if (rscreen->perfcounters)
586 rscreen->perfcounters->cleanup(rscreen);
587 }
588
r600_perfcounters_init(struct r600_perfcounters * pc,unsigned num_blocks)589 bool r600_perfcounters_init(struct r600_perfcounters *pc,
590 unsigned num_blocks)
591 {
592 pc->blocks = CALLOC(num_blocks, sizeof(struct r600_perfcounter_block));
593 if (!pc->blocks)
594 return false;
595
596 pc->separate_se = debug_get_bool_option("RADEON_PC_SEPARATE_SE", false);
597 pc->separate_instance = debug_get_bool_option("RADEON_PC_SEPARATE_INSTANCE", false);
598
599 return true;
600 }
601
r600_perfcounters_add_block(struct r600_common_screen * rscreen,struct r600_perfcounters * pc,const char * name,unsigned flags,unsigned counters,unsigned selectors,unsigned instances,void * data)602 void r600_perfcounters_add_block(struct r600_common_screen *rscreen,
603 struct r600_perfcounters *pc,
604 const char *name, unsigned flags,
605 unsigned counters, unsigned selectors,
606 unsigned instances, void *data)
607 {
608 struct r600_perfcounter_block *block = &pc->blocks[pc->num_blocks];
609
610 assert(counters <= R600_QUERY_MAX_COUNTERS);
611
612 block->basename = name;
613 block->flags = flags;
614 block->num_counters = counters;
615 block->num_selectors = selectors;
616 block->num_instances = MAX2(instances, 1);
617 block->data = data;
618
619 if (pc->separate_se && (block->flags & R600_PC_BLOCK_SE))
620 block->flags |= R600_PC_BLOCK_SE_GROUPS;
621 if (pc->separate_instance && block->num_instances > 1)
622 block->flags |= R600_PC_BLOCK_INSTANCE_GROUPS;
623
624 if (block->flags & R600_PC_BLOCK_INSTANCE_GROUPS) {
625 block->num_groups = block->num_instances;
626 } else {
627 block->num_groups = 1;
628 }
629
630 if (block->flags & R600_PC_BLOCK_SE_GROUPS)
631 block->num_groups *= rscreen->info.max_se;
632 if (block->flags & R600_PC_BLOCK_SHADER)
633 block->num_groups *= pc->num_shader_types;
634
635 ++pc->num_blocks;
636 pc->num_groups += block->num_groups;
637 }
638
r600_perfcounters_do_destroy(struct r600_perfcounters * pc)639 void r600_perfcounters_do_destroy(struct r600_perfcounters *pc)
640 {
641 unsigned i;
642
643 for (i = 0; i < pc->num_blocks; ++i) {
644 FREE(pc->blocks[i].group_names);
645 FREE(pc->blocks[i].selector_names);
646 }
647 FREE(pc->blocks);
648 FREE(pc);
649 }
650