+2005-10-11 Jan Beulich <jbeulich@novell.com>
+
+ * expr.h (enum expr_mode): New.
+ (expression): Pass thrid argument to expr.
+ (expression_and_evaluate): New.
+ (deferred_expression): New.
+ (expr): Add third parameter.
+ (resolve_expression): New.
+ * struc-symbol.h (struct symbol): New members sy_volatile and
+ sy_forward_ref.
+ * symbols.c, symbols.h (symbol_clone): New.
+ (symbol_clone_if_forward_ref): New.
+ (snapshot_symbol): New.
+ (S_IS_VOLATILE): New.
+ (S_IS_FORWARD_REF): New.
+ (S_SET_VOLATILE): New.
+ (S_SET_FORWARD_REF): New.
+ * as.c (macro_expr): Use expression_and_evaluate.
+ * cond.c (s_if): Likewise.
+ (s_elseif): Likewise.
+ * dw2gencfi.c (cfi_parse_reg): Likewise.
+ * expr.c (operand): Add second parameter. Optionally call
+ deferred_expression. Pass mode argument to itself and md_parse_name.
+ Check mode before trying to evaluate symbol. Call
+ symbol_clone_if_forward_ref for both operands.
+ (expr): Add thrid parameter. Pass mode to operand and itself.
+ Optionally call resolve_expression.
+ (resolve_expression): New.
+ (get_single_number): Pass second argument to operand.
+ * read.c (potable): New entry for .eqv.
+ (read_a_source_file): Handle new == operator.
+ (get_absolute_expr): Use expression_and_evaluate.
+ (s_lsym): Likewise.
+ (assign_symbol): Rename second parameter. Call symbol_clone on
+ legal and illegal redefinition. Call S_SET_VOLATILE and
+ S_SET_FORWARD_REF depending on mode.
+ (s_set): Update description.
+ (s_space): Call resolve_expression.
+ (pseudo_set): Optionally call deferred_expression. Check
+ S_IS_FORWARD_REF before trying to simplify/resolve an expression.
+ (equals): Handle ==.
+ * config/tc-ia64.h (md_parse_name): Add mode parameter.
+ * config/tc-arc.c (arc_parse_cons_expression): Likewise.
+ * config/tc-m32r.h (md_parse_name): Likewise.
+ (m32r_parse_name): Likewise.
+ * config/tc-mmix.h (md_parse_name): Likewise.
+ * config/tc-mn10300.h (md_parse_name): Likewise.
+ (mn10300_parse_name): Likewise.
+ * config/tc-ppc.h (md_parse_name): Likewise.
+ * config/tc-sh.h (md_parse_name): Likewise.
+ (sh_parse_name): Likewise.
+ * config/tc-sh64.h (md_parse_name): Likewise.
+ (sh64_consume_datalabel): Likewise.
+ * config/tc-tic54x.h (md_parse_name): Likewise.
+ * config/tc-m32r.c (m32r_parse_name): Add mode parameter. Check it
+ before trying to evaluate symbol.
+ * config/tc-mn10300.c (mn10300_parse_name): Likewise.
+ * config/tc-sh.c (sh_parse_name): Likewise.
+ * config/tc-sh64.c (sh64_consume_datalabel): Add mode parameter. Pass
+ second argument to operandf. Pass mode parameter to sh_parse_name.
+ * doc/as.texinfo: Document .eqv and the == assignment operator.
+
2005-10-10 Ian Lance Taylor <ian@airs.com>
* Makefile.am (EXTRA_DIST): Remove bfin-lex.l and bfin-defs.h.
hold = input_line_pointer;
input_line_pointer = in->ptr + idx;
- expression (&ex);
+ expression_and_evaluate (&ex);
idx = input_line_pointer - in->ptr;
input_line_pointer = hold;
}
else
{
- expression (&operand);
+ expression_and_evaluate (&operand);
if (operand.X_op != O_constant)
as_bad (_("non-constant expression in \".if\" statement"));
}
/* Leading whitespace is part of operand. */
SKIP_WHITESPACE ();
- expression (&operand);
+ expression_and_evaluate (&operand);
if (operand.X_op != O_constant)
as_bad (_("non-constant expression in \".elseif\" statement"));
code_symbol_fix = 1;
strcpy (p, "; ");
}
- expr (0, exp);
+ expression_and_evaluate (exp);
if (code_symbol_fix)
{
arc_code_symbol (exp);
#define tc_frob_symbol(s,p) p |= ia64_frob_symbol (s)
#endif /* TE_HPUX */
#define md_flush_pending_output() ia64_flush_pending_output ()
-#define md_parse_name(s,e,c) ia64_parse_name (s, e, c)
+#define md_parse_name(s,e,m,c) ia64_parse_name (s, e, c)
#define tc_canonicalize_symbol_name(s) ia64_canonicalize_symbol_name (s)
#define tc_canonicalize_section_name(s) ia64_canonicalize_symbol_name (s)
#define md_optimize_expr(l,o,r) ia64_optimize_expr (l, o, r)
}
int
-m32r_parse_name (char const *name, expressionS *exprP, char *nextcharP)
+m32r_parse_name (char const *name,
+ expressionS *exprP,
+ enum expr_mode mode,
+ char *nextcharP)
{
char *next = input_line_pointer;
char *next_end;
/* If we have an absolute symbol or a
reg, then we know its value now. */
segment = S_GET_SEGMENT (exprP->X_add_symbol);
- if (segment == absolute_section)
+ if (mode != expr_defer && segment == absolute_section)
{
exprP->X_op = O_constant;
exprP->X_add_number = S_GET_VALUE (exprP->X_add_symbol);
exprP->X_add_symbol = NULL;
}
- else if (segment == reg_section)
+ else if (mode != expr_defer && segment == reg_section)
{
exprP->X_op = O_register;
exprP->X_add_number = S_GET_VALUE (exprP->X_add_symbol);
#define elf_tc_final_processing m32r_elf_final_processing
extern void m32r_elf_final_processing (void);
-#define md_parse_name(name, exprP, nextcharP) \
- m32r_parse_name ((name), (exprP), (nextcharP))
-extern int m32r_parse_name (char const *, expressionS *, char *);
+#define md_parse_name(name, exprP, mode, nextcharP) \
+ m32r_parse_name ((name), (exprP), (mode), (nextcharP))
+extern int m32r_parse_name (char const *, expressionS *, enum expr_mode, char *);
/* This is used to construct expressions out of @GOTOFF, @PLT and @GOT
symbols. The relocation type is stored in X_md. */
else if (mips_opts.mips16
&& ! ip->use_extend
&& *reloc_type != BFD_RELOC_MIPS16_JMP)
- {
+ {
if ((pinfo & INSN_UNCOND_BRANCH_DELAY) == 0)
/* Make sure there is enough room to swap this instruction with
a following jump instruction. */
s_reset = s;
if (s[0] == '$')
{
-
if (ISDIGIT (s[1]))
{
++s;
The [DVWIOUZX]_Handler symbols are provided when-used. */
extern int mmix_gnu_syntax;
-#define md_parse_name(name, exp, cpos) \
+#define md_parse_name(name, exp, mode, cpos) \
(! mmix_gnu_syntax \
&& (name[0] == '@' \
? (! is_part_of_name (name[1]) \
}
int
-mn10300_parse_name (name, exprP, nextcharP)
+mn10300_parse_name (name, exprP, mode, nextcharP)
char const *name;
expressionS *exprP;
+ enum expr_mode mode;
char *nextcharP;
{
char *next = input_line_pointer;
/* If we have an absolute symbol or a reg,
then we know its value now. */
segment = S_GET_SEGMENT (exprP->X_add_symbol);
- if (segment == absolute_section)
+ if (mode != expr_defer && segment == absolute_section)
{
exprP->X_op = O_constant;
exprP->X_add_number = S_GET_VALUE (exprP->X_add_symbol);
exprP->X_add_symbol = NULL;
}
- else if (segment == reg_section)
+ else if (mode != expr_defer && segment == reg_section)
{
exprP->X_op = O_register;
exprP->X_add_number = S_GET_VALUE (exprP->X_add_symbol);
&& S_IS_DEFINED ((FIX)->fx_addsy) \
&& ! S_IS_COMMON ((FIX)->fx_addsy))))
-#define md_parse_name(name, exprP, nextcharP) \
- mn10300_parse_name ((name), (exprP), (nextcharP))
-int mn10300_parse_name PARAMS ((char const *, expressionS *, char *));
+#define md_parse_name(name, exprP, mode, nextcharP) \
+ mn10300_parse_name ((name), (exprP), (mode), (nextcharP))
+int mn10300_parse_name PARAMS ((char const *, expressionS *,
+ enum expr_mode, char *));
#define TC_CONS_FIX_NEW(FRAG, OFF, LEN, EXP) \
mn10300_cons_fix_new ((FRAG), (OFF), (LEN), (EXP))
#define MD_PCREL_FROM_SECTION(FIX, SEC) md_pcrel_from_section(FIX, SEC)
extern long md_pcrel_from_section PARAMS ((struct fix *, segT));
-#define md_parse_name(name, exp, c) ppc_parse_name (name, exp)
+#define md_parse_name(name, exp, mode, c) ppc_parse_name (name, exp)
extern int ppc_parse_name PARAMS ((const char *, struct expressionS *));
#define md_operand(x)
}
int
-sh_parse_name (char const *name, expressionS *exprP, char *nextcharP)
+sh_parse_name (char const *name,
+ expressionS *exprP,
+ enum expr_mode mode,
+ char *nextcharP)
{
char *next = input_line_pointer;
char *next_end;
/* If we have an absolute symbol or a reg, then we know its
value now. */
segment = S_GET_SEGMENT (exprP->X_add_symbol);
- if (segment == absolute_section)
+ if (mode != expr_defer && segment == absolute_section)
{
exprP->X_op = O_constant;
exprP->X_add_number = S_GET_VALUE (exprP->X_add_symbol);
exprP->X_add_symbol = NULL;
}
- else if (segment == reg_section)
+ else if (mode != expr_defer && segment == reg_section)
{
exprP->X_op = O_register;
exprP->X_add_number = S_GET_VALUE (exprP->X_add_symbol);
((FIX)->fx_r_type == BFD_RELOC_32_PLT_PCREL \
|| (sh_relax && SWITCH_TABLE (FIX)))
-#define md_parse_name(name, exprP, nextcharP) \
- sh_parse_name ((name), (exprP), (nextcharP))
-int sh_parse_name (char const *name, expressionS *exprP, char *nextchar);
+#define md_parse_name(name, exprP, mode, nextcharP) \
+ sh_parse_name ((name), (exprP), (mode), (nextcharP))
+int sh_parse_name (char const *name, expressionS *exprP,
+ enum expr_mode mode, char *nextchar);
#define TC_CONS_FIX_NEW(FRAG, OFF, LEN, EXP) \
sh_cons_fix_new ((FRAG), (OFF), (LEN), (EXP))
symbol hook. */
int
-sh64_consume_datalabel (const char *name, expressionS *exp, char *cp,
- segT (*operandf) (expressionS *))
+sh64_consume_datalabel (const char *name, expressionS *exp,
+ enum expr_mode mode, char *cp,
+ segT (*operandf) (expressionS *, enum expr_mode))
{
static int parsing_datalabel = 0;
*input_line_pointer = *cp;
parsing_datalabel = 1;
- (*operandf) (exp);
+ (*operandf) (exp, expr_normal);
parsing_datalabel = save_parsing_datalabel;
if (exp->X_op == O_symbol || exp->X_op == O_PIC_reloc)
return 1;
}
- return sh_parse_name (name, exp, cp);
+ return sh_parse_name (name, exp, mode, cp);
}
/* This function is called just before symbols are being output. It
expression, since we have handled it ourselves. FIXME: What we really
need is a new GAS infrastructure feature: md_qualifier. */
#undef md_parse_name
-#define md_parse_name(NAME, EXP, CP) \
- sh64_consume_datalabel (NAME, EXP, CP, operand)
-extern int sh64_consume_datalabel (const char *, expressionS *, char *,
- segT (*) (expressionS *));
+#define md_parse_name(NAME, EXP, MODE, CP) \
+ sh64_consume_datalabel (NAME, EXP, MODE, CP, operand)
+extern int sh64_consume_datalabel (const char *, expressionS *,
+ enum expr_mode, char *,
+ segT (*) (expressionS *, enum expr_mode));
/* Saying "$" is the same as saying ".". */
#define DOLLAR_DOT
extern void tic54x_adjust_symtab (void);
#define tc_unrecognized_line(ch) tic54x_unrecognized_line(ch)
extern int tic54x_unrecognized_line (int ch);
-#define md_parse_name(s,e,c) tic54x_parse_name(s,e)
+#define md_parse_name(s,e,m,c) tic54x_parse_name(s,e)
extern int tic54x_parse_name (char *name, expressionS *e);
#define md_undefined_symbol(s) tic54x_undefined_symbol(s)
extern symbolS *tic54x_undefined_symbol (char *name);
A symbol can be given an arbitrary value by writing a symbol, followed
by an equals sign @samp{=}, followed by an expression
(@pxref{Expressions}). This is equivalent to using the @code{.set}
-directive. @xref{Set,,@code{.set}}.
+directive. @xref{Set,,@code{.set}}. In the same way, using a double
+equals sign @samp{=}@samp{=} here represents an equivalent of the
+@code{.eqv} directive. @xref{Eqv,,@code{.eqv}}.
@node Symbol Names
@section Symbol Names
* Endif:: @code{.endif}
* Equ:: @code{.equ @var{symbol}, @var{expression}}
* Equiv:: @code{.equiv @var{symbol}, @var{expression}}
+* Eqv:: @code{.eqv @var{symbol}, @var{expression}}
* Err:: @code{.err}
* Error:: @code{.error @var{string}}
* Exitm:: @code{.exitm}
.endif
.equ SYM,VAL
@end smallexample
+plus it protects the symbol from later redefinition.
+
+@node Eqv
+@section @code{.eqv @var{symbol}, @var{expression}}
+@cindex @code{eqv} directive
+The @code{.eqv} directive is like @code{.equiv}, but no attempt is made to
+evaluate the expression or any part of it immediately. Instead each time
+the resulting symbol is used in an expression, a snapshot of its current
+value is taken.
@node Err
@section @code{.err}
}
#endif
- expression (&exp);
+ expression_and_evaluate (&exp);
switch (exp.X_op)
{
case O_register:
static void mri_char_constant (expressionS *);
static void current_location (expressionS *);
static void clean_up_expression (expressionS * expressionP);
-static segT operand (expressionS *);
+static segT operand (expressionS *, enum expr_mode);
static operatorT operator (int *);
extern const char EXP_CHARS[], FLT_CHARS[];
Input_line_pointer->(next non-blank) char after operand. */
static segT
-operand (expressionS *expressionP)
+operand (expressionS *expressionP, enum expr_mode mode)
{
char c;
symbolS *symbolP; /* Points to symbol. */
case '[':
#endif
/* Didn't begin with digit & not a name. */
- segment = expression (expressionP);
+ if (mode != expr_defer)
+ segment = expression (expressionP);
+ else
+ segment = deferred_expression (expressionP);
/* expression () will pass trailing whitespace. */
if ((c == '(' && *input_line_pointer != ')')
|| (c == '[' && *input_line_pointer != ']'))
if (0 && (c == '-' || c == '+') && *input_line_pointer == c)
goto target_op;
- operand (expressionP);
+ operand (expressionP, mode);
if (expressionP->X_op == O_constant)
{
/* input_line_pointer -> char after operand. */
specially in certain contexts. If a name always has a
specific value, it can often be handled by simply
entering it in the symbol table. */
- if (md_parse_name (name, expressionP, &c))
+ if (md_parse_name (name, expressionP, mode, &c))
{
*input_line_pointer = c;
break;
/* If we have an absolute symbol or a reg, then we know its
value now. */
segment = S_GET_SEGMENT (symbolP);
- if (segment == absolute_section)
+ if (mode != expr_defer && segment == absolute_section)
{
expressionP->X_op = O_constant;
expressionP->X_add_number = S_GET_VALUE (symbolP);
}
- else if (segment == reg_section)
+ else if (mode != expr_defer && segment == reg_section)
{
expressionP->X_op = O_register;
expressionP->X_add_number = S_GET_VALUE (symbolP);
if (expressionP->X_add_symbol)
symbol_mark_used (expressionP->X_add_symbol);
+ expressionP->X_add_symbol = symbol_clone_if_forward_ref (expressionP->X_add_symbol);
+ expressionP->X_op_symbol = symbol_clone_if_forward_ref (expressionP->X_op_symbol);
+
switch (expressionP->X_op)
{
default:
segT
expr (int rankarg, /* Larger # is higher rank. */
- expressionS *resultP /* Deliver result here. */)
+ expressionS *resultP, /* Deliver result here. */
+ enum expr_mode mode /* Controls behavior. */)
{
operator_rankT rank = (operator_rankT) rankarg;
segT retval;
if (rank == 0)
dot_value = frag_now_fix ();
- retval = operand (resultP);
+ retval = operand (resultP, mode);
/* operand () gobbles spaces. */
know (*input_line_pointer != ' ');
input_line_pointer += op_chars; /* -> after operator. */
- rightseg = expr (op_rank[(int) op_left], &right);
+ rightseg = expr (op_rank[(int) op_left], &right, mode);
if (right.X_op == O_absent)
{
as_warn (_("missing operand; zero assumed"));
if (resultP->X_add_symbol)
symbol_mark_used (resultP->X_add_symbol);
+ if (rank == 0 && mode == expr_evaluate)
+ resolve_expression (resultP);
+
return resultP->X_op == O_constant ? absolute_section : retval;
}
+
+/* Resolve an expression without changing any symbols/sub-expressions
+ used. */
+
+int
+resolve_expression (expressionS *expressionP)
+{
+ /* Help out with CSE. */
+ valueT final_val = expressionP->X_add_number;
+ symbolS *add_symbol = expressionP->X_add_symbol;
+ symbolS *op_symbol = expressionP->X_op_symbol;
+ operatorT op = expressionP->X_op;
+ valueT left, right;
+ segT seg_left, seg_right;
+ fragS *frag_left, *frag_right;
+
+ switch (op)
+ {
+ default:
+ return 0;
+
+ case O_constant:
+ case O_register:
+ left = 0;
+ break;
+
+ case O_symbol:
+ case O_symbol_rva:
+ if (!snapshot_symbol (add_symbol, &left, &seg_left, &frag_left))
+ return 0;
+
+ break;
+
+ case O_uminus:
+ case O_bit_not:
+ case O_logical_not:
+ if (!snapshot_symbol (add_symbol, &left, &seg_left, &frag_left))
+ return 0;
+
+ if (seg_left != absolute_section)
+ return 0;
+
+ if (op == O_logical_not)
+ left = !left;
+ else if (op == O_uminus)
+ left = -left;
+ else
+ left = ~left;
+ op = O_constant;
+ break;
+
+ case O_multiply:
+ case O_divide:
+ case O_modulus:
+ case O_left_shift:
+ case O_right_shift:
+ case O_bit_inclusive_or:
+ case O_bit_or_not:
+ case O_bit_exclusive_or:
+ case O_bit_and:
+ case O_add:
+ case O_subtract:
+ case O_eq:
+ case O_ne:
+ case O_lt:
+ case O_le:
+ case O_ge:
+ case O_gt:
+ case O_logical_and:
+ case O_logical_or:
+ if (!snapshot_symbol (add_symbol, &left, &seg_left, &frag_left)
+ || !snapshot_symbol (op_symbol, &right, &seg_right, &frag_right))
+ return 0;
+
+ /* Simplify addition or subtraction of a constant by folding the
+ constant into X_add_number. */
+ if (op == O_add)
+ {
+ if (seg_right == absolute_section)
+ {
+ final_val += right;
+ op = O_symbol;
+ break;
+ }
+ else if (seg_left == absolute_section)
+ {
+ final_val += left;
+ left = right;
+ seg_left = seg_right;
+ expressionP->X_add_symbol = expressionP->X_op_symbol;
+ op = O_symbol;
+ break;
+ }
+ }
+ else if (op == O_subtract)
+ {
+ if (seg_right == absolute_section)
+ {
+ final_val -= right;
+ op = O_symbol;
+ break;
+ }
+ }
+
+ /* Equality and non-equality tests are permitted on anything.
+ Subtraction, and other comparison operators are permitted if
+ both operands are in the same section. Otherwise, both
+ operands must be absolute. We already handled the case of
+ addition or subtraction of a constant above. */
+ if (!(seg_left == absolute_section
+ && seg_right == absolute_section)
+ && !(op == O_eq || op == O_ne)
+ && !((op == O_subtract
+ || op == O_lt || op == O_le || op == O_ge || op == O_gt)
+ && seg_left == seg_right
+ && (finalize_syms || frag_left == frag_right)
+ && ((seg_left != undefined_section
+ && seg_left != reg_section)
+ || add_symbol == op_symbol)))
+ return 0;
+
+ switch (op)
+ {
+ case O_add: left += right; break;
+ case O_subtract: left -= right; break;
+ case O_multiply: left *= right; break;
+ case O_divide:
+ if (right == 0)
+ return 0;
+ left = (offsetT) left / (offsetT) right;
+ break;
+ case O_modulus:
+ if (right == 0)
+ return 0;
+ left = (offsetT) left % (offsetT) right;
+ break;
+ case O_left_shift: left <<= right; break;
+ case O_right_shift: left >>= right; break;
+ case O_bit_inclusive_or: left |= right; break;
+ case O_bit_or_not: left |= ~right; break;
+ case O_bit_exclusive_or: left ^= right; break;
+ case O_bit_and: left &= right; break;
+ case O_eq:
+ case O_ne:
+ left = (left == right
+ && seg_left == seg_right
+ && (finalize_syms || frag_left == frag_right)
+ && ((seg_left != undefined_section
+ && seg_left != reg_section)
+ || add_symbol == op_symbol)
+ ? ~ (valueT) 0 : 0);
+ if (op == O_ne)
+ left = ~left;
+ break;
+ case O_lt:
+ left = (offsetT) left < (offsetT) right ? ~ (valueT) 0 : 0;
+ break;
+ case O_le:
+ left = (offsetT) left <= (offsetT) right ? ~ (valueT) 0 : 0;
+ break;
+ case O_ge:
+ left = (offsetT) left >= (offsetT) right ? ~ (valueT) 0 : 0;
+ break;
+ case O_gt:
+ left = (offsetT) left > (offsetT) right ? ~ (valueT) 0 : 0;
+ break;
+ case O_logical_and: left = left && right; break;
+ case O_logical_or: left = left || right; break;
+ default: abort ();
+ }
+
+ op = O_constant;
+ break;
+ }
+
+ if (op == O_symbol)
+ {
+ if (seg_left == absolute_section)
+ op = O_constant;
+ else if (seg_left == reg_section && final_val == 0)
+ op = O_register;
+ }
+ expressionP->X_op = op;
+
+ if (op == O_constant || op == O_register)
+ final_val += left;
+ expressionP->X_add_number = final_val;
+
+ return 1;
+}
\f
/* This lives here because it belongs equally in expr.c & read.c.
expr.c is just a branch office read.c anyway, and putting it
get_single_number (void)
{
expressionS exp;
- operand (&exp);
+ operand (&exp, expr_normal);
return exp.X_add_number;
}
unsigned short X_md;
} expressionS;
+enum expr_mode
+{
+ expr_evaluate,
+ expr_normal,
+ expr_defer
+};
+
/* "result" should be type (expressionS *). */
-#define expression(result) expr (0, result)
+#define expression(result) expr (0, result, expr_normal)
+#define expression_and_evaluate(result) expr (0, result, expr_evaluate)
+#define deferred_expression(result) expr (0, result, expr_defer)
/* If an expression is O_big, look here for its value. These common
data may be clobbered whenever expr() is called. */
extern char get_symbol_end (void);
extern void expr_begin (void);
extern void expr_set_precedence (void);
-extern segT expr (int rank, expressionS * resultP);
+extern segT expr (int, expressionS *, enum expr_mode);
extern unsigned int get_single_number (void);
extern symbolS *make_expr_symbol (expressionS * expressionP);
extern int expr_symbol_where (symbolS *, char **, unsigned int *);
extern symbolS *expr_build_uconstant (offsetT);
extern symbolS *expr_build_dot (void);
+
+int resolve_expression (expressionS *);
/* endef */
{"equ", s_set, 0},
{"equiv", s_set, 1},
+ {"eqv", s_set, -1},
{"err", s_err, 0},
{"error", s_errwarn, 1},
{"exitm", s_mexit, 0},
static offsetT
get_absolute_expr (expressionS *exp)
{
- expression (exp);
+ expression_and_evaluate (exp);
if (exp->X_op != O_constant)
{
if (exp->X_op != O_absent)
/* Input_line_pointer->after ':'. */
SKIP_WHITESPACE ();
}
+ else if (input_line_pointer[1] == '='
+ && (c == '='
+ || ((c == ' ' || c == '\t')
+ && input_line_pointer[2] == '=')))
+ {
+ equals (s, -1);
+ demand_empty_rest_of_line ();
+ }
else if ((c == '='
|| ((c == ' ' || c == '\t')
&& input_line_pointer[1] == '='))
}
input_line_pointer++;
- expression (&exp);
+ expression_and_evaluate (&exp);
if (exp.X_op != O_constant
&& exp.X_op != O_register)
}
static void
-assign_symbol (char *name, int no_reassign)
+assign_symbol (char *name, int mode)
{
symbolS *symbolP;
#endif
}
- /* Permit register names to be redefined. */
- if (no_reassign
- && S_IS_DEFINED (symbolP)
- && S_GET_SEGMENT (symbolP) != reg_section)
- as_bad (_("symbol `%s' is already defined"), name);
+ if (S_IS_DEFINED (symbolP))
+ {
+ /* Permit register names to be redefined. */
+ if ((mode != 0 || !S_IS_VOLATILE (symbolP))
+ && S_GET_SEGMENT (symbolP) != reg_section)
+ {
+ as_bad (_("symbol `%s' is already defined"), name);
+ symbolP = symbol_clone (symbolP, 0);
+ }
+ /* If the symbol is volatile, copy the symbol and replace the
+ original with the copy, so that previous uses of the symbol will
+ retain the value of the symbol at the point of use. */
+ else if (S_IS_VOLATILE (symbolP)
+ /* This could be avoided when the symbol wasn't used so far, but
+ the comment in struc-symbol.h says this flag isn't reliable. */
+ && (1 || symbol_used_p (symbolP)))
+ symbolP = symbol_clone (symbolP, 1);
+ }
+
+ if (mode == 0)
+ S_SET_VOLATILE (symbolP);
+ else if (mode < 0)
+ S_SET_FORWARD_REF (symbolP);
pseudo_set (symbolP);
}
-/* Handle the .equ, .equiv and .set directives. If EQUIV is 1, then
- this is .equiv, and it is an error if the symbol is already
- defined. */
+/* Handle the .equ, .equiv, .eqv, and .set directives. If EQUIV is 1,
+ then this is .equiv, and it is an error if the symbol is already
+ defined. If EQUIV is -1, the symbol additionally is a forward
+ reference. */
void
s_set (int equiv)
|| val.X_add_number > 0xff
|| (mult != 0 && mult != 1 && val.X_add_number != 0))
{
+ resolve_expression (&exp);
if (exp.X_op != O_constant)
as_bad (_("unsupported variable size or fill value"));
else
}
else
{
+ if (now_seg == absolute_section || mri_common_symbol != NULL)
+ resolve_expression (&exp);
+
if (exp.X_op == O_constant)
{
long repeat;
know (symbolP); /* NULL pointer is logic error. */
- (void) expression (&exp);
+ if (!S_IS_FORWARD_REF (symbolP))
+ (void) expression (&exp);
+ else
+ (void) deferred_expression (&exp);
if (exp.X_op == O_illegal)
as_bad (_("illegal expression"));
as_bad (_("floating point number invalid"));
}
else if (exp.X_op == O_subtract
+ && !S_IS_FORWARD_REF (symbolP)
&& SEG_NORMAL (S_GET_SEGMENT (exp.X_add_symbol))
&& (symbol_get_frag (exp.X_add_symbol)
== symbol_get_frag (exp.X_op_symbol)))
*symbol_X_add_number (symbolP) += exp.X_add_number;
break;
}
- else if (seg != undefined_section)
+ else if (!S_IS_FORWARD_REF (symbolP) && seg != undefined_section)
{
symbolS *s = exp.X_add_symbol;
input_line_pointer++;
if (*input_line_pointer == '=')
input_line_pointer++;
+ if (reassign < 0 && *input_line_pointer == '=')
+ input_line_pointer++;
while (*input_line_pointer == ' ' || *input_line_pointer == '\t')
input_line_pointer++;
if (flag_mri)
stop = mri_comment_field (&stopc);
- assign_symbol (sym_name, !reassign);
+ assign_symbol (sym_name, reassign >= 0 ? !reassign : reassign);
if (flag_mri)
{
a symbol is used in backend routines. */
unsigned int sy_used : 1;
+ /* Whether the symbol can be re-defined. */
+ unsigned int sy_volatile : 1;
+
+ /* Whether the symbol is a forward reference. */
+ unsigned int sy_forward_ref : 1;
+
/* This is set if the symbol is defined in an MRI common section.
We handle such sections as single common symbols, so symbols
defined within them must be treated specially by the relocation
return (symbolP);
}
+symbolS *
+symbol_clone (symbolS *orgsymP, int replace)
+{
+ symbolS *newsymP;
+
+ /* Running local_symbol_convert on a clone that's not the one currently
+ in local_hash would incorrectly replace the hash entry. Thus the
+ symbol must be converted here. Note that the rest of the function
+ depends on not encountering an unconverted symbol. */
+ if (LOCAL_SYMBOL_CHECK (orgsymP))
+ orgsymP = local_symbol_convert ((struct local_symbol *) orgsymP);
+
+ know (S_IS_DEFINED (orgsymP));
+
+ newsymP = obstack_alloc (¬es, sizeof (*newsymP));
+ *newsymP = *orgsymP;
+
+ if (replace)
+ {
+ if (symbol_rootP == orgsymP)
+ symbol_rootP = newsymP;
+ else if (orgsymP->sy_previous)
+ {
+ orgsymP->sy_previous->sy_next = newsymP;
+ orgsymP->sy_previous = NULL;
+ }
+ if (symbol_lastP == orgsymP)
+ symbol_lastP = newsymP;
+ else if (orgsymP->sy_next)
+ orgsymP->sy_next->sy_previous = newsymP;
+ orgsymP->sy_next = NULL;
+ debug_verify_symchain (symbol_rootP, symbol_lastP);
+
+ symbol_table_insert (newsymP);
+ }
+
+ return newsymP;
+}
+
+/* Referenced symbols, if they are forward references, need to be cloned
+ (without replacing the original) so that the value of the referenced
+ symbols at the point of use . */
+
+#undef symbol_clone_if_forward_ref
+symbolS *
+symbol_clone_if_forward_ref (symbolS *symbolP, int is_forward)
+{
+ if (symbolP && !LOCAL_SYMBOL_CHECK (symbolP))
+ {
+ symbolS *add_symbol = symbolP->sy_value.X_add_symbol;
+ symbolS *op_symbol = symbolP->sy_value.X_op_symbol;
+
+ if (symbolP->sy_forward_ref)
+ is_forward = 1;
+
+ if (is_forward)
+ {
+ /* assign_symbol() clones volatile symbols; pre-existing expressions
+ hold references to the original instance, but want the current
+ value. Just repeat the lookup. */
+ if (add_symbol && S_IS_VOLATILE (add_symbol))
+ add_symbol = symbol_find_exact (S_GET_NAME (add_symbol));
+ if (op_symbol && S_IS_VOLATILE (op_symbol))
+ op_symbol = symbol_find_exact (S_GET_NAME (op_symbol));
+ }
+
+ /* Re-using sy_resolving here, as this routine cannot get called from
+ symbol resolution code. */
+ if (symbolP->bsym->section == expr_section && !symbolP->sy_resolving)
+ {
+ symbolP->sy_resolving = 1;
+ add_symbol = symbol_clone_if_forward_ref (add_symbol, is_forward);
+ op_symbol = symbol_clone_if_forward_ref (op_symbol, is_forward);
+ symbolP->sy_resolving = 0;
+ }
+
+ if (symbolP->sy_forward_ref
+ || add_symbol != symbolP->sy_value.X_add_symbol
+ || op_symbol != symbolP->sy_value.X_op_symbol)
+ symbolP = symbol_clone (symbolP, 0);
+
+ symbolP->sy_value.X_add_symbol = add_symbol;
+ symbolP->sy_value.X_op_symbol = op_symbol;
+ }
+
+ return symbolP;
+}
+
symbolS *
symbol_temp_new (segT seg, valueT ofs, fragS *frag)
{
hash_traverse (local_hash, resolve_local_symbol);
}
+/* Obtain the current value of a symbol without changing any
+ sub-expressions used. */
+
+int
+snapshot_symbol (symbolS *symbolP, valueT *valueP, segT *segP, fragS **fragPP)
+{
+ if (LOCAL_SYMBOL_CHECK (symbolP))
+ {
+ struct local_symbol *locsym = (struct local_symbol *) symbolP;
+
+ *valueP = locsym->lsy_value;
+ *segP = locsym->lsy_section;
+ *fragPP = local_symbol_get_frag (locsym);
+ }
+ else
+ {
+ expressionS expr = symbolP->sy_value;
+
+ if (!symbolP->sy_resolved && expr.X_op != O_illegal)
+ {
+ int resolved;
+
+ if (symbolP->sy_resolving)
+ return 0;
+ symbolP->sy_resolving = 1;
+ resolved = resolve_expression (&expr);
+ symbolP->sy_resolving = 0;
+ if (!resolved)
+ return 0;
+
+ switch (expr.X_op)
+ {
+ case O_constant:
+ case O_register:
+ /* This check wouldn't be needed if pseudo_set() didn't set
+ symbols equated to bare symbols to undefined_section. */
+ if (symbolP->bsym->section != undefined_section
+ || symbolP->sy_value.X_op != O_symbol)
+ break;
+ /* Fall thru. */
+ case O_symbol:
+ case O_symbol_rva:
+ symbolP = expr.X_add_symbol;
+ break;
+ default:
+ return 0;
+ }
+ }
+
+ *valueP = expr.X_add_number;
+ *segP = symbolP->bsym->section;
+ *fragPP = symbolP->sy_frag;
+
+ if (*segP == expr_section)
+ switch (expr.X_op)
+ {
+ case O_constant: *segP = absolute_section; break;
+ case O_register: *segP = reg_section; break;
+ default: break;
+ }
+ }
+
+ return 1;
+}
+
/* Dollar labels look like a number followed by a dollar sign. Eg, "42$".
They are *really* local. That is, they go out of scope whenever we see a
label that isn't local. Also, like fb labels, there can be multiple
return S_GET_NAME (s) == 0;
}
+int
+S_IS_VOLATILE (const symbolS *s)
+{
+ if (LOCAL_SYMBOL_CHECK (s))
+ return 0;
+ return s->sy_volatile;
+}
+
+int
+S_IS_FORWARD_REF (const symbolS *s)
+{
+ if (LOCAL_SYMBOL_CHECK (s))
+ return 0;
+ return s->sy_forward_ref;
+}
+
const char *
S_GET_NAME (symbolS *s)
{
s->bsym->name = name;
}
+void
+S_SET_VOLATILE (symbolS *s)
+{
+ if (LOCAL_SYMBOL_CHECK (s))
+ s = local_symbol_convert ((struct local_symbol *) s);
+ s->sy_volatile = 1;
+}
+
+void
+S_SET_FORWARD_REF (symbolS *s)
+{
+ if (LOCAL_SYMBOL_CHECK (s))
+ s = local_symbol_convert ((struct local_symbol *) s);
+ s->sy_forward_ref = 1;
+}
+
/* Return the previous symbol in a chain. */
symbolS *
fragS * frag);
symbolS *symbol_create (const char *name, segT segment, valueT value,
fragS * frag);
+symbolS *symbol_clone (symbolS *, int);
+#undef symbol_clone_if_forward_ref
+symbolS *symbol_clone_if_forward_ref (symbolS *, int);
+#define symbol_clone_if_forward_ref(s) symbol_clone_if_forward_ref (s, 0)
symbolS *symbol_temp_new (segT, valueT, fragS *);
symbolS *symbol_temp_new_now (void);
symbolS *symbol_temp_make (void);
void symbol_table_insert (symbolS * symbolP);
valueT resolve_symbol_value (symbolS *);
void resolve_local_symbol_values (void);
+int snapshot_symbol (symbolS *, valueT *, segT *, fragS **);
void print_symbol_value (symbolS *);
void print_expr (expressionS *);
extern int S_IS_DEBUG (symbolS *);
extern int S_IS_LOCAL (symbolS *);
extern int S_IS_STABD (symbolS *);
+extern int S_IS_VOLATILE (const symbolS *);
+extern int S_IS_FORWARD_REF (const symbolS *);
extern const char *S_GET_NAME (symbolS *);
extern segT S_GET_SEGMENT (symbolS *);
extern void S_SET_SEGMENT (symbolS *, segT);
extern void S_CLEAR_EXTERNAL (symbolS *);
extern void S_SET_WEAK (symbolS *);
extern void S_SET_THREAD_LOCAL (symbolS *);
+extern void S_SET_VOLATILE (symbolS *);
+extern void S_SET_FORWARD_REF (symbolS *);
#ifndef WORKING_DOT_WORD
struct broken_word
+2005-10-11 Jan Beulich <jbeulich@novell.com>
+
+ * gas/all/cond.s: Add test for resolution of fully resolvable
+ forward references in .if/.endif.
+ * gas/all/cond.d: Rename to:
+ * gas/all/cond.l: New.
+ * gas/all/assign-bad.s: New.
+ * gas/all/assign-ok.s: New.
+ * gas/all/equ-bad.s: New.
+ * gas/all/equ-ok.s: New.
+ * gas/all/equiv1.s: New.
+ * gas/all/equiv2.s: New.
+ * gas/all/eqv-bad.s: New.
+ * gas/all/eqv-ok.s: New.
+ * gas/all/eval.[sd]: New.
+ * gas/all/forward.[sd]: New.
+ * gas/all/redef.[sd]: New.
+ * gas/all/gas.exp: Run new tests, but xfail equiv1 (PR/1387).
+
2005-10-10 Nick Clifton <nickc@redhat.com>
* gas/sh/reg-prefix.s: Use mov.l instruction in preference to
--- /dev/null
+ yyy == 3
+ yyy == 4
--- /dev/null
+ xxx = 1
+ xxx = 2
+ yyy == 3
+++ /dev/null
-# This should match the output of gas -alc cond.s.
-
-.*cond.s.*
-
-
- 1[ ]+.if 0
- 8[ ]+.else
- 9[ ]+.if 1
- 10[ ]+.endc
- 11 0000 0[02] ?00 ?00 ?0[02][ ]+.long[ ]+2
- 12[ ]+.if 0
- 14[ ]+.else
- 15 0004 0[04] ?00 ?00 ?0[04][ ]+.long[ ]+4
- 16[ ]+.endc
- 17[ ]+.endc
- 18[ ]+
- 19[ ]+.if 0
- 21[ ]+.elseif 1
- 22[ ]+.if 0
- 24[ ]+.elseif 1
- 25 0008 0[07] ?00 ?00 ?0[07][ ]+.long[ ]+7
- 26[ ]+.endif
- 27[ ]+.elseif 1
- 29[ ]+.else
- 31[ ]+.endif
-[ ]*[1-9][0-9]*[ ]+
-[ ]*[1-9][0-9]*[ ]+\.macro[ ]+m[ ]+x,[ ]*y[ ]*
-#...
-[ ]*[1-9][0-9]*[ ]+\.endm[ ]*
-[ ]*[1-9][0-9]*[ ]+[0-9a-f]+[048c] FF ?FF ?FF ?FF[ ]+m[ ]+,[ ]*
-[ ]*[1-9][0-9]*[ ]+FF ?FF ?FF ?FF[ ]*
-[ ]*[1-9][0-9]*[ ]+[0-9a-f]+[048c] FF ?FF ?FF ?FF[ ]+m[ ]+,[ ]*10[ ]*
-[ ]*[1-9][0-9]*[ ]+0[0A] ?00 ?00 ?0[0A][ ]*
-[ ]*[1-9][0-9]*[ ]+[0-9a-f]+[048c] 0[0B] ?00 ?00 ?0[0B][ ]+m[ ]+11,[ ]*
-[ ]*[1-9][0-9]*[ ]+FF ?FF ?FF ?FF[ ]*
-[ ]*[1-9][0-9]*[ ]+[0-9a-f]+[048c] 0[0C] ?00 ?00 ?0[0C][ ]+m[ ]+12,[ ]*13[ ]*
-[ ]*[1-9][0-9]*[ ]+0[0D] ?00 ?00 ?0[0D][ ]*
-[ ]*[1-9][0-9]*[ ]+
-[ ]*[1-9][0-9]*[ ]+.*\.p2align 5,0
-#pass
--- /dev/null
+# This should match the output of gas -alc cond.s.
+
+.*cond.s.*
+
+
+ 1[ ]+.if 0
+ 8[ ]+.else
+ 9[ ]+.if 1
+ 10[ ]+.endc
+ 11 0000 0[02] ?00 ?00 ?0[02][ ]+.long[ ]+2
+ 12[ ]+.if 0
+ 14[ ]+.else
+ 15 0004 0[04] ?00 ?00 ?0[04][ ]+.long[ ]+4
+ 16[ ]+.endc
+ 17[ ]+.endc
+ 18[ ]+
+ 19[ ]+.if 0
+ 21[ ]+.elseif 1
+ 22[ ]+.if 0
+ 24[ ]+.elseif 1
+ 25 0008 0[07] ?00 ?00 ?0[07][ ]+.long[ ]+7
+ 26[ ]+.endif
+ 27[ ]+.elseif 1
+ 29[ ]+.else
+ 31[ ]+.endif
+[ ]*[1-9][0-9]*[ ]+
+[ ]*[1-9][0-9]*[ ]+\.equiv[ ]+x,[ ]*y[ ]*
+[ ]*[1-9][0-9]*[ ]+\.equiv[ ]+y,[ ]*0[ ]*
+[ ]*[1-9][0-9]*[ ]+\.if[ ]+x[ ]*
+[ ]*[1-9][0-9]*[ ]+\.elseif[ ]+x[ ]*
+[ ]*[1-9][0-9]*[ ]+\.endif[ ]*
+[ ]*[1-9][0-9]*[ ]+
+[ ]*[1-9][0-9]*[ ]+\.macro[ ]+m[ ]+x,[ ]*y[ ]*
+#...
+[ ]*[1-9][0-9]*[ ]+\.endm[ ]*
+[ ]*[1-9][0-9]*[ ]+[0-9a-f]+[048c] FF ?FF ?FF ?FF[ ]+m[ ]+,[ ]*
+[ ]*[1-9][0-9]*[ ]+FF ?FF ?FF ?FF[ ]*
+[ ]*[1-9][0-9]*[ ]+[0-9a-f]+[048c] FF ?FF ?FF ?FF[ ]+m[ ]+,[ ]*10[ ]*
+[ ]*[1-9][0-9]*[ ]+0[0A] ?00 ?00 ?0[0A][ ]*
+[ ]*[1-9][0-9]*[ ]+[0-9a-f]+[048c] 0[0B] ?00 ?00 ?0[0B][ ]+m[ ]+11,[ ]*
+[ ]*[1-9][0-9]*[ ]+FF ?FF ?FF ?FF[ ]*
+[ ]*[1-9][0-9]*[ ]+[0-9a-f]+[048c] 0[0C] ?00 ?00 ?0[0C][ ]+m[ ]+12,[ ]*13[ ]*
+[ ]*[1-9][0-9]*[ ]+0[0D] ?00 ?00 ?0[0D][ ]*
+[ ]*[1-9][0-9]*[ ]+
+[ ]*[1-9][0-9]*[ ]+.*\.p2align 5,0
+#pass
.long 9
.endif
+ .equiv x, y
+ .equiv y, 0
+ .if x
+ .err
+ .elseif x
+ .err
+ .endif
+
.macro m x, y
.ifb \x
.long -1
--- /dev/null
+ .equ x, 1
+ .eqv x, 2
--- /dev/null
+ .equ x, 1
+ .equ x, 2
--- /dev/null
+;# Re-definition of an already .equiv-ed symbol (to another symbol).
+;# The assembler should reject this.
+ .equiv x, y
+ .equiv y, 1
+ .equiv x, 0
--- /dev/null
+;# Re-definition of an already .equiv-ed symbol (to an expression).
+;# The assembler should reject this.
+ .equiv x, y-z
+ .equiv y, 1
+ .equiv z, 1
+ .equiv x, 1
--- /dev/null
+ .eqv x, 1
+ .eqv x, 2
--- /dev/null
+ .eqv x, 1
--- /dev/null
+#objdump: -s -j .data
+#name: evaluation of simple expressions
+
+.*: .*
+
+Contents of section .data:
+ 0000 01010101 010101.. ........ ........ ................
+#pass
--- /dev/null
+.equ zero, 0
+.equ one, 1
+.equ two, 2
+
+
+ .data
+
+ .if two > one
+ .byte one
+ .else
+ .byte two
+ .endif
+
+ .if one == one
+ .byte one
+ .else
+ .byte two
+ .endif
+
+ .if one < two
+ .byte one
+ .else
+ .byte two
+ .endif
+
+ .if one <> two
+ .byte one
+ .else
+ .byte two
+ .endif
+
+ .if one != two
+ .byte one
+ .else
+ .byte two
+ .endif
+
+ .if one <= two
+ .byte one
+ .else
+ .byte two
+ .endif
+
+ .if two >= one
+ .byte one
+ .else
+ .byte two
+ .endif
--- /dev/null
+#objdump: -s -j .data
+#name: forward references
+
+.*: .*
+
+Contents of section .data:
+ 0000 01020304 ff0203fc 01020304 ff0203fc ................
+#pass
--- /dev/null
+ .equiv two, 2*one
+ .equiv minus_one, -one
+ .equ one, 1
+ .equiv three, 3*one
+ .eqv four, 4*one
+
+ .data
+
+ .if two > one
+ .byte one
+ .byte two
+ .endif
+
+ .if four > one
+ .byte three
+ .byte four
+ .endif
+
+ .equ one, -1
+ .byte one
+ .byte two
+
+ .if four < one
+ .byte three
+ .byte four
+ .endif
+
+ .equ one, -minus_one
+ .byte one
+ .byte two
+
+ .if four > one
+ .byte three
+ .byte four
+ .endif
+
+ .equ one, minus_one
+ .byte one
+ .byte two
+
+ .if four < one
+ .byte three
+ .byte four
+ .endif
gas_test_error "diff1.s" "" "difference of two undefined symbols"
}
+# PR/1387
+setup_xfail "*-*-*"
+gas_test_error "equiv1.s" "" ".equiv for symbol already set to another one"
+gas_test_error "equiv2.s" "" ".equiv for symbol already set to an expression"
+
+# .equ works differently on some targets.
+case $target_triplet in {
+ { hppa*-*-* } { }
+ { *c54x*-*-* } { }
+ default {
+ gas_test "equ-ok.s" "" "" ".equ for symbol already set"
+ gas_test_error "equ-bad.s" "" ".equ for symbol already set through .eqv"
+ }
+}
+
+gas_test "eqv-ok.s" "" "" ".eqv support"
+gas_test_error "eqv-bad.s" "" ".eqv for symbol already set"
+
+gas_test "assign-ok.s" "" "" "== assignment support"
+gas_test_error "assign-bad.s" "" "== assignment for symbol already set"
+
+# .equ works differently on some targets.
+# linkrelax-ing prevents most forward references from working.
+case $target_triplet in {
+ { crx*-*-* } { }
+ { h8300*-*-* } { }
+ { hppa*-*-* } { }
+ { mn10\[23\]00*-*-* } { }
+ { *c54x*-*-* } { }
+ default {
+ # Some targets don't manage to resolve BFD_RELOC_8 for constants.
+ setup_xfail "alpha*-*-*" "avr-*-*" "*c30*-*-*" "*c4x*-*-*" \
+ "d\[13\]0v*-*-*" "i860-*-*" "mips*-*-*" "msp430-*-*" \
+ "pdp11-*-*" "sparc*-*-*" "xtensa-*-*"
+ run_dump_test forward
+ }
+}
+
+# .set works differently on some targets.
+case $target_triplet in {
+ { alpha*-*-* } { }
+ { iq2000*-*-* } { }
+ { mips*-*-* } { }
+ { *c54x*-*-* } { }
+ default {
+ setup_xfail "*c30*-*-*" "*c4x*-*-*" "pdp11-*-*"
+ run_dump_test redef
+ }
+}
+
proc do_comment {} {
set testname "comment.s: comments in listings"
set x1 0
# character (it is allowed to be a line comment character).
if [string match "" [lindex [gas_run excl.s "-o /dev/null" ""] 0]] {
run_dump_test altmac2
+ # Similarly this test does not work when ! is a line seperator.
+ run_dump_test eval
}
}
}
send_log "$comp_output\n"
fail $testname
} else {
- if { [regexp_diff dump.out $srcdir/$subdir/cond.d] } {
+ if { [regexp_diff dump.out $srcdir/$subdir/cond.l] } {
fail $testname
} else {
pass $testname
--- /dev/null
+#objdump: -s -j .data
+#name: .equ redefinitions
+
+.*: .*
+
+Contents of section .data:
+ 0000 00000000 0[04]00000[04] 0[08]00000[08] 0[0c]00000[0c][ ]+................[ ]*
+#pass
--- /dev/null
+ .data
+_start:
+ .set x, .-_start
+ .long x
+ .balign 4
+ .set x, .-_start
+ .long x
+ .set x, .-_start
+ .long x
+ .set x, .-_start
+ .long x