Then it's funny that as uses 64-bit arithmetic, while it's clearly
supposed to be a 32-bit assembler (I have an i386 system without any
64-bit parts -- a P3, to be exact). The arithmetic gas uses depends on
bfd, which is 64-bit, but I think it might have to depend on the target
architecture instead, i.e., N-bit target architecture = N-bit
arithmetic. What do you think?

It's a reasonable idea. Do you care to contribute patches? :)

OK, I've given it a shot in the attached patch. The effect of the patch
is that all intermediate values are truncated to the platform's bit
width regardless of the BFD's bit width, so that the result of any
arithmetic is the same regardless of the BFD's bit width. I'm interested
in your feedback, so: fire away!

A description of the changes:

* Add value tc_arithmetic_cutoff_mask, tc_arithmetic_hibit and
ENABLE_ARITHMETIC_CUTOFF to config/tc-i386.h, and initialize them
appropriately in tc-i386.c (64-bits mask for flag_code == CODE_64BIT,
32-bits mask if not). The cutoff mask is basically a mask for the bit
width of the architecture, that can be used to adjust calculations. All
the other architectures are masked from this change because they don't
define ENABLE_ARITHMETIC_CUTOFF.

* Change expr() and operand() to call truncate_constant() for any result
expression that has X_op == O_constant. This effectively causes all
intermediate values to be truncated.

* Add truncate_constant() to expr.c. This function is based on the
truncation+warning code in read.c, but always truncates to the bits in
tc_arithmetic_cutoff_mask. It generates a warning when a value is cut
off, "intermediate value X truncated to Y". (Remark: it also says
"intermediate" for the outer expression. We should really suppress the
warning for the outer expression and leave it to the truncation code in
read.c -- but that's complicated, because we don't know whether we're at
the end of the expression.)

* Change the '~' operator implementation in expr.c to cut off bits from
the result according to the arithmetic cutoff. The input is already
within range (because of the other changes) so the output definitely
will be within range as well when we cut it off.

* Change the truncation warnings to report using unsigned long
longs instead of unsigned longs.
* Change the truncation warnings to report truncations of
overflows in the negative using negative values. This is to prevent
things like truncation for -0x80000001 to be reported as truncations
from 0xffffffff7fffffff to 0x7fffffff (this shows the 64-bit arithmetic
too much). Instead, this would be reported as a truncation from
-0x80000001 to 0x7fffffff, which conveys the message that the overflow
was in the negative much more clearly.

* Change the truncation warnings to take into account the sign of the
value. An unsigned value will now ALWAYS generate a truncation warning
when one of the high bits is set, instead of only when it cannot be
interpreted as a within-range negative number.