flat assembler
Message board for the users of flat assembler.
![]() |
Author |
|
revolution 27 Feb 2015, 07:26
I have always laid out my code manually with a top down approach (which seems to be the opposite of most C code I have seen) so my entry point is first and all subordinate functions are below the caller.
As far as doing it automatically, either forward or backward depending upon the users desires, I would expect macros would be the answer here. Perhaps extend the "proc" macro in some way and then "postpone" to figure it all out later. |
|||
![]() |
|
redsock 27 Feb 2015, 07:32
revolution wrote: I have always laid out my code manually with a top down approach (which seems to be the opposite of most C code I have seen) so my entry point is first and all subordinate functions are below the caller. All non-local symbols could be subjected to the same rules, thereby also including static table data as well as functions. If I end up having to write a preprocessor for it, it will ultimately have to follow the same logic as the "if used" component, and then start with the topmost (and as you say go forward or backward depending on user preference). I guess my case is the "if used" code already does similar things as to my call graph request. ![]() |
|||
![]() |
|
revolution 27 Feb 2015, 07:41
|
|||
![]() |
|
revolution 27 Feb 2015, 07:47
You would also need to override call/stdcall so as to know who is calling whom. Plus be careful with recursive functions calling themselves and/or each other.
|
|||
![]() |
|
redsock 27 Feb 2015, 07:55
revolution wrote: For the non-local variables you can build a list: ![]() I agree with you though, most of the code I write that isn't a library (and is one-off) gets done top-down anyway. I suppose because of my design choices with the library itself, call-graph ordered would potentially be MUCH better. The "if used" logic of the compiler means that all of the symbols I define based on the "if used" directive don't have fixed addresses until the final stage. If at the final stage, those same symbols could be ordered by the compiler (instead of my writing a preprocessor to do the same, which would not need the if used directive in the first place), that would be much better. Even better still if you could specify backward or forward order in same. |
|||
![]() |
|
revolution 27 Feb 2015, 08:02
There are still more opportunities for local variables. You can order larger structures first and smaller structures last so that [ebp-offset] has a better chance of fitting in one signed byte. Also have some way tell the assembler which is a highly used member and prioritise it to the end of the list.
|
|||
![]() |
|
redsock 27 Feb 2015, 08:06
Oh, I thought I should point out:
In order for the "if used" logic to work as it does across multiple passes, it must by default have knowledge of the call-graph in question. Whether the compiler is currently keeping track of this order or not, well, it most likely is not. And this is the crux of the feature I am requesting. When the "if used" directive is employed (especially across the board as I have done), if the compiler kept even a simple list of dependencies, the call graph is done then and there in the normal course of operations when using the "if used" directive. From there, at the "right before we commit and write some code" stage, that same dependency list could be traversed to determine function order (whether forward or backward), _this_ is what I am after. It isn't a far stretch from what is there already, and would be very useful IMO for code generation. ![]() ![]() Cheers again, and revolution, you are a legend as always. |
|||
![]() |
|
revolution 27 Feb 2015, 08:10
The "if used" feature is very simple minded and doesn't build any call graph or dependencies AFICT. I think it is a red-herring and independent to what you want to do.
|
|||
![]() |
|
redsock 27 Feb 2015, 08:15
revolution wrote: "if used" is very simple minded and doesn't build any call graph or dependencies AFICT. I think it is a red-herring and independent to what you want to do. ![]() The "if used" directive, if indeed it is simple minded as you say, allows me to declare 80 functions in a single include file, and only the ones I reference _outside_ get included the resultant binary. Similar rules apply to all of my data structures. So, in my library there are a huge number of data tables, but only the ones that are actually referenced end up in a given binary. As such, I categorically deny that there is no notion of a call graph, there MUST be (or the compiler could not pull this feat off to begin with). Perhaps it is semantics insofar as my using the word "call graph" when I really mean "symbol dependency graph, whether function or table or data or whatever graph". Either way, fasm already knows about what I am doing dependency-wise, and that is precisely what I want ordered. ![]() |
|||
![]() |
|
revolution 27 Feb 2015, 08:18
If just keeps a boolean flag to say whether it is referenced. By default they all start as not referenced and each successive pass will set more of the flags as more functions get referenced. The assembler keeps no data about who referenced it or if more than one function referenced it.
|
|||
![]() |
|
redsock 27 Feb 2015, 08:22
revolution wrote: If just keeps a boolean flag to say whether it is referenced. By default they all start as not referenced and each successive pass will set more of the flags as more functions get referenced. The assembler keeps no data about who referenced it or if more than one function referenced it. ![]() |
|||
![]() |
|
revolution 27 Feb 2015, 08:28
Okay, I misunderstood. You want to enhance the "used" flag to store more details. But can't the macro feature also do this for you?
|
|||
![]() |
|
redsock 27 Feb 2015, 08:37
revolution wrote: Okay, I misunderstood. You want to enhance the "used" flag to store more details. But can't the macro feature also do this for you? Code: call somefunction Code: mov rax, [someglobalsymbol I don't see how macros could solve symbol dependency based code generation, unless the code I wrote across the board were horrifically done, or a preprocessor is used to filter the assembly prior to fasm seeing it in the first place. I am certainly open to suggestions if there is an easy way to get around that. All up there are ~1800 symbols if I set the "include_everything" flag in my library (in which case, inside the context of this feature request, code would end up as-is, or better put, as I specified it in the code/compile-time). |
|||
![]() |
|
revolution 27 Feb 2015, 09:08
So if you introduce some new capabilities into the used flag how do you propose to exercise the capability? A new directive?
|
|||
![]() |
|
baldr 27 Feb 2015, 11:22
redsock,
Probably it worth to investigate why and where reordering gives such a speed-up at first. Topological sort based on cross-reference may yield adverse results. Function can be referenced in one place and indirectly used somewhere else. Can you make up an example to experiment with and see the difference? |
|||
![]() |
|
l_inc 27 Feb 2015, 14:12
redsock
First of all it is very questionable that the function ordering directly affects performance. While cache conflict misses indeed may cause performance degradation in case the cache associativity is just a bit not enough to service very often called functions, such a situation is very unlikely. What's much more likely to cause a significant performance difference is alignment. Especially if your CPU is new enough to have the decoded iCache and the loop stream detector (present since Sandy Bridge, iirc). Read the corresponding chapters in the Intel's optimization manual, and you'll probably won't need anything but to use the align directive. Secondly, as long as the function ordering measure is very machine dependent, it may both increase as well as decrease performance no matter what ordering rules you apply. For that reason I would not recommend to embed this optimization into a compiler. Another reason to vote against the feature is that an assembler compiler and especially fasm should not alter the layout directly imposed by the source code ordering. Any modifications on the binary layout should be achieved using macros logically decoupled from the assembler. Thirdly, from my initial view it seems to be possible to create the call graph using the current fasm capabilities. Function reordering according to the call graph within a single compilation is only potentially possible, but definitely impractical. A more or less efficient way is feasible by setting up a feedback from the assembler output into the preprocessor by using multiple compilations, making it a potential use case for this feature. Having said that I have to point out to the baldr's remark regarding indirect calls that might be impossible to resolve at compile time. _________________ Faith is a superposition of knowledge and fallacy |
|||
![]() |
|
< Last Thread | Next Thread > |
Forum Rules:
|
Copyright © 1999-2025, Tomasz Grysztar. Also on GitHub, YouTube.
Website powered by rwasa.