diff options
author | Miod Vallat <miod@cvs.openbsd.org> | 2002-12-02 09:00:27 +0000 |
---|---|---|
committer | Miod Vallat <miod@cvs.openbsd.org> | 2002-12-02 09:00:27 +0000 |
commit | 22703df0df76accb65607f28070a9771b5bb42de (patch) | |
tree | 2f2aa0ebf948ee4580b10bb0a41b7f258b40d0e8 /gnu/egcs/gcc/expr.c | |
parent | 560fab27442ac85618b1720e90581c295ae1ea83 (diff) |
Import propolice (http://www.trl.ibm.com/projects/security/ssp), a stack
attack protection scheme, into gcc.
This protection is enabled by default. It can be turned off by using the
-fno-stack-protector flag.
Code by Hiroaki Etoh (etoh at jp dot ibm dot com); work on openbsd-specific
integration by fgsch@, deraadt@ and myself; tests by fgsch@, naddy@ and
myself; beer drinking by myself.
Please note that system upgrades with this new code will require a new
libc and ld.so to be build and installed before the propolice-enabled
compiler can be installed.
Diffstat (limited to 'gnu/egcs/gcc/expr.c')
-rw-r--r-- | gnu/egcs/gcc/expr.c | 438 |
1 files changed, 301 insertions, 137 deletions
diff --git a/gnu/egcs/gcc/expr.c b/gnu/egcs/gcc/expr.c index 17fa1be1fdc..996c277a5cc 100644 --- a/gnu/egcs/gcc/expr.c +++ b/gnu/egcs/gcc/expr.c @@ -1,5 +1,6 @@ /* Convert tree expression to rtl instructions, for GNU compiler. - Copyright (C) 1988, 92-98, 1999 Free Software Foundation, Inc. + Copyright (C) 1988, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, + 2000, 2001 Free Software Foundation, Inc. This file is part of GNU CC. @@ -191,6 +192,7 @@ static rtx expand_builtin PROTO((tree, rtx, rtx, static int apply_args_size PROTO((void)); static int apply_result_size PROTO((void)); static rtx result_vector PROTO((int, rtx)); +static rtx expand_builtin_setjmp PROTO((tree, rtx)); static rtx expand_builtin_apply_args PROTO((void)); static rtx expand_builtin_apply PROTO((rtx, rtx, rtx)); static void expand_builtin_return PROTO((rtx)); @@ -1467,7 +1469,7 @@ move_by_pieces (to, from, len, align) if (USE_LOAD_PRE_DECREMENT (mode) && data.reverse && ! data.autinc_from) { - data.from_addr = copy_addr_to_reg (plus_constant (from_addr, len)); + data.from_addr = copy_addr_to_reg (plus_constant (from_addr, len-GET_MODE_SIZE (mode))); data.autinc_from = 1; data.explicit_inc_from = -1; } @@ -1481,7 +1483,7 @@ move_by_pieces (to, from, len, align) data.from_addr = copy_addr_to_reg (from_addr); if (USE_STORE_PRE_DECREMENT (mode) && data.reverse && ! data.autinc_to) { - data.to_addr = copy_addr_to_reg (plus_constant (to_addr, len)); + data.to_addr = copy_addr_to_reg (plus_constant (to_addr, len-GET_MODE_SIZE (mode))); data.autinc_to = 1; data.explicit_inc_to = -1; } @@ -1599,9 +1601,9 @@ move_by_pieces_1 (genfun, mode, data) MEM_IN_STRUCT_P (from1) = data->from_struct; if (HAVE_PRE_DECREMENT && data->explicit_inc_to < 0) - emit_insn (gen_add2_insn (data->to_addr, GEN_INT (-size))); + if (data->explicit_inc_to-- < -1) emit_insn (gen_add2_insn (data->to_addr, GEN_INT (-size))); if (HAVE_PRE_DECREMENT && data->explicit_inc_from < 0) - emit_insn (gen_add2_insn (data->from_addr, GEN_INT (-size))); + if (data->explicit_inc_from-- < -1) emit_insn (gen_add2_insn (data->from_addr, GEN_INT (-size))); emit_insn ((*genfun) (to1, from1)); if (HAVE_POST_INCREMENT && data->explicit_inc_to > 0) @@ -1710,6 +1712,37 @@ emit_block_move (x, y, size, align) } } + /* X, Y, or SIZE may have been passed through protect_from_queue. + + It is unsafe to save the value generated by protect_from_queue + and reuse it later. Consider what happens if emit_queue is + called before the return value from protect_from_queue is used. + + Expansion of the CALL_EXPR below will call emit_queue before + we are finished emitting RTL for argument setup. So if we are + not careful we could get the wrong value for an argument. + + To avoid this problem we go ahead and emit code to copy X, Y & + SIZE into new pseudos. We can then place those new pseudos + into an RTL_EXPR and use them later, even after a call to + emit_queue. + + Note this is not strictly needed for library calls since they + do not call emit_queue before loading their arguments. However, + we may need to have library calls call emit_queue in the future + since failing to do so could cause problems for targets which + define SMALL_REGISTER_CLASSES and pass arguments in registers. */ + x = copy_to_mode_reg (Pmode, XEXP (x, 0)); + y = copy_to_mode_reg (Pmode, XEXP (y, 0)); + +#ifdef TARGET_MEM_FUNCTIONS + size = copy_to_mode_reg (TYPE_MODE (sizetype), size); +#else + size = convert_to_mode (TYPE_MODE (integer_type_node), size, + TREE_UNSIGNED (integer_type_node)); + size = copy_to_mode_reg (TYPE_MODE (integer_type_node), size); +#endif + #ifdef TARGET_MEM_FUNCTIONS /* It is incorrect to use the libcall calling conventions to call memcpy in this context. @@ -1748,12 +1781,10 @@ emit_block_move (x, y, size, align) the last is a size_t byte count for the copy. */ arg_list = build_tree_list (NULL_TREE, - make_tree (build_pointer_type (void_type_node), - XEXP (x, 0))); + make_tree (build_pointer_type (void_type_node), x)); TREE_CHAIN (arg_list) = build_tree_list (NULL_TREE, - make_tree (build_pointer_type (void_type_node), - XEXP (y, 0))); + make_tree (build_pointer_type (void_type_node), y)); TREE_CHAIN (TREE_CHAIN (arg_list)) = build_tree_list (NULL_TREE, make_tree (sizetype, size)); TREE_CHAIN (TREE_CHAIN (TREE_CHAIN (arg_list))) = NULL_TREE; @@ -1767,8 +1798,7 @@ emit_block_move (x, y, size, align) retval = expand_expr (call_expr, NULL_RTX, VOIDmode, 0); #else emit_library_call (bcopy_libfunc, 0, - VOIDmode, 3, XEXP (y, 0), Pmode, - XEXP (x, 0), Pmode, + VOIDmode, 3, y, Pmode, x, Pmode, convert_to_mode (TYPE_MODE (integer_type_node), size, TREE_UNSIGNED (integer_type_node)), TYPE_MODE (integer_type_node)); @@ -2284,7 +2314,7 @@ clear_by_pieces (to, len, align) if (USE_STORE_PRE_DECREMENT (mode) && data.reverse && ! data.autinc_to) { - data.to_addr = copy_addr_to_reg (plus_constant (to_addr, len)); + data.to_addr = copy_addr_to_reg (plus_constant (to_addr, len-GET_MODE_SIZE (mode))); data.autinc_to = 1; data.explicit_inc_to = -1; } @@ -2354,7 +2384,7 @@ clear_by_pieces_1 (genfun, mode, data) MEM_IN_STRUCT_P (to1) = data->to_struct; if (HAVE_PRE_DECREMENT && data->explicit_inc_to < 0) - emit_insn (gen_add2_insn (data->to_addr, GEN_INT (-size))); + if (data->explicit_inc_to-- < -1) emit_insn (gen_add2_insn (data->to_addr, GEN_INT (-size))); emit_insn ((*genfun) (to1, const0_rtx)); if (HAVE_POST_INCREMENT && data->explicit_inc_to > 0) @@ -2444,69 +2474,97 @@ clear_storage (object, size, align) } } + /* OBJECT or SIZE may have been passed through protect_from_queue. -#ifdef TARGET_MEM_FUNCTIONS - /* It is incorrect to use the libcall calling conventions to call - memset in this context. + It is unsafe to save the value generated by protect_from_queue + and reuse it later. Consider what happens if emit_queue is + called before the return value from protect_from_queue is used. - This could be a user call to memset and the user may wish to - examine the return value from memset. + Expansion of the CALL_EXPR below will call emit_queue before + we are finished emitting RTL for argument setup. So if we are + not careful we could get the wrong value for an argument. - For targets where libcalls and normal calls have different conventions - for returning pointers, we could end up generating incorrect code. + To avoid this problem we go ahead and emit code to copy OBJECT + and SIZE into new pseudos. We can then place those new pseudos + into an RTL_EXPR and use them later, even after a call to + emit_queue. - So instead of using a libcall sequence we build up a suitable - CALL_EXPR and expand the call in the normal fashion. */ - if (fn == NULL_TREE) - { - tree fntype; + Note this is not strictly needed for library calls since they + do not call emit_queue before loading their arguments. However, + we may need to have library calls call emit_queue in the future + since failing to do so could cause problems for targets which + define SMALL_REGISTER_CLASSES and pass arguments in registers. */ + object = copy_to_mode_reg (Pmode, XEXP (object, 0)); - /* This was copied from except.c, I don't know if all this is - necessary in this context or not. */ - fn = get_identifier ("memset"); - push_obstacks_nochange (); - end_temporary_allocation (); - fntype = build_pointer_type (void_type_node); - fntype = build_function_type (fntype, NULL_TREE); - fn = build_decl (FUNCTION_DECL, fn, fntype); - DECL_EXTERNAL (fn) = 1; - TREE_PUBLIC (fn) = 1; - DECL_ARTIFICIAL (fn) = 1; - make_decl_rtl (fn, NULL_PTR, 1); - assemble_external (fn); - pop_obstacks (); - } +#ifdef TARGET_MEM_FUNCTIONS + size = copy_to_mode_reg (TYPE_MODE (sizetype), size); +#else + size = convert_to_mode (TYPE_MODE (integer_type_node), size, + TREE_UNSIGNED (integer_type_node)); + size = copy_to_mode_reg (TYPE_MODE (integer_type_node), size); +#endif - /* We need to make an argument list for the function call. - memset has three arguments, the first is a void * addresses, the - second a integer with the initialization value, the last is a size_t - byte count for the copy. */ - arg_list - = build_tree_list (NULL_TREE, - make_tree (build_pointer_type (void_type_node), - XEXP (object, 0))); - TREE_CHAIN (arg_list) - = build_tree_list (NULL_TREE, - make_tree (integer_type_node, const0_rtx)); - TREE_CHAIN (TREE_CHAIN (arg_list)) - = build_tree_list (NULL_TREE, make_tree (sizetype, size)); - TREE_CHAIN (TREE_CHAIN (TREE_CHAIN (arg_list))) = NULL_TREE; +#ifdef TARGET_MEM_FUNCTIONS + /* It is incorrect to use the libcall calling conventions to call + memset in this context. - /* Now we have to build up the CALL_EXPR itself. */ - call_expr = build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (fn)), fn); - call_expr = build (CALL_EXPR, TREE_TYPE (TREE_TYPE (fn)), - call_expr, arg_list, NULL_TREE); - TREE_SIDE_EFFECTS (call_expr) = 1; + This could be a user call to memset and the user may wish to + examine the return value from memset. - retval = expand_expr (call_expr, NULL_RTX, VOIDmode, 0); + For targets where libcalls and normal calls have different + conventions for returning pointers, we could end up generating + incorrect code. + + So instead of using a libcall sequence we build up a suitable + CALL_EXPR and expand the call in the normal fashion. */ + if (fn == NULL_TREE) + { + tree fntype; + + /* This was copied from except.c, I don't know if all this is + necessary in this context or not. */ + fn = get_identifier ("memset"); + push_obstacks_nochange (); + end_temporary_allocation (); + fntype = build_pointer_type (void_type_node); + fntype = build_function_type (fntype, NULL_TREE); + fn = build_decl (FUNCTION_DECL, fn, fntype); + DECL_EXTERNAL (fn) = 1; + TREE_PUBLIC (fn) = 1; + DECL_ARTIFICIAL (fn) = 1; + make_decl_rtl (fn, NULL_PTR, 1); + assemble_external (fn); + pop_obstacks (); + } + + /* We need to make an argument list for the function call. + + memset has three arguments, the first is a void * addresses, the + second a integer with the initialization value, the last is a + size_t byte count for the copy. */ + arg_list + = build_tree_list (NULL_TREE, + make_tree (build_pointer_type (void_type_node), + object)); + TREE_CHAIN (arg_list) + = build_tree_list (NULL_TREE, + make_tree (integer_type_node, const0_rtx)); + TREE_CHAIN (TREE_CHAIN (arg_list)) + = build_tree_list (NULL_TREE, make_tree (sizetype, size)); + TREE_CHAIN (TREE_CHAIN (TREE_CHAIN (arg_list))) = NULL_TREE; + + /* Now we have to build up the CALL_EXPR itself. */ + call_expr = build1 (ADDR_EXPR, + build_pointer_type (TREE_TYPE (fn)), fn); + call_expr = build (CALL_EXPR, TREE_TYPE (TREE_TYPE (fn)), + call_expr, arg_list, NULL_TREE); + TREE_SIDE_EFFECTS (call_expr) = 1; + + retval = expand_expr (call_expr, NULL_RTX, VOIDmode, 0); #else emit_library_call (bzero_libfunc, 0, - VOIDmode, 2, - XEXP (object, 0), Pmode, - convert_to_mode - (TYPE_MODE (integer_type_node), size, - TREE_UNSIGNED (integer_type_node)), + VOIDmode, 2, object, Pmode, size, TYPE_MODE (integer_type_node)); #endif } @@ -2623,19 +2681,79 @@ emit_move_insn_1 (x, y) } else { - /* Show the output dies here. This is necessary for pseudos; + rtx realpart_x, realpart_y; + rtx imagpart_x, imagpart_y; + + /* If this is a complex value with each part being smaller than a + word, the usual calling sequence will likely pack the pieces into + a single register. Unfortunately, SUBREG of hard registers only + deals in terms of words, so we have a problem converting input + arguments to the CONCAT of two registers that is used elsewhere + for complex values. If this is before reload, we can copy it into + memory and reload. FIXME, we should see about using extract and + insert on integer registers, but complex short and complex char + variables should be rarely used. */ + if (GET_MODE_BITSIZE (mode) < 2*BITS_PER_WORD + && (reload_in_progress | reload_completed) == 0) + { + int packed_dest_p = (REG_P (x) && REGNO (x) < FIRST_PSEUDO_REGISTER); + int packed_src_p = (REG_P (y) && REGNO (y) < FIRST_PSEUDO_REGISTER); + + if (packed_dest_p || packed_src_p) + { + enum mode_class reg_class = ((class == MODE_COMPLEX_FLOAT) + ? MODE_FLOAT : MODE_INT); + + enum machine_mode reg_mode = + mode_for_size (GET_MODE_BITSIZE (mode), reg_class, 1); + + if (reg_mode != BLKmode) + { + rtx mem = assign_stack_temp (reg_mode, + GET_MODE_SIZE (mode), 0); + + rtx cmem = change_address (mem, mode, NULL_RTX); + + current_function_cannot_inline + = "function using short complex types cannot be inline"; + + if (packed_dest_p) + { + rtx sreg = gen_rtx_SUBREG (reg_mode, x, 0); + emit_move_insn_1 (cmem, y); + return emit_move_insn_1 (sreg, mem); + } + else + { + rtx sreg = gen_rtx_SUBREG (reg_mode, y, 0); + emit_move_insn_1 (mem, sreg); + return emit_move_insn_1 (x, cmem); + } + } + } + } + + realpart_x = gen_realpart (submode, x); + realpart_y = gen_realpart (submode, y); + imagpart_x = gen_imagpart (submode, x); + imagpart_y = gen_imagpart (submode, y); + + /* Show the output dies here. This is necessary for SUBREGs + of pseudos since we cannot track their lifetimes correctly; hard regs shouldn't appear here except as return values. We never want to emit such a clobber after reload. */ if (x != y - && ! (reload_in_progress || reload_completed)) + && ! (reload_in_progress || reload_completed) + && (GET_CODE (realpart_x) == SUBREG + || GET_CODE (imagpart_x) == SUBREG)) { emit_insn (gen_rtx_CLOBBER (VOIDmode, x)); } emit_insn (GEN_FCN (mov_optab->handlers[(int) submode].insn_code) - (gen_realpart (submode, x), gen_realpart (submode, y))); + (realpart_x, realpart_y)); emit_insn (GEN_FCN (mov_optab->handlers[(int) submode].insn_code) - (gen_imagpart (submode, x), gen_imagpart (submode, y))); + (imagpart_x, imagpart_y)); } return get_last_insn (); @@ -2647,6 +2765,8 @@ emit_move_insn_1 (x, y) else if (GET_MODE_SIZE (mode) > UNITS_PER_WORD) { rtx last_insn = 0; + rtx seq; + int need_clobber; #ifdef PUSH_ROUNDING @@ -2659,15 +2779,9 @@ emit_move_insn_1 (x, y) } #endif - /* Show the output dies here. This is necessary for pseudos; - hard regs shouldn't appear here except as return values. - We never want to emit such a clobber after reload. */ - if (x != y - && ! (reload_in_progress || reload_completed)) - { - emit_insn (gen_rtx_CLOBBER (VOIDmode, x)); - } + start_sequence (); + need_clobber = 0; for (i = 0; i < (GET_MODE_SIZE (mode) + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD; i++) @@ -2689,9 +2803,27 @@ emit_move_insn_1 (x, y) if (xpart == 0 || ypart == 0) abort (); + need_clobber |= (GET_CODE (xpart) == SUBREG); + last_insn = emit_move_insn (xpart, ypart); } + seq = gen_sequence (); + end_sequence (); + + /* Show the output dies here. This is necessary for SUBREGs + of pseudos since we cannot track their lifetimes correctly; + hard regs shouldn't appear here except as return values. + We never want to emit such a clobber after reload. */ + if (x != y + && ! (reload_in_progress || reload_completed) + && need_clobber != 0) + { + emit_insn (gen_rtx_CLOBBER (VOIDmode, x)); + } + + emit_insn (seq); + return last_insn; } else @@ -5097,7 +5229,7 @@ init_noncopied_parts (lhs, list) for (tail = list; tail; tail = TREE_CHAIN (tail)) if (TREE_CODE (TREE_VALUE (tail)) == TREE_LIST) parts = chainon (parts, init_noncopied_parts (lhs, TREE_VALUE (tail))); - else + else if (TREE_PURPOSE (tail)) { tree part = TREE_VALUE (tail); tree part_type = TREE_TYPE (part); @@ -6206,12 +6338,8 @@ expand_expr (exp, target, tmode, modifier) } temp = gen_rtx_MEM (mode, op0); - /* If address was computed by addition, - mark this as an element of an aggregate. */ - if (TREE_CODE (exp1) == PLUS_EXPR - || (TREE_CODE (exp1) == SAVE_EXPR - && TREE_CODE (TREE_OPERAND (exp1, 0)) == PLUS_EXPR) - || AGGREGATE_TYPE_P (TREE_TYPE (exp)) + + if (AGGREGATE_TYPE_P (TREE_TYPE (exp)) || (TREE_CODE (exp1) == ADDR_EXPR && (exp2 = TREE_OPERAND (exp1, 0)) && AGGREGATE_TYPE_P (TREE_TYPE (exp2)))) @@ -6948,7 +7076,8 @@ expand_expr (exp, target, tmode, modifier) /* If adding to a sum including a constant, associate it to put the constant outside. */ if (GET_CODE (op1) == PLUS - && CONSTANT_P (XEXP (op1, 1))) + && CONSTANT_P (XEXP (op1, 1)) + && !(flag_propolice_protection && XEXP (op1, 0) == virtual_stack_vars_rtx)) { rtx constant_term = const0_rtx; @@ -8417,44 +8546,29 @@ expand_builtin_return_addr (fndecl_code, count, tem) return tem; } -/* __builtin_setjmp is passed a pointer to an array of five words (not - all will be used on all machines). It operates similarly to the C - library function of the same name, but is more efficient. Much of - the code below (and for longjmp) is copied from the handling of - non-local gotos. +/* Construct the leading half of a __builtin_setjmp call. Control will + return to RECEIVER_LABEL. This is used directly by sjlj exception + handling code. */ - NOTE: This is intended for use by GNAT and the exception handling - scheme in the compiler and will only work in the method used by - them. */ - -rtx -expand_builtin_setjmp (buf_addr, target, first_label, next_label) +void +expand_builtin_setjmp_setup (buf_addr, receiver_label) rtx buf_addr; - rtx target; - rtx first_label, next_label; + rtx receiver_label; { - rtx lab1 = gen_label_rtx (); enum machine_mode sa_mode = STACK_SAVEAREA_MODE (SAVE_NONLOCAL); - enum machine_mode value_mode; rtx stack_save; - value_mode = TYPE_MODE (integer_type_node); - #ifdef POINTERS_EXTEND_UNSIGNED buf_addr = convert_memory_address (Pmode, buf_addr); #endif buf_addr = force_reg (Pmode, buf_addr); - if (target == 0 || GET_CODE (target) != REG - || REGNO (target) < FIRST_PSEUDO_REGISTER) - target = gen_reg_rtx (value_mode); - emit_queue (); - /* We store the frame pointer and the address of lab1 in the buffer - and use the rest of it for the stack save area, which is - machine-dependent. */ + /* We store the frame pointer and the address of receiver_label in + the buffer and use the rest of it for the stack save area, which + is machine-dependent. */ #ifndef BUILTIN_SETJMP_FRAME_VALUE #define BUILTIN_SETJMP_FRAME_VALUE virtual_stack_vars_rtx @@ -8466,7 +8580,7 @@ expand_builtin_setjmp (buf_addr, target, first_label, next_label) (gen_rtx_MEM (Pmode, plus_constant (buf_addr, GET_MODE_SIZE (Pmode)))), - force_reg (Pmode, gen_rtx_LABEL_REF (Pmode, lab1))); + force_reg (Pmode, gen_rtx_LABEL_REF (Pmode, receiver_label))); stack_save = gen_rtx_MEM (sa_mode, plus_constant (buf_addr, @@ -8479,20 +8593,22 @@ expand_builtin_setjmp (buf_addr, target, first_label, next_label) emit_insn (gen_builtin_setjmp_setup (buf_addr)); #endif - /* Set TARGET to zero and branch to the first-time-through label. */ - emit_move_insn (target, const0_rtx); - emit_jump_insn (gen_jump (first_label)); - emit_barrier (); - emit_label (lab1); - - /* Tell flow about the strange goings on. Putting `lab1' on - `nonlocal_goto_handler_labels' to indicates that function - calls may traverse the arc back to this label. */ + /* Tell optimize_save_area_alloca that extra work is going to + need to go on during alloca. */ + current_function_calls_setjmp = 1; + /* Set this so all the registers get saved in our frame; we need to be + able to copy the saved values for any registers from frames we unwind. */ current_function_has_nonlocal_label = 1; - nonlocal_goto_handler_labels = - gen_rtx_EXPR_LIST (VOIDmode, lab1, nonlocal_goto_handler_labels); +} +/* Construct the trailing part of a __builtin_setjmp call. + This is used directly by sjlj exception handling code. */ + +void +expand_builtin_setjmp_receiver (receiver_label) + rtx receiver_label ATTRIBUTE_UNUSED; +{ /* Clobber the FP when we get here, so we have to make sure it's marked as used by this function. */ emit_insn (gen_rtx_USE (VOIDmode, hard_frame_pointer_rtx)); @@ -8539,7 +8655,7 @@ expand_builtin_setjmp (buf_addr, target, first_label, next_label) #ifdef HAVE_builtin_setjmp_receiver if (HAVE_builtin_setjmp_receiver) - emit_insn (gen_builtin_setjmp_receiver (lab1)); + emit_insn (gen_builtin_setjmp_receiver (receiver_label)); else #endif #ifdef HAVE_nonlocal_goto_receiver @@ -8551,11 +8667,67 @@ expand_builtin_setjmp (buf_addr, target, first_label, next_label) ; /* Nothing */ } - /* Set TARGET, and branch to the next-time-through label. */ - emit_move_insn (target, const1_rtx); - emit_jump_insn (gen_jump (next_label)); + /* @@@ This is a kludge. Not all machine descriptions define a blockage + insn, but we must not allow the code we just generated to be reordered + by scheduling. Specifically, the update of the frame pointer must + happen immediately, not later. So emit an ASM_INPUT to act as blockage + insn. */ + emit_insn (gen_rtx_ASM_INPUT (VOIDmode, "")); +} + + +/* __builtin_setjmp is passed a pointer to an array of five words (not + all will be used on all machines). It operates similarly to the C + library function of the same name, but is more efficient. Much of + the code below (and for longjmp) is copied from the handling of + non-local gotos. + + NOTE: This is intended for use by GNAT and the exception handling + scheme in the compiler and will only work in the method used by + them. */ + +static rtx +expand_builtin_setjmp (arglist, target) + tree arglist; + rtx target; +{ + rtx buf_addr, next_lab, cont_lab; + + if (arglist == 0 + || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_TYPE) + return NULL_RTX; + + if (target == 0 || GET_CODE (target) != REG + || REGNO (target) < FIRST_PSEUDO_REGISTER) + target = gen_reg_rtx (TYPE_MODE (integer_type_node)); + + buf_addr = expand_expr (TREE_VALUE (arglist), NULL_RTX, VOIDmode, 0); + + next_lab = gen_label_rtx (); + cont_lab = gen_label_rtx (); + + expand_builtin_setjmp_setup (buf_addr, next_lab); + + /* Set TARGET to zero and branch to the continue label. */ + emit_move_insn (target, const0_rtx); + emit_jump_insn (gen_jump (cont_lab)); emit_barrier (); + emit_label (next_lab); + + expand_builtin_setjmp_receiver (next_lab); + + /* Set TARGET to one. */ + emit_move_insn (target, const1_rtx); + emit_label (cont_lab); + + /* Tell flow about the strange goings on. Putting `next_lab' on + `nonlocal_goto_handler_labels' to indicates that function + calls may traverse the arc back to this label. */ + current_function_has_nonlocal_label = 1; + nonlocal_goto_handler_labels + = gen_rtx_EXPR_LIST (VOIDmode, next_lab, nonlocal_goto_handler_labels); + return target; } @@ -9576,18 +9748,10 @@ expand_builtin (exp, target, subtarget, mode, ignore) #endif case BUILT_IN_SETJMP: - if (arglist == 0 - || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_TYPE) - break; - else - { - rtx buf_addr = expand_expr (TREE_VALUE (arglist), subtarget, - VOIDmode, 0); - rtx lab = gen_label_rtx (); - rtx ret = expand_builtin_setjmp (buf_addr, target, lab, lab); - emit_label (lab); - return ret; - } + target = expand_builtin_setjmp (arglist, target); + if (target) + return target; + break; /* __builtin_longjmp is passed a pointer to an array of five words. It's similar to the C library longjmp function but works with |