freenode/#sicl - IRC Chatlog
Search
4:37:00
beach
Yesterday, I again worked on code generation. I made progress on how to handle the CATCH-INSTRUCTION, but doing so required some reorganization of the different systems, so it is taking some time.
4:37:59
beach
The idea is to handle the CATCH-INSTRUCTION as a FUNCALL-INSTRUCTION, but not translate it. I need to keep the arbitrary number of successors until the very end, or at least that's the plan.
4:39:19
beach
But I will preprocess the CATCH-INSTRUCTION just as I do with the FUNCALL-INSTRUCTION, i.e. make it have no outputs, and instead have its values in the distinguished global-values location.
4:40:00
beach
I can do this in HIR which makes it possible to test the entire thing in the HIR interpreter, which is a good thing.
4:40:17
beach
But I then have to import another function into the environments during bootstrapping.
4:41:49
beach
When I am done with the CATCH-INSTRUCTION, I will do the same with the UNWIND-INSTRUCTION and the BIND-INSTRUCTION, i.e. I will either treat them as FUNCALL-INSTRUCTIONs, or translate them to one.
5:14:15
alandipert
got local jump optimization done! the performance gain is incredible - 60 seconds to 23 milliseconds
5:28:16
beach
I must make sure that a CATCH-INSTRUCTION that does not have a corresponding UNWIND-INSTRUCTION does not call the function that augments the dynamic environment, and here is why:
5:28:17
beach
Every DEFUN results in a BLOCK, including the function that augments the dynamic environment with BLOCK/TAGBODY-ENTRY. That means that there is an infinite recursion in that augmentation function.
5:35:15
beach
I ought to be able to eliminate CATCH-INSTRUCTION without any corresponding UNWIND-INSTRUCTION. The "continuation" output of such a CATCH-INSTRUCTION is not used, so all I need to handle is the dynamic environment output.
5:36:15
beach
I could replace such a CATCH-INSTRUCTION by an ASSIGNMENT-INSTRUCTION that just assigns to the dynamic-environment output.
5:37:53
beach
The register allocator will recognize that they are not used simultaneously and assign them to the same place.
5:54:55
Bike
https://github.com/robert-strandh/SICL/blob/master/Code/Cleavir/HIR-transformations/eliminate-catches.lisp
6:11:46
beach
Now, I have to be careful. That transformation will work now, because the dynamic environment is not managed by side effects. But the day I want to use side effects, I must change the way I determine when to pop the environment.
6:11:47
beach
Currently, the idea is to keep a stack of lexical variables holding dynamic environments, and when an instruction has a successor that is not in the same dynamic environment, I would emit a certain number of POPs. I must make sure that no POP is emitted when the top environment variable was assigned to.
6:14:30
beach
So I guess an entry in the stack would not be a single lexical variable, but a list of lexical variables. When a dynamic-environment variable is merely assigned to, I would then just add the output variable to the topmost entry.
6:21:32
beach
Bike: Is this where Clasp just doesn't care, and does not free the entries when they are unused?
6:22:29
Bike
clasp does not have dynamic environments or entries. the later phases of translation ignore the dynamic-environment hir data.
6:24:37
Bike
i don't know what you mean by the scare quotes... clasp uses call-with-variable-bound right now.
6:26:42
Bike
well we don't, right now. i haven't incorporated cleavir2 into clasp since you're still working on it.
6:29:42
Bike
to sum up, every function in the runtime, like the actual machine code, has an exception table nearby. the unwinder function consults the tables for each frame. the table has a map of instruction pointers to "landing pads", which are other instruction pointers in the same function. The unwinder looks up the landing pad for the return address associated with that frame, finds the landing pad, and jumps to it.
6:31:10
beach
So does that imply that Clasp must have a function invocation for each "entry" in the dynamic environment?
6:32:34
Bike
we additionally attach an integer code to each possible return point. like, if a function has two (block ...)s in it, one will be 0 and one will be 1, and the associated return-froms get that ID from the code generator and pass it to the unwinder.
6:33:26
Bike
it's the C++ "zero cost" system, except we've progressively warped it away from C++ since C++ has several aspects that are inconvenient
6:33:59
Bike
"zero cost" in the sense that entering a block is supposed to not incur any runtime cost
6:35:29
beach
Presumably you then give up some things, like checking whether an exit point has been "abandoned"?
6:36:25
Bike
yeah, I don't think we do that right now... honestly i'm still not sure I understand those rules entirely
6:37:22
beach
In fact, your current system may make it hard to implement UNWIND-PROTECT in the first place.
6:38:41
Bike
i have been thinking about it. the landing pad code is allowed to resume unwinding rather than just transferring control, which i think should be enough
6:42:20
Bike
mm, i think you're right that abandonment only matters with unwind-protect, since that's the only time you might initiate an unwind during another unwind
6:43:38
Bike
if i'm reading CLHS 5.2 right, if you try to exit to an abandoned exit point, it's undefined behavior rather than an error
6:44:07
Bike
so i don't think leaving it to unwind to the abandoned exit point anyway is too terrible
6:46:48
Bike
we identify which frame to return to using the frame base pointer (probably need an additional identifier like sicl will have too, but that's unrelated), so we can just check if the point the cleanup is trying to unwind to is deeper in the stack or not
7:07:06
beach
So I think you get away with not having an explicit dynamic environment because 1. You use call-with-variable-bound, and 2. You don't deallocate saved multiple values when they go out of scope.
7:09:37
Bike
i don't think clasp needs it to bind dynamic variables. We could just transform (let ((*foo* bar)) ...) into (let ((#:old (tl-symbol-value '*foo*))) (unwind-protect (progn (setf (tl-symbol-value '*foo*) bar) ...) (setf (tl-symbol-value '*foo*) #:old)))
7:17:01
Bike
which as you pointed out before means that a loop repeatedly reentering an m-v-prog1 might extend the frame indefinitely, though i'm not totally sure how the LLVM semantics work out there
9:32:43
heisig
beach: Thanks! There are still some issues that I plan to address today. Like the TODO entries in the examples section.
9:33:51
heisig
I am glad that ELS exists. Writing the paper made me improve many aspects of that library.
9:34:52
beach
I know what you mean. But ELS does take a lot of time. I am counting a few months per year.
9:56:47
beach
Anyway, lunch guests will arrive in the next 15 minutes or so. Then I'll be busy for several hours.
13:08:42
scymtym
yesterday i thought of a funny way of speeding up the "applicable methods must not change check" in CALL-NEXT-METHOD: https://github.com/scymtym/sbcl/blob/6ed3332f053b555e2f605b64456989d14b72f1bf/src/pcl/cnm.lisp#L26 . i thought people here might be interested. does this matter in practice? yes: https://techfak.de/~jmoringe/clim.flamegraph-medium-draw-rectangle-star.png