1 /* 2 * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 * 23 */ 24 25 #include "c1/c1_IR.hpp" 26 #include "c1/c1_ValueMap.hpp" 27 #include "c1/c1_ValueSet.hpp" 28 #include "c1/c1_ValueStack.hpp" 29 #include "utilities/bitMap.inline.hpp" 30 31 #ifndef PRODUCT 32 33 int ValueMap::_number_of_finds = 0; 34 int ValueMap::_number_of_hits = 0; 35 int ValueMap::_number_of_kills = 0; 36 37 #define TRACE_VALUE_NUMBERING(code) if (PrintValueNumbering) { code; } 38 39 #else 40 41 #define TRACE_VALUE_NUMBERING(code) 42 43 #endif 44 45 46 ValueMap::ValueMap() 47 : _nesting(0) 48 , _entries(ValueMapInitialSize, ValueMapInitialSize, nullptr) 49 , _killed_values() 50 , _entry_count(0) 51 { 52 NOT_PRODUCT(reset_statistics()); 53 } 54 55 56 ValueMap::ValueMap(ValueMap* old) 57 : _nesting(old->_nesting + 1) 58 , _entries(old->_entries.length(), old->_entries.length(), nullptr) 59 , _killed_values() 60 , _entry_count(old->_entry_count) 61 { 62 for (int i = size() - 1; i >= 0; i--) { 63 _entries.at_put(i, old->entry_at(i)); 64 } 65 _killed_values.set_from(&old->_killed_values); 66 } 67 68 69 void ValueMap::increase_table_size() { 70 int old_size = size(); 71 int new_size = old_size * 2 + 1; 72 73 ValueMapEntryList worklist(8); 74 ValueMapEntryArray new_entries(new_size, new_size, nullptr); 75 int new_entry_count = 0; 76 77 TRACE_VALUE_NUMBERING(tty->print_cr("increasing table size from %d to %d", old_size, new_size)); 78 79 for (int i = old_size - 1; i >= 0; i--) { 80 ValueMapEntry* entry; 81 for (entry = entry_at(i); entry != nullptr; entry = entry->next()) { 82 if (!is_killed(entry->value())) { 83 worklist.push(entry); 84 } 85 } 86 87 while (!worklist.is_empty()) { 88 entry = worklist.pop(); 89 int new_index = entry_index(entry->hash(), new_size); 90 91 if (entry->nesting() != nesting() && new_entries.at(new_index) != entry->next()) { 92 // changing entries with a lower nesting than the current nesting of the table 93 // is not allowed because then the same entry is contained in multiple value maps. 94 // clone entry when next-pointer must be changed 95 entry = new ValueMapEntry(entry->hash(), entry->value(), entry->nesting(), nullptr); 96 } 97 entry->set_next(new_entries.at(new_index)); 98 new_entries.at_put(new_index, entry); 99 new_entry_count++; 100 } 101 } 102 103 _entries = new_entries; 104 _entry_count = new_entry_count; 105 } 106 107 108 Value ValueMap::find_insert(Value x) { 109 const intx hash = x->hash(); 110 if (hash != 0) { 111 // 0 hash means: exclude from value numbering 112 NOT_PRODUCT(_number_of_finds++); 113 114 for (ValueMapEntry* entry = entry_at(entry_index(hash, size())); entry != nullptr; entry = entry->next()) { 115 if (entry->hash() == hash) { 116 Value f = entry->value(); 117 118 if (!is_killed(f) && f->is_equal(x)) { 119 NOT_PRODUCT(_number_of_hits++); 120 TRACE_VALUE_NUMBERING(tty->print_cr("Value Numbering: %s %c%d equal to %c%d (size %d, entries %d, nesting-diff %d)", x->name(), x->type()->tchar(), x->id(), f->type()->tchar(), f->id(), size(), entry_count(), nesting() - entry->nesting())); 121 122 if (entry->nesting() != nesting() && f->as_Constant() == nullptr) { 123 // non-constant values of of another block must be pinned, 124 // otherwise it is possible that they are not evaluated 125 f->pin(Instruction::PinGlobalValueNumbering); 126 } 127 assert(x->type()->tag() == f->type()->tag(), "should have same type"); 128 129 return f; 130 131 } 132 } 133 } 134 135 // x not found, so insert it 136 if (entry_count() >= size_threshold()) { 137 increase_table_size(); 138 } 139 int idx = entry_index(hash, size()); 140 _entries.at_put(idx, new ValueMapEntry(hash, x, nesting(), entry_at(idx))); 141 _entry_count++; 142 143 TRACE_VALUE_NUMBERING(tty->print_cr("Value Numbering: insert %s %c%d (size %d, entries %d, nesting %d)", x->name(), x->type()->tchar(), x->id(), size(), entry_count(), nesting())); 144 } 145 146 return x; 147 } 148 149 150 #define GENERIC_KILL_VALUE(must_kill_implementation) \ 151 NOT_PRODUCT(_number_of_kills++); \ 152 \ 153 for (int i = size() - 1; i >= 0; i--) { \ 154 ValueMapEntry* prev_entry = nullptr; \ 155 for (ValueMapEntry* entry = entry_at(i); entry != nullptr; entry = entry->next()) { \ 156 Value value = entry->value(); \ 157 \ 158 must_kill_implementation(must_kill, entry, value) \ 159 \ 160 if (must_kill) { \ 161 kill_value(value); \ 162 \ 163 if (prev_entry == nullptr) { \ 164 _entries.at_put(i, entry->next()); \ 165 _entry_count--; \ 166 } else if (prev_entry->nesting() == nesting()) { \ 167 prev_entry->set_next(entry->next()); \ 168 _entry_count--; \ 169 } else { \ 170 prev_entry = entry; \ 171 } \ 172 \ 173 TRACE_VALUE_NUMBERING(tty->print_cr("Value Numbering: killed %s %c%d (size %d, entries %d, nesting-diff %d)", value->name(), value->type()->tchar(), value->id(), size(), entry_count(), nesting() - entry->nesting())); \ 174 } else { \ 175 prev_entry = entry; \ 176 } \ 177 } \ 178 } \ 179 180 #define MUST_KILL_MEMORY(must_kill, entry, value) \ 181 bool must_kill = value->as_LoadField() != nullptr || value->as_LoadIndexed() != nullptr; 182 183 #define MUST_KILL_ARRAY(must_kill, entry, value) \ 184 bool must_kill = value->as_LoadIndexed() != nullptr \ 185 && value->type()->tag() == type->tag(); 186 187 #define MUST_KILL_FIELD(must_kill, entry, value) \ 188 /* ciField's are not unique; must compare their contents */ \ 189 LoadField* lf = value->as_LoadField(); \ 190 bool must_kill = lf != nullptr \ 191 && lf->field()->holder() == field->holder() \ 192 && (all_offsets || lf->field()->offset_in_bytes() == field->offset_in_bytes()); 193 194 195 void ValueMap::kill_memory() { 196 GENERIC_KILL_VALUE(MUST_KILL_MEMORY); 197 } 198 199 void ValueMap::kill_array(ValueType* type) { 200 GENERIC_KILL_VALUE(MUST_KILL_ARRAY); 201 } 202 203 void ValueMap::kill_field(ciField* field, bool all_offsets) { 204 GENERIC_KILL_VALUE(MUST_KILL_FIELD); 205 } 206 207 void ValueMap::kill_map(ValueMap* map) { 208 assert(is_global_value_numbering(), "only for global value numbering"); 209 _killed_values.set_union(&map->_killed_values); 210 } 211 212 void ValueMap::kill_all() { 213 assert(is_local_value_numbering(), "only for local value numbering"); 214 for (int i = size() - 1; i >= 0; i--) { 215 _entries.at_put(i, nullptr); 216 } 217 _entry_count = 0; 218 } 219 220 221 #ifndef PRODUCT 222 223 void ValueMap::print() { 224 tty->print_cr("(size %d, entries %d, nesting %d)", size(), entry_count(), nesting()); 225 226 int entries = 0; 227 for (int i = 0; i < size(); i++) { 228 if (entry_at(i) != nullptr) { 229 tty->print(" %2d: ", i); 230 for (ValueMapEntry* entry = entry_at(i); entry != nullptr; entry = entry->next()) { 231 Value value = entry->value(); 232 tty->print("%s %c%d (%s%d) -> ", value->name(), value->type()->tchar(), value->id(), is_killed(value) ? "x" : "", entry->nesting()); 233 entries++; 234 } 235 tty->print_cr("null"); 236 } 237 } 238 239 _killed_values.print(); 240 assert(entry_count() == entries, "entry_count incorrect"); 241 } 242 243 void ValueMap::reset_statistics() { 244 _number_of_finds = 0; 245 _number_of_hits = 0; 246 _number_of_kills = 0; 247 } 248 249 void ValueMap::print_statistics() { 250 float hit_rate = 0; 251 if (_number_of_finds != 0) { 252 hit_rate = (float)_number_of_hits / _number_of_finds; 253 } 254 255 tty->print_cr("finds:%3d hits:%3d kills:%3d hit rate: %1.4f", _number_of_finds, _number_of_hits, _number_of_kills, hit_rate); 256 } 257 258 #endif 259 260 261 262 class ShortLoopOptimizer : public ValueNumberingVisitor { 263 private: 264 GlobalValueNumbering* _gvn; 265 BlockList _loop_blocks; 266 bool _too_complicated_loop; 267 bool _has_field_store[T_VOID]; 268 bool _has_indexed_store[T_VOID]; 269 270 // simplified access to methods of GlobalValueNumbering 271 ValueMap* current_map() { return _gvn->current_map(); } 272 ValueMap* value_map_of(BlockBegin* block) { return _gvn->value_map_of(block); } 273 274 // implementation for abstract methods of ValueNumberingVisitor 275 void kill_memory() { _too_complicated_loop = true; } 276 void kill_field(ciField* field, bool all_offsets) { 277 current_map()->kill_field(field, all_offsets); 278 assert(field->type()->basic_type() >= 0 && field->type()->basic_type() < T_VOID, "Invalid type"); 279 _has_field_store[field->type()->basic_type()] = true; 280 } 281 void kill_array(ValueType* type) { 282 current_map()->kill_array(type); 283 BasicType basic_type = as_BasicType(type); assert(basic_type < T_VOID, "Invalid type"); 284 _has_indexed_store[basic_type] = true; 285 } 286 287 public: 288 ShortLoopOptimizer(GlobalValueNumbering* gvn) 289 : _gvn(gvn) 290 , _loop_blocks(ValueMapMaxLoopSize) 291 , _too_complicated_loop(false) 292 { 293 for (int i = 0; i < T_VOID; i++) { 294 _has_field_store[i] = false; 295 _has_indexed_store[i] = false; 296 } 297 } 298 299 bool has_field_store(BasicType type) { 300 assert(type < T_VOID, "Invalid type"); 301 return _has_field_store[type]; 302 } 303 304 bool has_indexed_store(BasicType type) { 305 assert(type < T_VOID, "Invalid type"); 306 return _has_indexed_store[type]; 307 } 308 309 bool process(BlockBegin* loop_header); 310 }; 311 312 class LoopInvariantCodeMotion : public StackObj { 313 private: 314 GlobalValueNumbering* _gvn; 315 ShortLoopOptimizer* _short_loop_optimizer; 316 Instruction* _insertion_point; 317 ValueStack * _state; 318 bool _insert_is_pred; 319 320 bool is_invariant(Value v) const { return _gvn->is_processed(v); } 321 322 void process_block(BlockBegin* block); 323 324 public: 325 LoopInvariantCodeMotion(ShortLoopOptimizer *slo, GlobalValueNumbering* gvn, BlockBegin* loop_header, BlockList* loop_blocks); 326 }; 327 328 LoopInvariantCodeMotion::LoopInvariantCodeMotion(ShortLoopOptimizer *slo, GlobalValueNumbering* gvn, BlockBegin* loop_header, BlockList* loop_blocks) 329 : _gvn(gvn), _short_loop_optimizer(slo), _insertion_point(nullptr), _state(nullptr), _insert_is_pred(false) { 330 331 TRACE_VALUE_NUMBERING(tty->print_cr("using loop invariant code motion loop_header = %d", loop_header->block_id())); 332 TRACE_VALUE_NUMBERING(tty->print_cr("** loop invariant code motion for short loop B%d", loop_header->block_id())); 333 334 BlockBegin* insertion_block = loop_header->dominator(); 335 if (insertion_block->number_of_preds() == 0) { 336 return; // only the entry block does not have a predecessor 337 } 338 339 assert(insertion_block->end()->as_Base() == nullptr, "cannot insert into entry block"); 340 _insertion_point = insertion_block->end()->prev(); 341 _insert_is_pred = loop_header->is_predecessor(insertion_block); 342 343 BlockEnd *block_end = insertion_block->end(); 344 _state = block_end->state_before(); 345 346 if (!_state) { 347 // If, TableSwitch and LookupSwitch always have state_before when 348 // loop invariant code motion happens.. 349 assert(block_end->as_Goto(), "Block has to be goto"); 350 _state = block_end->state(); 351 } 352 353 // the loop_blocks are filled by going backward from the loop header, so this processing order is best 354 assert(loop_blocks->at(0) == loop_header, "loop header must be first loop block"); 355 process_block(loop_header); 356 for (int i = loop_blocks->length() - 1; i >= 1; i--) { 357 process_block(loop_blocks->at(i)); 358 } 359 } 360 361 class CheckInsertionPoint : public ValueVisitor { 362 private: 363 Value _insert; 364 bool _valid = true; 365 366 void visit(Value* vp) { 367 assert(*vp != nullptr, "value should not be null"); 368 if (_insert->dominator_depth() < (*vp)->dominator_depth()) { 369 _valid = false; 370 } 371 } 372 373 public: 374 bool is_valid() { return _valid; } 375 CheckInsertionPoint(Value insert) 376 : _insert(insert) { 377 assert(insert != nullptr, "insertion point should not be null"); 378 } 379 }; 380 381 // Check that insertion point has higher dom depth than all inputs to cur 382 static bool is_dominated_by_inputs(Instruction* insertion_point, Instruction* cur) { 383 CheckInsertionPoint v(insertion_point); 384 cur->input_values_do(&v); 385 return v.is_valid(); 386 } 387 388 void LoopInvariantCodeMotion::process_block(BlockBegin* block) { 389 TRACE_VALUE_NUMBERING(tty->print_cr("processing block B%d", block->block_id())); 390 391 Instruction* prev = block; 392 Instruction* cur = block->next(); 393 394 while (cur != nullptr) { 395 // determine if cur instruction is loop invariant 396 // only selected instruction types are processed here 397 bool cur_invariant = false; 398 399 if (cur->as_Constant() != nullptr) { 400 cur_invariant = !cur->can_trap(); 401 } else if (cur->as_ArithmeticOp() != nullptr || cur->as_LogicOp() != nullptr || cur->as_ShiftOp() != nullptr) { 402 assert(cur->as_Op2() != nullptr, "must be Op2"); 403 Op2* op2 = (Op2*)cur; 404 cur_invariant = !op2->can_trap() && is_invariant(op2->x()) && is_invariant(op2->y()); 405 } else if (cur->as_LoadField() != nullptr) { 406 LoadField* lf = (LoadField*)cur; 407 // deoptimizes on NullPointerException 408 cur_invariant = !lf->needs_patching() && !lf->field()->is_volatile() && !_short_loop_optimizer->has_field_store(lf->field()->type()->basic_type()) && is_invariant(lf->obj()) && _insert_is_pred; 409 } else if (cur->as_ArrayLength() != nullptr) { 410 ArrayLength *length = cur->as_ArrayLength(); 411 cur_invariant = is_invariant(length->array()); 412 } else if (cur->as_LoadIndexed() != nullptr) { 413 LoadIndexed *li = (LoadIndexed *)cur->as_LoadIndexed(); 414 cur_invariant = !_short_loop_optimizer->has_indexed_store(as_BasicType(cur->type())) && is_invariant(li->array()) && is_invariant(li->index()) && _insert_is_pred; 415 } else if (cur->as_NegateOp() != nullptr) { 416 NegateOp* neg = (NegateOp*)cur->as_NegateOp(); 417 cur_invariant = is_invariant(neg->x()); 418 } else if (cur->as_Convert() != nullptr) { 419 Convert* cvt = (Convert*)cur->as_Convert(); 420 cur_invariant = is_invariant(cvt->value()); 421 } 422 423 if (cur_invariant && is_dominated_by_inputs(_insertion_point, cur)) { 424 // perform value numbering and mark instruction as loop-invariant 425 _gvn->substitute(cur); 426 427 if (cur->as_Constant() == nullptr) { 428 // ensure that code for non-constant instructions is always generated 429 cur->pin(); 430 } 431 432 // remove cur instruction from loop block and append it to block before loop 433 Instruction* next = cur->next(); 434 Instruction* in = _insertion_point->next(); 435 _insertion_point = _insertion_point->set_next(cur); 436 cur->set_next(in); 437 438 // Deoptimize on exception 439 cur->set_flag(Instruction::DeoptimizeOnException, true); 440 441 // Clear exception handlers 442 cur->set_exception_handlers(nullptr); 443 444 TRACE_VALUE_NUMBERING(tty->print_cr("Instruction %c%d is loop invariant", cur->type()->tchar(), cur->id())); 445 TRACE_VALUE_NUMBERING(cur->print_line()); 446 447 if (cur->state_before() != nullptr) { 448 cur->set_state_before(_state->copy()); 449 } 450 if (cur->exception_state() != nullptr) { 451 cur->set_exception_state(_state->copy()); 452 } 453 454 cur = prev->set_next(next); 455 } else { 456 prev = cur; 457 cur = cur->next(); 458 } 459 } 460 } 461 462 bool ShortLoopOptimizer::process(BlockBegin* loop_header) { 463 TRACE_VALUE_NUMBERING(tty->print_cr("** loop header block")); 464 465 _too_complicated_loop = false; 466 _loop_blocks.clear(); 467 _loop_blocks.append(loop_header); 468 469 for (int i = 0; i < _loop_blocks.length(); i++) { 470 BlockBegin* block = _loop_blocks.at(i); 471 TRACE_VALUE_NUMBERING(tty->print_cr("processing loop block B%d", block->block_id())); 472 473 if (block->is_set(BlockBegin::exception_entry_flag)) { 474 // this would be too complicated 475 return false; 476 } 477 478 // add predecessors to worklist 479 for (int j = block->number_of_preds() - 1; j >= 0; j--) { 480 BlockBegin* pred = block->pred_at(j); 481 482 if (pred->is_set(BlockBegin::osr_entry_flag)) { 483 return false; 484 } 485 486 ValueMap* pred_map = value_map_of(pred); 487 if (pred_map != nullptr) { 488 current_map()->kill_map(pred_map); 489 } else if (!_loop_blocks.contains(pred)) { 490 if (_loop_blocks.length() >= ValueMapMaxLoopSize) { 491 return false; 492 } 493 _loop_blocks.append(pred); 494 } 495 } 496 497 // use the instruction visitor for killing values 498 for (Value instr = block->next(); instr != nullptr; instr = instr->next()) { 499 instr->visit(this); 500 if (_too_complicated_loop) { 501 return false; 502 } 503 } 504 } 505 506 bool optimistic = this->_gvn->compilation()->is_optimistic(); 507 508 if (UseLoopInvariantCodeMotion && optimistic) { 509 LoopInvariantCodeMotion code_motion(this, _gvn, loop_header, &_loop_blocks); 510 } 511 512 TRACE_VALUE_NUMBERING(tty->print_cr("** loop successfully optimized")); 513 return true; 514 } 515 516 517 GlobalValueNumbering::GlobalValueNumbering(IR* ir) 518 : _compilation(ir->compilation()) 519 , _current_map(nullptr) 520 , _value_maps(ir->linear_scan_order()->length(), ir->linear_scan_order()->length(), nullptr) 521 , _has_substitutions(false) 522 { 523 TRACE_VALUE_NUMBERING(tty->print_cr("****** start of global value numbering")); 524 525 ShortLoopOptimizer short_loop_optimizer(this); 526 527 BlockList* blocks = ir->linear_scan_order(); 528 int num_blocks = blocks->length(); 529 530 BlockBegin* start_block = blocks->at(0); 531 assert(start_block == ir->start() && start_block->number_of_preds() == 0 && start_block->dominator() == nullptr, "must be start block"); 532 assert(start_block->next()->as_Base() != nullptr && start_block->next()->next() == nullptr, "start block must not have instructions"); 533 534 // method parameters are not linked in instructions list, so process them separately 535 for_each_state_value(start_block->state(), value, 536 assert(value->as_Local() != nullptr, "only method parameters allowed"); 537 set_processed(value); 538 ); 539 540 // initial, empty value map with nesting 0 541 set_value_map_of(start_block, new ValueMap()); 542 543 for (int i = 1; i < num_blocks; i++) { 544 BlockBegin* block = blocks->at(i); 545 TRACE_VALUE_NUMBERING(tty->print_cr("**** processing block B%d", block->block_id())); 546 547 int num_preds = block->number_of_preds(); 548 assert(num_preds > 0, "block must have predecessors"); 549 550 BlockBegin* dominator = block->dominator(); 551 assert(dominator != nullptr, "dominator must exist"); 552 assert(value_map_of(dominator) != nullptr, "value map of dominator must exist"); 553 554 // create new value map with increased nesting 555 _current_map = new ValueMap(value_map_of(dominator)); 556 557 if (num_preds == 1 && !block->is_set(BlockBegin::exception_entry_flag)) { 558 assert(dominator == block->pred_at(0), "dominator must be equal to predecessor"); 559 // nothing to do here 560 561 } else if (block->is_set(BlockBegin::linear_scan_loop_header_flag)) { 562 // block has incoming backward branches -> try to optimize short loops 563 if (!short_loop_optimizer.process(block)) { 564 // loop is too complicated, so kill all memory loads because there might be 565 // stores to them in the loop 566 current_map()->kill_memory(); 567 } 568 569 } else { 570 // only incoming forward branches that are already processed 571 for (int j = 0; j < num_preds; j++) { 572 BlockBegin* pred = block->pred_at(j); 573 ValueMap* pred_map = value_map_of(pred); 574 575 if (pred_map != nullptr) { 576 // propagate killed values of the predecessor to this block 577 current_map()->kill_map(value_map_of(pred)); 578 } else { 579 // kill all memory loads because predecessor not yet processed 580 // (this can happen with non-natural loops and OSR-compiles) 581 current_map()->kill_memory(); 582 } 583 } 584 } 585 586 // phi functions are not linked in instructions list, so process them separateley 587 for_each_phi_fun(block, phi, 588 set_processed(phi); 589 ); 590 591 TRACE_VALUE_NUMBERING(tty->print("value map before processing block: "); current_map()->print()); 592 593 // visit all instructions of this block 594 for (Value instr = block->next(); instr != nullptr; instr = instr->next()) { 595 // check if instruction kills any values 596 instr->visit(this); 597 // perform actual value numbering 598 substitute(instr); 599 } 600 601 // remember value map for successors 602 set_value_map_of(block, current_map()); 603 } 604 605 if (_has_substitutions) { 606 SubstitutionResolver resolver(ir); 607 } 608 609 TRACE_VALUE_NUMBERING(tty->print("****** end of global value numbering. "); ValueMap::print_statistics()); 610 } 611 612 void GlobalValueNumbering::substitute(Instruction* instr) { 613 assert(!instr->has_subst(), "substitution already set"); 614 Value subst = current_map()->find_insert(instr); 615 if (subst != instr) { 616 assert(!subst->has_subst(), "can't have a substitution"); 617 618 TRACE_VALUE_NUMBERING(tty->print_cr("substitution for %c%d set to %c%d", instr->type()->tchar(), instr->id(), subst->type()->tchar(), subst->id())); 619 instr->set_subst(subst); 620 _has_substitutions = true; 621 } 622 set_processed(instr); 623 }