--- gcc/PROJECTS 2018/04/24 16:50:04 1.1.1.4 +++ gcc/PROJECTS 2018/04/24 16:56:20 1.1.1.8 @@ -1,3 +1,10 @@ +0. Improved efficiency. + +* Parse and output array initializers an element at a time, freeing +storage after each, instead of parsing the whole initializer first and +then outputting. This would reduce memory usage for large +initializers. + 1. Better optimization. * Constants in unused inline functions @@ -12,9 +19,10 @@ to point to the constant and cause it to * More cse -The techniques for doing full global cse are described in the -red dragon book. It is likely to be slow and use a lot of memory, -but it might be worth offering as an additional option. +The techniques for doing full global cse are described in the red +dragon book, or (a different version) in Frederick Chow's thesis from +Stanford. It is likely to be slow and use a lot of memory, but it +might be worth offering as an additional option. It is probably possible to extend cse to a few very frequent cases without so much expense. @@ -41,17 +49,92 @@ You would need to verify in the calling use the addresses of any local variables and does not use setjmp. * Put short statics vars at low addresses and use short addressing mode? + Useful on the 68000/68020 and perhaps on the 32000 series, provided one has a linker that works with the feature. This is said to make a 15% speedup on the 68000. -This brings to mind Hayes' changes for Stanford MIPS. + +* Keep global variables in registers. + +Here is a scheme for doing this. A global variable, or a local variable +whose address is taken, can be kept in a register for an entire function +if it does not use non-constant memory addresses and (for globals only) +does not call other functions. If the entire function does not meet +this criterion, a loop may. + +The VAR_DECL for such a variable would have to have two RTL expressions: +the true home in memory, and the pseudo-register used temporarily. +It is necessary to emit insns to copy the memory location into the +pseudo-register at the beginning of the function or loop, and perhaps +back out at the end. These insns should have REG_EQUIV notes so that, +if the pseudo-register does not get a hard register, it is spilled into +the memory location which exists in any case. + +The easiest way to set up these insns is to modify the routine +put_var_into_stack so that it does not apply to the entire function +(sparing any loops which contain nothing dangerous) and to call it at +the end of the function regardless of where in the function the +address of a local variable is taken. It would be called +unconditionally at the end of the function for all relevant global +variables. + +For debugger output, the thing to do is to invent a new binding level +around the appropriate loop and define the variable name as a register +variable with that scope. + +* Live-range splitting. + +Currently a variable is allocated a hard register either for the full +extent of its use or not at all. Sometimes it would be good to +allocate a variable a hard register for just part of a function; for +example, through a particular loop where the variable is mostly used, +or outside of a particular loop where the variable is not used. (The +latter is nice because it might let the variable be in a register most +of the time even though the loop needs all the registers.) + +It might not be very hard to do this in global-alloc.c when a variable +fails to get a hard register for its entire life span. + +The first step is to find a loop in which the variable is live, but +which is not the whole life span or nearly so. It's probably best to +use a loop in which the variable is heavily used. + +Then create a new pseudo-register to represent the variable in that loop. +Substitute this for the old pseudo-register there, and insert move insns +to copy between the two at the loop entry and all exits. (When several +such moves are inserted at the same place, some new feature should be +added to say that none of those registers conflict merely because of +overlap between the new moves. And the reload pass should reorder them +so that a store precedes a load, for any given hard register.) + +After doing this for all the reasonable candidates, run global-alloc +over again. With luck, one of the two pseudo-registers will be fit +somewhere. It may even have a much higher priority due to its reduced +life span. + +There will be no room in general for the new pseudo-registers in +basic_block_live_at_start, so there will need to be a second such +matrix exclusively for the new ones. Various other vectors indexed by +register number will have to be made bigger, or there will have to be +secondary extender vectors just for global-alloc. + +A simple new feature could arrange that both pseudo-registers get the +same stack slot if they both fail to get hard registers. + +Other compilers split live ranges when they are not connected, or +try to split off pieces `at the edge'. I think splitting around loops +will provide more speedup. + +Creating a fake binding block and a new like-named variable with +shorter life span and different address might succeed in describing +this technique for the debugger. * Detect dead stores into memory? A store into memory is dead if it is followed by another store into -the same location; and, in between, there is no reference to -that anything that might be that location (including no reference -to a variable address). +the same location; and, in between, there is no reference to anything +that might be that location (including no reference to a variable +address). * Loop optimization. @@ -59,7 +142,7 @@ Strength reduction and iteration variabl smarter. They should know how to decide which iteration variables are not worth making explicit because they can be computed as part of an address calculation. Based on this information, they should decide -when it is desirable to eliminate one iteration iable and create +when it is desirable to eliminate one iteration variable and create another in its place. It should be possible to compute what the value of an iteration @@ -133,30 +216,6 @@ use each alternative. Some work has been done on this, in combine.c. -* Jump-execute-next. - -Many recent machines have jumps which optionally execute the following -instruction before the instruction jumped to, either conditionally or -unconditionally. To take advantage of this capability requires a new -compiler pass that would reorder instructions when possible. After -reload may be a good place for it. - -On some machines, the result of a load from memory is not available -until after the following instruction. The easiest way to support -these machines is to output each RTL load instruction as two assembler -instructions, the second being a no-op. Putting useful instructions -after the load instructions may be a similar task to putting them -after jump instructions. - -* Pipeline scheduling. - -On many machines, code gets faster if instructions are reordered -so that pipelines are kept full. Doing the best possible job of this -requires knowing which functional units each kind of instruction executes -in and how long the functional unit stays busy with it. Then the -goal is to reorder the instructions to keep many functional units -busy but never feed them so fast they must wait. - * Can optimize by changing if (x) y; else z; into z; if (x) y; if z and x do not interfere and z has no effects not undone by y. This is desirable if z is faster than jumping. @@ -198,7 +257,42 @@ desirable. Pascal, Modula-2 and Ada require the implementation of functions within functions. Some of the mechanisms for this already exist. -4. Generalize the machine model. +4. More extensions. + +* Label-addresses as expressions. + +It would be nice to have access to the addresses of labels; to be able to +store them in variables, or initialize vectors of them. + +Alas, `&label0' is the address of the variable named label0, which is +unrelated to the label with that name. Some other syntax is needed. +Perhaps colon as a unary operator? That is ambiguous with `?:' with +the middle operand omitted. Perhaps ^ as a unary operator? Perhaps +`__label__ label0' could mean the value of label0? Its type could be +`void *'. `goto *EXP' could be used to go to a value of type `void +*'--no ambiguity there. + +Jump optimization and flow analysis must know about computed jumps, +but that is not hard. Each basic block headed by a possible target of +computed jumps must be considered a successor of each basic block +ending in a computed jump. Aside from this, I believe no other +optimizer changes are needed. + +Next question: stack levels. In most functions, there is no problem, +but it would be a shame to make a feature that doesn't work together +with other features. Here is an idea: + +For each label that might need stack level restoration, construct a +shadow-label which will restore the stack and jump to the user-label. +Then use the address of the shadow label for label0 when someone asks +for that of label0. Jump optimization will delete all the shadow labels +if the function has no computed gotos. + +* Generated unique labels. Have some way of generating distinct labels +for use in extended asm statements. I don't know what a good syntax would +be. + +5. Generalize the machine model. * Some new compiler features may be needed to do a good job on machines where static data needs to be addressed using base registers. @@ -207,124 +301,7 @@ where static data needs to be addressed for scalars and another for large objects. The compiler does not now have a way to understand this. -* Some machines use a caller-saves convention for saving registers over -function calls. Here is a design for how to handle such a convention. - -Classify all the hard registers as call-clobbered, then teach the -compiler how to put call-crossing pseudo-regs in call-clobbered hard -registers by generating save/restore insns around the call. Generate -the save/restores somewhere around the reload pass; this avoids -getting confused by cross jumping which happens after. Actually put -the save/restore insns into the RTL chain, so that cross-jumping and -peephole opt. will work on them. - -The time to generate these insns is when you can still tell what regs -are live at each call, but late enough to know exactly where the -pseudos have been allocated. Perhaps the best time is just before -reload_as_needed. - -This requires changes in local-alloc.c and global-alloc.c to be willing -to allocate call-crossing pseudos to call-clobbered registers. -That isn't hard if you do it with a special flag macro that simply says to -ignore (in those passes) whether a register is call-clobbered. -This macro would be defined if the target machine uses caller-saves. - -To get the best output, it is necessary to count the cost (in -additional save/restores) for each pseudo register of putting it in a -call-clobbered hard reg. This means counting *how many times* each -pseudo crosses a call. Depending on that value, it might be better to -put the pseudo in memory than in a call-clobbered register. Cost -counting would be done in flow.c, perhaps, and costs would affect -allocation decisions in local-alloc and global-alloc. Perhaps each -pseudo should have one priority for getting a call-saved reg and -another, lower (in general) priority for a call-clobbered reg. - -Then there would no longer be a need for the flag: the changes would -improve the output, on all machines. - -5. Precompilation of header files. - -In the future, many programs will use thousands of lines of header files. -Compiling the headers might be slower than compiling the guts of any one -source file. Here is a scheme for precompiling header files to make -compilation faster for a sequence of headers which is often used. - -A precompiled header corresponds to a sequence of header files. The -preprocessor recognizes when the input starts with a sequence of -`#include' commands and searches a data base for a precompiled header -corresponding to that sequence. The modtimes of all these files are -stored in the data base so that one can tell whether the precompiled -header is still valid. - -For robustness, each directory should have its own collection of -precompiled headers and its own data base of them. Probably each -precompiled header would be a file and the data base would be one -more file. - -The data base records the entire collection of predefined macros and -their definitions, except for __FILE__, __LINE__ and __DATE__, for -each precompiled header. If this collection does not match the setup -at the start of the current compilation (including the results of -D -and -U switches), the precompiled header is inapplicable. It might -be possible to have distinct precompiled headers for the same sequence -of header files but different collections of predefined macros. - -The state of any option that affects macro processing, such as -ansi -or -traditional, must also be recorded, and the precompiled header is -valid only if these options match. - -The precompiled header contains an ordered series of strings. Some -strings are marked "unconditional"; these must be compiled each time -the precompiled header is used. Other strings are have keys, which -are identifiers. A string with keys must be compiled if at least one -of its keys is mentioned in the input. The order these strings appear -in the precompiled header is called their intrinsic order. - -The C preprocessor reads in the precompiled header file and scan all -the strings, making for each key an entry in the same symbol table -used for macros, pointing at a list of all the strings for which it is -a key. Each string must have a flag (one flag per string, not one per -key per string). The same code in `rescan' that detects references to -macros would detect a reference to a key and flag all of the strings -that it belongs to as needing to be output. - -Each of these strings is immediately recursively macroexpanded (i.e. -`rescan' is called), but the output from this is discarded. The -expansion is to detect any other keys mentioned in the string, and to -define any macros for which the string contains a #define. The key's -symbol table entry is be deleted to save time if the key is -encountered again, and to avoid an infinite recursion. - -The unconditional strings are macroexpanded with `rescan' (but the -output is discarded) at some time before anything is actually output. - -At the end of compilation, before any of the actual input text is -output, the list of strings is scanned in the intrinsic order, and -each string that is unconditional or flagged is output verbatim, -except that any #define lines are discarded. - -Precompiled headers would be constructed by explicit request with a -special tool. The first step is to run cpp on the sequence of header -files' contents. This would use a new option that would cause all -#define lines to be output unchanged as well as defining the macro. -The second step is to divide the output into strings, some keyed and -some unconditional. This division is done without changing the order -of the text being divided up. - -6. Other possibly nice features. - -* cpp could have a #provide directive. -#provide would have the same syntax as #include, -and it would nullify any future #include directive -with the same argument. Thus, the file foo.h -could contain #provide to prevent itself from -being included twice. - -This is much cleaner than the alternative sometimes implemented, -which is to require the user to use something other than #include -in order to ensure inclusion only once. - -7. Better documentation of how GCC works and how to port it. +6. Better documentation of how GCC works and how to port it. Here is an outline proposed by Allan Adler. @@ -363,7 +340,7 @@ VI. Specification of what constitutes A. The components of GCC B. The itinerary of a C program through GCC C. A system of benchmark programs - D. What your RTL and assembler look should like with these benchmarks + D. What your RTL and assembler should look like with these benchmarks E. Fine tuning for speed and size of compiled code VII. A systematic procedure for debugging an implementation of GCC A. Use of GDB