📄 stmt.c
字号:
directly. This is rather an exceptional case, and there aren't that many places where this is necessary. */ if (output_bytecode) { expand_goto_internal (body, label, last_insn); return; } if (GET_CODE (label) != CODE_LABEL) abort (); /* If label has already been defined, we can tell now whether and how we must alter the stack level. */ if (PREV_INSN (label) != 0) { /* Find the innermost pending block that contains the label. (Check containment by comparing insn-uids.) Then restore the outermost stack level within that block, and do cleanups of all blocks contained in it. */ for (block = block_stack; block; block = block->next) { if (INSN_UID (block->data.block.first_insn) < INSN_UID (label)) break; if (block->data.block.stack_level != 0) stack_level = block->data.block.stack_level; /* Execute the cleanups for blocks we are exiting. */ if (block->data.block.cleanups != 0) { expand_cleanups (block->data.block.cleanups, NULL_TREE, 1, 1); do_pending_stack_adjust (); } } if (stack_level) { /* Ensure stack adjust isn't done by emit_jump, as this would clobber the stack pointer. This one should be deleted as dead by flow. */ clear_pending_stack_adjust (); do_pending_stack_adjust (); emit_stack_restore (SAVE_BLOCK, stack_level, NULL_RTX); } if (body != 0 && DECL_TOO_LATE (body)) error ("jump to `%s' invalidly jumps into binding contour", IDENTIFIER_POINTER (DECL_NAME (body))); } /* Label not yet defined: may need to put this goto on the fixup list. */ else if (! expand_fixup (body, label, last_insn)) { /* No fixup needed. Record that the label is the target of at least one goto that has no fixup. */ if (body != 0) TREE_ADDRESSABLE (body) = 1; } emit_jump (label);}/* Generate a jump with OPCODE to the given bytecode LABEL which is found within BODY. */static voidbc_expand_goto_internal (opcode, label, body) enum bytecode_opcode opcode; struct bc_label *label; tree body;{ struct nesting *block; int stack_level = -1; /* If the label is defined, adjust the stack as necessary. If it's not defined, we have to push the reference on the fixup list. */ if (label->defined) { /* Find the innermost pending block that contains the label. (Check containment by comparing bytecode uids.) Then restore the outermost stack level within that block. */ for (block = block_stack; block; block = block->next) { if (BYTECODE_BC_LABEL (block->data.block.first_insn)->uid < label->uid) break; if (block->data.block.bc_stack_level) stack_level = block->data.block.bc_stack_level; /* Execute the cleanups for blocks we are exiting. */ if (block->data.block.cleanups != 0) { expand_cleanups (block->data.block.cleanups, NULL_TREE, 1, 1); do_pending_stack_adjust (); } } /* Restore the stack level. If we need to adjust the stack, we must do so after the jump, since the jump may depend on what's on the stack. Thus, any stack-modifying conditional jumps (these are the only ones that rely on what's on the stack) go into the fixup list. */ if (stack_level >= 0 && stack_depth != stack_level && opcode != jump) bc_expand_fixup (opcode, label, stack_level); else { if (stack_level >= 0) bc_adjust_stack (stack_depth - stack_level); if (body && DECL_BIT_FIELD (body)) error ("jump to `%s' invalidly jumps into binding contour", IDENTIFIER_POINTER (DECL_NAME (body))); /* Emit immediate jump */ bc_emit_bytecode (opcode); bc_emit_bytecode_labelref (label); #ifdef DEBUG_PRINT_CODE fputc ('\n', stderr);#endif } } else /* Put goto in the fixup list */ bc_expand_fixup (opcode, label, stack_level);}/* Generate if necessary a fixup for a goto whose target label in tree structure (if any) is TREE_LABEL and whose target in rtl is RTL_LABEL. If LAST_INSN is nonzero, we pretend that the jump appears after insn LAST_INSN instead of at the current point in the insn stream. The fixup will be used later to insert insns just before the goto. Those insns will restore the stack level as appropriate for the target label, and will (in the case of C++) also invoke any object destructors which have to be invoked when we exit the scopes which are exited by the goto. Value is nonzero if a fixup is made. */static intexpand_fixup (tree_label, rtl_label, last_insn) tree tree_label; rtx rtl_label; rtx last_insn;{ struct nesting *block, *end_block; /* See if we can recognize which block the label will be output in. This is possible in some very common cases. If we succeed, set END_BLOCK to that block. Otherwise, set it to 0. */ if (cond_stack && (rtl_label == cond_stack->data.cond.endif_label || rtl_label == cond_stack->data.cond.next_label)) end_block = cond_stack; /* If we are in a loop, recognize certain labels which are likely targets. This reduces the number of fixups we need to create. */ else if (loop_stack && (rtl_label == loop_stack->data.loop.start_label || rtl_label == loop_stack->data.loop.end_label || rtl_label == loop_stack->data.loop.continue_label)) end_block = loop_stack; else end_block = 0; /* Now set END_BLOCK to the binding level to which we will return. */ if (end_block) { struct nesting *next_block = end_block->all; block = block_stack; /* First see if the END_BLOCK is inside the innermost binding level. If so, then no cleanups or stack levels are relevant. */ while (next_block && next_block != block) next_block = next_block->all; if (next_block) return 0; /* Otherwise, set END_BLOCK to the innermost binding level which is outside the relevant control-structure nesting. */ next_block = block_stack->next; for (block = block_stack; block != end_block; block = block->all) if (block == next_block) next_block = next_block->next; end_block = next_block; } /* Does any containing block have a stack level or cleanups? If not, no fixup is needed, and that is the normal case (the only case, for standard C). */ for (block = block_stack; block != end_block; block = block->next) if (block->data.block.stack_level != 0 || block->data.block.cleanups != 0) break; if (block != end_block) { /* Ok, a fixup is needed. Add a fixup to the list of such. */ struct goto_fixup *fixup = (struct goto_fixup *) oballoc (sizeof (struct goto_fixup)); /* In case an old stack level is restored, make sure that comes after any pending stack adjust. */ /* ?? If the fixup isn't to come at the present position, doing the stack adjust here isn't useful. Doing it with our settings at that location isn't useful either. Let's hope someone does it! */ if (last_insn == 0) do_pending_stack_adjust (); fixup->target = tree_label; fixup->target_rtl = rtl_label; /* Create a BLOCK node and a corresponding matched set of NOTE_INSN_BEGIN_BLOCK and NOTE_INSN_END_BLOCK notes at this point. The notes will encapsulate any and all fixup code which we might later insert at this point in the insn stream. Also, the BLOCK node will be the parent (i.e. the `SUPERBLOCK') of any other BLOCK nodes which we might create later on when we are expanding the fixup code. */ { register rtx original_before_jump = last_insn ? last_insn : get_last_insn (); start_sequence (); pushlevel (0); fixup->before_jump = emit_note (NULL_PTR, NOTE_INSN_BLOCK_BEG); last_block_end_note = emit_note (NULL_PTR, NOTE_INSN_BLOCK_END); fixup->context = poplevel (1, 0, 0); /* Create the BLOCK node now! */ end_sequence (); emit_insns_after (fixup->before_jump, original_before_jump); } fixup->block_start_count = block_start_count; fixup->stack_level = 0; fixup->cleanup_list_list = ((block->data.block.outer_cleanups || block->data.block.cleanups) ? tree_cons (NULL_TREE, block->data.block.cleanups, block->data.block.outer_cleanups) : 0); fixup->next = goto_fixup_chain; goto_fixup_chain = fixup; } return block != 0;}/* Generate bytecode jump with OPCODE to a fixup routine that links to LABEL. Make the fixup restore the stack level to STACK_LEVEL. */static voidbc_expand_fixup (opcode, label, stack_level) enum bytecode_opcode opcode; struct bc_label *label; int stack_level;{ struct goto_fixup *fixup = (struct goto_fixup *) oballoc (sizeof (struct goto_fixup)); fixup->label = bc_get_bytecode_label (); fixup->bc_target = label; fixup->bc_stack_level = stack_level; fixup->bc_handled = FALSE; fixup->next = goto_fixup_chain; goto_fixup_chain = fixup; /* Insert a jump to the fixup code */ bc_emit_bytecode (opcode); bc_emit_bytecode_labelref (fixup->label);#ifdef DEBUG_PRINT_CODE fputc ('\n', stderr);#endif}/* Expand any needed fixups in the outputmost binding level of the function. FIRST_INSN is the first insn in the function. */voidexpand_fixups (first_insn) rtx first_insn;{ fixup_gotos (NULL_PTR, NULL_RTX, NULL_TREE, first_insn, 0);}/* When exiting a binding contour, process all pending gotos requiring fixups. THISBLOCK is the structure that describes the block being exited. STACK_LEVEL is the rtx for the stack level to restore exiting this contour. CLEANUP_LIST is a list of expressions to evaluate on exiting this contour. FIRST_INSN is the insn that began this contour. Gotos that jump out of this contour must restore the stack level and do the cleanups before actually jumping. DONT_JUMP_IN nonzero means report error there is a jump into this contour from before the beginning of the contour. This is also done if STACK_LEVEL is nonzero. */static voidfixup_gotos (thisblock, stack_level, cleanup_list, first_insn, dont_jump_in) struct nesting *thisblock; rtx stack_level; tree cleanup_list; rtx first_insn; int dont_jump_in;{ register struct goto_fixup *f, *prev; if (output_bytecode) { /* ??? The second arg is the bc stack level, which is not the same as STACK_LEVEL. I have no idea what should go here, so I'll just pass 0. */ bc_fixup_gotos (thisblock, 0, cleanup_list, first_insn, dont_jump_in); return; } /* F is the fixup we are considering; PREV is the previous one. */ /* We run this loop in two passes so that cleanups of exited blocks are run first, and blocks that are exited are marked so afterwards. */ for (prev = 0, f = goto_fixup_chain; f; prev = f, f = f->next) { /* Test for a fixup that is inactive because it is already handled. */ if (f->before_jump == 0) { /* Delete inactive fixup from the chain, if that is easy to do. */ if (prev != 0) prev->next = f->next; } /* Has this fixup's target label been defined? If so, we can finalize it. */ else if (PREV_INSN (f->target_rtl) != 0) { register rtx cleanup_insns; /* Get the first non-label after the label this goto jumps to. If that's before this scope begins, we don't have a jump into the scope. */ rtx after_label = f->target_rtl; while (after_label != 0 && GET_CODE (after_label) == CODE_LABEL) after_label = NEXT_INSN (after_label); /* If this fixup jumped into this contour from before the beginning of this contour, report an error. */ /* ??? Bug: this does not detect jumping in through intermediate blocks that have stack levels or cleanups. It detects only a problem with the innermost block around the label. */ if (f->target != 0 && (dont_jump_in || stack_level || cleanup_list) /* If AFTER_LABEL is 0, it means the jump goes to the end of the rtl, which means it jumps into this scope. */ && (after_label == 0 || INSN_UID (first_insn) < INSN_UID (after_label)) && INSN_UID (first_insn) > INSN_UID (f->before_jump) && ! DECL_ERROR_ISSUED (f->target)) { error_with_decl (f->target, "label `%s' used before containing binding contour"); /* Prevent multiple errors for one label. */ DECL_ERROR_ISSUED (f->target) = 1; } /* We will expand the cleanups into a sequence of their own and then later on we will attach this new sequence to the insn stream just ahead of the actual jump insn. */ start_sequence (); /* Temporarily restore the lexical context where we will logically be inserting the fixup code. We do this for the sake of getting the debugging information right. */ pushlevel (0); set_block (f->context); /* Expand the cleanups for blocks this jump exits. */ if (f->cleanup_list_list) { tree lists; for (lists = f->cleanup_list_list; lists; lists = TREE_CHAIN (lists)) /* Marked elements correspond to blocks that have been closed. Do their cleanups. */ if (TREE_ADDRESSABLE (lists) && TREE_VALUE (lists) != 0) { expand_cleanups (TREE_VALUE (lists), NULL_TREE, 1, 1); /* Pop any pushes done in the cleanups, in case function is about to return. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -