freenode/#clasp - IRC Chatlog
Search
19:43:20
Bike
i can load cleavir in bclasp, and functions compiled with it seem to work, even if they do weird things like have &optional and &key that i didn't think about before
19:53:17
karlosz
interesting, the commit that upgrades to llvm6 builds successfully and gets past linking on arch
20:35:13
karlosz
Bike: do you have a more full backtrace for the unitialized data problem you ran into with the closure conversion fix? i couldnt succeed getting the latest clasp to build
20:36:02
Bike
no. i can get you one if you want it. a backtrace might nto be sufficient since it's (probably) a miscompile, though.
20:42:55
karlosz
okay, sounds good. i also have partial inlining and closure conversion as the first two steps in my passes, so it should help narrow down where the possible problems could be from, considering it builds for me
20:50:48
karlosz
im looking through the clasp source a bit; its something that check-for-uninitialized-inputs-dumb doesnt catch?
20:52:37
karlosz
thats interesting, because i didnt tthink those could be iintroduced given that inlining is the only thing that happens beforehand
20:53:17
karlosz
if thats the case it shouldnt be too hard to fix, but i want a test case where the graph shows it
20:53:43
karlosz
sadly, that would also mean fixing closure conversion by adding ast nodes wont work
21:14:32
karlosz
oh hey. a little unrelated but partial inlining does something wonky and wrong with this (lambda (n)
21:20:43
karlosz
yeah. i dont think it should be too hard to fix. we just have to check the place we are unwinding to from the funcall is still inside the catch arm and redirect it to a call to error or something
21:22:54
karlosz
the flow structure in HIR doesnt actually match the flow graph of execution for non local exits
21:44:06
Bike
my reasoning was that the flow graph for nonlocal exits can't actually be statically determined- you can have unwind-protects intervening.
22:04:38
karlosz
hm. it seems like its really the funcalls in the catch arm that have two different continuations, rather than cthe catch instruction
22:18:36
karlosz
the last sentence is a bit presuptuous, i don't actually know if there is a situation that an instruction could be followed by some other instructions not explicitly listed in that description
22:19:32
karlosz
by that i mean like how the current catch instructions are clearly succeeded by instructions that don't actually succeed it in execution
22:20:36
karlosz
it could be the case that F always takes its normal control flow edge - that's okay, as long as we don't have any instruction executed after F that isnt a successor to it
22:53:57
karlosz
here is a concrete way in which it is an improvement: in the old model, the first funcall in the catch arm would not dominate the instruction it unwinds to
22:54:07
karlosz
in this model, if it dominates the instruction, it indeed dominates the instruction
23:16:18
karlosz
after thinking it over again, i don't think this will work. things like (funcall (funcall (block nil (lambda (x) (lambda () (return))))) would be too hard to analyze
23:16:30
karlosz
there is probably some better way to not make inlining make that ub case into a loop
23:22:57
stassats
you can do the equivalent of (let (exit) (block nil (prog1 (lambda () (when exit (error "out of scope")) (setf exit t) (return)) (setf exit t))))
0:12:39
karlosz
i think the instances where functions capturing continuations can get inlined at call site are also places where we have enough information to hot wire what would be the return into into an error
1:00:37
karlosz
so we probably need a new hir instruction that denotes a return to an out of scope tag
1:01:03
karlosz
just so that inlining can wire itself to some sort of error when it meets an illegal return
4:00:09
beach
karlosz: Wouldn't the escape analysis prevent inlining in those cases so that the infinite loop is avoided?
4:06:51
karlosz
beach: how so? the inliner inlines something like (block nil (funcall () (return nil)) fine
4:11:32
beach
In those cases, the environment of the LAMBDA can not be allocated in the parent, which is one of the criteria for inlining.
4:12:26
Bike
yeah, i thought the thing ruled that out now that the continuation is explicitly in a variable
4:13:33
beach
If the environment of the callee can not be allocated in the caller, then the callee can not be inlined.
4:14:43
beach
Conservatively, then, when the environment of the callee is captured and escapes in some way that we can not determine to be safe, then we must conclude that the environment of the callee can not be allocated in the caller.
4:15:04
Bike
karlosz: i mean that's fine, but the inliner is too conservative to notice, or ought to be
4:16:29
Bike
karlosz: functions that return like that are closures (this is another reason i added catch-instruction)
4:17:34
beach
karlosz: Sure, in this case it could be inlined. But we need to come up with criteria that work for every possible case. And if we need to be a bit conservative so that this particular case is ruled out, then that's just too bad.
4:17:52
Bike
i'm saying that i expected the inlining criteria that currently exist to rule out inlining that example even though it is obviously possible
4:18:55
karlosz
the trapper criterion that is already there doesnt care about the the function being inlined escaping, but only about subfunctions capturing its environment escaping
4:19:13
karlosz
so it amkes sense that with nly one level of nesting the trapper criterion doesnt even come into play
4:20:04
beach
Also, in this case, at some point we should include the knowledge that FUNCALL does not capture its argument in some arbitrary way.
4:20:45
Bike
it does inline closures, just as long as the closure doesn't let the environment escape
4:21:10
karlosz
yeah, otherwise it wouldnt even make sense to have a method on inline, which there clearly is
4:26:54
karlosz
it wasn't very hard to add on to the inline method on unwind and make (funcall (block nil (lambda () (if n (return nil)))) inline correctly with a branch to an illegal unwind instruction
4:28:08
karlosz
and if it doesn't introduce an illegal unwind instruction instead of the nop instruction used for the valid case
4:32:22
beach
The criterion about the environment for inlining goes like this: The environment of the callee can be allocated in the caller only if 1) The environment of the callee does not survive the invocation of the caller, and 2) There is only ever a single active invocation of the callee for each invocation of the caller.
4:34:46
beach
But we need to avoid too many special cases, in particular if they handle code that is rarely seen.
4:35:17
Bike
other than the complicated code that's supposed to keep unwinds only if needed, i guess
4:36:34
Bike
if my code works, once the function in (block nil ((lambda () (return)))) is inlined, the unwind instruction will be removed and it will be just a regular control arc
4:37:47
Bike
but for example in (block nil (foo (lambda () ((lambda () (return))))) the inner function can be inlined but there's still an unwind.
4:41:39
karlosz
yes, i think the only thing that warrants changing is the UB example. it would be nice to not infinite loop and do the right thing
4:45:28
Bike
(loop with x do (block nil (if (null x) (setf x (lambda () (return))) (funcall x)))) which is extremely artificial
4:46:27
Bike
it won't cause any memory faults or weird shit like that which is why i'm not super concerned, but it's still unfortunate
4:49:25
karlosz
Bike: i just tried it with cleavir+clisp and it gave me an out of extent return error fine
4:49:51
karlosz
but this is with the closure conversion fix so maybe that has something to do with it
4:51:02
Bike
the idea is that it hits the catch instruction, sets x, then exits the block scope to the top of the loop, then hits the catch instruction which again sets the continuation to something valid
5:04:05
karlosz
well here's one issue: i think its written somewhere that the output of enclose is never assigned to by anthing else
5:13:43
karlosz
there should be a sentence like the output of an enclose instruction is never assigned to by construction
5:15:51
karlosz
::notify Bike i see the problem in your example. yes, if you always insert cells at the top of the procedure, you are going to run into the issue. if you insert the cell inside the loop using the closure fix i wrote with dominators it works correctly
5:31:43
karlosz
::notify Bike: putting create cells at the top of the procedure essentially means that the lambdas all share the same block tag cell which keeps getting updated as the loop goes on, exactly the same problem as the (loop i ->5 collect (let ((x i)) (lambda () x)))) example