aboutsummaryrefslogtreecommitdiff
path: root/gcc/c-common.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/c-common.c')
-rw-r--r--gcc/c-common.c1049
1 files changed, 1035 insertions, 14 deletions
diff --git a/gcc/c-common.c b/gcc/c-common.c
index b414915bc87..70131d49527 100644
--- a/gcc/c-common.c
+++ b/gcc/c-common.c
@@ -47,6 +47,8 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
#include "tree-mudflap.h"
#include "opts.h"
#include "real.h"
+/* APPLE LOCAL 64bit shorten warning 3865314 */
+#include "options.h"
cpp_reader *parse_in; /* Declared in c-pragma.h. */
@@ -158,6 +160,13 @@ cpp_reader *parse_in; /* Declared in c-pragma.h. */
tree char_array_type_node;
+ ** APPLE LOCAL begin pascal strings **
+ Type `unsigned char[SOMENUMBER]'.
+ Used for pascal-type strings ("\pstring").
+
+ tree pascal_string_type_node;
+ ** APPLE LOCAL end pascal strings **
+
Type `int[SOMENUMBER]' or something like it.
Used when an array of int needed and the size is irrelevant.
@@ -254,6 +263,13 @@ int flag_short_double;
int flag_short_wchar;
+/* APPLE LOCAL begin lvalue cast */
+/* Nonzero means allow assignment, increment or decrement of casts of
+ lvalues (e.g., '((foo *)p)++') if both the lvalue and its cast are
+ of POD type with identical size and alignment. */
+int flag_lvalue_cast_assign = 1;
+/* APPLE LOCAL end lvalue cast */
+
/* Nonzero means allow Microsoft extensions without warnings or errors. */
int flag_ms_extensions;
@@ -261,6 +277,11 @@ int flag_ms_extensions;
int flag_no_asm;
+/* APPLE LOCAL begin CW asm blocks */
+/* Nonzero means accept CW-style asm blocks. */
+int flag_cw_asm_blocks;
+/* APPLE LOCAL end CW asm blocks */
+
/* Nonzero means give string constants the type `const char *', as mandated
by the standard. */
@@ -330,6 +351,12 @@ int flag_hosted = 1;
int warn_main;
+/* APPLE LOCAL begin disable_typechecking_for_spec_flag */
+/* This makes type conflicts a warning, instead of an error,
+ to work around some problems with SPEC. */
+
+int disable_typechecking_for_spec_flag;
+/* APPLE LOCAL end disable_typechecking_for_spec_flag */
/* ObjC language option variables. */
@@ -347,6 +374,13 @@ int flag_next_runtime = 1;
int flag_next_runtime = 0;
#endif
+/* APPLE LOCAL begin ObjC C++ ivars */
+/* Generate special '- .cxx_construct' and '- .cxx_destruct' methods
+ to initialize any non-POD ivars in ObjC++ classes. */
+
+int flag_objc_call_cxx_cdtors = 0;
+/* APPLE LOCAL end ObjC C++ ivars */
+
/* Tells the compiler that this is a special run. Do not perform any
compiling, instead we are to test some platform dependent features
and output a C header file with appropriate definitions. */
@@ -459,6 +493,23 @@ int flag_permissive;
int flag_enforce_eh_specs = 1;
+/* APPLE LOCAL begin private extern Radar 2872481 --ilr */
+/* Nonzero if -fpreproceessed specified. This is needed by
+ init_reswords() so that it can make __private_extern__ have the
+ same rid code as extern when -fpreprocessed is specified. Normally
+ there is a -D on the command line for this. But if -fpreprocessed
+ was specified then macros aren't expanded. So we fake the token
+ value out using the rid code. */
+int flag_preprocessed = 0;
+/* APPLE LOCAL end private extern Radar 2872481 --ilr */
+
+/* APPLE LOCAL begin structor thunks */
+/* Nonzero if we prefer to clone con/de/structors. Alternative is to
+ gen multiple tiny thunk-esque things that call/jump to a unified
+ con/de/structor. This is a classic size/speed tradeoff. */
+int flag_clone_structors = 0;
+/* APPLE LOCAL end structor thunks */
+
/* Nonzero means to generate thread-safe code for initializing local
statics. */
@@ -511,6 +562,45 @@ static int constant_fits_type_p (tree, tree);
static tree check_case_value (tree);
static bool check_case_bounds (tree, tree, tree *, tree *);
+/* APPLE LOCAL begin CW asm blocks */
+/* State variable telling the lexer what to do. */
+enum cw_asm_states cw_asm_state = cw_asm_none;
+
+/* True in an asm block while parsing a decl. */
+int cw_asm_in_decl;
+
+/* This is true exactly within the interior of an asm block. It is
+ not quite the same as any of the states of cw_asm_state. */
+int cw_asm_block;
+
+/* An additional state variable, true when the next token returned
+ should be a BOL, false otherwise. */
+int cw_asm_at_bol;
+
+/* True when the lexer/parser is handling operands. */
+int cw_asm_in_operands;
+
+/* Count used for synthetic labels derived from asm block labels. */
+int cw_asm_labelno;
+
+/* Working buffer for building the assembly string. */
+static char *cw_asm_buffer;
+
+/* An array tracking which variables to list as inputs and outputs. */
+static GTY(()) varray_type cw_asm_operands;
+static GTY(()) varray_type cw_asm_operands_arg;
+
+/* Two arrays used as a map from user-supplied labels, local to an asm
+ block, to unique global labels that the assembler will like. */
+static GTY(()) varray_type cw_asm_labels;
+static GTY(()) varray_type cw_asm_labels_uniq;
+
+static int cw_asm_expr_val (tree arg);
+static void print_cw_asm_operand (char *, tree, unsigned, bool, bool);
+static int cw_asm_get_register_var (tree, unsigned, bool);
+static tree cw_asm_identifier (tree expr);
+/* APPLE LOCAL end CW asm blocks */
+
static tree handle_packed_attribute (tree *, tree, tree, int, bool *);
static tree handle_nocommon_attribute (tree *, tree, tree, int, bool *);
static tree handle_common_attribute (tree *, tree, tree, int, bool *);
@@ -542,6 +632,9 @@ static tree handle_no_limit_stack_attribute (tree *, tree, tree, int,
static tree handle_pure_attribute (tree *, tree, tree, int, bool *);
static tree handle_deprecated_attribute (tree *, tree, tree, int,
bool *);
+/* APPLE LOCAL begin "unavailable" attribute (Radar 2809697) --ilr */
+static tree handle_unavailable_attribute (tree *, tree, tree, int, bool *);
+/* APPLE LOCAL end "unavailable" attribute --ilr */
static tree handle_vector_size_attribute (tree *, tree, tree, int,
bool *);
static tree handle_nonnull_attribute (tree *, tree, tree, int, bool *);
@@ -613,6 +706,10 @@ const struct attribute_spec c_common_attribute_table[] =
handle_pure_attribute },
{ "deprecated", 0, 0, false, false, false,
handle_deprecated_attribute },
+ /* APPLE LOCAL begin "unavailable" attribute (Radar 2809697) --ilr */
+ { "unavailable", 0, 0, false, false, false,
+ handle_unavailable_attribute },
+ /* APPLE LOCAL end "unavailable" attribute --ilr */
{ "vector_size", 1, 1, false, true, false,
handle_vector_size_attribute },
{ "visibility", 1, 1, false, false, false,
@@ -746,7 +843,8 @@ fname_as_string (int pretty_p)
strname.text = (unsigned char *) namep;
strname.len = len - 1;
- if (cpp_interpret_string (parse_in, &strname, 1, &cstr, false))
+ /* APPLE LOCAL pascal strings add extra flag */
+ if (cpp_interpret_string (parse_in, &strname, 1, &cstr, false, false))
{
XDELETEVEC (namep);
return (char *) cstr.text;
@@ -835,6 +933,8 @@ fix_string_type (tree value)
{
const int wchar_bytes = TYPE_PRECISION (wchar_type_node) / BITS_PER_UNIT;
const int wide_flag = TREE_TYPE (value) == wchar_array_type_node;
+ /* APPLE LOCAL pascal strings */
+ const int pascal_flag = TREE_TYPE (value) == pascal_string_type_node;
const int nchars_max = flag_isoc99 ? 4095 : 509;
int length = TREE_STRING_LENGTH (value);
int nchars;
@@ -847,7 +947,8 @@ fix_string_type (tree value)
pedwarn ("string length %qd is greater than the length %qd ISO C%d compilers are required to support",
nchars - 1, nchars_max, flag_isoc99 ? 99 : 89);
- e_type = wide_flag ? wchar_type_node : char_type_node;
+ /* APPLE LOCAL pascal strings */
+ e_type = wide_flag ? wchar_type_node : (pascal_flag ? unsigned_char_type_node : char_type_node);
/* Create the array type for the string constant. flag_const_strings
says make the string constant an array of const char so that
copying it to a non-const pointer will get a warning. For C++,
@@ -861,13 +962,16 @@ fix_string_type (tree value)
unconditionally. */
i_type = build_index_type (build_int_cst (NULL_TREE, nchars - 1));
a_type = build_array_type (e_type, i_type);
- if (flag_const_strings)
+ /* APPLE LOCAL fwritable strings */
+ if (flag_const_strings && ! flag_writable_strings)
a_type = c_build_qualified_type (a_type, TYPE_QUAL_CONST);
TREE_TYPE (value) = a_type;
- TREE_CONSTANT (value) = 1;
- TREE_INVARIANT (value) = 1;
- TREE_READONLY (value) = 1;
+ /* APPLE LOCAL begin fwritable strings */
+ TREE_CONSTANT (value) = !flag_writable_strings;
+ TREE_INVARIANT (value) = !flag_writable_strings;
+ TREE_READONLY (value) = !flag_writable_strings;
+ /* APPLE LOCAL end fwritable strings */
TREE_STATIC (value) = 1;
return value;
}
@@ -981,6 +1085,14 @@ tree
convert_and_check (tree type, tree expr)
{
tree t = convert (type, expr);
+ /* APPLE LOCAL begin 64bit shorten warning 3865314 */
+ if (warn_shorten_64_to_32
+ && TYPE_PRECISION (TREE_TYPE (expr)) == 64
+ && TYPE_PRECISION (type) == 32)
+ {
+ warning ("implicit conversion shortens 64-bit value into a 32-bit value");
+ }
+ /* APPLE LOCAL end 64bit shorten warning 3865314 */
if (TREE_CODE (t) == INTEGER_CST)
{
if (TREE_OVERFLOW (t))
@@ -3064,6 +3176,10 @@ c_common_nodes_and_builtins (void)
array type. */
char_array_type_node
= build_array_type (char_type_node, array_domain_type);
+ /* APPLE LOCAL begin pascal strings */
+ pascal_string_type_node
+ = build_array_type (unsigned_char_type_node, array_domain_type);
+ /* APPLE LOCAL end pascal strings */
/* Likewise for arrays of ints. */
int_array_type_node
@@ -3299,7 +3415,9 @@ typedef struct disabled_builtin
} disabled_builtin;
static disabled_builtin *disabled_builtins = NULL;
-static bool builtin_function_disabled_p (const char *);
+/* APPLE LOCAL begin IMA built-in decl merging fix (radar 3645899) */
+bool builtin_function_disabled_p (const char *);
+/* APPLE LOCAL end */
/* Disable a built-in function specified by -fno-builtin-NAME. If NAME
begins with "__builtin_", give an error. */
@@ -3321,8 +3439,10 @@ disable_builtin_function (const char *name)
/* Return true if the built-in function NAME has been disabled, false
otherwise. */
-
-static bool
+/* APPLE LOCAL begin IMA built-in decl merging fix (radar 3645899) */
+/* Remove static */
+bool
+/* APPLE LOCAL end */
builtin_function_disabled_p (const char *name)
{
disabled_builtin *p;
@@ -4887,6 +5007,70 @@ handle_deprecated_attribute (tree *node, tree name,
return NULL_TREE;
}
+/* APPLE LOCAL begin "unavailable" attribute (Radar 2809697) --ilr */
+/* Handle a "unavailable" attribute; arguments as in
+ struct attribute_spec.handler. */
+
+static tree
+handle_unavailable_attribute (tree *node, tree name,
+ tree args ATTRIBUTE_UNUSED,
+ int flags ATTRIBUTE_UNUSED,
+ bool *no_add_attrs)
+{
+ tree type = NULL_TREE;
+ int warn = 0;
+ const char *what = NULL;
+
+ if (DECL_P (*node))
+ {
+ tree decl = *node;
+ type = TREE_TYPE (decl);
+
+ if (TREE_CODE (decl) == TYPE_DECL
+ || TREE_CODE (decl) == PARM_DECL
+ || TREE_CODE (decl) == VAR_DECL
+ || TREE_CODE (decl) == FUNCTION_DECL
+ || TREE_CODE (decl) == FIELD_DECL)
+ {
+ TREE_DEPRECATED (decl) = 1;
+ TREE_UNAVAILABLE (decl) = 1;
+ }
+ else
+ warn = 1;
+ }
+ else if (TYPE_P (*node))
+ {
+ if (!(flags & (int) ATTR_FLAG_TYPE_IN_PLACE))
+ *node = build_variant_type_copy (*node);
+ TREE_DEPRECATED (*node) = 1;
+ TREE_UNAVAILABLE (*node) = 1;
+ type = *node;
+ }
+ else
+ warn = 1;
+
+ if (warn)
+ {
+ *no_add_attrs = true;
+ if (type && TYPE_NAME (type))
+ {
+ if (TREE_CODE (TYPE_NAME (type)) == IDENTIFIER_NODE)
+ what = IDENTIFIER_POINTER (TYPE_NAME (*node));
+ else if (TREE_CODE (TYPE_NAME (type)) == TYPE_DECL
+ && DECL_NAME (TYPE_NAME (type)))
+ what = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (type)));
+ }
+ if (what)
+ warning ("`%s' attribute ignored for `%s'",
+ IDENTIFIER_POINTER (name), what);
+ else
+ warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name));
+ }
+
+ return NULL_TREE;
+}
+/* APPLE LOCAL end "unavailable" attribute --ilr */
+
/* Handle a "vector_size" attribute; arguments as in
struct attribute_spec.handler. */
@@ -5685,14 +5869,79 @@ fold_offsetof (tree expr)
return convert (size_type_node, fold_offsetof_1 (expr));
}
-/* Return nonzero if REF is an lvalue valid for this language;
- otherwise, print an error message and return zero. USE says
- how the lvalue is being used and so selects the error message. */
+/* APPLE LOCAL begin non lvalue assign */
+/* Return nonzero if the expression pointed to by REF is an lvalue
+ valid for this language; otherwise, print an error message and return
+ zero. USE says how the lvalue is being used and so selects the error
+ message. If -fnon-lvalue-assign has been specified, certain
+ non-lvalue expression shall be rewritten as lvalues and stored back
+ at the location pointed to by REF. */
int
-lvalue_or_else (tree ref, enum lvalue_use use)
+lvalue_or_else (tree *ref, enum lvalue_use use)
{
- int win = lvalue_p (ref);
+ tree r = *ref;
+ int win = lvalue_p (r);
+
+ /* If -fnon-lvalue-assign is specified, we shall allow assignments
+ to certain constructs that are not (stricly speaking) lvalues. */
+ if (!win && flag_non_lvalue_assign)
+ {
+ /* (1) Assignment to casts of lvalues, as long as both the lvalue and
+ the cast are POD types with identical size and alignment. */
+ if ((TREE_CODE (r) == NOP_EXPR || TREE_CODE (r) == CONVERT_EXPR
+ || TREE_CODE (r) == NON_LVALUE_EXPR)
+ && (use == lv_assign || use == lv_increment || use == lv_decrement
+ || use == lv_addressof)
+ /* APPLE LOCAL non lvalue assign */
+ && lvalue_or_else (&TREE_OPERAND (r, 0), use))
+ {
+ tree cast_to = TREE_TYPE (r);
+ tree cast_from = TREE_TYPE (TREE_OPERAND (r, 0));
+
+ if (simple_cst_equal (TYPE_SIZE (cast_to), TYPE_SIZE (cast_from))
+ && TYPE_ALIGN (cast_to) == TYPE_ALIGN (cast_from))
+ {
+ /* Rewrite '(cast_to)ref' as '*(cast_to *)&ref' so
+ that the back-end need not think too hard... */
+ *ref
+ = build_indirect_ref
+ (convert (build_pointer_type (cast_to),
+ build_unary_op
+ (ADDR_EXPR, TREE_OPERAND (r, 0), 0)), 0);
+
+ goto allow_as_lvalue;
+ }
+ }
+ /* (2) Assignment to conditional expressions, as long as both
+ alternatives are already lvalues. */
+ else if (TREE_CODE (r) == COND_EXPR
+ /* APPLE LOCAL non lvalue assign */
+ && lvalue_or_else (&TREE_OPERAND (r, 1), use)
+ /* APPLE LOCAL non lvalue assign */
+ && lvalue_or_else (&TREE_OPERAND (r, 2), use))
+ {
+ /* Rewrite 'cond ? lv1 : lv2' as '*(cond ? &lv1 : &lv2)' to
+ placate the back-end. */
+ *ref
+ = build_indirect_ref
+ (build_conditional_expr
+ (TREE_OPERAND (r, 0),
+ build_unary_op (ADDR_EXPR, TREE_OPERAND (r, 1), 0),
+ build_unary_op (ADDR_EXPR, TREE_OPERAND (r, 2), 0)),
+ 0);
+
+ allow_as_lvalue:
+ win = 1;
+ if (warn_non_lvalue_assign)
+ warning ("%s not really an lvalue; "
+ "this will be a hard error in the future",
+ (use == lv_addressof
+ ? "argument to '&'"
+ : "target of assignment"));
+ }
+ }
+/* APPLE LOCAL end non-lvalue assign */
if (!win)
{
@@ -5721,4 +5970,776 @@ lvalue_or_else (tree ref, enum lvalue_use use)
return win;
}
+/* APPLE LOCAL begin AltiVec */
+/* Convert the incoming expression EXPR into a vector constructor of
+ type VECTOR_TYPE, casting the individual vector elements as appropriate. */
+
+tree
+vector_constructor_from_expr (tree expr, tree vector_type)
+{
+ tree list = NULL_TREE, elttype = TREE_TYPE (vector_type);
+ int index;
+ bool final;
+ int all_constant = TREE_CONSTANT (expr);
+
+ /* If we already have a vector expression, then the user probably
+ wants to convert it to another. */
+ if (TREE_CODE (TREE_TYPE (expr)) == VECTOR_TYPE)
+ return convert (vector_type, expr);
+
+ /* Walk through the compound expression, gathering initializers. */
+ final = false;
+ for (index = 0; !final; ++index)
+ {
+ tree elem;
+
+ if (TREE_CODE (expr) == COMPOUND_EXPR)
+ {
+ elem = TREE_OPERAND (expr, 1);
+ expr = TREE_OPERAND (expr, 0);
+ }
+ else
+ {
+ final = true;
+ elem = expr;
+ }
+
+ while (TREE_CODE (elem) == COMPOUND_EXPR && TREE_CONSTANT (elem))
+ elem = TREE_OPERAND (elem, 1);
+ while (TREE_CODE (elem) == CONVERT_EXPR)
+ elem = TREE_OPERAND (elem, 0);
+
+ list = chainon (list,
+ build_tree_list (NULL_TREE,
+ convert (elttype, fold (elem))));
+ }
+
+ list = nreverse (list);
+
+ list = build_constructor (vector_type, list);
+ if (c_dialect_cxx ())
+ TREE_LANG_FLAG_4 (list) = 1; /* TREE_HAS_CONSTRUCTOR */
+
+ TREE_CONSTANT (list) = all_constant;
+
+ return list;
+}
+/* APPLE LOCAL end AltiVec */
+
+/* APPLE LOCAL begin CW asm blocks */
+/* Perform the default conversion of functions to pointers; simplified
+ version for use with functions mentioned in CW-style asm.
+ Return the result of converting EXP. For any other expression, just
+ return EXP. */
+
+static tree
+cw_asm_default_function_conversion (tree exp)
+{
+ tree type = TREE_TYPE (exp);
+ enum tree_code code = TREE_CODE (type);
+
+ /* Strip NON_LVALUE_EXPRs and no-op conversions, since we aren't using as
+ an lvalue.
+
+ Do not use STRIP_NOPS here! It will remove conversions from pointer
+ to integer and cause infinite recursion. */
+ while (TREE_CODE (exp) == NON_LVALUE_EXPR
+ || (TREE_CODE (exp) == NOP_EXPR
+ && TREE_TYPE (TREE_OPERAND (exp, 0)) == TREE_TYPE (exp)))
+ exp = TREE_OPERAND (exp, 0);
+
+ if (code == FUNCTION_TYPE)
+ return build_unary_op (ADDR_EXPR, exp, 0);
+
+ return exp;
+}
+
+/* The constraints table for CW style assembly. Things not listed are
+ usually considered as "+b", "+v" or "+f" depending upon context. */
+
+struct cw_op_constraint
+{
+ const char *opcode;
+ unsigned argnum;
+ const char *constraint;
+};
+
+/* Comparison function for bsearch to find an opcode/argument number
+ in the opcode constraint table. */
+
+static int
+cw_op_comp (const void *a, const void *b)
+{
+ const struct cw_op_constraint *x = a;
+ const struct cw_op_constraint *y = b;
+ int c = strcmp (x->opcode, y->opcode);
+ if (c)
+ return c;
+ if (x->argnum < y->argnum)
+ return -1;
+ if (x->argnum > y->argnum)
+ return 1;
+ return 0;
+}
+
+/* We lookup the OPCODE and return the constraint for the ARGNUM
+ argument. This is used only for otherwise ambiguous cases. */
+
+static const char*
+cw_constraint_for (const char *opcode, unsigned argnum)
+{
+ /* This table must be sorted. */
+ static struct cw_op_constraint db[] = {
+ { "la", 2, "m" },
+ { "lbz", 2, "m" },
+ { "lbzu", 2, "m" },
+ { "ld", 2, "m" },
+ { "ldu", 2, "m" },
+ { "lfd", 2, "m" },
+ { "lfdu", 2, "m" },
+ { "lfs", 2, "m" },
+ { "lfsu", 2, "m" },
+ { "lha", 2, "m" },
+ { "lhau", 2, "m" },
+ { "lhz", 2, "m" },
+ { "lhzu", 2, "m" },
+ { "lmw", 2, "m" },
+ { "lwa", 2, "m" },
+ { "lwz", 2, "m" },
+ { "lwzu", 2, "m" },
+ { "stb", 2, "m" },
+ { "stbu", 2, "m" },
+ { "std", 2, "m" },
+ { "stdu", 2, "m" },
+ { "stfd", 2, "m" },
+ { "stfdu", 2, "m" },
+ { "stfs", 2, "m" },
+ { "stfsu", 2, "m" },
+ { "sth", 2, "m" },
+ { "sthu", 2, "m" },
+ { "stmw", 2, "m" },
+ { "stw", 2, "m" },
+ { "stwu", 2, "m" },
+ };
+ struct cw_op_constraint key;
+ struct cw_op_constraint *r;
+
+ key.opcode = opcode;
+ key.argnum = argnum;
+ r = bsearch (&key, db, sizeof (db) / sizeof (db[0]), sizeof (db[0]), cw_op_comp);
+
+ /* Any explicitly listed contraint is always used. */
+ if (r)
+ return r->constraint;
+
+ return NULL;
+}
+
+/* Return true iff the opcode wants memory to be stable. We arrange
+ for a memory clobber in these instances. */
+static bool
+cw_memory_clobber (const char *opcode)
+{
+ return strncmp (opcode, "st", 2) == 0
+ || (strncmp (opcode, "l", 1) == 0 && (strcmp (opcode, "la") != 0
+ && strcmp (opcode, "li") != 0
+ && strcmp (opcode, "lis") != 0))
+ || strcmp (opcode, "sc") == 0
+ || strncmp (opcode, "td", 2) == 0
+ || strcmp (opcode, "trap") == 0
+ || strncmp (opcode, "tw", 2) == 0;
+}
+
+/* MUST_BE_REG is true, iff we know the operand must be a register. */
+
+static void
+cw_process_arg (const char *opcodename, tree var, unsigned argnum,
+ bool must_be_reg,
+ tree *outputsp, tree*inputsp)
+{
+ const char *s;
+ bool was_output = true;
+ tree str, one;
+
+ if (must_be_reg)
+ s = "+b";
+ else
+ s = cw_constraint_for (opcodename, argnum);
+
+ if (TREE_CODE (var) == FUNCTION_DECL)
+ {
+ str = build_string (1, "s");
+ was_output = false;
+ }
+ else
+ {
+ /* This is PowerPC-specific. */
+ if (s)
+ {
+ str = build_string (strlen (s), s);
+ was_output = ((s[0] == '=') | (s[0] == '+'));
+ }
+ else if (TREE_CODE (TREE_TYPE (var)) == REAL_TYPE)
+ str = build_string (2, "+f");
+ else
+ if (TREE_CODE (TREE_TYPE (var)) == VECTOR_TYPE)
+ str = build_string (2, "+v");
+ else
+ str = build_string (2, "+b");
+ }
+
+ one = build_tree_list (build_tree_list (NULL_TREE, str), var);
+ if (was_output)
+ *outputsp = chainon (*outputsp, one);
+ else
+ *inputsp = chainon (*inputsp, one);
+}
+
+/* CW identifier may include '.', '+' or '-'. Except that an operator
+ can only end in a '.'. This routine creates a new valid operator
+ parsed as a CW identifier. */
+
+static tree
+cw_asm_identifier (tree expr)
+{
+ const char *opcodename = IDENTIFIER_POINTER (expr);
+ int len = IDENTIFIER_LENGTH (expr);
+ int i;
+ for (i = 0; i < len; i++)
+ if (opcodename[i] == '.')
+ break;
+ if (i+1 < len) /* operator. is ok */
+ {
+ char *buf = (char *) alloca (IDENTIFIER_LENGTH (expr) + 1);
+ strncpy (buf, opcodename, i);
+ buf[i] = ' ';
+ strcpy (buf+i+1, opcodename + i);
+ return get_identifier (buf);
+ }
+ return expr;
+}
+
+/* Build an asm statement from CW-syntax bits. */
+tree
+cw_asm_stmt (tree expr, tree args, int lineno)
+{
+ tree sexpr;
+ tree arg, tail;
+ tree inputs, outputs, clobbers;
+ tree stmt;
+ unsigned int n;
+ const char *opcodename;
+
+ cw_asm_in_operands = 0;
+ VARRAY_TREE_INIT (cw_asm_operands, 30, "cw_asm_operands");
+ VARRAY_UINT_INIT (cw_asm_operands_arg, 30, "cw_asm_operands");
+ outputs = NULL_TREE;
+ inputs = NULL_TREE;
+ clobbers = NULL_TREE;
+
+ STRIP_NOPS (expr);
+
+ if (TREE_CODE (expr) == ADDR_EXPR)
+ expr = TREE_OPERAND (expr, 0);
+
+ expr = cw_asm_identifier (expr);
+
+ opcodename = IDENTIFIER_POINTER (expr);
+
+ /* Handle special directives specially. */
+ if (strcmp (opcodename, "entry") == 0)
+ return cw_asm_entry (expr, NULL_TREE, TREE_VALUE (args));
+ else if (strcmp (opcodename, "fralloc") == 0)
+ {
+ /* The correct default size is target-specific, so leave this as
+ a cookie for the backend. */
+ DECL_CW_ASM_FRAME_SIZE (current_function_decl) = -1;
+ if (args)
+ {
+ arg = TREE_VALUE (args);
+ STRIP_NOPS (arg);
+ if (TREE_CODE (arg) == INTEGER_CST)
+ {
+ int intval = tree_low_cst (arg, 0);
+ if (intval >= 0)
+ DECL_CW_ASM_FRAME_SIZE (current_function_decl) = intval;
+ else
+ error ("fralloc argument must be nonnegative");
+ }
+ else
+ error ("fralloc argument is not an integer");
+ }
+ return NULL_TREE;
+ }
+ else if (strcmp (opcodename, "frfree") == 0)
+ {
+ DECL_CW_ASM_NORETURN (current_function_decl) = 1;
+ /* Create a default-size frame retroactively. */
+ if (DECL_CW_ASM_FRAME_SIZE (current_function_decl) == (unsigned int)-2)
+ DECL_CW_ASM_FRAME_SIZE (current_function_decl) = (unsigned int)-1;
+ return NULL_TREE;
+ }
+ else if (strcmp (opcodename, "nofralloc") == 0)
+ {
+ DECL_CW_ASM_NORETURN (current_function_decl) = 1;
+ DECL_CW_ASM_FRAME_SIZE (current_function_decl) = -2;
+ return NULL_TREE;
+ }
+ else if (strcmp (opcodename, "machine") == 0)
+ {
+ return NULL_TREE;
+ }
+ else if (strcmp (opcodename, "opword") == 0)
+ {
+ opcodename = ".long";
+ }
+
+ if (cw_asm_buffer == NULL)
+ cw_asm_buffer = xmalloc (4000);
+
+ /* Build .file "file-name" directive. */
+ sprintf(cw_asm_buffer, "%s \"%s\"", ".file", input_filename);
+ sexpr = build_string (strlen (cw_asm_buffer), cw_asm_buffer);
+ stmt = build_stmt (ASM_EXPR, sexpr, NULL_TREE, NULL_TREE, NULL_TREE);
+ ASM_VOLATILE_P (stmt) = 1;
+ (void)add_stmt (stmt);
+
+ /* Build .line "line-number" directive. */
+ sprintf(cw_asm_buffer, "%s %d", ".line", lineno);
+ sexpr = build_string (strlen (cw_asm_buffer), cw_asm_buffer);
+ stmt = build_stmt (ASM_EXPR, sexpr, NULL_TREE, NULL_TREE, NULL_TREE);
+ ASM_VOLATILE_P (stmt) = 1;
+ (void)add_stmt (stmt);
+
+ cw_asm_buffer[0] = '\0';
+ strncat (cw_asm_buffer, opcodename, IDENTIFIER_LENGTH (expr));
+ strcat (cw_asm_buffer, " ");
+ n = 1;
+ /* Iterate through operands, "printing" each into the asm string. */
+ for (tail = args; tail; tail = TREE_CHAIN (tail))
+ {
+ arg = TREE_VALUE (tail);
+ if (tail != args)
+ strcat (cw_asm_buffer, ",");
+ print_cw_asm_operand (cw_asm_buffer, arg, n, false, false);
+ ++n;
+ }
+
+ sexpr = build_string (strlen (cw_asm_buffer), cw_asm_buffer);
+
+ /* Treat each C function seen as a input, and all parms/locals as
+ both inputs and outputs. */
+ for (n = 0; n < VARRAY_ACTIVE_SIZE (cw_asm_operands); ++n)
+ {
+ tree var = VARRAY_TREE (cw_asm_operands, n);
+ unsigned argnum = VARRAY_UINT (cw_asm_operands_arg, n);
+ bool must_be_reg;
+ must_be_reg = argnum & 1;
+ argnum >>= 1;
+
+ cw_process_arg (opcodename, var, argnum,
+ must_be_reg, &outputs, &inputs);
+ }
+
+ if (cw_memory_clobber (opcodename))
+ {
+ /* To not clobber all of memory, we would need to know what
+ memory locations were accessed; for now, punt. */
+ clobbers = tree_cons (NULL_TREE,
+ build_string (6, "memory"),
+ clobbers);
+ }
+
+ /* Perform default conversions on function inputs.
+ Don't do this for other types as it would screw up operands
+ expected to be in memory. */
+ for (tail = inputs; tail; tail = TREE_CHAIN (tail))
+ TREE_VALUE (tail) = cw_asm_default_function_conversion (TREE_VALUE (tail));
+
+ /* Treat as volatile always. */
+ stmt = build_stmt (ASM_EXPR, sexpr, outputs, inputs, clobbers);
+ ASM_VOLATILE_P (stmt) = 1;
+ stmt = add_stmt (stmt);
+ return stmt;
+}
+
+/* Compute the offset of a field, in bytes. Round down for bit
+ offsets, but that's OK for use in asm code. */
+
+static int
+cw_asm_field_offset (tree arg)
+{
+ return (tree_low_cst (DECL_FIELD_OFFSET (arg), 0)
+ + tree_low_cst (DECL_FIELD_BIT_OFFSET (arg), 0) / BITS_PER_UNIT);
+}
+
+/* Compute the int value for the expression. */
+
+static int
+cw_asm_expr_val (tree arg)
+{
+ if (TREE_CODE (arg) == FIELD_DECL)
+ return cw_asm_field_offset (arg);
+
+ if (TREE_CODE (arg) == INTEGER_CST)
+ return tree_low_cst (arg, 0);
+
+ if (TREE_CODE (arg) == PLUS_EXPR)
+ return cw_asm_expr_val (TREE_OPERAND (arg, 0))
+ + cw_asm_expr_val (TREE_OPERAND (arg, 1));
+
+ if (TREE_CODE (arg) == MINUS_EXPR)
+ return cw_asm_expr_val (TREE_OPERAND (arg, 0))
+ - cw_asm_expr_val (TREE_OPERAND (arg, 1));
+
+ if (TREE_CODE (arg) == NEGATE_EXPR)
+ return - cw_asm_expr_val (TREE_OPERAND (arg, 0));
+
+ error ("invalid operand for arithmetic in assembly block");
+ return 0;
+}
+
+/* Print an operand according to its tree type. MUST_BE_REG is true,
+ iff we know the operand must be a register. MUST_NOT_BE_REG is true,
+ iff we know the operand must not be a register. */
+
+static void
+print_cw_asm_operand (char *buf, tree arg, unsigned argnum,
+ bool must_be_reg, bool must_not_be_reg)
+{
+ int idnum;
+ HOST_WIDE_INT bitsize, bitpos;
+ tree offset;
+ enum machine_mode mode;
+ int unsignedp, volatilep;
+ tree op0;
+
+ STRIP_NOPS (arg);
+
+ switch (TREE_CODE (arg))
+ {
+ case INTEGER_CST:
+ sprintf (buf + strlen (buf), HOST_WIDE_INT_PRINT_DEC, tree_low_cst (arg, 0));
+ break;
+
+ case IDENTIFIER_NODE:
+ strncat (buf, IDENTIFIER_POINTER (arg), IDENTIFIER_LENGTH (arg));
+ break;
+
+ case VAR_DECL:
+ case PARM_DECL:
+ /* Named non-stack variables always refer to the address of that
+ variable. */
+ if (TREE_CODE (arg) == VAR_DECL
+ && TREE_STATIC (arg)
+ && MEM_P (DECL_RTL (arg)))
+ {
+ /* See assemble_name for details. */
+ const char *name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (arg));
+ const char *real_name;
+ tree id;
+
+ mark_referenced (DECL_ASSEMBLER_NAME (arg));
+ real_name = targetm.strip_name_encoding (name);
+ id = maybe_get_identifier (real_name);
+ if (id)
+ mark_referenced (id);
+
+ if (name[0] == '*')
+ strncat (buf, IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (arg)) + 1,
+ IDENTIFIER_LENGTH (DECL_ASSEMBLER_NAME (arg)) - 1);
+ else
+ {
+ sprintf (buf + strlen (buf), "%s", user_label_prefix);
+ strncat (buf, IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (arg)),
+ IDENTIFIER_LENGTH (DECL_ASSEMBLER_NAME (arg)));
+ }
+
+ mark_decl_referenced (arg);
+ }
+ else if ((idnum = cw_asm_get_register_var (arg, argnum, must_be_reg)) >= 0)
+ {
+ strcat (buf, "%");
+ sprintf (buf + strlen (buf), "%d", idnum);
+ }
+ break;
+
+ case FUNCTION_DECL:
+ if ((idnum = cw_asm_get_register_var (arg, argnum, must_be_reg)) >= 0)
+ {
+ strcat (buf, "%z");
+ sprintf (buf + strlen (buf), "%d", idnum);
+ }
+ break;
+
+ case COMPOUND_EXPR:
+ /* "Compound exprs" are really offset+register constructs. */
+ print_cw_asm_operand (buf, TREE_OPERAND (arg, 0), argnum,
+ false, true);
+ strcat (buf, "(");
+ print_cw_asm_operand (buf, TREE_OPERAND (arg, 1), argnum,
+ ! must_not_be_reg, must_not_be_reg);
+ strcat (buf, ")");
+ break;
+
+ case PLUS_EXPR:
+ case MINUS_EXPR:
+ sprintf (buf + strlen (buf), "%d", cw_asm_expr_val (arg));
+ break;
+
+ case FIELD_DECL:
+ sprintf (buf + strlen (buf), "%d", cw_asm_field_offset (arg));
+ break;
+
+ case COMPONENT_REF:
+ get_inner_reference (arg, &bitsize, &bitpos, &offset, &mode, &unsignedp, &volatilep, false);
+ /* Convert bit pos to byte pos, rounding down (this is asm,
+ after all). */
+ /* APPLE LOCAL 32-bit HOST_WIDE_INT */
+ sprintf (buf + strlen (buf), "%lld", (long long int) (bitpos / BITS_PER_UNIT));
+ strcat (buf, "(");
+ op0 = TREE_OPERAND (arg, 0);
+ /* Catch a couple different flavors of component refs. */
+ if (TREE_CODE (op0) == VAR_DECL)
+ print_cw_asm_operand (buf, op0, argnum, true, false);
+ else
+ print_cw_asm_operand (buf, TREE_OPERAND (op0, 0), argnum, true, false);
+ strcat (buf, ")");
+ break;
+
+ case ARRAY_REF:
+ error ("array references not supported");
+ break;
+
+ default:
+ /* Something is wrong, most likely a user error. */
+ error ("block assembly operand not recognized");
+ break;
+ }
+}
+
+/* Given an identifier name, come up with the index to use for the %0,
+ %1, etc in the asm string. MUST_BE_REG is true, iff we know the
+ operand must be a register. */
+
+static int
+cw_asm_get_register_var (tree var, unsigned argnum, bool must_be_reg)
+{
+ unsigned int n;
+
+ for (n = 0; n < VARRAY_ACTIVE_SIZE (cw_asm_operands); ++n)
+ {
+ if (var == VARRAY_TREE (cw_asm_operands, n))
+ return n;
+ }
+
+ VARRAY_PUSH_TREE (cw_asm_operands, var);
+ VARRAY_PUSH_UINT (cw_asm_operands_arg, (argnum<<1) + must_be_reg);
+ return VARRAY_ACTIVE_SIZE (cw_asm_operands) - 1;
+}
+
+tree
+cw_asm_reg_name (tree id)
+{
+#ifdef CW_ASM_REGISTER_NAME
+ char buf[100];
+ const char *newname = CW_ASM_REGISTER_NAME (IDENTIFIER_POINTER (id), buf);
+ if (newname)
+ return get_identifier (newname);
+#else
+ if (decode_reg_name (IDENTIFIER_POINTER (id)) >= 0)
+ return id;
+#endif
+ return NULL_TREE;
+}
+
+/* Build an asm label from CW-syntax bits. */
+tree
+cw_asm_label (tree labid, int atsign)
+{
+ tree sexpr;
+ tree inputs = NULL_TREE, outputs = NULL_TREE, clobbers = NULL_TREE;
+ tree stmt;
+
+ STRIP_NOPS (labid);
+
+ if (atsign)
+ labid = prepend_char_identifier (labid, '@');
+
+ if (cw_asm_buffer == NULL)
+ cw_asm_buffer = xmalloc (4000);
+
+ cw_asm_buffer[0] = '\0';
+ strcat (cw_asm_buffer, IDENTIFIER_POINTER (get_cw_asm_label (labid)));
+ strcat (cw_asm_buffer, ":");
+
+ sexpr = build_string (strlen (cw_asm_buffer), cw_asm_buffer);
+
+ /* Simple asm statements are treated as volatile. */
+ stmt = build_stmt (ASM_EXPR, sexpr, outputs, inputs, clobbers);
+ ASM_VOLATILE_P (stmt) = 1;
+ stmt = add_stmt (stmt);
+ return stmt;
+}
+
+/* Create a new identifier with an 'ch' stuck on the front. */
+
+tree
+prepend_char_identifier (tree ident, char ch)
+{
+ char *buf = (char *) alloca (IDENTIFIER_LENGTH (ident) + 20);
+ buf[0] = ch;
+ strcpy (buf + 1, IDENTIFIER_POINTER (ident));
+ return get_identifier (buf);
+}
+
+/* In CW assembly, '.', '-' and '+ can follow identifiers, and are
+ part of them. This routine joins a normal C identifier with such a
+ suffix. */
+
+tree
+cw_get_identifier (tree id, const char *str)
+{
+ char *buf;
+ int len = strlen (str);
+ buf = (char *) alloca (IDENTIFIER_LENGTH (id) + len + 1);
+ memcpy (buf, IDENTIFIER_POINTER (id), IDENTIFIER_LENGTH (id));
+ memcpy (buf + IDENTIFIER_LENGTH (id), str, len);
+ buf[IDENTIFIER_LENGTH (id) + len] = 0;
+ return get_identifier (buf);
+}
+
+void
+clear_cw_asm_labels (void)
+{
+ if (!cw_asm_labels)
+ VARRAY_TREE_INIT (cw_asm_labels, 40, "cw_asm_labels");
+ if (!cw_asm_labels_uniq)
+ VARRAY_TREE_INIT (cw_asm_labels_uniq, 40, "cw_asm_labels_uniq");
+ VARRAY_POP_ALL (cw_asm_labels);
+ VARRAY_POP_ALL (cw_asm_labels_uniq);
+}
+
+static GTY(()) tree cw_ha16;
+static GTY(()) tree cw_hi16;
+static GTY(()) tree cw_lo16;
+
+/* Given a label identifier and a flag indicating whether it had an @
+ preceding it, return a synthetic and unique label that the
+ assembler will like. */
+
+tree
+get_cw_asm_label (tree labid)
+{
+ unsigned int n;
+ const char *labname;
+ char *buf;
+ tree newid;
+
+ if (!cw_ha16)
+ {
+ cw_ha16 = get_identifier ("ha16");
+ cw_hi16 = get_identifier ("hi16");
+ cw_lo16 = get_identifier ("lo16");
+ }
+
+ /* lo16(), ha16() and hi16() should be left unmolested. */
+ if (labid == cw_lo16 || labid == cw_ha16 || labid == cw_hi16)
+ return labid;
+
+ for (n = 0; n < VARRAY_ACTIVE_SIZE (cw_asm_labels); ++n)
+ {
+ if (labid == VARRAY_TREE (cw_asm_labels, n))
+ return VARRAY_TREE (cw_asm_labels_uniq, n);
+ }
+ /* Not already seen, make up a label. */
+ VARRAY_PUSH_TREE (cw_asm_labels, labid);
+ buf = (char *) alloca (IDENTIFIER_LENGTH (labid) + 20);
+ sprintf (buf, "LASM%d$", cw_asm_labelno++);
+ /* Assembler won't like a leading @-sign, so make it into a $ if
+ seen. */
+ labname = IDENTIFIER_POINTER (labid);
+ if (*labname == '@')
+ {
+ strcat (buf, "$");
+ ++labname;
+ }
+ strcat (buf, labname);
+ newid = get_identifier (buf);
+ VARRAY_PUSH_TREE (cw_asm_labels_uniq, newid);
+ return newid;
+}
+
+/* The "offset(reg)" in assembly doesn't have an appropriate tree
+ node, so borrow COMPOUND_EXPR and just detect it when emitting the
+ assembly statement. */
+
+tree
+cw_asm_build_register_offset (tree offset, tree regname)
+{
+ tree t;
+
+ t = make_node (COMPOUND_EXPR);
+ /* No type is associated with this construct. */
+ TREE_TYPE (t) = NULL_TREE;
+ TREE_OPERAND (t, 0) = offset;
+ TREE_OPERAND (t, 1) = regname;
+ return t;
+}
+
+/* Given some bits of info from the parser, determine if this is a
+ valid entry statement, and then generate traditional asm statements
+ to create the label. The entry may be either static or extern. */
+tree
+cw_asm_entry (tree keyword, tree scspec, tree fn)
+{
+ int externify = 0;
+ tree stmt, inputs, str, one, strlab;
+
+ /* Validate all the arguments. The keyword arg should be "entry",
+ but we don't make it a reserved word and parse as a plain old
+ identifier, so need to check it here. */
+ if (strcmp (IDENTIFIER_POINTER (keyword), "entry") != 0)
+ {
+ error ("invalid asm entry statement syntax");
+ return error_mark_node;
+ }
+ if (scspec == NULL || strcmp (IDENTIFIER_POINTER (scspec), "extern") == 0)
+ externify = 1;
+ else if (strcmp (IDENTIFIER_POINTER (scspec), "static") == 0)
+ /* accept, but do nothing special */ ;
+ else
+ {
+ error ("entry point storage class much be `static' or `extern'");
+ return error_mark_node;
+ }
+ if (fn == NULL_TREE || TREE_CODE (fn) != FUNCTION_DECL)
+ {
+ error ("entry point not recognized as a function");
+ return error_mark_node;
+ }
+
+ fn = cw_asm_default_function_conversion (fn);
+ str = build_string (1, "s");
+ one = build_tree_list (build_tree_list (NULL_TREE, str), fn);
+ inputs = chainon (NULL_TREE, one);
+
+ if (externify)
+ {
+ strlab = build_string (9, ".globl %0");
+ /* Treat as volatile always. */
+ stmt = build_stmt (ASM_EXPR, strlab, NULL_TREE, inputs, NULL_TREE);
+ ASM_VOLATILE_P (stmt) = 1;
+ stmt = add_stmt (stmt);
+ }
+
+ strlab = build_string (3, "%0:");
+ /* Treat as volatile always. */
+ stmt = build_stmt (ASM_EXPR, strlab, NULL_TREE, inputs, NULL_TREE);
+ ASM_VOLATILE_P (stmt) = 1;
+ stmt = add_stmt (stmt);
+ return stmt;
+}
+/* APPLE LOCAL end CW asm blocks */
+
#include "gt-c-common.h"