mirror of
https://github.com/netwide-assembler/nasm.git
synced 2024-11-21 03:14:19 +08:00
Decouple forward references from optimization
Users who wish to control the level of optimization can continue to specify -O0, -O1, or -Ox, where x can be the letter itself, or any number > 1. However, even with optimization turned off, NASM will always make enough passes to resolve forward references. As a result, INCBIN is now the only item left in the critical expressions list, although TIMES still has its own constant value check.
This commit is contained in:
parent
325a4bff50
commit
2581c869b1
11
assemble.c
11
assemble.c
@ -249,13 +249,10 @@ static int jmp_match(int32_t segment, int64_t offset, int bits,
|
||||
|
||||
if (c != 0370 && c != 0371)
|
||||
return 0;
|
||||
if (ins->oprs[0].opflags & OPFLAG_FORWARD) {
|
||||
if ((optimizing < 0 || (ins->oprs[0].type & STRICT))
|
||||
&& c == 0370)
|
||||
return 1;
|
||||
else
|
||||
return (pass0 == 0); /* match a forward reference */
|
||||
}
|
||||
if ((optimizing <= 0 || (ins->oprs[0].type & STRICT)))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
isize = calcsize(segment, offset, bits, ins, code);
|
||||
if (ins->oprs[0].segment != segment)
|
||||
return 0;
|
||||
|
@ -783,33 +783,26 @@ with a \i{stub preprocessor} which does nothing.
|
||||
|
||||
\S{opt-On} The \i\c{-On} Option: Specifying \i{Multipass Optimization}.
|
||||
|
||||
NASM defaults to being a two pass assembler. This means that if you
|
||||
have a complex source file which needs more than 2 passes to assemble
|
||||
optimally, you have to enable extra passes.
|
||||
|
||||
Using the \c{-O} option, you can tell NASM to carry out multiple passes.
|
||||
NASM defaults to not optimizing operands which can fit into a signed byte.
|
||||
This means that if you want the shortest possible object code,
|
||||
you have to enable optimization.
|
||||
|
||||
Using the \c{-O} option, you can tell NASM to carry out different levels of optimization.
|
||||
The syntax is:
|
||||
|
||||
\b \c{-O0} strict two-pass assembly, JMP and Jcc are handled more
|
||||
like v0.98, except that backward JMPs are short, if possible.
|
||||
Immediate operands take their long forms if a short form is
|
||||
not specified.
|
||||
\b \c{-O0} No optimization. All operands take their long forms,
|
||||
if a short form is not specified.
|
||||
|
||||
\b \c{-O1} strict two-pass assembly, but forward branches are assembled
|
||||
with code guaranteed to reach; may produce larger code than
|
||||
-O0, but will produce successful assembly more often if
|
||||
branch offset sizes are not specified.
|
||||
Additionally, immediate operands which will fit in a signed byte
|
||||
are optimized, unless the long form is specified.
|
||||
\b \c{-O1} Minimal optimization. As above, but immediate operands
|
||||
which will fit in a signed byte are optimized,
|
||||
unless the long form is specified.
|
||||
|
||||
\b \c{-On} multi-pass optimization, minimize branch offsets; also will
|
||||
\b \c{-Ox} where \c{x} is the actual letter \c{x} Multi-pass optimization,
|
||||
minimize branch offsets; also will
|
||||
minimize signed immediate bytes, overriding size specification
|
||||
unless the \c{strict} keyword has been used (see \k{strict}).
|
||||
The number specifies the maximum number of passes. The more
|
||||
passes, the better the code, but the slower is the assembly.
|
||||
|
||||
\b \c{-Ox} where \c{x} is the actual letter \c{x}, indicates to NASM
|
||||
to do unlimited passes.
|
||||
For compatability with earlier releases, the letter \c{x} may also be any
|
||||
number greater than one. This number has no effect on the actual number of passes.
|
||||
|
||||
Note that this is a capital \c{O}, and is different from a small \c{o}, which
|
||||
is used to specify the output file name. See \k{opt-o}.
|
||||
@ -1275,9 +1268,7 @@ redefined later. This is not a \i{preprocessor} definition either:
|
||||
the value of \c{msglen} is evaluated \e{once}, using the value of
|
||||
\c{$} (see \k{expr} for an explanation of \c{$}) at the point of
|
||||
definition, rather than being evaluated wherever it is referenced
|
||||
and using the value of \c{$} at the point of reference. Note that
|
||||
the operand to an \c{EQU} is also a \i{critical expression}
|
||||
(\k{crit}).
|
||||
and using the value of \c{$} at the point of reference.
|
||||
|
||||
|
||||
\S{times} \i\c{TIMES}: \i{Repeating} Instructions or Data
|
||||
@ -1306,8 +1297,7 @@ Note that there is no effective difference between \c{times 100 resb
|
||||
1} and \c{resb 100}, except that the latter will be assembled about
|
||||
100 times faster due to the internal structure of the assembler.
|
||||
|
||||
The operand to \c{TIMES}, like that of \c{EQU} and those of \c{RESB}
|
||||
and friends, is a critical expression (\k{crit}).
|
||||
The operand to \c{TIMES} is a critical expression (\k{crit}).
|
||||
|
||||
Note also that \c{TIMES} can't be applied to \i{macros}: the reason
|
||||
for this is that \c{TIMES} is processed after the macro phase, which
|
||||
@ -1813,52 +1803,7 @@ NASM rejects these examples by means of a concept called a
|
||||
\e{critical expression}, which is defined to be an expression whose
|
||||
value is required to be computable in the first pass, and which must
|
||||
therefore depend only on symbols defined before it. The argument to
|
||||
the \c{TIMES} prefix is a critical expression; for the same reason,
|
||||
the arguments to the \i\c{RESB} family of pseudo-instructions are
|
||||
also critical expressions.
|
||||
|
||||
Critical expressions can crop up in other contexts as well: consider
|
||||
the following code.
|
||||
|
||||
\c mov ax,symbol1
|
||||
\c symbol1 equ symbol2
|
||||
\c symbol2:
|
||||
|
||||
On the first pass, NASM cannot determine the value of \c{symbol1},
|
||||
because \c{symbol1} is defined to be equal to \c{symbol2} which NASM
|
||||
hasn't seen yet. On the second pass, therefore, when it encounters
|
||||
the line \c{mov ax,symbol1}, it is unable to generate the code for
|
||||
it because it still doesn't know the value of \c{symbol1}. On the
|
||||
next line, it would see the \i\c{EQU} again and be able to determine
|
||||
the value of \c{symbol1}, but by then it would be too late.
|
||||
|
||||
NASM avoids this problem by defining the right-hand side of an
|
||||
\c{EQU} statement to be a critical expression, so the definition of
|
||||
\c{symbol1} would be rejected in the first pass.
|
||||
|
||||
There is a related issue involving \i{forward references}: consider
|
||||
this code fragment.
|
||||
|
||||
\c mov eax,[ebx+offset]
|
||||
\c offset equ 10
|
||||
|
||||
NASM, on pass one, must calculate the size of the instruction \c{mov
|
||||
eax,[ebx+offset]} without knowing the value of \c{offset}. It has no
|
||||
way of knowing that \c{offset} is small enough to fit into a
|
||||
one-byte offset field and that it could therefore get away with
|
||||
generating a shorter form of the \i{effective-address} encoding; for
|
||||
all it knows, in pass one, \c{offset} could be a symbol in the code
|
||||
segment, and it might need the full four-byte form. So it is forced
|
||||
to compute the size of the instruction to accommodate a four-byte
|
||||
address part. In pass two, having made this decision, it is now
|
||||
forced to honour it and keep the instruction large, so the code
|
||||
generated in this case is not as small as it could have been. This
|
||||
problem can be solved by defining \c{offset} before using it, or by
|
||||
forcing byte size in the effective address by coding \c{[byte
|
||||
ebx+offset]}.
|
||||
|
||||
Note that use of the \c{-On} switch (with n>=2) makes some of the above
|
||||
no longer true (see \k{opt-On}).
|
||||
the \c{TIMES} prefix is a critical expression.
|
||||
|
||||
\H{locallab} \i{Local Labels}
|
||||
|
||||
|
11
nasm.c
11
nasm.c
@ -294,7 +294,7 @@ int main(int argc, char **argv)
|
||||
|
||||
time(&official_compile_time);
|
||||
|
||||
pass0 = 1;
|
||||
pass0 = 0;
|
||||
want_usage = terminate_after_phase = false;
|
||||
report_error = report_error_gnu;
|
||||
|
||||
@ -1166,8 +1166,7 @@ static void assemble_file(char *fname, StrList **depend_ptr)
|
||||
report_error(ERR_FATAL, "command line: "
|
||||
"32-bit segment size requires a higher cpu");
|
||||
|
||||
pass_max = (optimizing > 0 ? optimizing : 0) + 2; /* passes 1, optimizing, then 2 */
|
||||
pass0 = !(optimizing > 0); /* start at 1 if not optimizing */
|
||||
pass_max = (INT_MAX >> 1) + 2; /* Almost unlimited */
|
||||
for (passn = 1; pass0 <= 2; passn++) {
|
||||
int pass1, pass2;
|
||||
ldfunc def_label;
|
||||
@ -1500,7 +1499,7 @@ static void assemble_file(char *fname, StrList **depend_ptr)
|
||||
parse_line(pass1, line, &output_ins,
|
||||
report_error, evaluate, def_label);
|
||||
|
||||
if (!(optimizing > 0) && pass0 == 2) {
|
||||
if (optimizing > 0) {
|
||||
if (forwref != NULL && globallineno == forwref->lineno) {
|
||||
output_ins.forw_ref = true;
|
||||
do {
|
||||
@ -1513,7 +1512,7 @@ static void assemble_file(char *fname, StrList **depend_ptr)
|
||||
output_ins.forw_ref = false;
|
||||
}
|
||||
|
||||
if (!(optimizing > 0) && output_ins.forw_ref) {
|
||||
if (optimizing > 0) {
|
||||
if (passn == 1) {
|
||||
for (i = 0; i < output_ins.operands; i++) {
|
||||
if (output_ins.oprs[i].
|
||||
@ -1768,7 +1767,7 @@ static void assemble_file(char *fname, StrList **depend_ptr)
|
||||
preproc->cleanup(0);
|
||||
nasmlist.cleanup();
|
||||
#if 1
|
||||
if (optimizing > 0 && opt_verbose_info) /* -On and -Ov switches */
|
||||
if (opt_verbose_info) /* -On and -Ov switches */
|
||||
fprintf(stdout,
|
||||
"info:: assembly required 1+%d+1 passes\n", passn-3);
|
||||
#endif
|
||||
|
14
parser.c
14
parser.c
@ -308,21 +308,13 @@ restart_parse:
|
||||
result->condition = tokval.t_inttwo;
|
||||
|
||||
/*
|
||||
* RESB, RESW and RESD cannot be satisfied with incorrectly
|
||||
* INCBIN cannot be satisfied with incorrectly
|
||||
* evaluated operands, since the correct values _must_ be known
|
||||
* on the first pass. Hence, even in pass one, we set the
|
||||
* `critical' flag on calling evaluate(), so that it will bomb
|
||||
* out on undefined symbols. Nasty, but there's nothing we can
|
||||
* do about it.
|
||||
*
|
||||
* For the moment, EQU has the same difficulty, so we'll
|
||||
* include that.
|
||||
* out on undefined symbols.
|
||||
*/
|
||||
if (result->opcode == I_RESB || result->opcode == I_RESW ||
|
||||
result->opcode == I_RESD || result->opcode == I_RESQ ||
|
||||
result->opcode == I_REST || result->opcode == I_RESO ||
|
||||
result->opcode == I_RESY ||
|
||||
result->opcode == I_INCBIN) {
|
||||
if (result->opcode == I_INCBIN) {
|
||||
critical = (pass0 < 2 ? 1 : 2);
|
||||
|
||||
} else
|
||||
|
Loading…
Reference in New Issue
Block a user