From 2a8f6b90c17bc2eb63bb218e7b11b6c6178c9d70 Mon Sep 17 00:00:00 2001 From: Jan Hubicka Date: Thu, 13 Apr 2000 13:59:00 +0000 Subject: [PATCH] alias.c (nonlocal_reference_p): Take a care for CALL_INSNS's fusage field. * alias.c (nonlocal_reference_p): Take a care for CALL_INSNS's fusage field. * calls.c (ECF_PURE): New flag. (emit_call_1): Handle ECF_PURE calls. (initialize_argument_information): Unset ECF_PURE flag too. (precompute_arguments): Precompute for ECF_PURE too. (expand_call): Handle ECF_PURE calls too. (emit_library_call_value_1): Rename no_queue argument to fn_type, accept value of 2 as pure function. (emit_library_call_value, emit_library_call): Rename no_queue argument to fn_type. * optabs.c (prepare_cmp_insn): Pass fn_type 2 to memcmp call. * tree.h (DECL_IS_PURE): New macro. (struct tree_decl): Add pure_flag. * c-common.c (enum attrs): Add attribute "pure". (init_attributes): Initialize attribute "pure" (decl_attributes): Handle attribute "pure". * extend.texi (Attribute "pure"): Document. * calls.c (expand_call): Add (mem:BLK (scratch)) to "equal from" in pure function. (flags_from_decl_or_type): Support attribute "pure". From-SVN: r33138 --- gcc/ChangeLog | 25 ++++++++++++ gcc/alias.c | 13 +++++-- gcc/c-common.c | 13 ++++++- gcc/calls.c | 101 +++++++++++++++++++++++++++++------------------- gcc/extend.texi | 40 +++++++++++++------ gcc/optabs.c | 4 +- gcc/tree.h | 5 +++ 7 files changed, 143 insertions(+), 58 deletions(-) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 87c05226729..ad5c15d867d 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,28 @@ +Thu Apr 13 15:55:08 MET DST 2000 Jan Hubicka + + * alias.c (nonlocal_reference_p): Take a care for + CALL_INSNS's fusage field. + * calls.c (ECF_PURE): New flag. + (emit_call_1): Handle ECF_PURE calls. + (initialize_argument_information): Unset ECF_PURE flag too. + (precompute_arguments): Precompute for ECF_PURE too. + (expand_call): Handle ECF_PURE calls too. + (emit_library_call_value_1): Rename no_queue argument to + fn_type, accept value of 2 as pure function. + (emit_library_call_value, emit_library_call): Rename no_queue argument + to fn_type. + * optabs.c (prepare_cmp_insn): Pass fn_type 2 to memcmp call. + + * tree.h (DECL_IS_PURE): New macro. + (struct tree_decl): Add pure_flag. + * c-common.c (enum attrs): Add attribute "pure". + (init_attributes): Initialize attribute "pure" + (decl_attributes): Handle attribute "pure". + * extend.texi (Attribute "pure"): Document. + * calls.c (expand_call): Add (mem:BLK (scratch)) to "equal from" + in pure function. + (flags_from_decl_or_type): Support attribute "pure". + 2000-04-13 Jason Merrill * cpplex.c (_cpp_lex_token): Handle digraphs. Don't null-terminate diff --git a/gcc/alias.c b/gcc/alias.c index db89ecc173e..fb493f8e7b0 100644 --- a/gcc/alias.c +++ b/gcc/alias.c @@ -1424,10 +1424,15 @@ nonlocal_reference_p (x) if (GET_RTX_CLASS (code) == 'i') { - /* Constant functions are constant. */ + /* Constant functions can be constant if they don't use + scratch memory used to mark function w/o side effects. */ if (code == CALL_INSN && CONST_CALL_P (x)) - return 0; - x = PATTERN (x); + { + x = CALL_INSN_FUNCTION_USAGE (x); + if (!x) return 0; + } + else + x = PATTERN (x); code = GET_CODE (x); } @@ -1520,7 +1525,7 @@ nonlocal_reference_p (x) for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) { - if (fmt[i] == 'e') + if (fmt[i] == 'e' && XEXP (x, i)) { if (nonlocal_reference_p (XEXP (x, i))) return 1; diff --git a/gcc/c-common.c b/gcc/c-common.c index 97aba258b12..1033035db23 100644 --- a/gcc/c-common.c +++ b/gcc/c-common.c @@ -145,7 +145,7 @@ enum attrs {A_PACKED, A_NOCOMMON, A_COMMON, A_NORETURN, A_CONST, A_T_UNION, A_NO_CHECK_MEMORY_USAGE, A_NO_INSTRUMENT_FUNCTION, A_CONSTRUCTOR, A_DESTRUCTOR, A_MODE, A_SECTION, A_ALIGNED, A_UNUSED, A_FORMAT, A_FORMAT_ARG, A_WEAK, A_ALIAS, A_MALLOC, - A_NO_LIMIT_STACK}; + A_NO_LIMIT_STACK, A_PURE}; enum format_type { printf_format_type, scanf_format_type, strftime_format_type }; @@ -457,6 +457,7 @@ init_attributes () add_attribute (A_NO_CHECK_MEMORY_USAGE, "no_check_memory_usage", 0, 0, 1); add_attribute (A_MALLOC, "malloc", 0, 0, 1); add_attribute (A_NO_LIMIT_STACK, "no_stack_limit", 0, 0, 1); + add_attribute (A_PURE, "pure", 0, 0, 1); } /* Default implementation of valid_lang_attribute, below. By default, there @@ -596,6 +597,7 @@ decl_attributes (node, attributes, prefix_attributes) case A_MALLOC: if (TREE_CODE (decl) == FUNCTION_DECL) DECL_IS_MALLOC (decl) = 1; + /* ??? TODO: Support types. */ else warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name)); break; @@ -625,6 +627,15 @@ decl_attributes (node, attributes, prefix_attributes) warning ( "`%s' attribute ignored", IDENTIFIER_POINTER (name)); break; + case A_PURE: + if (TREE_CODE (decl) == FUNCTION_DECL) + DECL_IS_PURE (decl) = 1; + /* ??? TODO: Support types. */ + else + warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name)); + break; + + case A_T_UNION: if (is_type && TREE_CODE (type) == UNION_TYPE diff --git a/gcc/calls.c b/gcc/calls.c index 642495569a2..2592a8e7fb3 100644 --- a/gcc/calls.c +++ b/gcc/calls.c @@ -168,6 +168,9 @@ static int calls_function_1 PARAMS ((tree, int)); the current one. */ #define ECF_FORK_OR_EXEC 128 #define ECF_SIBCALL 256 +/* Nonzero if this is a call to "pure" function (like const function, + but may read memory. */ +#define ECF_PURE 512 static void emit_call_1 PARAMS ((rtx, tree, tree, HOST_WIDE_INT, HOST_WIDE_INT, HOST_WIDE_INT, rtx, @@ -555,6 +558,15 @@ emit_call_1 (funexp, fndecl, funtype, stack_size, rounded_stack_size, if (! call_insn) abort (); + /* Mark memory as used for "pure" function call. */ + if (ecf_flags & ECF_PURE) + { + call_fusage = gen_rtx_EXPR_LIST (VOIDmode, + gen_rtx_USE (VOIDmode, + gen_rtx_MEM (BLKmode, + gen_rtx_SCRATCH (VOIDmode))), call_fusage); + } + /* Put the register usage information on the CALL. If there is already some usage information, put ours at the end. */ if (CALL_INSN_FUNCTION_USAGE (call_insn)) @@ -571,7 +583,7 @@ emit_call_1 (funexp, fndecl, funtype, stack_size, rounded_stack_size, CALL_INSN_FUNCTION_USAGE (call_insn) = call_fusage; /* If this is a const call, then set the insn's unchanging bit. */ - if (ecf_flags & ECF_CONST) + if (ecf_flags & (ECF_CONST | ECF_PURE)) CONST_CALL_P (call_insn) = 1; /* If this call can't throw, attach a REG_EH_REGION reg note to that @@ -610,7 +622,7 @@ emit_call_1 (funexp, fndecl, funtype, stack_size, rounded_stack_size, if (rounded_stack_size != 0) { if (flag_defer_pop && inhibit_defer_pop == 0 - && !(ecf_flags & ECF_CONST)) + && !(ecf_flags & (ECF_CONST | ECF_PURE))) pending_stack_adjust += rounded_stack_size; else adjust_stack (rounded_stack_size_rtx); @@ -759,6 +771,10 @@ flags_from_decl_or_type (exp) if (DECL_P (exp) && DECL_IS_MALLOC (exp)) flags |= ECF_MALLOC; + /* The function exp may have the `pure' attribute. */ + if (DECL_P (exp) && DECL_IS_PURE (exp)) + flags |= ECF_PURE; + if (TREE_NOTHROW (exp)) flags |= ECF_NOTHROW; } @@ -1195,7 +1211,7 @@ initialize_argument_information (num_actuals, args, args_size, n_named_args, MEM_SET_IN_STRUCT_P (copy, AGGREGATE_TYPE_P (type)); store_expr (args[i].tree_value, copy, 0); - *ecf_flags &= ~ECF_CONST; + *ecf_flags &= ~(ECF_CONST | ECF_PURE); args[i].tree_value = build1 (ADDR_EXPR, build_pointer_type (type), @@ -1254,7 +1270,7 @@ initialize_argument_information (num_actuals, args, args_size, n_named_args, /* If this is an addressable type, we cannot pre-evaluate it. Thus, we cannot consider this function call constant. */ if (TREE_ADDRESSABLE (type)) - *ecf_flags &= ~ECF_CONST; + *ecf_flags &= ~(ECF_CONST | ECF_PURE); /* Compute the stack-size of this argument. */ if (args[i].reg == 0 || args[i].partial != 0 @@ -1435,7 +1451,7 @@ precompute_arguments (flags, must_preallocate, num_actuals, args, args_size) which have already been stored into the stack. */ for (i = 0; i < num_actuals; i++) - if ((flags & ECF_CONST) + if ((flags & (ECF_CONST | ECF_PURE)) || ((args_size->var != 0 || args_size->constant != 0) && calls_function (args[i].tree_value, 1)) || (must_preallocate @@ -2038,7 +2054,7 @@ expand_call (exp, target, ignore) if (aggregate_value_p (exp)) { /* This call returns a big structure. */ - flags &= ~ECF_CONST; + flags &= ~(ECF_CONST | ECF_PURE); #ifdef PCC_STATIC_STRUCT_RETURN { @@ -2295,7 +2311,7 @@ expand_call (exp, target, ignore) /* When calling a const function, we must pop the stack args right away, so that the pop is deleted or moved with the call. */ - if (flags & ECF_CONST) + if (flags & (ECF_CONST | ECF_PURE)) NO_DEFER_POP; /* Don't let pending stack adjusts add up to too much. @@ -2416,7 +2432,7 @@ expand_call (exp, target, ignore) Also do not make a sibling call. */ - flags &= ~ECF_CONST; + flags &= ~(ECF_CONST | ECF_PURE); must_preallocate = 1; sibcall_failure = 1; } @@ -2470,7 +2486,7 @@ expand_call (exp, target, ignore) /* Now we are about to start emitting insns that can be deleted if a libcall is deleted. */ - if (flags & (ECF_CONST | ECF_MALLOC)) + if (flags & (ECF_CONST | ECF_PURE | ECF_MALLOC)) start_sequence (); old_stack_allocated = stack_pointer_delta - pending_stack_adjust; @@ -2653,7 +2669,7 @@ expand_call (exp, target, ignore) { /* When the stack adjustment is pending, we get better code by combining the adjustments. */ - if (pending_stack_adjust && ! (flags & ECF_CONST) + if (pending_stack_adjust && ! (flags & (ECF_CONST | ECF_PURE)) && ! inhibit_defer_pop) { int adjust; @@ -2821,7 +2837,8 @@ expand_call (exp, target, ignore) Test valreg so we don't crash; may safely ignore `const' if return type is void. Disable for PARALLEL return values, because we have no way to move such values into a pseudo register. */ - if ((flags & ECF_CONST) && valreg != 0 && GET_CODE (valreg) != PARALLEL) + if ((flags & (ECF_CONST | ECF_PURE)) + && valreg != 0 && GET_CODE (valreg) != PARALLEL) { rtx note = 0; rtx temp = gen_reg_rtx (GET_MODE (valreg)); @@ -2840,11 +2857,17 @@ expand_call (exp, target, ignore) insns = get_insns (); end_sequence (); + if (flags & ECF_PURE) + note = gen_rtx_EXPR_LIST (VOIDmode, + gen_rtx_USE (VOIDmode, + gen_rtx_MEM (BLKmode, + gen_rtx_SCRATCH (VOIDmode))), note); + emit_libcall_block (insns, temp, valreg, note); valreg = temp; } - else if (flags & ECF_CONST) + else if (flags & (ECF_CONST | ECF_PURE)) { /* Otherwise, just write out the sequence without a note. */ rtx insns = get_insns (); @@ -3171,11 +3194,11 @@ libfunc_nothrow (fun) The RETVAL parameter specifies whether return value needs to be saved, other parameters are documented in the emit_library_call function bellow. */ static rtx -emit_library_call_value_1 (retval, orgfun, value, no_queue, outmode, nargs, p) +emit_library_call_value_1 (retval, orgfun, value, fn_type, outmode, nargs, p) int retval; rtx orgfun; rtx value; - int no_queue; + int fn_type; enum machine_mode outmode; int nargs; va_list p; @@ -3223,8 +3246,10 @@ emit_library_call_value_1 (retval, orgfun, value, no_queue, outmode, nargs, p) #endif #endif - if (no_queue) + if (fn_type == 1) flags |= ECF_CONST; + else if (fn_type == 2) + flags |= ECF_PURE; fun = orgfun; if (libfunc_nothrow (fun)) @@ -3258,7 +3283,7 @@ emit_library_call_value_1 (retval, orgfun, value, no_queue, outmode, nargs, p) #endif /* This call returns a big structure. */ - flags &= ~ECF_CONST; + flags &= ~(ECF_CONST | ECF_PURE); } /* ??? Unfinished: must pass the memory address as an argument. */ @@ -3282,7 +3307,7 @@ emit_library_call_value_1 (retval, orgfun, value, no_queue, outmode, nargs, p) /* Now we are about to start emitting insns that can be deleted if a libcall is deleted. */ - if (flags & ECF_CONST) + if (flags & (ECF_CONST | ECF_PURE)) start_sequence (); push_temp_slots (); @@ -3726,7 +3751,7 @@ emit_library_call_value_1 (retval, orgfun, value, no_queue, outmode, nargs, p) Test valreg so we don't crash; may safely ignore `const' if return type is void. Disable for PARALLEL return values, because we have no way to move such values into a pseudo register. */ - if ((flags & ECF_CONST) + if ((flags & (ECF_CONST | ECF_PURE)) && valreg != 0 && GET_CODE (valreg) != PARALLEL) { rtx note = 0; @@ -3743,11 +3768,17 @@ emit_library_call_value_1 (retval, orgfun, value, no_queue, outmode, nargs, p) insns = get_insns (); end_sequence (); + if (flags & ECF_PURE) + note = gen_rtx_EXPR_LIST (VOIDmode, + gen_rtx_USE (VOIDmode, + gen_rtx_MEM (BLKmode, + gen_rtx_SCRATCH (VOIDmode))), note); + emit_libcall_block (insns, temp, valreg, note); valreg = temp; } - else if (flags & ECF_CONST) + else if (flags & (ECF_CONST | ECF_PURE)) { /* Otherwise, just write out the sequence without a note. */ rtx insns = get_insns (); @@ -3830,26 +3861,18 @@ emit_library_call_value_1 (retval, orgfun, value, no_queue, outmode, nargs, p) and machine_modes to convert them to. The rtx values should have been passed through protect_from_queue already. - NO_QUEUE will be true if and only if the library call is a `const' call - which will be enclosed in REG_LIBCALL/REG_RETVAL notes; it is equivalent - to the flag ECF_CONST in expand_call. - - NO_QUEUE must be true for const calls, because if it isn't, then - any pending increment will be emitted between REG_LIBCALL/REG_RETVAL notes, - and will be lost if the libcall sequence is optimized away. - - NO_QUEUE must be false for non-const calls, because if it isn't, the - call insn will have its CONST_CALL_P bit set, and it will be incorrectly - optimized. For instance, the instruction scheduler may incorrectly - move memory references across the non-const call. */ + FN_TYPE will is zero for `normal' calls, one for `const' calls, wich + which will be enclosed in REG_LIBCALL/REG_RETVAL notes and two for `pure' + calls, that are handled like `const' calls with extra + (use (memory (scratch)). */ void -emit_library_call VPARAMS((rtx orgfun, int no_queue, enum machine_mode outmode, +emit_library_call VPARAMS((rtx orgfun, int fn_type, enum machine_mode outmode, int nargs, ...)) { #ifndef ANSI_PROTOTYPES rtx orgfun; - int no_queue; + int fn_type; enum machine_mode outmode; int nargs; #endif @@ -3859,12 +3882,12 @@ emit_library_call VPARAMS((rtx orgfun, int no_queue, enum machine_mode outmode, #ifndef ANSI_PROTOTYPES orgfun = va_arg (p, rtx); - no_queue = va_arg (p, int); + fn_type = va_arg (p, int); outmode = va_arg (p, enum machine_mode); nargs = va_arg (p, int); #endif - emit_library_call_value_1 (0, orgfun, NULL_RTX, no_queue, outmode, nargs, p); + emit_library_call_value_1 (0, orgfun, NULL_RTX, fn_type, outmode, nargs, p); va_end (p); } @@ -3878,13 +3901,13 @@ emit_library_call VPARAMS((rtx orgfun, int no_queue, enum machine_mode outmode, If VALUE is nonzero, VALUE is returned. */ rtx -emit_library_call_value VPARAMS((rtx orgfun, rtx value, int no_queue, +emit_library_call_value VPARAMS((rtx orgfun, rtx value, int fn_type, enum machine_mode outmode, int nargs, ...)) { #ifndef ANSI_PROTOTYPES rtx orgfun; rtx value; - int no_queue; + int fn_type; enum machine_mode outmode; int nargs; #endif @@ -3895,12 +3918,12 @@ emit_library_call_value VPARAMS((rtx orgfun, rtx value, int no_queue, #ifndef ANSI_PROTOTYPES orgfun = va_arg (p, rtx); value = va_arg (p, rtx); - no_queue = va_arg (p, int); + fn_type = va_arg (p, int); outmode = va_arg (p, enum machine_mode); nargs = va_arg (p, int); #endif - value = emit_library_call_value_1 (1, orgfun, value, no_queue, outmode, nargs, p); + value = emit_library_call_value_1 (1, orgfun, value, fn_type, outmode, nargs, p); va_end (p); diff --git a/gcc/extend.texi b/gcc/extend.texi index 4cce201bc17..22fe7414d2e 100644 --- a/gcc/extend.texi +++ b/gcc/extend.texi @@ -1379,22 +1379,44 @@ typedef void voidfn (); volatile voidfn fatal; @end smallexample -@cindex @code{const} function attribute -@item const -Many functions do not examine any values except their arguments, and -have no effects except the return value. Such a function can be subject +@cindex @code{pure} function attribute +@item pure +Many functions have no effects except the return value and their +return value and depends only on the parameters and/or global variables. +Such a function can be subject to common subexpression elimination and loop optimization just as an arithmetic operator would be. These functions should be declared -with the attribute @code{const}. For example, +with the attribute @code{pure}. For example, @smallexample -int square (int) __attribute__ ((const)); +int square (int) __attribute__ ((pure)); @end smallexample @noindent says that the hypothetical function @code{square} is safe to call fewer times than the program says. +Some of common examples of pure functions are @code{strlen} or @code{memcmp}. +Interesting non-pure functions are functions with infinite loops or those +depending on volatile memory or other system resource, that may change between +two consetuctive calls (such as @code{feof} in multithreding environment). + +The attribute @code{pure} is not implemented in GNU C versions earlier +than 2.96. +@cindex @code{const} function attribute +@item const +Many functions do not examine any values except their arguments, and +have no effects except the return value. Basically this is just slightly +more strict class than the "pure" attribute above, since function is not +alloved to read global memory. + +@cindex pointer arguments +Note that a function that has pointer arguments and examines the data +pointed to must @emph{not} be declared @code{const}. Likewise, a +function that calls a non-@code{const} function usually must not be +@code{const}. It does not make sense for a @code{const} function to +return @code{void}. + The attribute @code{const} is not implemented in GNU C versions earlier than 2.5. An alternative way to declare that a function has no side effects, which works in the current version and in some older versions, @@ -1409,12 +1431,6 @@ extern const intfn square; This approach does not work in GNU C++ from 2.6.0 on, since the language specifies that the @samp{const} must be attached to the return value. -@cindex pointer arguments -Note that a function that has pointer arguments and examines the data -pointed to must @emph{not} be declared @code{const}. Likewise, a -function that calls a non-@code{const} function usually must not be -@code{const}. It does not make sense for a @code{const} function to -return @code{void}. @item format (@var{archetype}, @var{string-index}, @var{first-to-check}) @cindex @code{format} function attribute diff --git a/gcc/optabs.c b/gcc/optabs.c index dbdea0412ba..85bb24eae61 100644 --- a/gcc/optabs.c +++ b/gcc/optabs.c @@ -3020,14 +3020,14 @@ prepare_cmp_insn (px, py, pcomparison, size, pmode, punsignedp, align, #endif { #ifdef TARGET_MEM_FUNCTIONS - emit_library_call (memcmp_libfunc, 0, + emit_library_call (memcmp_libfunc, 2, TYPE_MODE (integer_type_node), 3, XEXP (x, 0), Pmode, XEXP (y, 0), Pmode, convert_to_mode (TYPE_MODE (sizetype), size, TREE_UNSIGNED (sizetype)), TYPE_MODE (sizetype)); #else - emit_library_call (bcmp_libfunc, 0, + emit_library_call (bcmp_libfunc, 2, TYPE_MODE (integer_type_node), 3, XEXP (x, 0), Pmode, XEXP (y, 0), Pmode, convert_to_mode (TYPE_MODE (integer_type_node), diff --git a/gcc/tree.h b/gcc/tree.h index 03321025181..9c426fa727f 100644 --- a/gcc/tree.h +++ b/gcc/tree.h @@ -1253,6 +1253,10 @@ struct tree_type not an alias. */ #define DECL_IS_MALLOC(NODE) (FUNCTION_DECL_CHECK (NODE)->decl.malloc_flag) +/* Nonzero in a FUNCTION_DECL means this function should be treated + as "pure" function (like const function, but may read global memory). */ +#define DECL_IS_PURE(NODE) (FUNCTION_DECL_CHECK (NODE)->decl.pure_flag) + /* Nonzero in a FIELD_DECL means it is a bit field, and must be accessed specially. */ #define DECL_BIT_FIELD(NODE) (FIELD_DECL_CHECK (NODE)->decl.bit_field_flag) @@ -1392,6 +1396,7 @@ struct tree_decl unsigned comdat_flag : 1; unsigned malloc_flag : 1; unsigned no_limit_stack : 1; + unsigned pure_flag : 1; #ifdef ONLY_INT_FIELDS unsigned int built_in_class : 2; #else -- 2.30.2