New feature patches: -mlong-calls command line switch on ARM.

Dmitri Makarov dim@windriver.com
Mon Feb 7 14:03:00 GMT 2000


This is an extension of -mlong-calls feature on ARM.  This implements
a heuristic to determine which calls need not be made indirect when
-mlong-calls command line option is used. Calls to functions declared
static or the ones that were defined in the same source file before
the call in question being parsed considered to be close enough to use
BL instruction for all calls to the functions and no indirect calls are
generated in this case.  Also a new #pragma and function attributes
are implemented.  The pragma allows to override both -mlong-call switch
and the heuristic. Its syntax is #pragma longcall (x), where x is
either 1 or 0, which means that all function declarations after the
pragma are considered to be "far" (i.e. the functions must be called
indirectly) iff x == 1 and "near" (i.e. BL insn should be used to call
the functions), iff x == 0, for all declarations until another #pragma
longcall is seen.  New attributes __attribute__ ((shortcall)) and
__attribute__ ((longcall)) can be used to refine the effects of
-mlong-calls comand line switch, the heuristics and the #pragma
longcall and to specify how particular function should be called.

Thanks.
Dmitri Makarov.


ChangeLog:

2000-02-07  Dmitri Makarov  <dim@wrs.com>

	* config/arm/arm.md (define_expand "call" and subsequent call
 	define_insn-s): added support for indirect calls and -mlong-calls
 	command line switch.
	
	* config/arm/arm.[ch]: support for -mlong-calls command line
	switch.  New functions: arm_valid_machine_type_attribute,
	arm_init_cumulative_args, arm_function_arg  -- replaced
	corresponding macros in arm.h.  New functions:
	current_file_function_operand, arm_is_longcall_p,
	arm_set_default_type_attributes, arm_handle_other_pragma_token --
	implementation of longcall/shortcall heuristic and support for
	longcall/shortcall function attributes and #pragma longcall ()
	features.

	* c-pragma.[ch] (HANDLE_OTHER_PRAGMA_TOKEN): support for #pragma
	longcall () feature.

	* varasm.c (assemble_start_function): added SYMBOL_REF_FLAG to
	support longcall/shortcall heuristic.

	* invoke.texi and extend.texi: description of new features added.


The patch itself:

Index: c-pragma.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/c-pragma.c,v
retrieving revision 1.29
diff -c -3 -p -r1.29 c-pragma.c
*** c-pragma.c	2000/02/06 03:40:45	1.29
--- c-pragma.c	2000/02/07 20:44:10
*************** handle_pragma_token (string, token)
*** 261,266 ****
--- 261,271 ----
  	case ps_poison:
  	  ret_val = 1;
  	  break;
+ #ifdef HANDLE_OTHER_PRAGMA_TOKEN
+ 	case ps_other:
+           HANDLE_OTHER_PRAGMA_TOKEN (string, token);
+           break;
+ #endif /* HANDLE_OTHER_PRAGMA_TOKEN */
  	}
  
        type = state = ps_start;
*************** handle_pragma_token (string, token)
*** 300,308 ****
--- 305,327 ----
        if (strcmp (string, "weak") == 0)
  	type = state = ps_weak;
  #endif
+ #ifdef HANDLE_OTHER_PRAGMA_TOKEN
+       /* If we haven't already handled it then do so now */
+       if (type == ps_done)
+       {
+         HANDLE_OTHER_PRAGMA_TOKEN (string, token);
+ 	type = state = ps_other;
+       }
+ #endif
        if (strcmp (string, "poison") == 0)
  	type = state = ps_poison;
        break;
+ 
+ #ifdef HANDLE_OTHER_PRAGMA_TOKEN
+     case ps_other:
+       HANDLE_OTHER_PRAGMA_TOKEN (string, token);
+       break;
+ #endif
  
  #ifdef HANDLE_PRAGMA_WEAK
      case ps_weak:
Index: c-pragma.h
===================================================================
RCS file: /cvs/gcc/egcs/gcc/c-pragma.h,v
retrieving revision 1.13
diff -c -3 -p -r1.13 c-pragma.h
*** c-pragma.h	2000/01/12 22:47:12	1.13
--- c-pragma.h	2000/02/07 20:44:10
*************** extern int add_weak PARAMS ((char *, cha
*** 65,76 ****
--- 65,90 ----
     With #pragma poison, this is always set.  */
  #define HANDLE_GENERIC_PRAGMAS 1
  
+ /* Define HANDLE_OTHER_PRAGMA_TOKEN(string, token) if you want
+    to handle pragmas other than align and weak. When handle_pragma_token 
+    comes across a pragma it doesn't understand, it will delegate 
+    responsibility for handling it to HANDLE_OTHER_PRAGMA_TOKEN (if defined),
+    passing it the current token (token) and its printable form (string).
+    The latter should implement its own state machine along the lines of the 
+    one in handle_pragma_token. */
+    
+ #if defined HANDLED_OTHER_PRAGMA_TOKEN
+ #define HANDLE_GENERIC_PRAGMAS 1
+ #endif
  
  #ifdef HANDLE_GENERIC_PRAGMAS
  enum pragma_state
  {
    ps_start,
    ps_done,
+ #ifdef HANDLE_OTHER_PRAGMA_TOKEN
+   ps_other,
+ #endif
  #ifdef HANDLE_PRAGMA_WEAK
    ps_weak,
    ps_name,
Index: extend.texi
===================================================================
RCS file: /cvs/gcc/egcs/gcc/extend.texi,v
retrieving revision 1.41
diff -c -3 -p -r1.41 extend.texi
*** extend.texi	1999/11/28 20:45:34	1.41
--- extend.texi	2000/02/07 21:21:15
*************** compiler to always call the function via
*** 1613,1618 ****
--- 1613,1627 ----
  which reside further than 64 megabytes (67,108,864 bytes) from the
  current location can be called.
  
+ @item longcall/shortcall
+ @cindex indirect calls on ARM and THUMB
+ This attribute allows to specify how to call a particular function on
+ ARM and THUMB.  Both attribute override @code{-mlong-calls} (@pxref{ARM
+ Options}) command line switch and @code{#pragma longcall} settings.  The
+ @code{longcall} attribute causes the compiler to always call the
+ function via a pointer and the @code{shortcall} attribute to always call
+ the function via @samp{BL} instruction.
+ 
  @item dllimport
  @cindex functions which are imported from a dll on PowerPC Windows NT
  On the PowerPC running Windows NT, the @code{dllimport} attribute causes
Index: invoke.texi
===================================================================
RCS file: /cvs/gcc/egcs/gcc/invoke.texi,v
retrieving revision 1.167
diff -c -3 -p -r1.167 invoke.texi
*** invoke.texi	2000/02/03 14:10:01	1.167
--- invoke.texi	2000/02/07 21:21:21
*************** in the following sections.
*** 269,274 ****
--- 269,275 ----
  -mstructure-size-boundary=
  -mbsd -mxopen -mno-symrename
  -mabort-on-noreturn
+ -mlong-calls -mno-long-calls
  -mnop-fun-dllimport -mno-nop-fun-dllimport
  -msingle-pic-base -mno-single-pic-base
  -mpic-register=
*************** value as future versions of the toolchai
*** 4577,4582 ****
--- 4578,4593 ----
  @kindex -mnoabort-on-noreturn
  Generate a call to the function abort at the end of a noreturn function.
  It will be executed if the function tries to return.
+ 
+ @item -mlong-calls
+ @itemx -mno-long-calls
+ Do calls indirect with loading of 32-bit address of the callee into
+ @samp{PC} register.  You need to use this switch, if you call outside of
+ the current 64 megabyte segment to functions that are not through
+ pointers.  Not all calls made indirect when this option is used.  The
+ heuristic is that functions declared static and also the functions which
+ definitions have been seen in the same source file, before the call in
+ question being parsed, are close enough and need not be called indirectly.
  
  @item -mnop-fun-dllimport
  @kindex -mnop-fun-dllimport
Index: varasm.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/varasm.c,v
retrieving revision 1.96
diff -c -3 -p -r1.96 varasm.c
*** varasm.c	2000/01/17 15:55:18	1.96
--- varasm.c	2000/02/07 20:44:21
*************** assemble_start_function (decl, fnname)
*** 997,1002 ****
--- 997,1005 ----
  
    app_disable ();
  
+   /* make known that the function is defined */
+   SYMBOL_REF_FLAG (XEXP (DECL_RTL (decl), 0)) = 1;
+ 
    if (CONSTANT_POOL_BEFORE_FUNCTION)
      output_constant_pool (fnname, decl);
  
Index: config/arm/arm-protos.h
===================================================================
RCS file: /cvs/gcc/egcs/gcc/config/arm/arm-protos.h,v
retrieving revision 1.4
diff -c -3 -p -r1.4 arm-protos.h
*** arm-protos.h	2000/01/19 23:44:14	1.4
--- arm-protos.h	2000/02/07 20:44:22
*************** extern int    shift_operator			PARAMS ((
*** 145,153 ****
  extern int    shiftable_operator		PARAMS ((rtx, Mmode));
  extern int    soft_df_operand			PARAMS ((rtx, Mmode));
  extern int    store_multiple_operation		PARAMS ((rtx, Mmode));
  #if defined TREE_CODE
  extern rtx    arm_function_arg			PARAMS ((CUMULATIVE_ARGS *, Mmode, tree, int));
! extern void   arm_init_cumulative_args		PARAMS ((CUMULATIVE_ARGS *, tree, rtx, int));
  #endif /* TREE_CODE */
  #endif /* HAVE_MACHINE_MODES */
  #endif /* RTX_CODE */
--- 145,157 ----
  extern int    shiftable_operator		PARAMS ((rtx, Mmode));
  extern int    soft_df_operand			PARAMS ((rtx, Mmode));
  extern int    store_multiple_operation		PARAMS ((rtx, Mmode));
+ 
+ extern int    arm_is_longcall_p			PARAMS ((rtx, int, int));
+ extern rtx    arm_longcall_ref			PARAMS ((rtx));
+ 
  #if defined TREE_CODE
  extern rtx    arm_function_arg			PARAMS ((CUMULATIVE_ARGS *, Mmode, tree, int));
! extern void   arm_init_cumulative_args		PARAMS ((CUMULATIVE_ARGS *, tree, rtx));
  #endif /* TREE_CODE */
  #endif /* HAVE_MACHINE_MODES */
  #endif /* RTX_CODE */
Index: config/arm/arm.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/config/arm/arm.c,v
retrieving revision 1.78
diff -c -3 -p -r1.78 arm.c
*** arm.c	2000/01/22 19:42:34	1.78
--- arm.c	2000/02/07 20:44:24
*************** soft_df_operand (op, mode)
*** 2312,2318 ****
    
    if (GET_CODE (op) == SUBREG)
      op = SUBREG_REG (op);
!   
    switch (GET_CODE (op))
      {
      case CONST_DOUBLE:
--- 2312,2318 ----
    
    if (GET_CODE (op) == SUBREG)
      op = SUBREG_REG (op);
! 
    switch (GET_CODE (op))
      {
      case CONST_DOUBLE:
*************** arm_valid_machine_decl_attribute (decl, 
*** 3190,3195 ****
--- 3190,3223 ----
    return 0;
  }
  
+ 
+ /* Return non-zero if ATTR with arguments ARGS is a valid machine
+    specific attribute for TYPE.  The attributes in ATTRIBUTES have
+    previously been assigned to TYPE. */
+ 
+ int
+ arm_valid_machine_type_attribute (type, attributes, attr, args)
+   tree type;
+   tree attributes;
+   tree attr;
+   tree args;
+ {
+   if (args != NULL_TREE)
+     return 0;
+ 
+   /* Longcall attribute says that the function is not within 2**26 bytes
+      of the current function, and to do an indirect call.  */
+   if (is_attribute_p ("longcall", attr))
+     return (TREE_CODE (type) == FUNCTION_TYPE);
+ 
+   /* Shortcall attribute overrides both command line switch
+      -mlong-calls and #pragma longcall (1). */
+   if (is_attribute_p ("shortcall", attr))
+     return (TREE_CODE (type) == FUNCTION_TYPE);
+ 
+   return 0;
+ } /* arm_valid_machine_type_attribute */
+ 
  /* Return non-zero if FUNC is a naked function.  */
  
  static int
*************** arm_gen_movstrqi (operands)
*** 3433,3439 ****
  	  MEM_IN_STRUCT_P (mem) = dst_in_struct_p;
  	  MEM_SCALAR_P (mem) = dst_scalar_p;
  	  emit_move_insn (mem, gen_rtx_SUBREG (QImode, part_bytes_reg, 0));
! 	  
  	  if (--last_bytes)
  	    {
  	      tmp = gen_reg_rtx (SImode);
--- 3461,3467 ----
  	  MEM_IN_STRUCT_P (mem) = dst_in_struct_p;
  	  MEM_SCALAR_P (mem) = dst_scalar_p;
  	  emit_move_insn (mem, gen_rtx_SUBREG (QImode, part_bytes_reg, 0));
! 
  	  if (--last_bytes)
  	    {
  	      tmp = gen_reg_rtx (SImode);
*************** arm_gen_movstrqi (operands)
*** 3455,3461 ****
  	  MEM_IN_STRUCT_P (mem) = dst_in_struct_p;
  	  MEM_SCALAR_P (mem) = dst_scalar_p;
  	  emit_move_insn (mem, gen_rtx_SUBREG (QImode, part_bytes_reg, 0));
! 	  
  	  if (--last_bytes)
  	    {
  	      rtx tmp = gen_reg_rtx (SImode);
--- 3483,3489 ----
  	  MEM_IN_STRUCT_P (mem) = dst_in_struct_p;
  	  MEM_SCALAR_P (mem) = dst_scalar_p;
  	  emit_move_insn (mem, gen_rtx_SUBREG (QImode, part_bytes_reg, 0));
! 
  	  if (--last_bytes)
  	    {
  	      rtx tmp = gen_reg_rtx (SImode);
*************** print_multi_reg (stream, instr, reg, mas
*** 4549,4554 ****
--- 4577,4836 ----
    fprintf (stream, "}%s\n", hat ? "^" : "");
  }
  
+ /* Initialize a variable CUM of type CUMULATIVE_ARGS
+    for a call to a function whose data type is FNTYPE.
+    For a library call, FNTYPE is 0. */
+ 
+ void
+ arm_init_cumulative_args (cum, fntype, libname)
+   CUMULATIVE_ARGS *cum;
+   tree fntype;
+   rtx libname ATTRIBUTE_UNUSED;
+ {
+   if (fntype && aggregate_value_p (TREE_TYPE (fntype)) )
+     cum->args = 1;
+   else
+     cum->args = 0;
+ 
+   cum->call_cookie = CALL_NORMAL;
+ 
+   /* check for longcall/shortcall attribute  */
+   if (fntype && lookup_attribute ("longcall", TYPE_ATTRIBUTES (fntype)))
+     cum->call_cookie = CALL_LONG;
+ 
+   /* It is possible that a func declaration has both longcall and
+      short call attributes.  For example if #pragma longcall (1)
+      and subsequent __attribute__ (shortcall) present.  In this case
+      shortcall overrides longcall */
+   if (fntype && lookup_attribute ("shortcall", TYPE_ATTRIBUTES (fntype)))
+     cum->call_cookie = CALL_SHORT;
+ } /* arm_init_cumulative_args */
+ 
+ /* Define where to put the arguments to a function.  Value is zero to
+    push the argument on the stack, or a hard register in which to
+    store the argument.
+ 
+    CUM is a variable of type CUMULATIVE_ARGS which gives info about
+    the preceding args and about the function being called.
+    
+    MODE is the argument's machine mode.
+    
+    TYPE is the data type of the argument (as a tree).  This is null
+    for libcalls where that information may not be available.
+    
+    NAMED is nonzero if this argument is a named parameter
+    (otherwise it is an extra parameter matching an ellipsis).
+    
+    On the ARM, normally the first 16 bytes are passed in registers
+    r0-r3; all other arguments are passed on the stack.
+    If (NAMED == 0) (which happens only in assign_parms, since
+    SETUP_INCOMING_VARARGS is defined), say it is passed in the stack
+    (function_prologue will indeed make it pass in the stack if
+    necessary). */
+ 
+ struct rtx_def *
+ arm_function_arg (cum, mode, type, named)
+   CUMULATIVE_ARGS *cum;
+   enum machine_mode mode;
+   tree type;
+   int named;
+ {
+   if (mode == VOIDmode)
+     return GEN_INT (cum->call_cookie);
+ 
+   if (named && (cum->args < NUM_ARG_REGS))
+     return gen_rtx_REG (mode, cum->args);
+ 
+   return NULL_RTX;
+ } /* arm_function_arg */
+ 
+ 
+ /* Return 1 if the operand is a SYMBOL_REF for a function known to be in
+    this file.  */
+ 
+ static int
+ current_file_function_operand (sym_ref)
+   rtx sym_ref;
+ {
+   return (SYMBOL_REF_FLAG (sym_ref)
+ 	  || sym_ref == XEXP (DECL_RTL (current_function_decl), 0));
+ } /* current_file_function_operand */
+ 
+ /* Return non-zero if a 32 bit "longcall" should be generated for this
+    call. We generate a longcall if the function is not declared
+    __attribute__ ((shortcall),
+ 
+    AND:
+    
+    (1)  the function is declared __attribute__ ((longcall))
+         (either explicitly in the source code or implicitly
+ 	when the declaration is within a #pragma longcall (1)),
+ 
+    OR
+ 
+    (2) -mlong-calls is enabled and we don't know whether the target
+         function is declared in this file.
+ 	 
+    This function will typically be called by C fragments in the
+    machine description file. CALL_REF and CALL_COOKIE will correspond
+    to matched rtl operands. CALL_SYMBOL is used to distinguish between
+    two different callers of the function.  It is set to 1 in
+    define_insn "call_symbol" or "call_symbol_value" patterns in arm.md
+    and to 0 in define_expand "call" and "call_value".  This is because
+    of the difference of SYM_REFs passed from "call_symbol" and "call"
+    patterns.
+ */
+ 
+ int
+ arm_is_longcall_p (sym_ref, call_cookie, call_symbol)
+   rtx sym_ref;
+   int call_cookie;
+   int call_symbol;
+ {
+   if (GET_CODE (sym_ref) != MEM && ! call_symbol)
+     {
+       return 0;
+     }
+ 
+   if (! call_symbol)
+     {
+       sym_ref = XEXP (sym_ref, 0);
+     }
+ 
+   if (GET_CODE (sym_ref) != SYMBOL_REF)
+     {
+       return 0;
+     }
+ 
+   if (call_cookie & CALL_SHORT)
+     {
+       return 0;
+     }
+ 
+   if (current_file_function_operand (sym_ref, VOIDmode))
+     {
+       return 0;
+     }
+ 
+   if ((call_cookie & CALL_LONG) || TARGET_LONG_CALLS)
+     {
+       return 1;
+     }
+ 
+   return 0;
+ } /* arm_is_longcall_p */
+ 
+ /* Three-state flag. Non-zero positive iff #pragma longcall is
+    currently on.  The initial value must be set to -1, that means no
+    longcall/shortcall #pragma have ever been seen. Zero iff shortcall
+    is on. */
+ 
+ int arm_pragma_longcall = -1;
+ 
+ void
+ arm_set_default_type_attributes (type)
+   tree type ATTRIBUTE_UNUSED;
+ {
+   /* add __attribute__ ((longcall)) to all functions, when
+      inside #pragma longcall (1) or __attribute__ ((shortcall)),
+      when inside #pragma longcall (0). */
+   
+   if (TREE_CODE (type) == FUNCTION_TYPE)
+     {
+       tree type_attr_list, attr_name;
+       type_attr_list = TYPE_ATTRIBUTES (type);
+       if (arm_pragma_longcall > 0)
+ 	attr_name = get_identifier ("longcall");
+       else if (arm_pragma_longcall == 0)
+ 	attr_name = get_identifier ("shortcall");
+       else
+ 	return;
+ 
+       type_attr_list = tree_cons (attr_name, NULL_TREE, type_attr_list);
+       TYPE_ATTRIBUTES (type) = type_attr_list;
+     }
+ } /* arm_set_default_type_attributes */
+ 
+ /* Handle one token of a pragma directive.  TOKEN is the
+    current token, and STRING is its printable form. */
+ void
+ arm_handle_other_pragma_token (string, token)
+   char *string;
+   tree token;
+ {
+   static enum other_pragma_state state = ops_start, type;
+   static int longcall;
+   if (string == 0)
+     {
+     if (type == ops_longcall)
+       {
+ 	if (state == ops_right_longcall)
+ 	  arm_pragma_longcall = longcall;
+ 	else
+ 	  warning ("malformed `#pragma longcall'");
+       }
+       type = state = ops_start;
+       return;
+     }
+ 
+   switch (state)
+     {
+     case ops_start:
+       if (token && TREE_CODE (token) == IDENTIFIER_NODE)
+ 	{
+ 	  if (strcmp (IDENTIFIER_POINTER (token), "longcall") == 0)
+ 	    type = state = ops_longcall;
+ 	  else
+ 	    type = state = ops_done;
+ 	}
+       else
+ 	type = state = ops_done;
+       break;
+ 
+     case ops_longcall:
+       if (strcmp (string, "(") == 0)
+ 	state = ops_left_longcall;
+       else
+ 	state = ops_bad;
+       break;
+ 
+     case ops_left_longcall:
+       if (token && TREE_CODE (token) == INTEGER_CST
+ 	  && TREE_INT_CST_HIGH (token) == 0)
+ 	switch (TREE_INT_CST_LOW (token))
+ 	  {
+ 	  case 0:
+ 	  case 1:
+ 	    longcall = TREE_INT_CST_LOW (token);
+ 	    state = ops_switch_longcall;
+ 	    break;
+ 
+ 	  default:
+ 	    state = ops_bad;
+ 	  }
+       else if (! token && strcmp (string, ")") == 0)
+ 	{
+ 	  longcall = 0;
+ 	  state = ops_right_longcall;
+ 	}
+       else
+ 	state = ops_bad;
+       break;
+ 
+     case ops_switch_longcall:
+       if (strcmp (string, ")") == 0)
+ 	state = ops_right_longcall;
+       else
+ 	state = ops_bad;
+       break;
+ 
+     case ops_right_longcall:
+       state = ops_bad;
+       break;
+ 
+     }
+ } /* arm_handle_other_pragma_token */
+ 
  /* Output a 'call' insn. */
  
  char *
*************** output_call (operands)
*** 4562,4568 ****
        operands[0] = gen_rtx_REG (SImode, IP_REGNUM);
        output_asm_insn ("mov%?\t%0, %|lr", operands);
      }
!   
    output_asm_insn ("mov%?\t%|lr, %|pc", operands);
    
    if (TARGET_INTERWORK)
--- 4844,4850 ----
        operands[0] = gen_rtx_REG (SImode, IP_REGNUM);
        output_asm_insn ("mov%?\t%0, %|lr", operands);
      }
! 
    output_asm_insn ("mov%?\t%|lr, %|pc", operands);
    
    if (TARGET_INTERWORK)
*************** eliminate_lr2ip (x)
*** 4595,4608 ****
      default:
        /* Scan through the sub-elements and change any references there */
        fmt = GET_RTX_FORMAT (code);
!       
        for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
  	if (fmt[i] == 'e')
  	  something_changed |= eliminate_lr2ip (&XEXP (x0, i));
  	else if (fmt[i] == 'E')
  	  for (j = 0; j < XVECLEN (x0, i); j++)
  	    something_changed |= eliminate_lr2ip (&XVECEXP (x0, i, j));
!       
        return something_changed;
      }
  }
--- 4877,4890 ----
      default:
        /* Scan through the sub-elements and change any references there */
        fmt = GET_RTX_FORMAT (code);
! 
        for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
  	if (fmt[i] == 'e')
  	  something_changed |= eliminate_lr2ip (&XEXP (x0, i));
  	else if (fmt[i] == 'E')
  	  for (j = 0; j < XVECLEN (x0, i); j++)
  	    something_changed |= eliminate_lr2ip (&XVECEXP (x0, i, j));
! 
        return something_changed;
      }
  }
*************** output_mov_long_double_fpu_from_arm (ope
*** 4655,4661 ****
    
    output_asm_insn ("stm%?fd\t%|sp!, {%0, %1, %2}", ops);
    output_asm_insn ("ldf%?e\t%0, [%|sp], #12", operands);
!   
    return "";
  }
  
--- 4937,4943 ----
    
    output_asm_insn ("stm%?fd\t%|sp!, {%0, %1, %2}", ops);
    output_asm_insn ("ldf%?e\t%0, [%|sp], #12", operands);
! 
    return "";
  }
  
*************** output_move_double (operands)
*** 4817,4823 ****
  	      otherops[1] = GEN_INT (CONST_DOUBLE_HIGH (operands[1]));
  	      operands[1] = GEN_INT (CONST_DOUBLE_LOW (operands[1]));
  	    }
! 	  
  	  output_mov_immediate (operands);
  	  output_mov_immediate (otherops);
  	}
--- 5099,5105 ----
  	      otherops[1] = GEN_INT (CONST_DOUBLE_HIGH (operands[1]));
  	      operands[1] = GEN_INT (CONST_DOUBLE_LOW (operands[1]));
  	    }
! 
  	  output_mov_immediate (operands);
  	  output_mov_immediate (otherops);
  	}
*************** arm_output_epilogue ()
*** 5805,5811 ****
  	      if (regs_ever_live[reg] && ! call_used_regs[reg])
  		{
  		  floats_offset += 12;
! 		  
  		  /* We can't unstack more than four registers at once */
  		  if (start_reg - reg == 3)
  		    {
--- 6087,6093 ----
  	      if (regs_ever_live[reg] && ! call_used_regs[reg])
  		{
  		  floats_offset += 12;
! 
  		  /* We can't unstack more than four registers at once */
  		  if (start_reg - reg == 3)
  		    {
Index: config/arm/arm.h
===================================================================
RCS file: /cvs/gcc/egcs/gcc/config/arm/arm.h,v
retrieving revision 1.58
diff -c -3 -p -r1.58 arm.h
*** arm.h	2000/01/19 09:42:11	1.58
--- arm.h	2000/02/07 20:44:26
*************** Unrecognized value in TARGET_CPU_DEFAULT
*** 365,371 ****
       "Generate re-entrant, PIC code" },				\
    {"no-apcs-reentrant",	       -ARM_FLAG_APCS_REENT, "" },	\
    {"alignment-traps",           ARM_FLAG_MMU_TRAPS,		\
!      "The MMU will trap on unaligned accesses" },\
    {"no-alignment-traps",       -ARM_FLAG_MMU_TRAPS, "" },	\
    {"short-load-bytes",		ARM_FLAG_MMU_TRAPS, "" },	\
    {"no-short-load-bytes",      -ARM_FLAG_MMU_TRAPS, "" },	\
--- 365,371 ----
       "Generate re-entrant, PIC code" },				\
    {"no-apcs-reentrant",	       -ARM_FLAG_APCS_REENT, "" },	\
    {"alignment-traps",           ARM_FLAG_MMU_TRAPS,		\
!      "The MMU will trap on unaligned accesses" },		\
    {"no-alignment-traps",       -ARM_FLAG_MMU_TRAPS, "" },	\
    {"short-load-bytes",		ARM_FLAG_MMU_TRAPS, "" },	\
    {"no-short-load-bytes",      -ARM_FLAG_MMU_TRAPS, "" },	\
*************** Unrecognized value in TARGET_CPU_DEFAULT
*** 394,400 ****
       "Do not load the PIC register in function prologues" },	\
    {"no-single-pic-base",       -ARM_FLAG_SINGLE_PIC_BASE, "" },	\
    {"long-calls",		ARM_FLAG_LONG_CALLS,		\
!    "Generate all call instructions as indirect calls"},		\
    {"no-long-calls",	       -ARM_FLAG_LONG_CALLS, ""},	\
    SUBTARGET_SWITCHES						\
    {"",				TARGET_DEFAULT, "" }		\
--- 394,400 ----
       "Do not load the PIC register in function prologues" },	\
    {"no-single-pic-base",       -ARM_FLAG_SINGLE_PIC_BASE, "" },	\
    {"long-calls",		ARM_FLAG_LONG_CALLS,		\
!    "Generate call insns as indirect calls, if necessary" },	\
    {"no-long-calls",	       -ARM_FLAG_LONG_CALLS, ""},	\
    SUBTARGET_SWITCHES						\
    {"",				TARGET_DEFAULT, "" }		\
*************** extern int arm_is_6_or_7;
*** 491,496 ****
--- 491,520 ----
  
  #define TARGET_MEM_FUNCTIONS 1
  
+ /* Define HANDLE_OTHER_PRAGMA_TOKEN(string, token) if you want
+    to handle pragmas other than align and weak. When handle_pragma_token
+    comes across a pragma it doesn't understand, it will delegate
+    responsibility for handling it to HANDLE_OTHER_PRAGMA_TOKEN (if defined),
+    passing it the current token (token) and its printable form (string).
+    The latter should implement its own state machine along the lines of the
+    one in handle_pragma_token. */
+ 
+ #define HANDLE_OTHER_PRAGMA_TOKEN(string, token) \
+   arm_handle_other_pragma_token(string, token)
+ 
+ extern int arm_pragma_longcall;
+ 
+ enum other_pragma_state
+ {
+   ops_start,
+   ops_done,
+   ops_bad,
+   ops_longcall,
+   ops_left_longcall,
+   ops_switch_longcall,
+   ops_right_longcall
+ };
+ 
  #define OVERRIDE_OPTIONS  arm_override_options ()
  
  /* Nonzero if PIC code requires explicit qualifiers to generate
*************** enum reg_class
*** 1136,1141 ****
--- 1160,1186 ----
     than a word, or if they contain elements offset from zero in the struct. */
  #define DEFAULT_PCC_STRUCT_RETURN 0
  
+ /* Flags for the call/call_value rtl operations set up by function_arg */
+ #define CALL_NORMAL		0x00000000	/* no special processing */
+ #define CALL_LONG		0x00000001	/* always call indirect */
+ #define CALL_SHORT		0x00000002	/* never call indirect */
+ 
+ /* Define a data type for recording info about an argument list
+    during the scan of that argument list.  This data type should
+    hold all necessary information about the function itself
+    and about the args processed so far, enough to enable macros
+    such as FUNCTION_ARG to determine where the next arg should go.
+ 
+    On the ARM, this is a structure.  The first element is  the number
+    of bytes of arguments scanned so far, second is the place to store
+    call type (direct or indirect).   */
+ 
+ typedef struct arm_args
+ {
+   int args;			/* number of arguments scanned so far */
+   int call_cookie;		/* Do special things for this call */
+ } cumulative_args;
+ 
  /* Define where to put the arguments to a function.
     Value is zero to push the argument on the stack,
     or a hard register in which to store the argument.
*************** enum reg_class
*** 1154,1191 ****
     only in assign_parms, since SETUP_INCOMING_VARARGS is defined), say it is
     passed in the stack (function_prologue will indeed make it pass in the
     stack if necessary).  */
! #define FUNCTION_ARG(CUM, MODE, TYPE, NAMED)		\
!   ((NAMED)						\
!    ? ((CUM) >= NUM_ARG_REGS ? 0 : gen_rtx_REG (MODE, CUM))\
!    : 0)
  
  /* For an arg passed partly in registers and partly in memory,
     this is the number of registers used.
     For args passed entirely in registers or entirely in memory, zero.  */
  #define FUNCTION_ARG_PARTIAL_NREGS(CUM, MODE, TYPE, NAMED)	\
!   (    NUM_ARG_REGS > (CUM)					\
!    && (NUM_ARG_REGS < ((CUM) + NUM_REGS2 (MODE, TYPE)))		\
!    ?   NUM_ARG_REGS - (CUM) : 0)
  
  /* A C type for declaring a variable that is used as the first argument of
     `FUNCTION_ARG' and other related values.  For some target machines, the
     type `int' suffices and can hold the number of bytes of argument so far.
  
!    On the ARM, this is the number of bytes of arguments scanned so far.  */
! #define CUMULATIVE_ARGS  int
  
  /* Initialize a variable CUM of type CUMULATIVE_ARGS
     for a call to a function whose data type is FNTYPE.
     For a library call, FNTYPE is 0.
     On the ARM, the offset starts at 0.  */
  #define INIT_CUMULATIVE_ARGS(CUM, FNTYPE, LIBNAME, INDIRECT)  \
!   ((CUM) = (((FNTYPE) && aggregate_value_p (TREE_TYPE ((FNTYPE)))) ? 1 : 0))
  
  /* Update the data in CUM to advance over an argument
     of mode MODE and data type TYPE.
     (TYPE is null for libcalls where that information may not be available.)  */
  #define FUNCTION_ARG_ADVANCE(CUM, MODE, TYPE, NAMED)	\
!   (CUM) += NUM_REGS2 (MODE, TYPE)
  
  /* 1 if N is a possible register number for function argument passing.
     On the ARM, r0-r3 are used to pass args.  */
--- 1199,1235 ----
     only in assign_parms, since SETUP_INCOMING_VARARGS is defined), say it is
     passed in the stack (function_prologue will indeed make it pass in the
     stack if necessary).  */
! 
! #define FUNCTION_ARG(CUM, MODE, TYPE, NAMED) \
!   arm_function_arg (&CUM, MODE, TYPE, NAMED)
  
  /* For an arg passed partly in registers and partly in memory,
     this is the number of registers used.
     For args passed entirely in registers or entirely in memory, zero.  */
  #define FUNCTION_ARG_PARTIAL_NREGS(CUM, MODE, TYPE, NAMED)	\
!   (    NUM_ARG_REGS > (CUM).args					\
!    && (NUM_ARG_REGS < ((CUM).args + NUM_REGS2 (MODE, TYPE)))		\
!    ?   NUM_ARG_REGS - (CUM).args : 0)
  
  /* A C type for declaring a variable that is used as the first argument of
     `FUNCTION_ARG' and other related values.  For some target machines, the
     type `int' suffices and can hold the number of bytes of argument so far.
  
!    On the ARM, this is the number of arguments scanned so far.  */
! #define CUMULATIVE_ARGS  cumulative_args
  
  /* Initialize a variable CUM of type CUMULATIVE_ARGS
     for a call to a function whose data type is FNTYPE.
     For a library call, FNTYPE is 0.
     On the ARM, the offset starts at 0.  */
  #define INIT_CUMULATIVE_ARGS(CUM, FNTYPE, LIBNAME, INDIRECT)  \
!   arm_init_cumulative_args (&CUM, FNTYPE, LIBNAME)
  
  /* Update the data in CUM to advance over an argument
     of mode MODE and data type TYPE.
     (TYPE is null for libcalls where that information may not be available.)  */
  #define FUNCTION_ARG_ADVANCE(CUM, MODE, TYPE, NAMED)	\
!   (CUM).args += NUM_REGS2 (MODE, TYPE)
  
  /* 1 if N is a possible register number for function argument passing.
     On the ARM, r0-r3 are used to pass args.  */
*************** enum reg_class
*** 1209,1216 ****
  {									\
    extern int current_function_anonymous_args;				\
    current_function_anonymous_args = 1;					\
!   if ((CUM) < NUM_ARG_REGS)						\
!     (PRETEND_SIZE) = (NUM_ARG_REGS - (CUM)) * UNITS_PER_WORD;		\
  }
  
  /* Generate assembly output for the start of a function.  */
--- 1253,1260 ----
  {									\
    extern int current_function_anonymous_args;				\
    current_function_anonymous_args = 1;					\
!   if ((CUM).args < NUM_ARG_REGS)						\
!     (PRETEND_SIZE) = (NUM_ARG_REGS - (CUM).args) * UNITS_PER_WORD;		\
  }
  
  /* Generate assembly output for the start of a function.  */
*************** enum reg_class
*** 1383,1388 ****
--- 1427,1445 ----
  		  (FNADDR));						\
  }
  
+ /* If defined, a C expression whose value is nonzero if NAME
+    with arguments ARGS is a valid machine specific attribute for TYPE.
+    The attributes in ATTRIBUTES have previously been assigned to TYPE.  */
+ 
+ #define VALID_MACHINE_TYPE_ATTRIBUTE(TYPE, ATTRIBUTES, NAME, ARGS) \
+   (arm_valid_machine_type_attribute (TYPE, ATTRIBUTES, NAME, ARGS))
+ 
+ /* If defined, a C statement that assigns default attributes to newly
+    defined TYPE.  */
+ 
+ #define SET_DEFAULT_TYPE_ATTRIBUTES(TYPE) \
+   (arm_set_default_type_attributes (TYPE))
+ 
  
  /* Addressing modes, and classification of registers for them.  */
  
*************** enum reg_class
*** 1457,1464 ****
--- 1514,1546 ----
                   ? TREE_CST_RTL (decl) : DECL_RTL (decl));		\
        SYMBOL_REF_FLAG (XEXP (rtl, 0)) = 1;				\
      }									\
+ 									\
+   if (TREE_CODE (decl) == FUNCTION_DECL			                \
+       && (TREE_ASM_WRITTEN (decl) || ! TREE_PUBLIC (decl)))             \
+     SYMBOL_REF_FLAG (XEXP (DECL_RTL (decl), 0)) = 1;                    \
  }
+ #else
+ /* If we are referencing a function that is static or is known to be 
+    in this file, make the SYMBOL_REF special.  We can use this to indicate
+    that we can do direct call to that function.  */
+ #define ENCODE_SECTION_INFO(decl)					\
+ {									\
+   if (TREE_CODE (decl) == FUNCTION_DECL			                \
+       && (TREE_ASM_WRITTEN (decl) || ! TREE_PUBLIC (decl)))             \
+     SYMBOL_REF_FLAG (XEXP (DECL_RTL (decl), 0)) = 1;                    \
+ }
  #endif
+ 
+ /* Record the information that a function has been defined
+    in this file */
+ 
+ /* When we have -mlong-calls we could use this extra information
+    so that we can be smarter about which functions should be called
+    indirectly. */
+ 
+ #define MARK_FN_DEFINED_IN_THIS_FILE(DECL) \
+   if (TREE_CODE (DECL) == FUNCTION_DECL) \
+ 	 SYMBOL_REF_FLAG (XEXP (DECL_RTL (DECL), 0)) = 1
  
  /* The macros REG_OK_FOR..._P assume that the arg is a REG rtx
     and check its validity for a certain class.
Index: config/arm/arm.md
===================================================================
RCS file: /cvs/gcc/egcs/gcc/config/arm/arm.md,v
retrieving revision 1.41
diff -c -3 -p -r1.41 arm.md
*** arm.md	2000/01/09 19:42:57	1.41
--- arm.md	2000/02/07 20:44:31
***************
*** 4505,4515 ****
  (define_expand "call"
    [(parallel [(call (match_operand 0 "memory_operand" "")
  	            (match_operand 1 "general_operand" ""))
  	      (clobber (reg:SI 14))])]
    ""
!   "
!   {
!     if (TARGET_LONG_CALLS && GET_CODE (XEXP (operands[0], 0)) != REG)
        XEXP (operands[0], 0) = force_reg (Pmode, XEXP (operands[0], 0));
    }"
  )
--- 4505,4525 ----
  (define_expand "call"
    [(parallel [(call (match_operand 0 "memory_operand" "")
  	            (match_operand 1 "general_operand" ""))
+ 	      (use (match_operand 2 "" ""))
  	      (clobber (reg:SI 14))])]
    ""
!   
!   "{ /* This is to generate indirect calls by loading the 32 bit address
!      of the callee directly into pc.  SYMBOL_REF, which is a part of
!      operand[1] is forced into a register and then mem:SI of the
!      register is loaded into pc.  operand[2] is used to pass the
!      longcall/shosrtcall attribute to arm_is_longcall_p().  This attribute
!      is set/reset whenever __attribute__ or pargma longcall () is
!      used.  See arm.c and arm.h for info about it.  The third
!      parameter to arm_is_longcall_p is used to tell when the predicat is
!      called.  The function defined in arm.c, there's more info there. */
! 
!     if (arm_is_longcall_p (operands[0], INTVAL (operands[2]), 0))
        XEXP (operands[0], 0) = force_reg (Pmode, XEXP (operands[0], 0));
    }"
  )
***************
*** 4517,4522 ****
--- 4527,4533 ----
  (define_insn "*call_reg"
    [(call (mem:SI (match_operand:SI 0 "s_register_operand" "r"))
           (match_operand 1 "" "g"))
+    (use (match_operand:SI 2 "immediate_operand" "n"))
     (clobber (reg:SI 14))]
    ""
    "*
***************
*** 4529,4534 ****
--- 4540,4546 ----
  (define_insn "*call_mem"
    [(call (mem:SI (match_operand:SI 0 "memory_operand" "m"))
  	 (match_operand 1 "general_operand" "g"))
+    (use (match_operand:SI 2 "immediate_operand" "n"))
     (clobber (reg:SI 14))]
    ""
    "*
***************
*** 4541,4552 ****
    [(parallel [(set (match_operand 0 "" "=rf")
  	           (call (match_operand 1 "memory_operand" "m")
  		         (match_operand 2 "general_operand" "g")))
  	      (clobber (reg:SI 14))])]
    ""
!   "
!   {
!     if (TARGET_LONG_CALLS && GET_CODE (XEXP (operands[1], 0)) != REG)
!       XEXP (operands[1], 0) = force_reg (Pmode, XEXP (operands[1], 0));
    }"
  )
  
--- 4553,4565 ----
    [(parallel [(set (match_operand 0 "" "=rf")
  	           (call (match_operand 1 "memory_operand" "m")
  		         (match_operand 2 "general_operand" "g")))
+ 	      (use (match_operand 3 "" ""))
  	      (clobber (reg:SI 14))])]
    ""
!   "{ /* See the comment in define_expand \"call\" */
! 
!      if (arm_is_longcall_p (operands[1], INTVAL (operands[3]), 0))
!        XEXP (operands[1], 0) = force_reg (Pmode, XEXP (operands[1], 0));
    }"
  )
  
***************
*** 4554,4559 ****
--- 4567,4573 ----
    [(set (match_operand 0 "" "=rf")
          (call (mem:SI (match_operand:SI 1 "s_register_operand" "r"))
  	      (match_operand 2 "general_operand" "g")))
+    (use (match_operand:SI 3 "immediate_operand" "n"))
     (clobber (reg:SI 14))]
    ""
    "*
***************
*** 4566,4571 ****
--- 4580,4586 ----
    [(set (match_operand 0 "" "=rf")
  	(call (mem:SI (match_operand 1 "memory_operand" "m"))
  	(match_operand 2 "general_operand" "g")))
+    (use (match_operand:SI 3 "immediate_operand" "n"))
     (clobber (reg:SI 14))]
    "! CONSTANT_ADDRESS_P (XEXP (operands[1], 0))"
    "*
***************
*** 4580,4587 ****
  (define_insn "*call_symbol"
    [(call (mem:SI (match_operand:SI 0 "" "X"))
  	 (match_operand:SI 1 "general_operand" "g"))
     (clobber (reg:SI 14))]
!   "! TARGET_LONG_CALLS && GET_CODE (operands[0]) == SYMBOL_REF"
    "*
    {
      return NEED_PLT_RELOC ? \"bl%?\\t%a0(PLT)\" : \"bl%?\\t%a0\";
--- 4595,4604 ----
  (define_insn "*call_symbol"
    [(call (mem:SI (match_operand:SI 0 "" "X"))
  	 (match_operand:SI 1 "general_operand" "g"))
+    (use (match_operand:SI 2 "immediate_operand" "n"))
     (clobber (reg:SI 14))]
!   "! arm_is_longcall_p (operands[0], INTVAL (operands[2]), 1) \
!    && GET_CODE (operands[0]) == SYMBOL_REF"
    "*
    {
      return NEED_PLT_RELOC ? \"bl%?\\t%a0(PLT)\" : \"bl%?\\t%a0\";
***************
*** 4592,4599 ****
    [(set (match_operand 0 "s_register_operand" "=rf")
  	(call (mem:SI (match_operand:SI 1 "" "X"))
  	(match_operand:SI 2 "general_operand" "g")))
     (clobber (reg:SI 14))]
!   "! TARGET_LONG_CALLS && GET_CODE(operands[1]) == SYMBOL_REF"
    "*
    {
      return NEED_PLT_RELOC ? \"bl%?\\t%a1(PLT)\" : \"bl%?\\t%a1\";
--- 4609,4618 ----
    [(set (match_operand 0 "s_register_operand" "=rf")
  	(call (mem:SI (match_operand:SI 1 "" "X"))
  	(match_operand:SI 2 "general_operand" "g")))
+    (use (match_operand:SI 3 "immediate_operand" "n"))
     (clobber (reg:SI 14))]
!   "! arm_is_longcall_p (operands[1], INTVAL (operands[3]), 1) \
!    && GET_CODE(operands[1]) == SYMBOL_REF"
    "*
    {
      return NEED_PLT_RELOC ? \"bl%?\\t%a1(PLT)\" : \"bl%?\\t%a1\";


More information about the Gcc-patches mailing list