|Re: code generation on 8086? maccer@MT.net (1995-03-01)|
|Re: code generation on 8086? firstname.lastname@example.org (1995-03-06)|
|Re: code generation on 8086? john_reiser@MENTORG.COM (1995-03-07)|
|From:||maccer@MT.net (Johnathon McAlister)|
|Keywords:||code, optimize,386, comment|
|Date:||Wed, 1 Mar 1995 17:18:07 GMT|
email@example.com (Paul Rubin) wrote:
> What are the basic techniques for code generation on the 8086?
> This mainly means register allocation on a cpu where every register
> is weird and special in a different way. (If you're familiar
> with the 386, you may not know that the 8086 is a *lot* worse).
> The other issue is near/far pointers and calls, etc.
What exactly is the question? If I remember my assmbly course on XTs
correctly, only about half of the registers have special purposes ;-)
Actually, you can use most registers for general purposes, but need to be
careful not to conflict when a special purpose comes up. This either
means looking ahead for those cases and coding around them, or saving and
restoring registers as the special cases come up.
As for near and far stuff, that's similar to th old question of relative
vs absolute branching. Many processors used to have only 1 or 2 bytes to
store the offset for relative branches, forcing those branches to be
short. If you wanted to branch farther away, then you used absolute
branching, but you then lose relocatability. Some instructions _required_
relative branching (conditional branches on the Z-80, for instance), which
further complicated things.
At least the 8086's segmentation scheme is better than the Z-80's
relative/absolute address scheme. If you want an address in the current
segment, then use near addressing - it's smaller and faster. If you want
an address outside the current segment, you change the current segment by
changing the appropriate segment register(s) and continue on. If you save
the old value of the segment register, you can restore it later, making
the change temporary.
In practice, I did not need to use these methods often. Each segment is
64 Kbytes long, and can start at any paragraph (16 byte) boundary. You
also have several segments - code segment, stack segment, data segment.
If your code is less than 64 KBytes long, then you have it made.
Otherwise, the compiler needs to keep track of how far jumps are and what
the current segment is - it can then generate long jumps as needed. The
same goes for data, with its own segment register. The stack is a little
harder - you need to check if the stack is likely to try and go beyond the
end of the segment before pushing data, and change to a new (free)
segment. Likewise, if you have a multiple segment stack, you need to see
if the current segment is empty. If it is, then go to the previous stack
segment, if there is one. However, 64 Kbytes should be plenty for a
stack, so this is not a common thing to see.
Other than that, it should be like using an advanced 8080. The hard part
would be recognizing when advanced instructions could be used to optimize
maccer@MT.net - preferred, as it's flat rate
firstname.lastname@example.org - backup, as it's paid hourly
[I looked at the issue in some detail a while ago. The hardest problem
seems to me that for the best code you have to look fairly far ahead so
that results of operations land in preferred registers. There are also
some instructions like LOOP that you can use if A) your loop counter is in
the CX register, B) you're counting down to zero, and C) the loop fits in
256 bytes, but in newer processors you can ignore them because the simpler
instructions are usually faster. There's also some random wierdness, e.g.
if you have an 8-bit value in AL and you know that the rest of the A
register is junk, the best instruction to add 1 to it is INC AX (or INC
EAX in 32 bit mode) because it's shorter than INC AL and just as fast.
Return to the
Search the comp.compilers archives again.