flat assembler
Message board for the users of flat assembler.
Index
> Compiler Internals > [bug] Wrong segment selected for [ebp*2] (and friends). |
Author |
|
SeproMan 15 Apr 2018, 13:56
FASM assembles these with no USE setting, so implied USE16:
Code: mov ax, [ebp*1] 67 8B 45 00 mov ax, [ebp*2] 67 8B 44 2D 00 mov ax, [ebp*3] 67 8B 44 6D 00 mov ax, [ebp*4] 67 8B 04 AD 00 00 00 00 mov ax, [ebp*5] 67 8B 44 AD 00 mov ax, [ebp*8] 67 8B 04 ED 00 00 00 00 mov ax, [ebp*9] 67 8B 44 ED 00 The codes for [ebp*4] and [ebp*8] are correct. The other encodings are wrong because by splitting the scaled-index-only address form into a base plus scaled-index address form (in order to avoid the 32-bit displacement component), EBP is now used as a base and so architecturally the default segment becomes SS. When EBP is merely an index the default segment is DS. The assembler can not make the assumption that SS=DS. Personally I would prefer that if the programmer writes a scaled address form, it remains just that. Always correct an no trickery involved. If on the other hand obtaining the shortest code has priority, then FASM should add a DS: override prefix. Code: mov ax, [ebp*1] 3E 67 8B 45 00 mov ax, [ebp*2] 3E 67 8B 44 2D 00 mov ax, [ebp*3] 3E 67 8B 44 6D 00 mov ax, [ebp*4] 67 8B 04 AD 00 00 00 00 mov ax, [ebp*5] 3E 67 8B 44 AD 00 mov ax, [ebp*8] 67 8B 04 ED 00 00 00 00 mov ax, [ebp*9] 3E 67 8B 44 ED 00 Of course when these scaled-index-only address forms are use with a LEA instruction, none of the above matters. To see how NASM suffers the same problem click here. _________________ Real Address Mode. |
|||
15 Apr 2018, 13:56 |
|
Tomasz Grysztar 15 Apr 2018, 15:42
SeproMan wrote: The assembler can not make the assumption that SS=DS. Code: mov ax, [ds:ebp*1] ; 3E 67 8B 45 00 mov ax, [ds:ebp*2] ; 3E 67 8B 44 2D 00 mov ax, [ds:ebp*3] ; 3E 67 8B 44 6D 00 mov ax, [ds:ebp*4] ; 67 8B 04 AD 00 00 00 00 mov ax, [ds:ebp*5] ; 3E 67 8B 44 AD 00 mov ax, [ds:ebp*8] ; 67 8B 04 ED 00 00 00 00 mov ax, [ds:ebp*9] ; 3E 67 8B 44 ED 00 mov ax, [ss:ebp*1] ; 67 8B 45 00 mov ax, [ss:ebp*2] ; 67 8B 44 2D 00 mov ax, [ss:ebp*3] ; 67 8B 44 6D 00 mov ax, [ss:ebp*4] ; 36 67 8B 04 AD 00 00 00 00 mov ax, [ss:ebp*5] ; 67 8B 44 AD 00 mov ax, [ss:ebp*8] ; 36 67 8B 04 ED 00 00 00 00 mov ax, [ss:ebp*9] ; 67 8B 44 ED 00 mov ax, [ebp*1] ; 67 8B 45 00 mov ax, [ebp*2] ; 67 8B 44 2D 00 mov ax, [ebp*3] ; 67 8B 44 6D 00 mov ax, [ebp*4] ; 67 8B 04 AD 00 00 00 00 mov ax, [ebp*5] ; 67 8B 44 AD 00 mov ax, [ebp*8] ; 67 8B 04 ED 00 00 00 00 mov ax, [ebp*9] ; 67 8B 44 ED 00 This is a general and fundamental paradigm of assembly languages as understood in fasm's design, that assembly language provides abstraction over the machine language such that the commands of assembly define exactly what the generated instruction is supposed to do and the choice of appropriate machine code is up to the assembler. This abstraction is especially apparent in case of x86 architecture, where even many basic instructions can be encoded in many ways (this also allows to the "fingerprinting" of assembler/compiler used to generate code). On top of that, fasm's method is such that it seeks to generate smallest possible encoding for the requested instructions, as demonstrated by the choice of multi-pass assembly variant that was one of the fundamental blocks of fasm's design. This is the main reason why the above approach to segmented addressing was chosen (on this board you can find some older discussions of the problem from many years ago). |
|||
15 Apr 2018, 15:42 |
|
Tomasz Grysztar 15 Apr 2018, 16:18
Nevertheless, the above is not the full truth about the current state of fasm. A strict application of the principle mentioned above would lead to accepting things like LEA to MOV optimization, TEST optimization or changing the order of registers in expression to optimize [EBP+ESI] in flat mode into [ESI+EBP]. I personally have nothing against such optimization paths, but I recognize the reasoning of people that see this as a step too far, when the instruction they see in disassembly is too much removed from what they have written in the source (see the discussion in the thread about TEST optimization I linked to). Therefore fasm in its final iteration is more of a compromise and refrains from applying any more controversial optimizations. I really should make a more optimizing version of x86 macros for fasmg, though - there adding alternative variants of instruction encoders costs next to nothing.
|
|||
15 Apr 2018, 16:18 |
|
DimonSoft 15 Apr 2018, 19:34
I’m surprised. Well, I never thought about the case since it not common to add something to EBP and vice versa, but I’m surprised FASM does it so (and NASM as well?).
Tomasz Grysztar wrote: the commands of assembly define exactly what the generated instruction is supposed to do and the choice of appropriate machine code is up to the assembler But this is definitely not the case. The commands of assembly do not state explicitly that DS = SS, thus I’d expect such code to work the way it is defined in SDM. And such behavior definitely violates the least astonishment principle: one generally expects Intel SDM to be the source of information by default and any behavior not explicitly stated in FASM help should conform to Intel SDM. Of course, it is possible to define some rules that differ from the official documentation, for the purpose of opimizations. But I’m absolutely sure this should be stated explicitly in FASM help. Although a better solution would be to disable such behaviour and let the optimization find its way to assembly projects the same way other clever tricks do: “If you want to save a few bytes, you can do this and that but it comes with these consequences”. This particular “optimization” (which it really is not since the reaulting behaviour differs from the original one) seems to be more like C/C++ undefined behavior stuff: it started as a set of opportunities for optimization and ended up as an ideology of the languages and a gun that lets you shoot your foot unexpectedly. |
|||
15 Apr 2018, 19:34 |
|
Tomasz Grysztar 15 Apr 2018, 20:08
DimonSoft wrote: But this is definitely not the case. The commands of assembly do not state explicitly that DS = SS, thus I’d expect such code to work the way it is defined in SDM. And such behavior definitely violates the least astonishment principle: one generally expects Intel SDM to be the source of information by default and any behavior not explicitly stated in FASM help should conform to Intel SDM. DimonSoft wrote: Of course, it is possible to define some rules that differ from the official documentation, for the purpose of opimizations. But I’m absolutely sure this should be stated explicitly in FASM help. Although a better solution would be to disable such behaviour and let the optimization find its way to assembly projects the same way other clever tricks do: “If you want to save a few bytes, you can do this and that but it comes with these consequences”. Code: mov ax,[(ebp+ebx)*3-(ebx+100h)*2+ebp] label a at ebp+ebx label b at ebx+100h mov ax,[ebp+a*3-b*2] ; the same address, but its register components hidden |
|||
15 Apr 2018, 20:08 |
|
DimonSoft 15 Apr 2018, 20:39
Tomasz Grysztar wrote: there is really no other way in fasm's syntax to "state explicitly that DS = SS" (while you can easily state that you need to use specific segment with a regular syntax) Well, I can state explicitly that I want to use DS even though SS should be used by default. I mean, the optimization technique could be moved from the assembler to the programmer. Tomasz Grysztar wrote: The way fasm handles register algebra is really outside of the scope of what Intel defines. You can compute addresses like this with fasm: <…> You’re right, I missed this part. And it makes things more complicated. Still, if the rules of the register algebra are consistent for different expressions, it is the part of FASM that is worth being documented and then choosing the base and index registers can also be defined by a few simple rules without breaking compatibility with Intel SDM. What I’m trying to say is that it can be done this way or that but I vote for the solution to follow the least astonishment principle whenever possible, i.e. it is either well-documented behavior or works as expected without the need to know FASM is flat and optimizing assembler. IMNSHO. |
|||
15 Apr 2018, 20:39 |
|
Tomasz Grysztar 15 Apr 2018, 21:03
DimonSoft wrote: Well, I can state explicitly that I want to use DS even though SS should be used by default. DimonSoft wrote: I mean, the optimization technique could be moved from the assembler to the programmer. An assembler where it is the programmer who is choosing and optimizing the opcodes does not need multiple passes or register algebra. And is generally much easier to create. Though there is also an option of making it as another alternative package of x86 macros for fasmg, relieved from handling these complexities they would probably become much simpler and faster compared to the fasm-compatible ones. |
|||
15 Apr 2018, 21:03 |
|
DimonSoft 16 Apr 2018, 09:10
Tomasz Grysztar wrote:
Tomasz Grysztar wrote:
Optimization already implies “no changes in code behavior”. I don’t see how being careful applying optimizations enforces the removal of optimizations other than this one. And we already have to know about, say, Allison’s algorithm and possibly prefer it over straightforward implementation. And to choose between movzx/movsx combinations of xor, cdq and mov. Avoiding optimizations that are not valid in certain conditions seem to be the right thing to do in general. But I understand this would make some cool stuff in FASM useless, so I’ll say this stuff should just be expressed in the docs explicitly. In fact, I don’t really care about this particular use case for my own projects and the assumption really seems perfectly valid for most cases except, maybe, some (?) OSDev projects. But since I’m teaching students and was the one who made FASM the assembler of choice in our university department, I’m surprised in a bad way: it was supposed to be used as the best tool that does everything the right way™ and now it turns out there might be lots of hidden surprises like this one. |
|||
16 Apr 2018, 09:10 |
|
Tomasz Grysztar 16 Apr 2018, 10:45
DimonSoft wrote:
When one writes something like [ebp*X], it is already not apparent whether this is going to be addressed through SS or not. It isn't obvious whether EBP is a base or index register then - when an address is simply an algebraic expression we have no universally obvious way of telling "there is no base register here". One could invent some rules like make [ebp] mean base register and [0+ebp] mean index register addressing, or even distinguish [ebp] from [ebp*1], but such rules would not be something that obviously follows from how the Intel instructions are defined, they would be specific quirks of a given assembly language. Again, because Intel manuals define things as related to machine encoding and assembly languages abstract many of these things away. Whichever variant would get chosen, it would be surprising from some point of view. If one expects the address expression to actually reflect encoding structure like GAS does, then the fact that is is treated algebraically is going to be surprising. On the other hand having "A+0" mean something different than "0+A" or "A*1" would be just as surprising for anyone used to the rules for expressions in the most of existing languages. I would argue that the latter surprise would be much stronger, and the experience I had with fasm development seems to demonstrate it. Therefore I always considered expressions like [ebp*2] ambiguous with respect to segment addressing, something that requires clarification, by writing either [ds:ebp*2] or [ss:ebp*2] if the distinction is really important in context (and leaving the bare address when the ambiguity does not matter, like in the flat mode). It is in some way similar to adding round brackets around sub-expression "just in case the operator precedence is different that I remember". It not only makes sure that things mean what they should, but also creates a better code, easier to correctly interpret by someone else reading it. DimonSoft wrote: Optimization already implies “no changes in code behavior”. This is not inconsistent with the approach to the addresses we just discussed. For me it was obvious that since "ebp+ebp" and "ebp*2", and "ebp*3-ebp" are all algebraically equivalent, there is no clear definition of base register for them and therefore there is really no well-defined standard behavior that the assembler would have to preserve. Adding the segment prefix clearly states the instruction purpose, but the bare variant is up to the assembler to define. Now, that fasm's manual failed to provide a detailed discussion of this is a different story. It may turn out that long after the fasm's development is finished I am still going to keep working on all these never-really-finished manuals. |
|||
16 Apr 2018, 10:45 |
|
DimonSoft 16 Apr 2018, 11:03
Tomasz Grysztar wrote: Therefore I always considered expressions like [ebp*2] ambiguous with respect to segment addressing, something that requires clarification, by writing either [ds:ebp*2] or [ss:ebp*2] if the distinction is really important in context (and leaving the bare address when the ambiguity does not matter, like in the flat mode). It is in some way similar to adding round brackets around sub-expression "just in case the operator precedence is different that I remember". It not only makes sure that things mean what they should, but also creates a better code, easier to correctly interpret by someone else reading it. Well, having experience in real-mode programming I thought the fact of EBP occuring in such expression already means we use SS. Never really paid attention to the quirk in Intel’s addressing. So, still SS is less surprising but, like I said, I got your idea. And it makes sense. Tomasz Grysztar wrote: Since the beginning of the development of fasm I focused on having a syntax that clearly defines what the instruction is supposed to do. Which is the best feature I’ve been looking for for ages until I’ve found FASM. Tomasz Grysztar wrote: Now, that fasm's manual failed to provide a detailed discussion of this is a different story. It may turn out that long after the fasm's development is finished I am still going to keep working on all these never-really-finished manuals. I’d say a separate section related to FASM optimizations might be a good idea. It is the feature that influences the assembler output and is generally the feature to be proud of. |
|||
16 Apr 2018, 11:03 |
|
< Last Thread | Next Thread > |
Forum Rules:
|
Copyright © 1999-2024, Tomasz Grysztar. Also on GitHub, YouTube.
Website powered by rwasa.