On 07/29/2013 11:25 AM, Brendan Eich wrote:

OTOH, inlining is not required. Lars Thomas Hansen years ago talked about optimizing Scheme implementations (Larceny? Not sure), where the callee looks at its continuation and avoids the allocation collaboratively -- some kind of call PIC deal without the inlining being mandatory. Jim Blandy may know more ;-).

Scheme likes to try to treat continuations just like other functions, so that code like (+ (f x) y) can be treated as nice notation for a call to f that passes the implicit function (lambda (result) (+ result y)) as an invisible argument. (Ignore the question of who receives the result from +.) And that's what call-with-current-continuation does: it hands you that implicit function, which C would never let you touch.

(It's funny how the same person may praise C for being so concrete, granting programmers so much control, and exposing its implementation details; but then vociferously defend treating the call stack as this completely opaque, untouchable thing, so as to enable optimizations! At least Scheme lets you do stuff with your stack.)

The "problem" with this is that, while one can write ordinary functions that take multiple arguments - (lambda (a b) ...) - there's no way to write code whose implicit continuation functions take multiple arguments. So they added call-with-values:

(call-with-values (lambda () ...) (lambda (a b) ...))

which calls its first argument (the no-argument lambda), and applies its second argument to the *values* returned the first argument. Then, you can write functions that return multiple values using 'values', which is just:

(define values (lambda vs (call-with-current-continuation (lambda (k) (apply k vs)))))

So, the expression (values 1 2 3) returns the values 1, 2, and 3, and requires a continuation expecting three values.

The nifty thing here is that, if your compiler's internal representation rewrites your code to make those implicit continuation functions explicit - which apparently is a viable plan - then the code your compiler already has that figures out how to pass arguments in registers will work just as well for returning multiple values in registers. So returning multiple values need not require any allocation at all.


The Haskell people also talked about optimizing the case where you have a function that's walking a list with pattern-matching, and the list is being produced (lazily) by some other function: since the consumer is the one forcing the computation of successive elements in the list, the pairs the producer allocates are being immediately torn apart by the consumer's pattern-match statement - so there's really no point in even allocating the list at all, if you can show that nobody else is holding on to it: just pass the head and tail directly to the consumer, in registers.

I thought that was completely awesome... but I think Bryan O'Sullivan told me that the optimization can't actually be applied very often, in practice. *sigh*

_______________________________________________
dev-tech-js-engine-internals mailing list
[email protected]
https://lists.mozilla.org/listinfo/dev-tech-js-engine-internals

Reply via email to