It sounds like the suggestion is to implement more or less the "normal" C interrupt model when user tags a function as interrupt: save the (very small number of ) caller-saved registers, and store everything in the interrupt handler on the stack. It'd still be desirable to have the main program (i.e., the part not reachable from the interrupt handler) still use static stacks and other optimizations. If that's incorrect, let me know
That's essentially what the "interrupt" attribute does. First, it makes all functions use less zero page so an interrupt handler can be timely. Second, it does a call graph analysis, just to figure out which parts of the program are interrupt handler and which parts aren't.
If it didn't make functions use less zero page, then the ISR save sequence would push 256 bytes of zero page to the hard stack and crash. Or I'd have to rewrite it so it pushed 256 bytes of zero page to the soft stack. The entire zero page could plausibly contain live values at the time of the interrupt, and any C function that the interrupt handler calls could plausibly overwrite all of it. We could just carte-blanche limit the amount of zero page that the compiler's allowed to use, whether or not there are interrupts. Or we could reserve dedicated regions of zero page for interrupt routines; that's already in the works.
EDIT: I missed a few posts above; BigEd made a suggestion to have only the interrupt handlers use less zero page. This would work, and I was originally going to do this, but a subtle problem sprung up. We use zero page registers to pass and return arguments, so doing this would amount to having a different calling convention for functions inside and outside interrupt handlers. Trouble is, you can stick them both inside a function pointer, pass them around arbitrarily, and it becomes impossible to tell what calling convention you should use to call the pointee. Without a perfect pointer analysis or user hand-annotation, there doesn't seem to be any way to support two silently-different calling conventions in C. Hence, if there's a shift in calling convention due to the presence of interrupts, it needs to be program-wide.
EDIT2: Of course, where the identity of callees are statically known, the calling convention could differ wildly. I'm planning on taking advantage of this later...
If it didn't do the call graph analysis, we'd either have to require the user to tag each individual function as part of an interrupt handler or not, or we'd have to globally disable static stacks, even for the main part of the program. Otherwise there's no way to know which parts of the program can safely use static stacks, and which part can't.
MichaelM wrote:
(1) what is being handled in the function, (2) what is the maximum rate at which the function can be vectored to, (3) what happens if a second event queues before the function completes, etc.
The only information our optimizations need at present is a conservative estimate of which function can be active at the same time. This is because we can do the static stack optimization if and only if at most one invocation of the same function can ever be active.
The only affect (2) and (3) have on that question is whether an interrupt handler can be called while another invocation of that same interrupt handler is active. (We assume that different interrupt handlers can interrupt each other.) We allow users to make that determination based on their knowledge of their interrupt handlers: if that can't happen (which is expected to be the usual case), they can annotate it with "interrupt_norecurse." If they can't or don't want to verify this property, they can say "interrupt", which is always safe, but might be slower.