This is the mail archive of the
gcc@gcc.gnu.org
mailing list for the GCC project.
Re: aliasing problem with va_arg
- To: egcs at cygnus dot com
- Subject: Re: aliasing problem with va_arg
- From: Jim Wilson <wilson at cygnus dot com>
- Date: Tue, 27 Oct 1998 11:12:46 -0800
- cc: mark at markmitchell dot com, wilson at cygnus dot com
I dug out the experimental va_arg code I wrote about a year and a half ago.
It would need some cleaning up before it would be useful.
Jim
I managed to move the varargs support from the preprocessor level to the
tree->RTL generator with little trouble. I didn't bother to try to get
all details of ANSI C right, or to gracefully handle user errors, but the
patches are good enough that I can bootstrap the irix6 gcc port.
--
Some interesting problems I ran into along the way...
va_arg takes a type as the second parameter, thus it is not possible to
handle it as an ordinary function call. It needs explicit support in the
parser, just like sizeof does.
The stdarg va_start function can be passed a variable of any type as its
second argument. There is no way to declare such a function, so this
requires a little special support. The current code pretends that it is
a stdarg function with one named argument; this allows us to type check the
first argument while accepting anything for the second argument.
The type used for va_list must be target dependent. (Some C library
functions take a va_list parameter, e.g. vfprintf. In order for these
functions to work when called from gcc, the va_list type used by gcc must
be the same type as the va_list type used by the compiler that compiled
the C library.) This means that varargs.h/stdarg.h can't be completely
generic. We either need to have a target dependent fragment that defines
this type, or else have gcc define another predefined type (like __SIZE_TYPE__)
and then assume in these files that this type will be defined by the
preprocessor. I have not tried to address this problem in these patches.
I suspect the type used for the varargs va_dcl macro is not important, but
it is something that should be watched for. For instance, on a 64 bit target,
that has 32 bit ints, it might need to be long long instead of int. If this
type matters, this is another part of the varargs.h file that will be target
dependent, or else require a gcc assumption.
--
These patches are for the FSF gcc development sources as of 97-03-14,
and only handle the irix6 N32 varargs/stdarg conventions.
Diffs for these generated files have not been included:
c-gperf.h
c-parse.c
c-parse.h
c-parse.y
objc-parse.c
objc-parse.y
diff -pr clean-ss-970314/c-decl.c tmp/c-decl.c
*** clean-ss-970314/c-decl.c Sun Feb 2 04:13:50 1997
--- tmp/c-decl.c Thu Mar 20 22:18:10 1997
*************** init_decl_processing ()
*** 3184,3189 ****
--- 3184,3213 ----
endlink)),
BUILT_IN_ALLOCA, "alloca");
builtin_function ("__builtin_ffs", int_ftype_int, BUILT_IN_FFS, NULL_PTR);
+
+ builtin_function ("__builtin_varargs_start",
+ build_function_type (void_type_node,
+ tree_cons (NULL_TREE, ptr_type_node,
+ endlink)),
+ BUILT_IN_VARARGS_START, NULL_PTR);
+
+ /* ??? The second parameter here can actually be any type. This will
+ have to be handled somehow. Perhaps by not specifying the parameter
+ types here, and then checking them in expand_builtin. Perhaps by adding
+ a special match-anything type. This works by pretending that it is a
+ stdarg function with one named argument. */
+ builtin_function ("__builtin_stdarg_start",
+ build_function_type (void_type_node,
+ tree_cons (NULL_TREE, ptr_type_node,
+ NULL_TREE)),
+ BUILT_IN_STDARG_START, NULL_PTR);
+
+ builtin_function ("__builtin_va_end",
+ build_function_type (void_type_node,
+ tree_cons (NULL_TREE, ptr_type_node,
+ endlink)),
+ BUILT_IN_VA_END, NULL_PTR);
+
/* Define alloca, ffs as builtins.
Declare _exit just to mark it as volatile. */
if (! flag_no_builtin && !flag_no_nonansi_builtin)
diff -pr clean-ss-970314/c-parse.gperf tmp/c-parse.gperf
*** clean-ss-970314/c-parse.gperf Wed Oct 9 04:24:35 1996
--- tmp/c-parse.gperf Thu Mar 20 15:52:22 1997
*************** __asm, ASM_KEYWORD, NORID
*** 21,26 ****
--- 21,27 ----
__asm__, ASM_KEYWORD, NORID
__attribute, ATTRIBUTE, NORID
__attribute__, ATTRIBUTE, NORID
+ __builtin_va_arg, VA_ARG, NORID
__complex, TYPESPEC, RID_COMPLEX
__complex__, TYPESPEC, RID_COMPLEX
__const, TYPE_QUAL, RID_CONST
diff -pr clean-ss-970314/c-parse.in tmp/c-parse.in
*** clean-ss-970314/c-parse.in Tue Jan 7 13:58:08 1997
--- tmp/c-parse.in Thu Mar 20 16:46:28 1997
*************** void yyerror ();
*** 139,145 ****
/* the reserved words */
/* SCO include files test "ASM", so use something else. */
! %token SIZEOF ENUM STRUCT UNION IF ELSE WHILE DO FOR SWITCH CASE DEFAULT
%token BREAK CONTINUE RETURN GOTO ASM_KEYWORD TYPEOF ALIGNOF
%token ATTRIBUTE EXTENSION LABEL
%token REALPART IMAGPART
--- 139,145 ----
/* the reserved words */
/* SCO include files test "ASM", so use something else. */
! %token SIZEOF VA_ARG ENUM STRUCT UNION IF ELSE WHILE DO FOR SWITCH CASE DEFAULT
%token BREAK CONTINUE RETURN GOTO ASM_KEYWORD TYPEOF ALIGNOF
%token ATTRIBUTE EXTENSION LABEL
%token REALPART IMAGPART
*************** unary_expr:
*** 503,508 ****
--- 503,510 ----
{ $$ = build_unary_op (REALPART_EXPR, $2, 0); }
| IMAGPART cast_expr %prec UNARY
{ $$ = build_unary_op (IMAGPART_EXPR, $2, 0); }
+ | VA_ARG '(' expr_no_commas ',' typename ')'
+ { $$ = build_va_arg ($3, groktypename ($5)); }
;
sizeof:
diff -pr clean-ss-970314/c-tree.h tmp/c-tree.h
*** clean-ss-970314/c-tree.h Sun Jan 19 12:04:55 1997
--- tmp/c-tree.h Thu Mar 20 15:55:36 1997
*************** extern void incomplete_type_error PROTO
*** 323,328 ****
--- 323,329 ----
extern tree common_type PROTO((tree, tree));
extern int comptypes PROTO((tree, tree));
extern int self_promoting_args_p PROTO((tree));
+ extern tree build_va_arg PROTO((tree, tree));
extern tree c_sizeof PROTO((tree));
extern tree c_sizeof_nowarn PROTO((tree));
extern tree c_size_in_bytes PROTO((tree));
diff -pr clean-ss-970314/c-typeck.c tmp/c-typeck.c
*** clean-ss-970314/c-typeck.c Sun Mar 2 10:20:26 1997
--- tmp/c-typeck.c Thu Mar 20 16:43:56 1997
*************** signed_or_unsigned_type (unsignedp, type
*** 793,798 ****
--- 793,805 ----
return type;
}
+ tree
+ build_va_arg (expr, type)
+ tree expr, type;
+ {
+ return build1 (VA_ARG_EXPR, type, expr);
+ }
+
/* Compute the value of the `sizeof' operator. */
tree
diff -pr clean-ss-970314/expr.c tmp/expr.c
*** clean-ss-970314/expr.c Sun Jan 5 04:26:02 1997
--- tmp/expr.c Thu Mar 20 22:23:03 1997
*************** expand_expr (exp, target, tmode, modifie
*** 7262,7267 ****
--- 7262,7299 ----
return op0;
return const0_rtx;
+ case VA_ARG_EXPR:
+ {
+ /* ??? This code must be target dependent. */
+ /* Get AP. */
+ op0 = expand_expr (TREE_OPERAND (exp, 0), NULL_RTX, VOIDmode, 0);
+
+ /* Compute new value for AP. */
+ temp = expand_binop (GET_MODE (op0), add_optab, op0, GEN_INT (7),
+ NULL_RTX, 0, OPTAB_LIB_WIDEN);
+ temp = expand_binop (GET_MODE (op0), and_optab, temp, GEN_INT (-8),
+ NULL_RTX, 0, OPTAB_LIB_WIDEN);
+ temp = expand_binop (GET_MODE (op0), add_optab, temp,
+ GEN_INT (((TREE_INT_CST_LOW (TYPE_SIZE (type))
+ / BITS_PER_UNIT)
+ + 7)
+ / 8 * 8),
+ NULL_RTX, 0, OPTAB_LIB_WIDEN);
+ /* Store new value back into AP. */
+ emit_move_insn (op0, temp);
+
+ /* Compute address of argument from new AP value. */
+ temp = expand_binop (GET_MODE (op0), add_optab, temp,
+ GEN_INT (- (TREE_INT_CST_LOW (TYPE_SIZE (type))
+ / BITS_PER_UNIT)),
+ NULL_RTX, 0, OPTAB_LIB_WIDEN);
+ /* Create MEM to reference argument. */
+ temp = gen_rtx (MEM, TYPE_MODE (type), temp);
+ MEM_IN_STRUCT_P (temp) = 1;
+
+ return temp;
+ }
+
default:
return (*lang_expand_expr) (exp, original_target, tmode, modifier);
}
*************** expand_builtin_return_addr (fndecl_code,
*** 8022,8027 ****
--- 8054,8101 ----
return tem;
}
+ static rtx
+ expand_builtin_next_arg (arglist)
+ tree arglist;
+ {
+ tree fntype = TREE_TYPE (current_function_decl);
+
+ if ((TYPE_ARG_TYPES (fntype) == 0
+ || (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype))) == void_type_node))
+ && ! current_function_varargs)
+ {
+ error ("`va_start' used in function with fixed args");
+ return const0_rtx;
+ }
+
+ if (arglist)
+ {
+ tree last_parm = tree_last (DECL_ARGUMENTS (current_function_decl));
+ tree arg = TREE_VALUE (arglist);
+
+ /* Strip off all nops for the sake of the comparison. This
+ is not quite the same as STRIP_NOPS. It does more.
+ We must also strip off INDIRECT_EXPR for C++ reference
+ parameters. */
+ while (TREE_CODE (arg) == NOP_EXPR
+ || TREE_CODE (arg) == CONVERT_EXPR
+ || TREE_CODE (arg) == NON_LVALUE_EXPR
+ || TREE_CODE (arg) == INDIRECT_REF)
+ arg = TREE_OPERAND (arg, 0);
+ if (arg != last_parm)
+ warning ("second parameter of `va_start' not last named argument");
+ }
+ else if (! current_function_varargs)
+ /* Evidently an out of date version of <stdarg.h>; can't validate
+ va_start's second argument, but can still work as intended. */
+ warning ("`__builtin_next_arg' called without an argument");
+
+ return expand_binop (Pmode, add_optab,
+ current_function_internal_arg_pointer,
+ current_function_arg_offset_rtx,
+ NULL_RTX, 0, OPTAB_LIB_WIDEN);
+ }
+
/* Expand an expression EXP that calls a built-in function,
with result going to TARGET if that's convenient
(and in mode MODE if that's convenient).
*************** expand_builtin (exp, target, subtarget,
*** 8339,8383 ****
/* Return the address of the first anonymous stack arg. */
case BUILT_IN_NEXT_ARG:
! {
! tree fntype = TREE_TYPE (current_function_decl);
!
! if ((TYPE_ARG_TYPES (fntype) == 0
! || (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype)))
! == void_type_node))
! && ! current_function_varargs)
! {
! error ("`va_start' used in function with fixed args");
! return const0_rtx;
! }
!
! if (arglist)
! {
! tree last_parm = tree_last (DECL_ARGUMENTS (current_function_decl));
! tree arg = TREE_VALUE (arglist);
!
! /* Strip off all nops for the sake of the comparison. This
! is not quite the same as STRIP_NOPS. It does more.
! We must also strip off INDIRECT_EXPR for C++ reference
! parameters. */
! while (TREE_CODE (arg) == NOP_EXPR
! || TREE_CODE (arg) == CONVERT_EXPR
! || TREE_CODE (arg) == NON_LVALUE_EXPR
! || TREE_CODE (arg) == INDIRECT_REF)
! arg = TREE_OPERAND (arg, 0);
! if (arg != last_parm)
! warning ("second parameter of `va_start' not last named argument");
! }
! else if (! current_function_varargs)
! /* Evidently an out of date version of <stdarg.h>; can't validate
! va_start's second argument, but can still work as intended. */
! warning ("`__builtin_next_arg' called without an argument");
! }
!
! return expand_binop (Pmode, add_optab,
! current_function_internal_arg_pointer,
! current_function_arg_offset_rtx,
! NULL_RTX, 0, OPTAB_LIB_WIDEN);
case BUILT_IN_CLASSIFY_TYPE:
if (arglist != 0)
--- 8413,8419 ----
/* Return the address of the first anonymous stack arg. */
case BUILT_IN_NEXT_ARG:
! return expand_builtin_next_arg (arglist);
case BUILT_IN_CLASSIFY_TYPE:
if (arglist != 0)
*************** expand_builtin (exp, target, subtarget,
*** 9094,9099 ****
--- 9130,9177 ----
return const0_rtx;
}
+
+ case BUILT_IN_VARARGS_START:
+ {
+ /* ??? This code must be target dependent. */
+ rtx temp;
+
+ /* Get AP. */
+ op0 = expand_expr (TREE_VALUE (arglist), NULL_RTX, VOIDmode, 0);
+
+ /* Compute starting value for AP. */
+ temp = expand_builtin_next_arg (NULL_TREE);
+ if (current_function_args_info.arg_words >= 8)
+ temp = expand_binop (GET_MODE (temp), add_optab, temp, GEN_INT (-8),
+ NULL_RTX, 0, OPTAB_LIB_WIDEN);
+
+ /* Store starting value into AP. */
+ emit_move_insn (op0, temp);
+
+ return const0_rtx;
+ }
+
+ case BUILT_IN_STDARG_START:
+ {
+ /* ??? This code must be target dependent. */
+ rtx temp;
+
+ /* Get AP. */
+ op0 = expand_expr (TREE_VALUE (arglist), NULL_RTX, VOIDmode, 0);
+
+ /* Compute starting value for AP. */
+ temp = expand_builtin_next_arg (TREE_CHAIN (arglist));
+
+ /* Store starting value into AP. */
+ emit_move_insn (op0, temp);
+
+ return const0_rtx;
+ }
+
+ case BUILT_IN_VA_END:
+ /* ??? This code must be target dependent. */
+ /* No action is necesary here. */
+ return const0_rtx;
default: /* just do library call, if unknown builtin */
error ("built-in function `%s' not currently supported",
diff -pr clean-ss-970314/ginclude/stdarg.h tmp/ginclude/stdarg.h
*** clean-ss-970314/ginclude/stdarg.h Wed Dec 11 13:24:09 1996
--- tmp/ginclude/stdarg.h Thu Mar 20 18:44:46 1997
***************
*** 1,3 ****
--- 1,18 ----
+ #if 1
+
+ /* ??? This type is OS (actually C library) dependent. */
+ typedef char * __gnuc_va_list;
+
+ typedef __gnuc_va_list va_list;
+
+ #define va_start __builtin_stdarg_start
+
+ #define va_arg __builtin_va_arg
+
+ #define va_end __builtin_va_end
+
+ #else
+
/* stdarg.h for GNU.
Note that the type used in va_arg is supposed to match the
actual type **after default promotions**.
*************** typedef __gnuc_va_list va_list;
*** 176,178 ****
--- 191,195 ----
#endif /* not _ANSI_STDARG_H_ */
#endif /* not _STDARG_H */
+
+ #endif
diff -pr clean-ss-970314/ginclude/varargs.h tmp/ginclude/varargs.h
*** clean-ss-970314/ginclude/varargs.h Sun Mar 2 14:37:21 1997
--- tmp/ginclude/varargs.h Thu Mar 20 18:43:39 1997
***************
*** 1,3 ****
--- 1,23 ----
+ #if 1
+
+ /* ??? This type is OS (actually C library) dependent. */
+ typedef char * __gnuc_va_list;
+
+ typedef __gnuc_va_list va_list;
+
+ #define va_alist __builtin_va_alist
+
+ /* ??? It would be nice to get rid of the ellipsis here. */
+ #define va_dcl int __builtin_va_alist; ...
+
+ #define va_start __builtin_varargs_start
+
+ #define va_arg __builtin_va_arg
+
+ #define va_end __builtin_va_end
+
+ #else
+
/* Record that this is varargs.h; this turns off stdarg.h. */
#ifndef _VARARGS_H
*************** typedef __gnuc_va_list va_list;
*** 194,197 ****
--- 214,219 ----
undefined instead of _VA_LIST_. */
#ifdef _BSD_VA_LIST
#undef _BSD_VA_LIST
+ #endif
+
#endif
diff -pr clean-ss-970314/tree.def tmp/tree.def
*** clean-ss-970314/tree.def Fri Nov 8 17:30:11 1996
--- tmp/tree.def Thu Mar 20 15:50:12 1997
*************** DEFTREECODE (PREDECREMENT_EXPR, "predecr
*** 689,694 ****
--- 689,696 ----
DEFTREECODE (PREINCREMENT_EXPR, "preincrement_expr", "e", 2)
DEFTREECODE (POSTDECREMENT_EXPR, "postdecrement_expr", "e", 2)
DEFTREECODE (POSTINCREMENT_EXPR, "postincrement_expr", "e", 2)
+
+ DEFTREECODE (VA_ARG_EXPR, "va_arg_expr", "e", 1)
/* These types of expressions have no useful value,
and always have side effects. */
diff -pr clean-ss-970314/tree.h tmp/tree.h
*** clean-ss-970314/tree.h Sun Feb 16 02:04:51 1997
--- tmp/tree.h Thu Mar 20 18:48:04 1997
*************** enum built_in_function
*** 100,105 ****
--- 100,109 ----
BUILT_IN_SETJMP,
BUILT_IN_LONGJMP,
+ BUILT_IN_VARARGS_START,
+ BUILT_IN_STDARG_START,
+ BUILT_IN_VA_END,
+
/* C++ extensions */
BUILT_IN_NEW,
BUILT_IN_VEC_NEW,