Re: RFC: Speeding up small ISRs: PR20296
Georg-Johann Lay <[email protected]> Sat, 17 Jun 2017 15:41:01 +0200
| Newsgroups | gmane.comp.hardware.avr.gcc |
|---|---|
| Message-ID | <[email protected]> |
Erik Christiansen schrieb: > Reply-To: [email protected] > > On 15.06.17 14:43, Georg-Johann Lay wrote: >> https://gcc.gnu.org/PR20296 >> >> is about speeding up "small" ISRs, and is open for 12 years now... >> >> Anyone familiar with avr-gcc knows that a fix would be high effort and risk, >> and that's the major reason for why PR20296 is still open (and even >> classified "suspended"). >> >> In some forum discussion (again!) on that issue, there was the following >> proposal to approach that PR: > > Reading the PR causes me to infer that moving that code generation out > of gcc into gas is the proposed fix for unmanageable optimisation > complexity in gcc in the use case. Allow me not to go into GCC details how... >> 1) Let GCC emit directives / pseudo-instructions in non-naked ISR prologue / >> epilogue > > If only existing directives and macro invocations are emitted, then the > need to modify gas code is obviated. I.e. if: > > .maybe_isr_prologue 123 ; were instead: > maybe_isr_prologue 123 What I meant is a pseudo instruction. Something that's handled like an instruction but not available in hardware, indicated by a leading ".". If you don't like that "." just drop it, it's just sugar. > then a gas macro could generate the required code without unnecessary > complexity. If the desired code to be generated from the parameters Can you make this explicit? How can a macro know whether the following code, hundreds of insns, clobbers tmp_reg or SREG? Macro expansion runs prior to instruction parse. > supplied can be described, then I'll write the macro(s). After some > iterations, we should have some good results for some useful use cases. Maybe I don't get your point. Can you provide such a macro? >> 2) Let GAS scan the code and replace the directives with code as needed. >> Currently, >> >> #include <avr/io.h> >> #include <avr/interrupt.h> >> >> ISR (INT0_vect) >> { >> __asm ("; Code"); >> } >> >> emit something like: >> >> >> __vector_1: >> push r1 >> push r0 >> in r0,__SREG__ >> push r0 >> clr __zero_reg__ >> .L__stack_usage = 3 >> >> ; Code >> >> pop r0 >> out __SREG__,r0 >> pop r0 >> pop r1 >> reti >> >> >> which would change to: >> >> >> __vector_1: >> .maybe_isr_prologue 123 >> ;; Rest of prologue >> >> ; Code >> >> ;; Rest of epilogue >> .maybe_isr_epilogue 123 >> reti >> >> GAS would then scan the code associated to the function and replace the >> .maybe by appropriate sequence to safe / init / restore tmp-reg, zero-reg >> and SREG. > > That sets things up handily for finishing by simple macro. Let us say > that gcc emits "_maybe_isr_prologue 1 2 3", then 1 could be the switch > for save, 2 for init, and 3 for restore, if desired. Gas macros readily > handle omission of the last parameter (with it then taking an internally > defined default value), which can be useful if gas knows the default, > and gcc doesn't. Lumping it all into a single parameter would lead to 8 > parameter values, just to cover 3 binary switches, IIUC the use case. The "123" was just intended as tag (in the case that's helpful for GAS). To be more precise, consider ;; Start of ISR1 __vector_1: .maybe_isr_prologue 123 ;; Rest of prologue ; CodeA ;; Rest of epilogue .maybe_isr_epilogue 123 reti ; CodeB ;; Rest of epilogue .maybe_isr_epilogue 123 reti ; CodeC ;; End of ISR1 If CodeA+CodeB+CodeC clobber SREG, Rzero, Rtmp then .maybe_isr_prologue shall expand to push r1 push r0 in r0,__SREG__ push r0 clr __zero_reg__ If CodeA+CodeB+CodeC clobber SREG, Rzero but not Rtmp then .maybe_isr_prologue shall expand to push r1 in r1,__SREG__ push r1 clr __zero_reg__ If CodeA+CodeB+CodeC clobber SREG but neither Rzero nor Rtmp then .maybe_isr_prologue shall expand to (this might be optimized further) push r0 in r0,__SREG__ push r0 If CodeA+CodeB+CodeC clobber Rtmp but neither Rzero nor SREG then .maybe_isr_prologue shall expand to push r0 If CodeA+CodeB+CodeC clobber neither Rtmp nor Rzero nor SREG then .maybe_isr_prologue shall expand to ;; Empty etc. and with epilogues (2 in this case) accordingly. >> Other registers like R24 are handled by GCC as usual. For example, if >> the scan reveals that tmp-reg is not needed but zero-reg is (which >> will imply SREG due to the CLR) the replacement code would be: > >> __vector_1: >> push r1 >> in r1,__SREG__ >> push r1 >> clr __zero_reg__ >> >> ; Code >> >> pop r1 >> out __SREG__,r1 >> pop r1 >> reti > > An epilogue macro can be made to know whether its matching prologue > saved tmp-reg, even if that is stretching assembler macros slightly. > That would not require any additional code scan. So long as nesting ISRs Can you give an example? In particular, how the prologue macro gains knowledge about whether tmp-reg needs to be handled or not, i.e. how it draws that information from "Code"? > is illegal, then it would not even clutter the gas symbol table > perceptibly. ISR nesting is no issue. ISR nesting occurs dynamically, not during statically. >> Maybe someone is interested in implementing the GAS part, and if that is the >> case and the proposal is feasible, I would take care of the GCC part. > > I propose that we minimise toolchain modification by choosing an elegant > implementation, based on existing gas capabilities, if feasible. Thus > far, I have not seen any proposed code generation which ought not be > achievable that way. I've seen such examples, e.g. working around specific kind of silicon bugs might be considerably less effort in GAS compared to GCC. >> Caveats: >> >> a) .L__stack_usage can no more be computed by GCC > > It is no effort for gas to implement lines like: .L__stack_usage = 3 > As we know exactly how many bytes we are adding to the stack frame, we > can effortlessly dimension and emit that line - and yes, it is gas which > converts that 'L' into a unique integer. Whatever code later uses all the > .nnnn__stack_usage sizes should continue to work as before. > >> b) It's hard to find the end of the relevant code. We might have >> interleaved sections (like with dispatch tables), there might be code that >> is emit after the epilogue, there might be more than 1 epilogue, > > If gcc doesn't know what it is doing, then gas can't fix that part. ;-) GAS can fix it in principle, and it would be orders of magnitude less work than in GCC. That's the point of the proposal. >> dunno if GAS can infer whether JMP is local or non-local. > > If it's one of its recognised local labels. Macro-local symbols may be > defined by a "LOCAL" directive, and local scope symbols by use of '@' or > concatenating a parameter suffix to part-label. There are also the I am not talking about assembly input. I am talking about GAS internals. > numbered local labels. It knows and uses local symbols beginning with > 'L', and omits them from the symbol table, IIRC. > > If the JMP destination is external, then ld will handle the linking. > That's well outside the remit of gas. If any relaxation is hoped for, > then that will be also provided by ld, if available. GAS might also relax, and any pseudo-instruction expansion must not occur after GAS relaxing. IIUC any GAS relaxing is forwarded to LD since --mlink-relax, hence that option would have to be forced to be always on provided pseudo expansion would have to be run after GAS relaxing. >> The .maybe gets function-unique identifiers (123 in the example) so that GAS >> knows which epilogue belongs to which .prologue provided that's helpful. > > That greatly simplifies the work to be done in gas. Now linking prologue > with epilogue is effortless. But as an ISR will be contained in one > compile unit, and no other ISR will sanely be nested, is that required? As said, ISR nesting doesn't occur statically. >> I am not familiar with Binutils / GAS though and don't know if it's easy to >> add the 2 new passes: One to scan and one to replace the .maybe with >> appropriate code. > > Neither appear necessary. Gas makes the substitutions as is. hä? Would you make that explicit? Like a macro that catches all 5 situations I outlined above, for any CodeA+CodeB+CodeC ? > >> IIUC GAS only works on sections, > > Sorry, no, it only works on compile units, i.e a source file and its > includes. It knows little else. Its relationship to sections is that > understands .section directives to the extent that it will put code into > whichever section is named in one. It even has a section stack, so that > it can pop back to the prior section after an excursion into another. Ya, I know. My questions however are not re. the GAS front, but re. internal working and representation, passes etc. >> and the scan would be on BFD internal representation (like relaxing) >> after the parser read in the asm sources? > > Gas takes assembler source code text as input, and generates an ELF > relocatable object file. It is only ld which can perform relaxation. That's GAS as a black box. Internally, they are using BFD, no? (Except macro expansion and parse input which are strings of course). >> The GCC change would add a new option, configure test whether GAS supports >> this, let ISR prologue and epilogue emit new unspec_volatile pseudo insns > > Hmmm, "unspec_volatile" doesn't appear in the described use case. If it unspec_volatile is a GCC internal device. You'll never see it except with internal dumps like with -fdump-rtl-all. >> and add a scan pass to detect situations that are pointless should fall back >> to old code, like when dispatch tables, calls or non-local goto is seen. > > The examples provided are within existing gas capability. Let's go with Okay. I missed that. I just don't see how to do it. Johann