Re: guile 3 update, halloween edition

2019-11-17 Thread Andy Wingo
Hi :)

On Sat 16 Nov 2019 16:26, Ludovic Courtès  writes:

> Andy Wingo  skribis:
>
>> On Fri 15 Nov 2019 10:03, Ludovic Courtès  writes:
>>
>>> I guess we could add a specific ‘’ exception or similar,
>>> which would allow us to improve error reporting (that can come later, of
>>> course.)
>
> What I meant is that type errors are “special” enough to deserve their
> own type more specific than the catch-all ‘’ (just
> like there’s already a separate ‘’, for instance.)

Agreed!

> Speaking of which, it seems that ‘set-guile-exception-converter!’ is
> currently private, but I wonder if the goal was to make it public (it
> seems to be unused)?

It was private also in the exception conversion work that Mark did,
FWIW; it just moved over as-is.

Honestly I think that now that exceptions are "primary" we should
probably move in the opposite direction: instead of adding more
converters from key+args to exception objects, we should encourage
exception throwers to switch from "throw" to "raise-exception", and
allow library authors to define converters in the other way from
exception object to the equivalent arguments for "catch".  So I think
exposing set-guile-exception-converter! might be the wrong thing at this
point.  Dunno tho.

> For instance, C bindings that currently call ‘throw’ could provide
> additional “exception converters” for the benefit of Scheme users
> who’d rather use structured exceptions.  (That would also give less of
> an incentive to provide a C API for all of this.)

This is a good point!

FWIW Regarding C and migration, I have the impression that probably 90%
of exception throwers in C use the helpers from error.h
(scm_wrong_num_args and so on), which we can change transparently.  A
remaining 5% might use scm_error_scm, for which a registry might make
sense, and 5% use scm_throw directly.  These are just guesses tho.

>>> 4. Is ‘’ actually used?  Is the goal to make it continuable?
>>> That sounds great.
>>
>> Any exception can be raised in a continuable way.  Whether a raise is
>> continuable or not depends on the value of the #:continuable? keyword to
>> raise-exception.  I think that's the intention of  but I don't
>> really have instincts about how it might be used.  Guile defines it
>> because it's in R6RS, but how it will be used is an open question :)
>
> I suppose the intent is to effectively allow users to implement the UI
> stuff as a sort of co-routine to support separation of concerns: you
> just raise a ‘’ that some other code displays in its preferred
> way (console message, popup window, whatever) and eventually calls your
> continuation.
>
> That’s something I’ve been wanting for some time, because right now
> we’re able to separate out the UI concern for exception display, but not
> for warnings.
>
> However, it seems that the handler passed to ‘with-exception-handler’
> does not receive the continuation, so is it the case that currently
> handlers cannot resume exceptions?  (Again not a showstopper IMO but
> rather another wishlist item :-)).

The handler runs within the continuation of "raise-continuable":

  (with-exception-handler
   (lambda (exn) (+ exn 30))
   (lambda () (+ 2 (raise-exception 10 #:continuable? #t
  => 42

However I'm not sure this facility is what you want.  Like for example
there's lots of false-if-exception / catch #t out there; that's
equivalent to:

  (define-syntax-rule (false-if-exception expr)
(let/ec k
  (with-exception-handler
   (lambda (exn) (k #f))
   (lambda () expr

So the exception handler there would intervene and get a first crack at
the warning, messing up your intent.  To me warnings are like logging,
and logging is notoriously difficult to standardize :)  If it were me I
would make a mechanism for warnings that had a with-warning-handler and
I would make sure to raise all warnings via a separate raise-warning
procedure or something, independent of exceptions.  But that's just me
:)

Andy



Re: guile 3 update, halloween edition

2019-11-16 Thread Ludovic Courtès
Hi Andy!

Andy Wingo  skribis:

> On Fri 15 Nov 2019 10:03, Ludovic Courtès  writes:
>
>> 0. Do I get it right that ‘throw’ and ‘catch’ are not “deprecated” in
>> the sense of (ice-9 deprecated) and are instead simply not the
>> “preferred” exception mechanism?
>
> Correct.  I think we could envision deprecating them in some future but
> not within the next couple years at least.

Alright, sounds good.

>> I guess we could add a specific ‘’ exception or similar,
>> which would allow us to improve error reporting (that can come later, of
>> course.)

What I meant is that type errors are “special” enough to deserve their
own type more specific than the catch-all ‘’ (just
like there’s already a separate ‘’, for instance.)

> Yes.  So right now Guile is in a bit of a transitional state -- it still
> signals 99.9% of errors via `throw'.  Probably we want to change to have
> structured exceptions for almost all of these.  To preserve
> compatibility we would probably need to mix in an
>  to all of these exceptions, or otherwise
> augment `exception-kind' and `exception-args' to synthesize these values
> when appropriate.

Yes, I agree.

Speaking of which, it seems that ‘set-guile-exception-converter!’ is
currently private, but I wonder if the goal was to make it public (it
seems to be unused)?

If it were public, I imagine that users could take advantage of it to
support both exception styles, just like Guile does.  For instance, C
bindings that currently call ‘throw’ could provide additional “exception
converters” for the benefit of Scheme users who’d rather use structured
exceptions.  (That would also give less of an incentive to provide a C
API for all of this.)

WDYT?

>> Guix has ‘’ error conditions, which I’ve found useful when
>> combined with other error conditions in cases where location info from
>> the stack isn’t useful:
>>
>>   https://git.savannah.gnu.org/cgit/guix.git/tree/guix/utils.scm#n832
>>
>> I wonder if (ice-9 exceptions) should provide something like that.
>
> Neat :)  Yes sure.  I think, any exception type can be added to (ice-9
> exceptions) -- there's little "name cost" like there is in boot-9.
> Which reminds me, I want to make boot-9 do a (resolve-module '(ice-9
> exceptions)) so that the more capable make-exception-from-throw always
> gets installed.
>
>> 2. What are you thoughts regarding exposing structured exceptions to C?
>> I’ve always been frustrated by ‘system-error’ :-).  Guix has a hack to
>> augment ‘system-error’ with information about the offending file name:
>>
>>   https://git.savannah.gnu.org/cgit/guix.git/tree/guix/ui.scm#n520
>>
>> If the POSIX bindings would emit a structured ‘’ record,
>> that’d be pretty cool.
>
> I don't know :)  Right now raise-exception is marked SCM_INTERNAL.
> Probably it should be public.  There is no public C API for any of this
> new functionality, as it stands; a TODO.
>
> Regarding exception objects, there are two questions: one, how to create
> exceptions of specific kinds; I suspect scm_make_system_error () would
> be fine, and probably you want scm_make_exception to be able to mix
> various exceptions.  Second question, do we want to expose accessors
> too?  It can be a lot of API surface and I am a bit wary of it.  But,
> perhaps it is the right thing.  I do not know.

Yeah, it might be that having C stick to ‘throw’ + allowing for
user-defined exception converters would be enough.

>> 4. Is ‘’ actually used?  Is the goal to make it continuable?
>> That sounds great.
>
> Any exception can be raised in a continuable way.  Whether a raise is
> continuable or not depends on the value of the #:continuable? keyword to
> raise-exception.  I think that's the intention of  but I don't
> really have instincts about how it might be used.  Guile defines it
> because it's in R6RS, but how it will be used is an open question :)

I suppose the intent is to effectively allow users to implement the UI
stuff as a sort of co-routine to support separation of concerns: you
just raise a ‘’ that some other code displays in its preferred
way (console message, popup window, whatever) and eventually calls your
continuation.

That’s something I’ve been wanting for some time, because right now
we’re able to separate out the UI concern for exception display, but not
for warnings.

However, it seems that the handler passed to ‘with-exception-handler’
does not receive the continuation, so is it the case that currently
handlers cannot resume exceptions?  (Again not a showstopper IMO but
rather another wishlist item :-)).

Thank you!

Ludo’.



Re: guile 3 update, halloween edition

2019-11-15 Thread Andy Wingo
Hey thanks for the review :)

On Fri 15 Nov 2019 10:03, Ludovic Courtès  writes:

> 0. Do I get it right that ‘throw’ and ‘catch’ are not “deprecated” in
> the sense of (ice-9 deprecated) and are instead simply not the
> “preferred” exception mechanism?

Correct.  I think we could envision deprecating them in some future but
not within the next couple years at least.

> 1. I see things like:
>
> +(define (make-condition type . field+value)
> +  "Return a new condition of type TYPE with fields initialized as specified
> +by FIELD+VALUE, a sequence of field names (symbols) and values."
> +  (unless (exception-type? type)
> +(scm-error 'wrong-type-arg "make-condition" "Not a condition type: ~S"
> +   (list type) #f))
>
> and:
>
> +  (unless (symbol? key)
> +(throw 'wrong-type-arg "throw" "Wrong type argument in position ~a: 
> ~a"
> +   (list 1 key) (list key)))
>
> I guess we could add a specific ‘’ exception or similar,
> which would allow us to improve error reporting (that can come later, of
> course.)

Yes.  So right now Guile is in a bit of a transitional state -- it still
signals 99.9% of errors via `throw'.  Probably we want to change to have
structured exceptions for almost all of these.  To preserve
compatibility we would probably need to mix in an
 to all of these exceptions, or otherwise
augment `exception-kind' and `exception-args' to synthesize these values
when appropriate.

> Guix has ‘’ error conditions, which I’ve found useful when
> combined with other error conditions in cases where location info from
> the stack isn’t useful:
>
>   https://git.savannah.gnu.org/cgit/guix.git/tree/guix/utils.scm#n832
>
> I wonder if (ice-9 exceptions) should provide something like that.

Neat :)  Yes sure.  I think, any exception type can be added to (ice-9
exceptions) -- there's little "name cost" like there is in boot-9.
Which reminds me, I want to make boot-9 do a (resolve-module '(ice-9
exceptions)) so that the more capable make-exception-from-throw always
gets installed.

> 2. What are you thoughts regarding exposing structured exceptions to C?
> I’ve always been frustrated by ‘system-error’ :-).  Guix has a hack to
> augment ‘system-error’ with information about the offending file name:
>
>   https://git.savannah.gnu.org/cgit/guix.git/tree/guix/ui.scm#n520
>
> If the POSIX bindings would emit a structured ‘’ record,
> that’d be pretty cool.

I don't know :)  Right now raise-exception is marked SCM_INTERNAL.
Probably it should be public.  There is no public C API for any of this
new functionality, as it stands; a TODO.

Regarding exception objects, there are two questions: one, how to create
exceptions of specific kinds; I suspect scm_make_system_error () would
be fine, and probably you want scm_make_exception to be able to mix
various exceptions.  Second question, do we want to expose accessors
too?  It can be a lot of API surface and I am a bit wary of it.  But,
perhaps it is the right thing.  I do not know.

> 3. I wonder if we could take advantage of the new ‘’ exception
> to start i18n of error messages.  It might be as simple as telling
> xgettext to recognize ‘make-exception-with-message’ as a keyword, though
> currently there are few calls to ‘make-exception-with-message’ followed
> by a literal.

Eventually this will get called by `error', I think; Guile's R7RS layer
in wip-r7rs defines `error' as being:

  (define (error message . irritants)
(raise-exception
 (let ((exn (make-exception-with-message message)))
   (if (null? irritants)
   exn
   (make-exception exn
   (make-exception-with-irritants irritants))

But yes this is definitely something to think about it.

> 4. Is ‘’ actually used?  Is the goal to make it continuable?
> That sounds great.

Any exception can be raised in a continuable way.  Whether a raise is
continuable or not depends on the value of the #:continuable? keyword to
raise-exception.  I think that's the intention of  but I don't
really have instincts about how it might be used.  Guile defines it
because it's in R6RS, but how it will be used is an open question :)

> Bah, you give us a present and I reply with an additional wishlist.
> ;-)

:)  I hope that the exceptions work can serve as a foundation for
further incremental, compatible improvement.

Cheers,

Andy



Re: guile 3 update, halloween edition

2019-11-15 Thread Ludovic Courtès
Hello Andy & all!

Thanks for the great summary.

I’ve taken a look at ‘wip-exceptions’, which is also remarkably easy to
follow because all the changes are incremental and follow the path you
explained in your message; thanks a lot for making it this clear!

I’ve very much support this change, I always found the key+args
convention to be poor compared to structured error condition objects.

The changes in ‘wip-exceptions’ all make sense to me; some random
comments below.


0. Do I get it right that ‘throw’ and ‘catch’ are not “deprecated” in
the sense of (ice-9 deprecated) and are instead simply not the
“preferred” exception mechanism?

At least, that’s how I would view it :-), because ‘throw’ cannot (yet)
disappear from C code, and because it’s a migration that could take more
than two stable series to really complete.


1. I see things like:

+(define (make-condition type . field+value)
+  "Return a new condition of type TYPE with fields initialized as specified
+by FIELD+VALUE, a sequence of field names (symbols) and values."
+  (unless (exception-type? type)
+(scm-error 'wrong-type-arg "make-condition" "Not a condition type: ~S"
+   (list type) #f))

and:

+  (unless (symbol? key)
+(throw 'wrong-type-arg "throw" "Wrong type argument in position ~a: ~a"
+   (list 1 key) (list key)))

I guess we could add a specific ‘’ exception or similar,
which would allow us to improve error reporting (that can come later, of
course.)

Guix has ‘’ error conditions, which I’ve found useful when
combined with other error conditions in cases where location info from
the stack isn’t useful:

  https://git.savannah.gnu.org/cgit/guix.git/tree/guix/utils.scm#n832

I wonder if (ice-9 exceptions) should provide something like that.


2. What are you thoughts regarding exposing structured exceptions to C?
I’ve always been frustrated by ‘system-error’ :-).  Guix has a hack to
augment ‘system-error’ with information about the offending file name:

  https://git.savannah.gnu.org/cgit/guix.git/tree/guix/ui.scm#n520

If the POSIX bindings would emit a structured ‘’ record,
that’d be pretty cool.


3. I wonder if we could take advantage of the new ‘’ exception
to start i18n of error messages.  It might be as simple as telling
xgettext to recognize ‘make-exception-with-message’ as a keyword, though
currently there are few calls to ‘make-exception-with-message’ followed
by a literal.


4. Is ‘’ actually used?  Is the goal to make it continuable?
That sounds great.


Bah, you give us a present and I reply with an additional wishlist.  ;-)

Anyway, ‘wip-exceptions’ looks great to me as it is, so I’m all for
merging it to ‘master’.

Thanks a lot!

Ludo’.



Re: guile 3 update, halloween edition

2019-11-03 Thread Mark H Weaver
>> For the record, the bijection between R6RS conditions and Guile's throw
>> arguments was my work, not Julian's.
>
> An honest mistake on my part.  My sincere apologies!

Thank you, Andy, I appreciate this.  Thanks also for asking for input on
the mailing list.  I'm heavily overloaded at the moment, but I will try
to respond to the other points in your email soon.  It seems like a good
approach, anyway.

Best,
 Mark



Re: guile 3 update, halloween edition

2019-11-03 Thread Andy Wingo
On Sat 02 Nov 2019 06:20, Mark H Weaver  writes:

> Andy Wingo  writes:
>
>> So, now the pending task is to somehow get a condition/exception
>> hierarchy into Guile core.  I will try to mostly push things off to side
>> modules but it won't always be possible.  There will be bijections
>> between a Guile's "throw" arguments and structured exceptions, mostly
>> inspired with what Julian did in the R6RS layer already.
>
> For the record, the bijection between R6RS conditions and Guile's throw
> arguments was my work, not Julian's.

An honest mistake on my part.  My sincere apologies!

Warm regards,

Andy



Re: guile 3 update, halloween edition

2019-11-01 Thread Mark H Weaver
Andy Wingo  writes:

> So, now the pending task is to somehow get a condition/exception
> hierarchy into Guile core.  I will try to mostly push things off to side
> modules but it won't always be possible.  There will be bijections
> between a Guile's "throw" arguments and structured exceptions, mostly
> inspired with what Julian did in the R6RS layer already.

For the record, the bijection between R6RS conditions and Guile's throw
arguments was my work, not Julian's.  See:

  
https://git.savannah.gnu.org/cgit/guile.git/commit/?id=02500d44775a77e46febfd47a0dab8233b0c99d0

Prior to that commit, there was no representation for Guile's "throw"
arguments in terms of R6RS conditions, and moreover the R6RS exception
guards were incapable of catching anything other than R6RS conditions.
That terrible bug was reported by Göran Weinholt:

  https://bugs.gnu.org/14922

I fixed that bug, and announced the resulting work on guile-devel:

  https://lists.gnu.org/archive/html/guile-devel/2013-08/msg4.html

Whatever you may think of me, please don't write me out of Guile's
history.

  Thanks,
Mark



Re: guile 3 update, halloween edition

2019-10-31 Thread Chris Vine
On Thu, 31 Oct 2019 17:20:37 +0100
Andy Wingo  wrote:
> Greets :)
> 
> On Thu 31 Oct 2019 01:01, Chris Vine  writes:
> 
> > "Condition" is a strange word for describing structured error objects,
> > I agree.  However, I think it would be quite confusing to describe
> > error objects as exceptions.  "Error object" or "error condition object"
> > seems a reasonable alternative if the bare word "condition" is thought
> > to be inappropriate.
> 
> I'm very sympathetic to this argument -- an exception seems like a
> thing-in-motion, not a thing-at-rest.  But perhaps it's just the effect
> of habit, setting up expectations about what good names are.  (After
> all, plenty of people seem happy with the term "condition"!)
> 
> Perhaps there is a middle ground of sorts: maybe the manual can
> comprehensively describe what R6RS refers to as conditions using the
> term "exception objects".  WDYT?

I think "exception objects" would be fine.

More broadly, I view an exception as something which makes the current
thread of execution follow an exceptional path (say, implemented by
some kind of continuation object), used generally but not exclusively
to indicate that an error has occurred.  An R6RS or SRFI-35 condition
object on the other hand is a structured error information service,
intended to be a thing (but not the only thing) which might be
propagated as the payload of the exception, and which you can
conveniently match on.

Chris



Re: guile 3 update, halloween edition

2019-10-31 Thread Sjoerd van Leent
Op wo 30 okt. 2019 21:55 schreef Andy Wingo :

>
>
> Thoughts welcome!  Also: should these structured error objects be named
> exceptions or conditions?  SRFI-35, R6RS, and R7RS say "conditions", but
> racket and my heart say "exceptions"; wdyt?
>

To my experience they are different things. An exception breaks execution
until caught/handled whereas conditions don't necessarily break execution,
and are of more informational nature. What is described are exceptions
(flow outside of the expected flow) yet thee should still be a condition
based solution, perhaps using continuation mechanisms.

My 2 cents
Sjoerd

>
>


Re: guile 3 update, halloween edition

2019-10-31 Thread Andy Wingo
Greets :)

On Thu 31 Oct 2019 01:01, Chris Vine  writes:

> "Condition" is a strange word for describing structured error objects,
> I agree.  However, I think it would be quite confusing to describe
> error objects as exceptions.  "Error object" or "error condition object"
> seems a reasonable alternative if the bare word "condition" is thought
> to be inappropriate.

I'm very sympathetic to this argument -- an exception seems like a
thing-in-motion, not a thing-at-rest.  But perhaps it's just the effect
of habit, setting up expectations about what good names are.  (After
all, plenty of people seem happy with the term "condition"!)

Perhaps there is a middle ground of sorts: maybe the manual can
comprehensively describe what R6RS refers to as conditions using the
term "exception objects".  WDYT?

Andy



Re: guile 3 update, halloween edition

2019-10-31 Thread Andy Wingo
Hey :)

On Thu 31 Oct 2019 15:17, Mikael Djurfeldt  writes:

> How does the record subtyping relate to GOOPS? I do realize that there
> are issues related to keeping bootstrapping lean, but shouldn't record
> types and classes share mechanisms?

They share the struct layer.

Records are simple: their fields are laid out in order, are all unboxed,
and can be treated as a simple kind of nominal product type.  `match'
takes advantage of this.

GOOPS is more flexible: it can have different slot allocations, unboxed
slots, multiple inheritance, and so on.  Because of multiple inheritance
its accessors have to do dynamic dispatch; whereas for records, if
supertype A allocates slot X to index I, all subtypes will have that
slot in the same place.  A check whether a record is an instance of A is
a simple O(1) check, rather than the CPL search of GOOPS.

Exceptions have a kind of multiple inheritance, but it's more about
object composition than typing.  You can make a compound condition from
a heterogeneous collection of other conditions.  While you could
implement compound conditions with subtyping, it's more straightforward
to use composition.

I think the current status is close to the sweet spot but your thoughts
are welcome :)  I would dearly like to be able to subtype records in
GOOPS, but having looked at it a few times I haven't found quite the
right way to do it.

Cheers,

Andy



Re: guile 3 update, halloween edition

2019-10-31 Thread David Pirotte
Hello Andy,

> ...
> Thoughts welcome!  Also: should these structured error objects be
> named exceptions or conditions?  SRFI-35, R6RS, and R7RS say
> "conditions", but racket and my heart say "exceptions"; wdyt?

I personally prefer "exceptions" over "conditions", though I did read
and understand Chris answer ...

Now, slightly of topic, but since/while  you are working on (ice-9
boot), exceptions ...  please allow me this gentle ping and invite you
to (re)read my answer to the following 'bug report' (1), which is a
'claim' in favor of, in 3.0, to have the repl, errors and backtraces
printers to truncate by default, then, to make things so that it would
be 'dead easy' to toggle those to full print 'on demand' ... up to one
could then bind the toggle proc(s) to an FN key ...

(1) https://debbugs.gnu.org/cgi/bugreport.cgi?bug=36677#23

To 'quote myself':

Imo, no matter how easy it would be/is to change, the default
should be to enable truncated printing for the repl, erros and
backtraces, then indeed we should have 'dead easy' 'toggle'
mechanism for those 'extremely rare' guilers/situations
who/that requires/want (occasionally in my experience, I
sometimes do to of course) full print ...

I was recently reading an email (or was it on irc, I don't remember)
fom Rekado, who wrote guile-studio, which is great news, and an attempt
make guile's newbie first experience(s) quite a lot more
friendly ...

With the above claim (and a 'dead easy' toggle mechanism ...), I would
feel quite a lot more 'relax' to (try to) recommend guile-cv ... and
together with guile-studio ... it then really can be said 'try it,
you'll even have fun ...'

David


pgpWhaXJuiTLI.pgp
Description: OpenPGP digital signature


Re: guile 3 update, halloween edition

2019-10-31 Thread Mikael Djurfeldt
Saying this without having looked at your code and also without currently
promising to do any work:

How does the record subtyping relate to GOOPS? I do realize that there are
issues related to keeping bootstrapping lean, but shouldn't record types
and classes share mechanisms?

Best regards,
Mikael

On Wed, Oct 30, 2019 at 9:55 PM Andy Wingo  wrote:

> Hey folks!
>
> I wanted to send out an update on Guile 3.  Do take a look at
> https://git.savannah.gnu.org/cgit/guile.git/tree/NEWS to see where we've
> come; basically the JIT is done, and we're ready to release soonish.
>
> However!  Here begins a long chain of yak-shaving:
>
> I wanted good benchmarks.  Generally up to now, we haven't really been
> doing good incremental benchmarks.  Ideally we could see some benchmark
> results historically, on every commit, and against other Scheme
> implementations.  To a degree it's been possible with
> https://ecraven.github.io/r7rs-benchmarks/, but those benchmarks have a
> few problems:
>
>  (1) They use unsafe optimizations on e.g. Chez and Gambit
>  (2) They are infrequently run
>  (3) They rely on R7RS, adding their own little compat layer for Guile,
>  which isn't optimal.
>
> Now, regarding (3), probably Guile should just have its own R7RS layer.
> And it should be easier to enable R6RS too.  So I added an --r6rs
> option, and started importing some R7RS code from Göran Weinholt
> (thanks!), with the idea of adding --r7rs.  That way we can just
> benchmark the different implementations, just passing --r7rs or whatever
> to get the behavior we want.  We can reduce the set of other scheme
> implementations to just the high-performance ones: Gambit, Larceny,
> Chez, and Racket.
>
> However!  R7RS, like R6RS and like SRFI-35/SRFI-34, and also like
> Racket, specifies an error-handling system in terms of "raise" and
> "with-exception-handler".  Guile uses "throw" and "catch".  There is a
> pretty good compatibility layer in Guile's R6RS exceptions/conditions
> code, but it's not shared by SRFI-35/SRFI-34, and unless we built R7RS
> in terms of R6RS -- something I prefer not to do; these things should be
> layered on Guile core directly -- we'd have to duplicate the mechanism.
>
> Which, of course, is a bit trash.  And when you come to think of it,
> throw/catch/with-throw-handler is also a bit trash.  It is too hard to
> handle exceptions in Guile; the addition of `print-exception' a few
> years back improved things, but still, making any sense out of the
> "args" corresponding to a "key" is a mess.
>
> All this made me think -- Guile should probably switch to
> raise/with-exception-handler and structured exceptions.  (Or conditions,
> or whatever we choose to call them.  I find the "condition" name a bit
> weird but maybe that's just a personal problem.)  Racket does this too,
> for what it's worth, though they have their own historical baggage.
>
> But, we need to maintain compatibility with throw/catch, because that's
> not going anywhere any time soon (if ever).  So I hacked a bit and
> eventually came up with a decent implementation of throw/catch on top of
> raise/with-exception-handler, and I think it's compatible in all the
> weird ways that it needs to be.
>
> But!  Now we have bootstrapping problems; how to get the implementation
> in boot-9?  Exceptions in SRFI-35, R6RS, R7RS, and Racket are these
> hierarchical things: they form a DAG of subtypes.  But core records in
> Guile aren't subtypeable, so what to do?
>
> Well, my thinking was that we needed to sedimentarily deposit down into
> Guile core those commonalities between the different record
> implementations in Guile: SRFI-35 conditions, R6RS records, and SRFI-9
> records.  So core now has the notion of field mutability on the record
> layer (as opposed to the struct layer), a notion of subtyping, a notion
> of extensibility, and so on.  This is all now in the manual and will be
> in NEWS.
>
> With that, we now have just one implementation of records!!!  I am very
> pleased about this.  Now you can use core record introspection
> facilities on any record in Guile.  Cool.  This also helped untangle
> some knots in the R6RS inter-module graph.
>
> So, now the pending task is to somehow get a condition/exception
> hierarchy into Guile core.  I will try to mostly push things off to side
> modules but it won't always be possible.  There will be bijections
> between a Guile's "throw" arguments and structured exceptions, mostly
> inspired with what Julian did in the R6RS layer already.
>
> Thoughts welcome!  Also: should these structured error objects be named
> exceptions or conditions?  SRFI-35, R6RS, and R7RS say "conditions", but
> racket and my heart say "exceptions"; wdyt?
>
> Cheers,
>
> Andy
>
>


Re: guile 3 update, halloween edition

2019-10-30 Thread Thompson, David
On Wed, Oct 30, 2019 at 4:55 PM Andy Wingo  wrote:
>
> Thoughts welcome!  Also: should these structured error objects be named
> exceptions or conditions?  SRFI-35, R6RS, and R7RS say "conditions", but
> racket and my heart say "exceptions"; wdyt?

I think "exceptions" is a better name for the reasons others have
already stated.

Excited for 3.0!

- Dave



Re: guile 3 update, halloween edition

2019-10-30 Thread Nala Ginrut
Hi Andy! Thanks for all the work!

On Thu, Oct 31, 2019 at 4:55 AM Andy Wingo  wrote:

> Hey folks!
>
> I wanted to send out an update on Guile 3.  Do take a look at
> https://git.savannah.gnu.org/cgit/guile.git/tree/NEWS to see where we've
> come; basically the JIT is done, and we're ready to release soonish.
>

Guile powered by JIT was confirmed to increase at least 20% performance for
Artanis.
And this is a rough result of the early-bird version of Guile-2.9. I
haven't tried the latest.
I'm looking forward to an official release of Guile-3 so that I can manage
to support the version detection correctly. It was 2.9 but should detect as
3.0, IIRC.



> But!  Now we have bootstrapping problems; how to get the implementation
> in boot-9?  Exceptions in SRFI-35, R6RS, R7RS, and Racket are these
> hierarchical things: they form a DAG of subtypes.  But core records in
> Guile aren't subtypeable, so what to do?
>

Are you talking about Guile specific record-type?
Personally, I've gradually reduced my usage of Guile records. I think R6RS
records are better for me.
I didn't know the Guile bootstrapping requires Guile specific record-type.
So I don't know better advice.


> There will be bijections
> between a Guile's "throw" arguments and structured exceptions, mostly
> inspired with what Julian did in the R6RS layer already.
>

That's cool!


> Thoughts welcome!  Also: should these structured error objects be named
> exceptions or conditions?  SRFI-35, R6RS, and R7RS say "conditions", but
> racket and my heart say "exceptions"; wdyt?
>

I never say "condition" to describe an exception. I always say "exception".
Most other languages use "exception" too. The term "condition" sounds like
conditional branching.

Best regards.


Re: guile 3 update, halloween edition

2019-10-30 Thread Chris Vine
On Wed, 30 Oct 2019 21:13:49 +0100
Andy Wingo  wrote:
> Also: should these structured error objects be named
> exceptions or conditions?  SRFI-35, R6RS, and R7RS say "conditions", but
> racket and my heart say "exceptions"; wdyt?

R6RS and R7RS speak of raising an exception, and handling the exception
in an exception handler, and racket uses similar language.  According to
R6RS "when an exception is raised, an object is provided that describes
the nature of the exceptional situation.  The report uses the condition
system described in library section 7.2 to describe exceptional
situations, classifying them by condition types".  However, condition
objects are optional when an exception is raised - you can just as well
use a symbol, or a symbol/string pair, for simple cases.

"Condition" is a strange word for describing structured error objects,
I agree.  However, I think it would be quite confusing to describe
error objects as exceptions.  "Error object" or "error condition object"
seems a reasonable alternative if the bare word "condition" is thought
to be inappropriate.



Re: guile 3 update, halloween edition

2019-10-30 Thread Christopher Lemmer Webber
Hi Andy,

Wonderful update.  I'll only comment on one thing.

Andy Wingo writes:

> Thoughts welcome!  Also: should these structured error objects be named
> exceptions or conditions?  SRFI-35, R6RS, and R7RS say "conditions", but
> racket and my heart say "exceptions"; wdyt?

Exceptions, since it's what everyone uses, so "conditions" would make
Guile be an exception to the use of "exceptions".



guile 3 update, halloween edition

2019-10-30 Thread Andy Wingo
Hey folks!

I wanted to send out an update on Guile 3.  Do take a look at
https://git.savannah.gnu.org/cgit/guile.git/tree/NEWS to see where we've
come; basically the JIT is done, and we're ready to release soonish.

However!  Here begins a long chain of yak-shaving:

I wanted good benchmarks.  Generally up to now, we haven't really been
doing good incremental benchmarks.  Ideally we could see some benchmark
results historically, on every commit, and against other Scheme
implementations.  To a degree it's been possible with
https://ecraven.github.io/r7rs-benchmarks/, but those benchmarks have a
few problems:

 (1) They use unsafe optimizations on e.g. Chez and Gambit
 (2) They are infrequently run
 (3) They rely on R7RS, adding their own little compat layer for Guile,
 which isn't optimal.

Now, regarding (3), probably Guile should just have its own R7RS layer.
And it should be easier to enable R6RS too.  So I added an --r6rs
option, and started importing some R7RS code from Göran Weinholt
(thanks!), with the idea of adding --r7rs.  That way we can just
benchmark the different implementations, just passing --r7rs or whatever
to get the behavior we want.  We can reduce the set of other scheme
implementations to just the high-performance ones: Gambit, Larceny,
Chez, and Racket.

However!  R7RS, like R6RS and like SRFI-35/SRFI-34, and also like
Racket, specifies an error-handling system in terms of "raise" and
"with-exception-handler".  Guile uses "throw" and "catch".  There is a
pretty good compatibility layer in Guile's R6RS exceptions/conditions
code, but it's not shared by SRFI-35/SRFI-34, and unless we built R7RS
in terms of R6RS -- something I prefer not to do; these things should be
layered on Guile core directly -- we'd have to duplicate the mechanism.

Which, of course, is a bit trash.  And when you come to think of it,
throw/catch/with-throw-handler is also a bit trash.  It is too hard to
handle exceptions in Guile; the addition of `print-exception' a few
years back improved things, but still, making any sense out of the
"args" corresponding to a "key" is a mess.

All this made me think -- Guile should probably switch to
raise/with-exception-handler and structured exceptions.  (Or conditions,
or whatever we choose to call them.  I find the "condition" name a bit
weird but maybe that's just a personal problem.)  Racket does this too,
for what it's worth, though they have their own historical baggage.

But, we need to maintain compatibility with throw/catch, because that's
not going anywhere any time soon (if ever).  So I hacked a bit and
eventually came up with a decent implementation of throw/catch on top of
raise/with-exception-handler, and I think it's compatible in all the
weird ways that it needs to be.

But!  Now we have bootstrapping problems; how to get the implementation
in boot-9?  Exceptions in SRFI-35, R6RS, R7RS, and Racket are these
hierarchical things: they form a DAG of subtypes.  But core records in
Guile aren't subtypeable, so what to do?

Well, my thinking was that we needed to sedimentarily deposit down into
Guile core those commonalities between the different record
implementations in Guile: SRFI-35 conditions, R6RS records, and SRFI-9
records.  So core now has the notion of field mutability on the record
layer (as opposed to the struct layer), a notion of subtyping, a notion
of extensibility, and so on.  This is all now in the manual and will be
in NEWS.

With that, we now have just one implementation of records!!!  I am very
pleased about this.  Now you can use core record introspection
facilities on any record in Guile.  Cool.  This also helped untangle
some knots in the R6RS inter-module graph.

So, now the pending task is to somehow get a condition/exception
hierarchy into Guile core.  I will try to mostly push things off to side
modules but it won't always be possible.  There will be bijections
between a Guile's "throw" arguments and structured exceptions, mostly
inspired with what Julian did in the R6RS layer already.

Thoughts welcome!  Also: should these structured error objects be named
exceptions or conditions?  SRFI-35, R6RS, and R7RS say "conditions", but
racket and my heart say "exceptions"; wdyt?

Cheers,

Andy



Re: guile 3 update, september edition

2018-09-18 Thread Ludovic Courtès
Hello,

Andy Wingo  skribis:

> On Mon 17 Sep 2018 11:35, l...@gnu.org (Ludovic Courtès) writes:
>
>>> The threshold at which Guile will automatically JIT-compile is set from
>>> the GUILE_JIT_THRESHOLD environment variable.  By default it is 5.
>>> If you set it to -1, you disable the JIT.  If you set it to 0, *all*
>>> code will be JIT-compiled.  The test suite passes at
>>> GUILE_JIT_THRESHOLD=0, indicating that all features in Guile are
>>> supported by the JIT.  Set the GUILE_JIT_LOG environment variable to 1
>>> or 2 to see JIT progress.
>>
>> Just to be clear, does GUILE_JIT_THRESHOLD represents the number of
>> times a given instruction pointer is hit?
>
> No.  It is an abstract "hotness" counter associated with a function's
> code.  (I say "function's code" because many closures can share the same
> code and thus the same counter.  It's not in the scm_tc7_program object
> because some procedures don't have these.)
>
> All counters start at 0 when Guile starts.  A function's counters
> increment by 30 when a function is called, currently, and 2 on every
> loop back-edge.  I have not attempted to tweak these values yet.

OK, I see.

Exciting times!

Ludo’.



Re: guile 3 update, september edition

2018-09-18 Thread Andy Wingo
Greets :)

On Mon 17 Sep 2018 11:35, l...@gnu.org (Ludovic Courtès) writes:

>> The threshold at which Guile will automatically JIT-compile is set from
>> the GUILE_JIT_THRESHOLD environment variable.  By default it is 5.
>> If you set it to -1, you disable the JIT.  If you set it to 0, *all*
>> code will be JIT-compiled.  The test suite passes at
>> GUILE_JIT_THRESHOLD=0, indicating that all features in Guile are
>> supported by the JIT.  Set the GUILE_JIT_LOG environment variable to 1
>> or 2 to see JIT progress.
>
> Just to be clear, does GUILE_JIT_THRESHOLD represents the number of
> times a given instruction pointer is hit?

No.  It is an abstract "hotness" counter associated with a function's
code.  (I say "function's code" because many closures can share the same
code and thus the same counter.  It's not in the scm_tc7_program object
because some procedures don't have these.)

All counters start at 0 when Guile starts.  A function's counters
increment by 30 when a function is called, currently, and 2 on every
loop back-edge.  I have not attempted to tweak these values yet.

>> Using GNU Lightning has been useful but in the long term I don't think
>> it's the library that we need, for a few reasons:
>
> [...]
>
> It might be that the lightning 1.x branch would be a better fit (it was
> exactly as you describe.)  I think that’s what Racket was (is?) using.

Could be!  I will have a look.

Cheers,

Andy



Re: guile 3 update, september edition

2018-09-17 Thread Amirouche Boubekki
Le lun. 17 sept. 2018 à 10:26, Andy Wingo  a écrit :

> Hi!
>
> This is an update on progress towards Guile 3.  In our last update, we
> saw the first bits of generated code:
>
>   https://lists.gnu.org/archive/html/guile-devel/2018-08/msg5.html
>
> Since then, the JIT is now feature-complete.  It can JIT-compile *all*
> code in Guile, including delimited continuations, dynamic-wind, all
> that.  It runs automatically, in response to a function being called a
> lot.  It can also tier up from within hot loops.
>

This looks very good!

When the merge will be done, maybe it will be time to move guile-next to
master in guix? WDYT?

Keep it steady!


Re: guile 3 update, september edition

2018-09-17 Thread Ludovic Courtès
Hello!

Andy Wingo  skribis:

> This is an update on progress towards Guile 3.  In our last update, we
> saw the first bits of generated code:
>
>   https://lists.gnu.org/archive/html/guile-devel/2018-08/msg5.html
>
> Since then, the JIT is now feature-complete.  It can JIT-compile *all*
> code in Guile, including delimited continuations, dynamic-wind, all
> that.  It runs automatically, in response to a function being called a
> lot.  It can also tier up from within hot loops.

Woohoo!  It’s awesome that JIT can already handle all Guile code and run
all the test suite.  To me that means it can be merged into ‘master’.

> The threshold at which Guile will automatically JIT-compile is set from
> the GUILE_JIT_THRESHOLD environment variable.  By default it is 5.
> If you set it to -1, you disable the JIT.  If you set it to 0, *all*
> code will be JIT-compiled.  The test suite passes at
> GUILE_JIT_THRESHOLD=0, indicating that all features in Guile are
> supported by the JIT.  Set the GUILE_JIT_LOG environment variable to 1
> or 2 to see JIT progress.

Just to be clear, does GUILE_JIT_THRESHOLD represents the number of
times a given instruction pointer is hit?

> For debugging (single-stepping, tracing, breakpoints), Guile will fall
> back to the bytecode interpreter (the VM), for the thread that has
> debugging enabled.  Once debugging is no longer enabled (no more hooks
> active), that thread can return to JIT-compiled code.

Cool.

> Using GNU Lightning has been useful but in the long term I don't think
> it's the library that we need, for a few reasons:

[...]

It might be that the lightning 1.x branch would be a better fit (it was
exactly as you describe.)  I think that’s what Racket was (is?) using.

> Meaning that "eval" in Guile 3 is somewhere around 80% faster than in
> Guile 2.2 -- because "eval" is now JIT-compiled.

Very cool.

> Incidentally, as a comparison, Guile 2.0 (whose "eval" is slower for
> various reasons) takes 70s real time for the same benchmark.  Guile 1.8,
> whose eval was written in C, takes 4.536 seconds real time.  It's still
> a little faster than Guile 3's eval-in-Scheme, but it's close and we're
> catching up :)

It’s an insightful comparison; soon we can say it’s “as fast as
hand-optimized C” (and more readable, too :-)).

> I have also tested with ecraven's r7rs-benchmarks and we make a nice
> jump past the 2.2 results; but not yet at Racket or Chez levels yet.  I
> think we need to tighten up our emitted code.  There's another 2x of
> performance that we should be able to get with incremental improvements.
> For the last bit we will need global register allocation though, I
> think.

Looking forward to reading ecraven’s updated benchmark results.

Thank you for the awesomeness!

Ludo’.



guile 3 update, september edition

2018-09-17 Thread Andy Wingo
Hi!

This is an update on progress towards Guile 3.  In our last update, we
saw the first bits of generated code:

  https://lists.gnu.org/archive/html/guile-devel/2018-08/msg5.html

Since then, the JIT is now feature-complete.  It can JIT-compile *all*
code in Guile, including delimited continuations, dynamic-wind, all
that.  It runs automatically, in response to a function being called a
lot.  It can also tier up from within hot loops.

The threshold at which Guile will automatically JIT-compile is set from
the GUILE_JIT_THRESHOLD environment variable.  By default it is 5.
If you set it to -1, you disable the JIT.  If you set it to 0, *all*
code will be JIT-compiled.  The test suite passes at
GUILE_JIT_THRESHOLD=0, indicating that all features in Guile are
supported by the JIT.  Set the GUILE_JIT_LOG environment variable to 1
or 2 to see JIT progress.

For debugging (single-stepping, tracing, breakpoints), Guile will fall
back to the bytecode interpreter (the VM), for the thread that has
debugging enabled.  Once debugging is no longer enabled (no more hooks
active), that thread can return to JIT-compiled code.

Right now the JIT-compiled code exactly replicates what the bytecode
interpreter does: the same stack reads and writes, etc.  There is some
specialization when a bytecode has immediate operands of course.
However the choice to do debugging via the bytecode interpreter --
effectively, to always have bytecode around -- will allow machine code
(compiled either just-in-time or ahead-of-time) to do register
allocation.  JIT will probably do a simple block-local allocation.  An
AOT compiler is free to do something smarter.

As far as I can tell, with the default setting of
GUILE_JIT_THRESHOLD=5, JIT does not increase startup latency for any
workload, and always increases throughput.  More benchmarking is needed
though.

Using GNU Lightning has been useful but in the long term I don't think
it's the library that we need, for a few reasons:

  * When Lightning does a JIT compilation, it builds a graph of
operations, does some minor optimizations, and then emits code.  But
the graph phase takes time and memory.  I think we just need a
library that just emits code directly.  That would lower the cost of
JIT and allow us to lower the default GUILE_JIT_THRESHOLD.

  * The register allocation phase in Lightning exists essentially for
calls.  However we have a very restricted set of calls that we need
to do, and can do the allocation by hand on each architecture.  This
(We don't use CPU call instructions for Scheme function calls
because we use the VM stack.  We might be able to revise this in the
future but again Lightning is in the way).  Doing it by hand would
allow a few benefits:

  * Hand allocation would free up more temporary registers.  Right
now lightning reserves all registers used as part of the platform
calling convention; they are unavailable to the JIT.

  * Sometimes when Lightning needs a temporary register, it can
clobber one that we're using as part of an internal calling
convention.  I believe this is fixed for x86-64 but I can't be
sure for other architectures!  See commit
449ef7d9755b553cb0ad2629bca3bc42c5913e88.

  * We need to do our own register allocation; having Lightning also
do it is a misfeature.

  * Sometimes we know that we can get better emitted code, but the
lightning abstraction doesn't let us do it.  We should allow
ourselves to punch through that abstraction.

The platform-specific Lightning files basically expose most of the API
we need.  We could consider incrementally punching through lightning.h
to reach those files.  Something to think about for the future.

Finally, as far as performance goes -- we're generally somewhere around
80% faster than 2.2.  Sometimes more, sometimes less, always faster
though AFAIK.  As an example, here's a simple fib.scm:

   $ cat /tmp/fib.scm
   (define (fib n)
 (if (< n 2)
 1
 (+ (fib (- n 1))
(fib (- n 2)

Now let's use eval-in-scheme to print the 35th fibonacci number.  For
Guile 2.2:

   $ time /opt/guile-2.2/bin/guile -c \
   '(begin (primitive-load "/tmp/fib.scm") (pk (fib 35)))'

   ;;; (14930352)

   real 0m9.610s
   user 0m10.547s
   sys  0m0.040s

But with Guile from the lightning branch, we get:

   $ time /opt/guile/bin/guile -c \
   '(begin (primitive-load "/tmp/fib.scm") (pk (fib 35)))'

   ;;; (14930352)

   real 0m5.299s
   user 0m6.167s
   sys  0m0.064s

Meaning that "eval" in Guile 3 is somewhere around 80% faster than in
Guile 2.2 -- because "eval" is now JIT-compiled.  (Otherwise it's the
same program.)  This improves bootstrap times, though Guile 3's compiler
will generally make more CPS nodes than Guile 2.2 for the same
expression, which takes more time and memory, so the gain isn't
earth-shattering.

Incidentally, as a comparison, Guile 2.0 (whose 

Re: Guile 3 update, August edition

2018-08-24 Thread Thompson, David
On Mon, Aug 20, 2018 at 10:27 AM, Andy Wingo  wrote:
> In this particular example, the JITted code runs about 3x faster than
> the interpreted code.  The JIT doesn't do register allocation; not sure
> precisely how to do that.  A future topic.  For the moment I want to
> consolidate what we have and once it's all just magically working and
> everybody's programs are faster, we release Guile 3.

Echoing everyone else: This is very exciting! I can't wait to take it
for a spin.  Thank you!

- Dave



Re: Guile 3 update, August edition

2018-08-24 Thread Amirouche Boubekki
Le ven. 24 août 2018 à 14:19, Christopher Lemmer Webber <
cweb...@dustycloud.org> a écrit :

> Andy Wingo writes:
>
> > In this particular example, the JITted code runs about 3x faster than
> > the interpreted code.  The JIT doesn't do register allocation; not sure
> > precisely how to do that.  A future topic.  For the moment I want to
> > consolidate what we have and once it's all just magically working and
> > everybody's programs are faster, we release Guile 3.
>
> This is great news!  Very excited about Guile 3 over here!  :)
>
>
Same here! Thanks a lot for this updates. I abandonned my Chez scheme
adventures somewhat thanks to those updates!

Keep it steady and happy hacking!


Re: Guile 3 update, August edition

2018-08-24 Thread Christopher Lemmer Webber
Andy Wingo writes:

> In this particular example, the JITted code runs about 3x faster than
> the interpreted code.  The JIT doesn't do register allocation; not sure
> precisely how to do that.  A future topic.  For the moment I want to
> consolidate what we have and once it's all just magically working and
> everybody's programs are faster, we release Guile 3.

This is great news!  Very excited about Guile 3 over here!  :)



Guile 3 update, August edition

2018-08-20 Thread Andy Wingo
Hi!

Last dispatch was here:

  https://lists.gnu.org/archive/html/guile-devel/2018-07/msg00037.html

To recap, I merged in GNU lightning and added an extra machine-code
return address to frames, but hadn't actually written the JIT yet.

Since July, I made it so that all Guile bytecode function entry points
start with an "instrument-entry" bytecode that holds a counter.  The
intention is that when the counter increments beyond a certain value,
the function should be automatically JIT-compiled.  Associated with the
counter is a native-code pointer corresponding to the function.  I also
added "instrument-loop" bytecodes to all loops, to be able to tier up
from within hot loops.

With all of this done and some other bytecode tweaks, I was able to move
on to the JIT compiler itself.  I'm happy to say that I now have a first
version.  It's about 3500 lines of C, so a bit gnarly.  It's
architecture-independent, as it uses lightning, and there are lightning
backends for about every architecture.  Lightning seems OK.  Not
optimal, but OK, and an OK thing to use for now anyway.  I did have to
write special cases for 32-bit machines, as Guile's VM supports 64-bit
arithmetic, and some-endian-specific code.  I probably got some of that
wrong; review is very welcome:

  https://git.savannah.gnu.org/cgit/guile.git/tree/libguile/jit.c?h=lightning

If you have fixes and are a committer, please feel free to just commit
them directly.  If you aren't a committer yet and you spot some fixes,
mail the list; you should definitely be a committer if you can do that
:)

I just got the JIT working today.  For the time being, the interface is
a public function, %jit-compile.  Eventually I will remove this when I
have more confidence, relying only on the automatic compilation
triggered by function entry and loop iterations.

As an example:

  $ cat foo.scm
  (use-modules (rnrs bytevectors))
  (define (f32v-sum bv)
(let lp ((n 0) (sum 0.0))
  (if (< n (bytevector-length bv))
  (lp (+ n 4)
  (+ sum (bytevector-ieee-single-native-ref bv n)))
  sum)))
  (define ones (make-f32vector #e1e7 1.0))

  # The JIT currently doesn't emit hook code.
  $ meta/guile --no-debug
  scheme@(guile-user)> (load "foo.scm")
  scheme@(guile-user)> ,time (f32v-sum ones)
  $2 = 1.0e7
  ;; 0.143017s real time, 0.142986s run time.  0.00s spent in GC.
  scheme@(guile-user)> (%jit-compile f32v-sum)
  scheme@(guile-user)> ,time (f32v-sum ones)
  $3 = 1.0e7
  ;; 0.048514s real time, 0.048499s run time.  0.00s spent in GC.

In this particular example, the JITted code runs about 3x faster than
the interpreted code.  The JIT doesn't do register allocation; not sure
precisely how to do that.  A future topic.  For the moment I want to
consolidate what we have and once it's all just magically working and
everybody's programs are faster, we release Guile 3.

Cheers,

Andy



guile 3 update, july edition

2018-07-21 Thread Andy Wingo
Hi :)

Just a brief update with Guile 3.  Last one was here:

  https://lists.gnu.org/archive/html/guile-devel/2018-06/msg00026.html

There is a now a "lightning" branch that has GNU lightning merged in and
built statically into Guile.  It causes about 1 MB of overhead in the
-Og libguile-3.0.so, bringing it to 6.1 MB, or 1.36 MB stripped.  Seems
OK for now.  By way of contrast, libguile-2.2.so is 5.65 MB when built
with -Og, or 1.19 MB stripped.

There's some scaffolding for making JIT code emitters for each
instruction.  But then I ran into a problem about how to intermingle JIT
and interpreter returns on the stack.  I was hoping to avoid having
separate interpreter and JIT return addresses in a stack frame, to avoid
adding overhead.  That didn't work out:

  https://lists.gnu.org/archive/html/guile-devel/2018-07/msg00013.html

So, I added a slot to the "overhead" part of stack frames.  From
frames.h:

   Stack frame layout
   --

   | ...  |
   +==+ <- fp + 3 = SCM_FRAME_PREVIOUS_SP (fp)
   | Dynamic link |
   +--+
   | Virtual return address (vRA) |
   +--+
   | Machine return address (mRA) |
   +==+ <- fp
   | Local 0  |
   +--+
   | Local 1  |
   +--+
   | ...  |
   +--+
   | Local N-1|
   \--/ <- sp

   The stack grows down.

   The calling convention is that a caller prepares a stack frame
   consisting of the saved FP, the saved virtual return addres, and the
   saved machine return address of the calling function, followed by the
   procedure and then the arguments to the call, in order.  Thus in the
   beginning of a call, the procedure being called is in slot 0, the
   first argument is in slot 1, and the SP points to the last argument.
   The number of arguments, including the procedure, is thus FP - SP.

That took a while.  Anything that changes calling conventions is gnarly.
While I was at it, I changed the return calling convention to expect
return values from slot 0 instead of from slot 1, and made some other
minor changes to instructions related to calls and returns.

The next step will be to add an "enter-function" instruction or
something to function entries.  This instruction's only real purpose
will be to increment a counter associated with the function.  If the
counter exceeds some threshold, JIT code will be emitted for the
function and the function will tier up.  If the enter-function
instruction sees that the function already has JIT code (e.g. emitted
from another thread), then it will tier up directly.

Because enter-function is in the right place to run the apply hook for
debugging, we'll probably move that functionality there, instead of
being inline with the call instructions.

The "enter-function" opcode will take an offset to writable data for the
counter, allocated in the ELF image.  This data will have the form:

  struct jit_data {
void* mcode;
uint32_t counter;
uint32_t start;
uint32_t end;
  }

The mcode pointer indicates the JIT code, if any.  It will probably need
to be referenced atomically (maybe release/consume ordering?).  The
counter is the counter associated with this function; entering a
function will increment it by some amount.  The start and end elements
indicate the bounds of the function, and are offsets into the vcode,
relative to the jit_data struct.  These are not writable.

Loops will also have an instruction that increments the counter,
possibly tiering up if needed.  The whole function will share one
"struct jit_data".

I am currently thinking that we can make JIT-JIT function calls peek
ahead in the vcode of the callee to find the callee JIT code, if any.
I.e.:

  (if (has-tc7? callee %tc7-program)
  (let ((vcode (word-ref callee 1)))
(if (= (logand (u32-ref vcode 0) #xff)
   %enter-function-opcode)
(let ((mcode ((+ vcode (* (u32-ref vcode 1) 4)
  (if (zero? mcode)
  (jmp! mcode)
  (return!))) ;; return to interpreter
(return!)))
  (return!))

It's a dependent memory load on the function-call hot path but it will
predict really well.  The upside of this is that there is just one
mutable mcode pointer for a function, for all its closures in all
threads.  It also avoids reserving more space on the heap for another
mcode word in program objects.

Loops will tier up ("on-stack replacement") by jumping to an offset in
the mcode corresponding to the vcode for the counter-incrementing
instruction.  The offset will be determined by running the JIT compiler
for the function but without actually emitting the code and flushing
icache; the compiler is run in a mode just to determine the 

Re: guile 3 update, june 2018 edition

2018-07-17 Thread dsmich


 dsm...@roadrunner.com wrote: 
> Ok! now getting past the "make -j" issue, but I'm still getting a segfault.

And now commit e6461cf1b2b63e3ec9a2867731742db552b61b71 has gotten past the 
segfault.

Wooo!

-Dale




Re: guile 3 update, june 2018 edition

2018-07-05 Thread Andy Wingo
Hi :)

On Mon 02 Jul 2018 11:28, l...@gnu.org (Ludovic Courtès) writes:

> Andy Wingo  skribis:
>
>> My current plan is that the frame overhead will still be two slots: the
>> saved previous FP, and the saved return address.  Right now the return
>> address is always a bytecode address.  In the future it will be bytecode
>> or native code.  Guile will keep a runtime routine marking regions of
>> native code so it can know if it needs to if an RA is bytecode or native
>> code, for debugging reasons; but in most operation, Guile won't need to
>> know.  The interpreter will tier up to JIT code through an adapter frame
>> that will do impedance matching over virtual<->physical addresses.  To
>> tier down to the interpreter (e.g. when JIT code calls interpreted
>> code), the JIT will simply return to the interpreter, which will pick up
>> state from the virtual IP, SP, and FP saved in the VM state.
>
> What will the “adapter frame” look like?

Aah, sadly it won't work like this.  Somehow I was thinking of an
adapter frame on the C stack.  However an adapter frame corresponds to a
continuation, so it would have to have the life of a continuation, so it
would have to be on the VM stack.  I don't think I want adapter frames
on the VM stack, so I have to scrap this.  More below...

>> We do walk the stack from Scheme sometimes, notably when making a
>> backtrace.  So, we'll make the runtime translate the JIT return
>> addresses to virtual return addresses in the frame API.  To Scheme, it
>> will be as if all things were interpreted.
>
> Currently you can inspect the locals of a stack frame.  Will that be
> possible with frames corresponding to native code? (I suppose that’d be
> difficult.)

Yes, because native code manipulates the VM stack in exactly the same
way as bytecode.  Eventually we should do register allocation and avoid
always writing values to the stack, but that is down the road.

>> My current problem is knowing when a callee has JIT code.  Say you're in
>> JITted function F which calls G.  Can you directly jump to G's native
>> code, or is G not compiled yet and you need to use the interpreter?  I
>> haven't solved this yet.  "Known calls" that use call-label and similar
>> can of course eagerly ensure their callees are JIT-compiled, at
>> compilation time.  Unknown calls are the problem.  I don't know whether
>> to consider reserving another word in scm_tc7_program objects for JIT
>> code.  I have avoided JIT overhead elsewhere and would like to do so
>> here as well!
>
> In the absence of a native code pointer in scm_tc7_program objects, how
> will libguile find the native code for a given program?

This is a good question and it was not clear to me when I wrote this!  I
think I have a solution now but it involves memory overhead.  Oh well.

Firstly, I propose to add a slot to stack frames.  Stack frames will now
store the saved FP, the virtual return address (vRA), and the machine
return address IP (mRA).  When in JIT code, a return will check if the
mRA is nonzero, and if so jump to that mRA.  Otherwise it will return
from JIT, and the interpreter should continue.

Likewise when doing a function return from the interpreter and the mRA
is nonzero, the interpreter should return by entering JIT code to that
address.

When building an interpreter-only Guile (Guile without JIT) or an
AOT-only Guile (doesn't exist currently), we could configure Guile to
not reserve this extra stack word.  However that would be a different
ABI: a .go file built with interpreter-only Guile wouldn't work on
Guile-with-JIT, because interpreter-only Guile would think stack frames
only need two reserved words, whereas Guile-with-JIT would write three
words.  To avoid the complication, for 3.0 I think we should just use
3-word frames all the time.

So, that's returns.  Other kinds of non-local returns like
abort-to-prompt, resuming delimited continuations, or calling
undelimited continuations would work similarly: the continuation would
additionally record an mRA, and resuming would jump there instead, if
appropriate.

Now, calls.  One of the reasons that I wanted to avoid an extra program
word was because scm_tc7_program doesn't exist in a one-to-one
relationship with code.  "Well-known" procedures get compiled by closure
optimization to be always called via call-label or tail-call-label -- so
some code doesn't have program objects.  On the other hand, closures
mean that some code has many program objects.

So I thought about using side tables indexed by code; or inline
"maybe-tier-up-here" instructions, which would reference a code pointer
location, that if nonzero, would be the JIT code.

However I see now that really we need to optimize for the JIT-to-JIT
call case, as by definition that's going to be the hot case.  Of course
call-label from JIT can do an unconditional jmp.  But calling a program
object... how do we do this?  This is complicated by code pages being
read-only, so we don't have space to store a pointer in 

Re: guile 3 update, june 2018 edition

2018-07-02 Thread Ludovic Courtès
Hello!

Andy Wingo  skribis:

> The news is that the VM has been completely converted over to call out
> to the Guile runtime through an "intrinsics" vtable.  For some
> intrinsics, the compiler will emit specialized call-intrinsic opcodes.
> (There's one of these opcodes for each intrinsic function type.)  For
> others that are a bit more specialized, like the intrinsic used in
> call-with-prompt, the VM calls out directly to the intrinsic.
>
> The upshot is that we're now ready to do JIT compilation.  JIT-compiled
> code will use the intrinsics vtable to embed references to runtime
> routines.  In some future, AOT-compiled code can keep the intrinsics
> vtable in a register, and call indirectly through that register.

Exciting!  It sounds like a really good strategy because it means that
the complex instructions don’t have to be implemented in lightning
assembly by hand, which would be a pain.

> My current plan is that the frame overhead will still be two slots: the
> saved previous FP, and the saved return address.  Right now the return
> address is always a bytecode address.  In the future it will be bytecode
> or native code.  Guile will keep a runtime routine marking regions of
> native code so it can know if it needs to if an RA is bytecode or native
> code, for debugging reasons; but in most operation, Guile won't need to
> know.  The interpreter will tier up to JIT code through an adapter frame
> that will do impedance matching over virtual<->physical addresses.  To
> tier down to the interpreter (e.g. when JIT code calls interpreted
> code), the JIT will simply return to the interpreter, which will pick up
> state from the virtual IP, SP, and FP saved in the VM state.

What will the “adapter frame” look like?

> We do walk the stack from Scheme sometimes, notably when making a
> backtrace.  So, we'll make the runtime translate the JIT return
> addresses to virtual return addresses in the frame API.  To Scheme, it
> will be as if all things were interpreted.

Currently you can inspect the locals of a stack frame.  Will that be
possible with frames corresponding to native code? (I suppose that’d be
difficult.)

> My current problem is knowing when a callee has JIT code.  Say you're in
> JITted function F which calls G.  Can you directly jump to G's native
> code, or is G not compiled yet and you need to use the interpreter?  I
> haven't solved this yet.  "Known calls" that use call-label and similar
> can of course eagerly ensure their callees are JIT-compiled, at
> compilation time.  Unknown calls are the problem.  I don't know whether
> to consider reserving another word in scm_tc7_program objects for JIT
> code.  I have avoided JIT overhead elsewhere and would like to do so
> here as well!

In the absence of a native code pointer in scm_tc7_program objects, how
will libguile find the native code for a given program?

Thanks for sharing this plan!  Good times ahead!

Ludo’.




Re: guile 3 update, june 2018 edition

2018-07-01 Thread dsmich
Ok! now getting past the "make -j" issue, but I'm still getting a segfault.

Here is a backtrace from the core dump.

Line 25:
#25 0x7efeb518b09f in scm_error (key=0x563599bbb120, subr=subr@entry=0x0, 
message=message@entry=0x7efeb521c0cd "Unbound variable: ~S", 
args=0x563599f8f260, 
rest=rest@entry=0x4) at error.c:62

Looks kinda suspicious.  Should subr be 0x0 there?


Thread 4 (Thread 0x7efeb282c700 (LWP 10059)):
#0  pthread_cond_wait@@GLIBC_2.3.2 () at 
../sysdeps/unix/sysv/linux/x86_64/pthread_cond_wait.S:185
#1  0x7efeb4401cc7 in GC_wait_marker () from 
/usr/lib/x86_64-linux-gnu/libgc.so.1
#2  0x7efeb43f85ca in GC_help_marker () from 
/usr/lib/x86_64-linux-gnu/libgc.so.1
#3  0x7efeb440033c in GC_mark_thread () from 
/usr/lib/x86_64-linux-gnu/libgc.so.1
#4  0x7efeb49f6494 in start_thread (arg=0x7efeb282c700) at 
pthread_create.c:333
#5  0x7efeb4738acf in clone () at 
../sysdeps/unix/sysv/linux/x86_64/clone.S:97

Thread 3 (Thread 0x7efeb382e700 (LWP 10057)):
#0  pthread_cond_wait@@GLIBC_2.3.2 () at 
../sysdeps/unix/sysv/linux/x86_64/pthread_cond_wait.S:185
#1  0x7efeb4401cc7 in GC_wait_marker () from 
/usr/lib/x86_64-linux-gnu/libgc.so.1
#2  0x7efeb43f85ca in GC_help_marker () from 
/usr/lib/x86_64-linux-gnu/libgc.so.1
#3  0x7efeb440033c in GC_mark_thread () from 
/usr/lib/x86_64-linux-gnu/libgc.so.1
#4  0x7efeb49f6494 in start_thread (arg=0x7efeb382e700) at 
pthread_create.c:333
#5  0x7efeb4738acf in clone () at 
../sysdeps/unix/sysv/linux/x86_64/clone.S:97

Thread 2 (Thread 0x7efeb302d700 (LWP 10058)):
#0  pthread_cond_wait@@GLIBC_2.3.2 () at 
../sysdeps/unix/sysv/linux/x86_64/pthread_cond_wait.S:185
#1  0x7efeb4401cc7 in GC_wait_marker () from 
/usr/lib/x86_64-linux-gnu/libgc.so.1
#2  0x7efeb43f85ca in GC_help_marker () from 
/usr/lib/x86_64-linux-gnu/libgc.so.1
#3  0x7efeb440033c in GC_mark_thread () from 
/usr/lib/x86_64-linux-gnu/libgc.so.1
#4  0x7efeb49f6494 in start_thread (arg=0x7efeb302d700) at 
pthread_create.c:333
#5  0x7efeb4738acf in clone () at 
../sysdeps/unix/sysv/linux/x86_64/clone.S:97

Thread 1 (Thread 0x7efeb565d740 (LWP 10034)):
#0  0x7efeb51afd26 in scm_maybe_resolve_module 
(name=name@entry=0x563599f8f140) at modules.c:195
#1  0x7efeb51b01bf in scm_public_variable (module_name=0x563599f8f140, 
name=0x563599da50e0) at modules.c:656
#2  0x7efeb518036a in init_print_frames_var_and_frame_to_stack_vector_var 
() at backtrace.c:103
#3  0x7efeb49fd739 in __pthread_once_slow (once_control=0x7efeb545d828 
, init_routine=0x7efeb5180340 
)
at pthread_once.c:116
#4  0x7efeb49fd7e5 in __GI___pthread_once 
(once_control=once_control@entry=0x7efeb545d828 , 
init_routine=init_routine@entry=0x7efeb5180340 
) at pthread_once.c:143
#5  0x7efeb51801b0 in display_backtrace_body (a=0x7ffe2b3b7ea0) at 
backtrace.c:218
#6  0x7efeb520040f in vm_regular_engine (thread=0x563599b14dc0) at 
vm-engine.c:610
#7  0x7efeb52046d3 in scm_call_n (proc=proc@entry=0x563599da5aa0, 
argv=argv@entry=0x0, nargs=nargs@entry=0) at vm.c:1440
#8  0x7efeb518cab9 in scm_call_0 (proc=proc@entry=0x563599da5aa0) at 
eval.c:489
#9  0x7efeb51f8cd6 in catch (tag=tag@entry=0x404, thunk=0x563599da5aa0, 
handler=0x563599da5940, pre_unwind_handler=0x4) at throw.c:144
#10 0x7efeb51f9015 in scm_catch_with_pre_unwind_handler 
(key=key@entry=0x404, thunk=, handler=, 
pre_unwind_handler=)
at throw.c:262
#11 0x7efeb51f91cf in scm_c_catch (tag=tag@entry=0x404, 
body=body@entry=0x7efeb5180190 , 
body_data=body_data@entry=0x7ffe2b3b7ea0, 
handler=handler@entry=0x7efeb5180580 , 
handler_data=handler_data@entry=0x563599baf000, 
pre_unwind_handler=pre_unwind_handler@entry=0x0, 
pre_unwind_handler_data=0x0) at throw.c:387
#12 0x7efeb51f91de in scm_internal_catch (tag=tag@entry=0x404, 
body=body@entry=0x7efeb5180190 , 
body_data=body_data@entry=0x7ffe2b3b7ea0, 
handler=handler@entry=0x7efeb5180580 , 
handler_data=handler_data@entry=0x563599baf000) at throw.c:396
#13 0x7efeb5180185 in scm_display_backtrace_with_highlights 
(stack=stack@entry=0x563599da5b60, port=port@entry=0x563599baf000, 
first=first@entry=0x4, 
depth=depth@entry=0x4, highlights=highlights@entry=0x304) at backtrace.c:277
#14 0x7efeb51f8fec in handler_message (tag=tag@entry=0x563599bbb120, 
args=args@entry=0x563599c0cdb0, handler_data=) at throw.c:548
#15 0x7efeb51f93cb in scm_handle_by_message (handler_data=, 
tag=0x563599bbb120, args=0x563599c0cdb0) at throw.c:585
#16 0x7efeb51f94fe in default_exception_handler (args=0x563599c0cdb0, 
k=0x563599bbb120) at throw.c:174
#17 throw_without_pre_unwind (tag=0x563599bbb120, args=0x563599c0cdb0) at 
throw.c:248
#18 0x7efeb520040f in vm_regular_engine (thread=0x563599b14dc0) at 
vm-engine.c:610
#19 0x7efeb52046d3 in scm_call_n (proc=proc@entry=0x563599baf9c0, 
argv=, nargs=5) at vm.c:1440
---Type  to continue, or q  to quit---
#20 0x7efeb518ce4b in scm_apply_0 

Re: guile 3 update, june 2018 edition

2018-06-29 Thread dsmich
Greetings Andy!

 Andy Wingo  wrote: 
> Hi,
> 
> Just wanted to give an update on Guile 3 developments.  Last note was
> here:
> 
>   https://lists.gnu.org/archive/html/guile-devel/2018-04/msg4.html
> 
> The news is that the VM has been completely converted over to call out
> to the Guile runtime through an "intrinsics" vtable.  For some
> intrinsics, the compiler will emit specialized call-intrinsic opcodes.
> (There's one of these opcodes for each intrinsic function type.)  For
> others that are a bit more specialized, like the intrinsic used in
> call-with-prompt, the VM calls out directly to the intrinsic.

Very exciting!

However, master is not building for me. :(

  git clean -dxf; ./autogen.sh && ./configure && make -j5

gives me

  SNARF  atomic.x
  SNARF  backtrace.x
  SNARF  boolean.x
In file included from atomic.c:29:0:
extensions.h:26:30: fatal error: libguile/libpath.h: No such file or directory
 #include "libguile/libpath.h"
  ^
compilation terminated.
Makefile:3893: recipe for target 'atomic.x' failed
make[2]: *** [atomic.x] Error 1



Maybe some dependency tuning is needed?

 

So.  Building without -j :

  make clean; make

gives gives a segfault when generating the docs


  SNARF  regex-posix.doc
  GEN  guile-procedures.texi
Uncaught exception:
Backtrace:
/bin/bash: line 1: 13428 Broken pipe cat alist.doc array-handle.doc 
array-map.doc arrays.doc async.doc atomic.doc backtrace.doc boolean.doc 
bitvectors.doc bytevectors.doc chars.doc control.doc continuations.doc 
debug.doc deprecated.doc deprecation.doc dynl.doc dynwind.doc eq.doc error.doc 
eval.doc evalext.doc expand.doc extensions.doc fdes-finalizers.doc feature.doc 
filesys.doc fluids.doc foreign.doc fports.doc gc-malloc.doc gc.doc gettext.doc 
generalized-arrays.doc generalized-vectors.doc goops.doc gsubr.doc 
guardians.doc hash.doc hashtab.doc hooks.doc i18n.doc init.doc ioext.doc 
keywords.doc list.doc load.doc macros.doc mallocs.doc memoize.doc modules.doc 
numbers.doc objprop.doc options.doc pairs.doc ports.doc print.doc procprop.doc 
procs.doc promises.doc r6rs-ports.doc random.doc rdelim.doc read.doc rw.doc 
scmsigs.doc script.doc simpos.doc smob.doc sort.doc srcprop.doc srfi-1.doc 
srfi-4.doc srfi-13.doc srfi-14.doc srfi-60.doc stackchk.doc stacks.doc 
stime.doc strings.doc strorder.doc strports.doc struct.doc symbols.doc 
syntax.doc threads.doc throw.doc trees.doc unicode.doc uniform.doc values.doc 
variable.doc vectors.doc version.doc vports.doc weak-set.doc weak-table.doc 
weak-vector.doc dynl.doc posix.doc net_db.doc socket.doc regex-posix.doc
 13429 Segmentation fault  | GUILE_AUTO_COMPILE=0 ../meta/build-env 
guild snarf-check-and-output-texi > guile-procedures.texi
Makefile:3910: recipe for target 'guile-procedures.texi' failed

This is

$ git describe
v2.2.2-504-gb5dcdf2e2

And gcc is
$ gcc --version
gcc (Debian 6.3.0-18+deb9u1) 6.3.0 20170516

On an up to date Debian 9.4 system:
$ uname -a
Linux debmetrix 4.9.0-6-amd64 #1 SMP Debian 4.9.88-1+deb9u1 (2018-05-07) x86_64 
GNU/Linux


-Dale





guile 3 update, june 2018 edition

2018-06-29 Thread Andy Wingo
Hi,

Just wanted to give an update on Guile 3 developments.  Last note was
here:

  https://lists.gnu.org/archive/html/guile-devel/2018-04/msg4.html

The news is that the VM has been completely converted over to call out
to the Guile runtime through an "intrinsics" vtable.  For some
intrinsics, the compiler will emit specialized call-intrinsic opcodes.
(There's one of these opcodes for each intrinsic function type.)  For
others that are a bit more specialized, like the intrinsic used in
call-with-prompt, the VM calls out directly to the intrinsic.

The upshot is that we're now ready to do JIT compilation.  JIT-compiled
code will use the intrinsics vtable to embed references to runtime
routines.  In some future, AOT-compiled code can keep the intrinsics
vtable in a register, and call indirectly through that register.

My current plan is that the frame overhead will still be two slots: the
saved previous FP, and the saved return address.  Right now the return
address is always a bytecode address.  In the future it will be bytecode
or native code.  Guile will keep a runtime routine marking regions of
native code so it can know if it needs to if an RA is bytecode or native
code, for debugging reasons; but in most operation, Guile won't need to
know.  The interpreter will tier up to JIT code through an adapter frame
that will do impedance matching over virtual<->physical addresses.  To
tier down to the interpreter (e.g. when JIT code calls interpreted
code), the JIT will simply return to the interpreter, which will pick up
state from the virtual IP, SP, and FP saved in the VM state.

We do walk the stack from Scheme sometimes, notably when making a
backtrace.  So, we'll make the runtime translate the JIT return
addresses to virtual return addresses in the frame API.  To Scheme, it
will be as if all things were interpreted.

This strategy relies on the JIT being a simple code generator, not an
optimizer -- the state of the stack whether JIT or interpreted is the
same.  We can consider relaxing this in the future.

My current problem is knowing when a callee has JIT code.  Say you're in
JITted function F which calls G.  Can you directly jump to G's native
code, or is G not compiled yet and you need to use the interpreter?  I
haven't solved this yet.  "Known calls" that use call-label and similar
can of course eagerly ensure their callees are JIT-compiled, at
compilation time.  Unknown calls are the problem.  I don't know whether
to consider reserving another word in scm_tc7_program objects for JIT
code.  I have avoided JIT overhead elsewhere and would like to do so
here as well!

For actual JIT code generation, I think my current plan is to import a
copy of GNU lightning into Guile's source, using git-subtree merges.
Lightning is fine for our purposes as we only need code generation, not
optimization, and it supports lots of architectures: ARM, MIPS, PPC,
SPARC, x86 / x86-64, IA64, HPPA, AArch64, S390, and Alpha.

Lightning will be built statically into libguile.  This has the
advantage that we always know the version being used, and we are able to
extend lightning without waiting for distros to pick up a new version.
Already we will need to extend it to support atomic ops.  Subtree merges
should allow us to pick up upstream improvements without too much pain.
This strategy also allows us to drop lightning in the future if that's
the right thing.  Basically from the user POV it should be transparent.
The whole thing will be behind an --enable-jit / --disable-jit configure
option.  When it is working we can consider enabling shared lightning
usage.

Happy hacking,

Andy



Guile 3 update -- more instruction explosion, intrinsics

2018-04-13 Thread Andy Wingo
Hello all :)

This mail is an update on Guile 3 progress.  Some previous updates were
here:

  https://lists.gnu.org/archive/html/guile-devel/2018-01/msg9.html
  https://lists.gnu.org/archive/html/guile-devel/2018-01/msg3.html

I took a break for a while and picked up this work again a couple weeks
ago.  I think the reason I stopped was because I got to agonizing about
how to call out to run-time routines from eventual native code.  Like,
you want to call out from native code to scm_string_set_x; or some more
specialized routine that's part of the ABI but not the API, like an
internal internal_string_set_x routine.  How do you get the pointer to
that code?  How do you manage saving the VM state and restoring it?  Can
you have custom calling conventions?  How do you preserve cross-process
sharing of code pages?

For a while I thought inline caches would be a kind of answer, but after
looking at it for a while I think I am going to punt:

  
https://wingolog.org/archives/2018/02/07/design-notes-on-inline-caches-in-guile

Guile simply isn't all that polymorphic, and the set of runtime routines
callable from native code compiled by Guile is bounded.  Better instead
to simply provide a vtable to native code that contains function
pointers to anything that might need to be callable at run-time.

So that's what I settled on.  I call them "intrinsics" (I would call
them "builtins" but that word is used for something else).  I started
moving over "macro-instructions" that can't be usefully broken apart,
like string-set!, to be intrinsic calls.  AOT-compiled native code will
compile these to vtable calls ("call [reg + offset]"), where reg holds a
pointer to the intrinsics and offset is a fixed offset.  JIT-compiled
native code can inline the intrinsic address of course.

I also pushed ahead with instruction explosion for string-length,
string-ref, the atomics, integer/char conversion, make-closure, and
f64->scm.  I made a bunch more instructions be intrinsics.  The VM is
thus now smaller and has fewer instructions whose implementations
contain internal branches, which means we're getting closer to native
code.

There are still some more instructions to push to intrinsics.  (Q: When
should an instruction be an intrinsic rather than "exploded" (lowered to
more primitive instructions and control flow)?  A: When the optimizer is
unlikely to be able to elide components of the exploded implementation.)

Then the biggest remaining task is dealing with the call instructions,
which are somewhat large still.  Probably they need exploding.  Once
that's done I think we can look to implementing a simple template method
JIT.  If the performance of that beats 2.2 (as I think it should!), then
it could be a good point to release 3.0 just like that.

Long-term I think it makes sense to do AOT compilation.  That also opens
the door for self-hosted adaptive optimization.  But, I don't know quite
how to get there and keep our portability story.  Do we keep bytecode
forever?  Only in some cases?  I don't know.  I certainly want to
minimize the amount of C that we have to maintain :)

Likewise in the medium term I think we should be actively moving library
code from C to Scheme.  With the move to intrinsics in the VM, the VM
itself is relying less and less on libguile, making the whole system
less coupled to libguile.  For Guile to prosper in the next 10 years, we
need to be able to retarget it, to WebAssembly and Racket-on-Chez and
PyPy and Graal and a whole host of other things.  The compiler is
producing low-level, fairly portable output currently, which is
appropriate to this goal, but we are limited by libguile.  So let's be
thinking about how to move much of the remaining 80KLOC of C over to
Scheme.  We won't be able to move all of it, but certainly we can move
some of it.

Happy hacking,

Andy



Re: guile 3 update: instruction explosion for pairs, vectors

2018-01-19 Thread Mikael Djurfeldt
On Tue, Jan 16, 2018 at 4:55 PM, Andy Wingo  wrote:

> Thinking more globally, there are some more issues -- one is that
> ideally we need call-site specialization.  A GF could be highly
> polymorphic globally but monomorphic for any given call site.  We need
> away to specialize.
>

Yes, but I imagine that the gain of having polymorphic inline caches
compared to just GFs will decrease the more that type dispatch can be
eliminated from compiled method bodies (the monomorphic case will be
replaced by a direct call of the IM (compiled method)). Then, of course, a
polymorphic inline cache can perhaps be regarded as an anonymous GF such
that there isn't really much difference.

Dynamically typed data structures would be the remaining main source of
type dispatch.


> Secondly, it would be nice of course to have speculative optimization,
> including speculative inlining and type specialization not only on GF
> arguments but also arguments to regular calls, types of return values,
> and so on.
>

Yes! I think that dispatch on the return values is interesting.

What I'm now going to write is based on almost zero knowledge of compiler
construction, and I still will have to learn the Guile compiler
infrastructure (where is a good start?), so please bear with me. For the
same reason, what I write might be completely obvious and well-known
already.

Imagine that we are compiling the body of a method and we arrive at an
integer addition. At some step of compilation, there has been a conversion
to CPS such that we (at some level) can regard the operation as:

 (+ a b k)

where k is the continuation. This means that k will be called with the
result of the addition:

  (k )

In a framework where essentially all procedures are GFs, also k, here, is a
GF. This allows it to dispatch on its argument such that it can treat
inums, bignums, floats and doubles differently.

Note now that in such a framework, a GF might have only one method (M) but
the instantiated/compiled methods (IMs) can still be many. If k above is
called with an inum, there will be an IM of k which is specialized to
inums. This means that the compiler later can choose operations relevant
for inums inside k.

The "exploded" (in a slightly different sense) code for + above might, in
turn, contain a branch which handles the transition into bignums at "inum
overflow". If now k above come to occur in a branch of the +-code, inlined
in an outer function, where the argument of k is guaranteed to be an inum,
then our GF dispatch elimination will replace k with with the k-IM for
inums. So, the *only* branch remaining in the code will be the overflow
check in +. (BTW, I wonder if this inlining/specialization to an outer
function could in some sense rely on type dispatch on the continuation k?)

>
> Finally I wonder that if we had the latter, if it matters so much about
> optimizing generic functions in any kind of specific way -- instead you
> could just have a generic optimizer.
>

Yes. I guess I'm mostly using my GFs as a "hook" for my thoughts on this.
The reason I do this is that I can imagine a reasonably simple
implementation where (almost) everything is a GF. :) There, the GFs would,
in some sense, work as data structures for the compiler/optimizer.

>
> Of course the speculative optimizer could work on traces instead of
> methods, and in that case we'd get a lot of this stuff for free... but
> that's a question for farther down the road.  See
> http://scheme2016.snow-fort.org/static/scheme16-paper3.pdf.
>

Yes, this is very nice and exciting work. :)

Best regards,
Mikael


guile 3 update: instruction explosion for bytevectors

2018-01-17 Thread Andy Wingo
Greets,

FYI I wrote up a new update on the road to Guile 3; as I seem to be
getting closer, it's in blog form:

  https://wingolog.org/archives/2018/01/17/instruction-explosion-in-guile

Cheers,

A



Re: guile 3 update: instruction explosion for pairs, vectors

2018-01-16 Thread Andy Wingo
On Tue 09 Jan 2018 15:30, Mikael Djurfeldt  writes:

> Maybe this is all completely obvious to you, but I want to remind,
> again, about the plans and ideas I had for GOOPS before I had to leave
> it at its rather prototypical and unfinished state:
>
> As you recall, generic functions (GFs) then carried a cache (ideas
> taken from PCL) with "instantiated methods" (IM; there is probably a
> better term and I might have called them "compiled methods" or
> "cmethods" before)---method code where the types of the arguments are
> known since each instance come from an actual invocation of the
> GF. Given a new invocation, the dispatch mechanism would just use
> argument types to look up the correct IM.
>
> I then had the idea that since we know the types of the arguments of
> the IM, a lot of the type dispatch could be eliminated within the IM
> based on flow information---very much in line with what you are doing
> here. If we now add one more piece, things get really exciting:
> Wherever there is a call to other GFs within one IM and the types of
> the arguments can be deduced at the point of the call, then the
> polymorphic type dispatch of the GF can be eliminated and we can
> replace the GF call with a direct call to the corresponding IM.

I totally agree!  You could call the compiler at runtime to produce a
type-specialized method.  There are some tricky things (how to embed a
precompiled Tree-IL representation of the method into the reified .go
file?  Possible but needs work).

Thinking more globally, there are some more issues -- one is that
ideally we need call-site specialization.  A GF could be highly
polymorphic globally but monomorphic for any given call site.  We need
away to specialize.

Secondly, it would be nice of course to have speculative optimization,
including speculative inlining and type specialization not only on GF
arguments but also arguments to regular calls, types of return values,
and so on.

Finally I wonder that if we had the latter, if it matters so much about
optimizing generic functions in any kind of specific way -- instead you
could just have a generic optimizer.

Of course the speculative optimizer could work on traces instead of
methods, and in that case we'd get a lot of this stuff for free... but
that's a question for farther down the road.  See
http://scheme2016.snow-fort.org/static/scheme16-paper3.pdf.

Cheers, and happy new year to you too!

Andy



Re: guile 3 update: instruction explosion for pairs, vectors

2018-01-09 Thread Mikael Djurfeldt
Hi,

Hi think this is a marvelous development and, for what it's worth, in the
right direction. Many, many thanks!

Maybe this is all completely obvious to you, but I want to remind, again,
about the plans and ideas I had for GOOPS before I had to leave it at its
rather prototypical and unfinished state:

As you recall, generic functions (GFs) then carried a cache (ideas taken
from PCL) with "instantiated methods" (IM; there is probably a better term
and I might have called them "compiled methods" or "cmethods"
before)---method code where the types of the arguments are known since each
instance come from an actual invocation of the GF. Given a new invocation,
the dispatch mechanism would just use argument types to look up the correct
IM.

I then had the idea that since we know the types of the arguments of the
IM, a lot of the type dispatch could be eliminated within the IM based on
flow information---very much in line with what you are doing here. If we
now add one more piece, things get really exciting: Wherever there is a
call to other GFs within one IM and the types of the arguments can be
deduced at the point of the call, then the polymorphic type dispatch of the
GF can be eliminated and we can replace the GF call with a direct call to
the corresponding IM.

Given now that most of the standard scheme functions can be regarded as
polymorphic GFs, I then imagined that most of the type dispatch in a
program could be eliminated. Actual execution would mostly be direct calls
of IMs to IMs, something which the optimizer could continue to work on,
especially if it all was represented as CPS.

Given your work here, I think that something like this could now rather
easily be implemented. That is, we re-introduce IMs, make them directly
callable, and substitute IM calls for GF calls when compiling them. I
gather that the code of IMs do not necessarily have to be hung onto GFs but
could be handled by some separate manager/data structures.

Happy new year!
Mikael


On Mon, Jan 8, 2018 at 4:01 PM, Andy Wingo  wrote:

> Hey all!
>
> This is an update along the road to Guile 3.  Check
> https://lists.gnu.org/archive/html/guile-devel/2017-11/msg00016.html for
> the previous entry.
>
> Since 25 November there have been around 100 commits or so.  Firstly I
> merged in patches from stable-2.0, including patches corresponding to
> the improvements in the Guile 2.2.3 stable series release.
>
> Then, I started to look at "instruction explosion" for vector-ref et
> al.  Basically the idea is to transform the various subcomponents of
> e.g. vector-ref into their constituent parts.  In the concrete case of
> vector-ref, we have to check that the vector is a heap object, that its
> heap object tag is "vector", we have to extract the length from the heap
> object, then we have to check that the index is an integer between 0 and
> length-1, and finally we dereference the field in the vector.
> Instruction explosion turns all of these into different primcalls and
> branches.
>
> One thing that became apparent was that with instruction explosion, we'd
> have a lot more control flow.  Information that the optimizer would
> learn in a specific way (e.g. via specialzied type inference / effects
> analysis handlers for vector-ref) would instead be learned by generic
> control flow.
>
> Concretely --
>
> scheme@(guile-user)> ,x (lambda (v i) (vector-ref v i))
> Disassembly of #:1:3 (v i)> at
> #x29f5b4c:
>
>0(assert-nargs-ee/locals 3 1);; 4 slots (2 args)   at
> (unknown file):1:3
>1(immediate-tag=? 2 7 0) ;; heap-object?   at
> (unknown file):1:17
>3(jne 23);; -> L3
>4(heap-tag=? 2 127 13)   ;; vector?
>6(jne 20);; -> L3
>7(word-ref/immediate 3 2 0)
>8(ursh/immediate 3 3 8)
>9(immediate-tag=? 1 3 2) ;; fixnum?
>   11(jne 13);; -> L2
>   12(untag-fixnum 0 1)
>   13(s64-imm   14(jl 8)  ;; -> L1
>   15(s64   16(jnl 6) ;; -> L1
>   17(mov 3 0)
>   18(uadd/immediate 3 3 1)
>   19(scm-ref 2 2 3)
>   20(handle-interrupts)
>   21(return-values 2)   ;; 1 value
> L1:
>   22(throw/value+data 1 201);; #(out-of-range "vector-ref"
> "Argument 2 out of range: ~S")
> L2:
>   24(throw/value+data 1 225);; #(wrong-type-arg
> "vector-ref" "Wrong type argument in position 2 (expecting small integer):
> ~S")
> L3:
>   26(throw/value+data 2 239);; #(wrong-type-arg
> "vector-ref" "Wrong type argument in position 1 (expecting vector): ~S")
>
> So this is a bit horrible and I need to make the disassembler do a
> better job, but anyway.  Instructions 1 through 6 check that V is a
> vector; instructions 7 and 8 extract the length; 9 and 

guile 3 update: instruction explosion for pairs, vectors

2018-01-08 Thread Andy Wingo
Hey all!

This is an update along the road to Guile 3.  Check
https://lists.gnu.org/archive/html/guile-devel/2017-11/msg00016.html for
the previous entry.

Since 25 November there have been around 100 commits or so.  Firstly I
merged in patches from stable-2.0, including patches corresponding to
the improvements in the Guile 2.2.3 stable series release.

Then, I started to look at "instruction explosion" for vector-ref et
al.  Basically the idea is to transform the various subcomponents of
e.g. vector-ref into their constituent parts.  In the concrete case of
vector-ref, we have to check that the vector is a heap object, that its
heap object tag is "vector", we have to extract the length from the heap
object, then we have to check that the index is an integer between 0 and
length-1, and finally we dereference the field in the vector.
Instruction explosion turns all of these into different primcalls and
branches.

One thing that became apparent was that with instruction explosion, we'd
have a lot more control flow.  Information that the optimizer would
learn in a specific way (e.g. via specialzied type inference / effects
analysis handlers for vector-ref) would instead be learned by generic
control flow.

Concretely --

scheme@(guile-user)> ,x (lambda (v i) (vector-ref v i))
Disassembly of #:1:3 (v i)> at 
#x29f5b4c:

   0(assert-nargs-ee/locals 3 1);; 4 slots (2 args)   at (unknown 
file):1:3
   1(immediate-tag=? 2 7 0) ;; heap-object?   at (unknown 
file):1:17
   3(jne 23);; -> L3
   4(heap-tag=? 2 127 13)   ;; vector?
   6(jne 20);; -> L3
   7(word-ref/immediate 3 2 0)  
   8(ursh/immediate 3 3 8)  
   9(immediate-tag=? 1 3 2) ;; fixnum?
  11(jne 13);; -> L2
  12(untag-fixnum 0 1)  
  13(s64-imm L1
  15(s64 L1
  17(mov 3 0)   
  18(uadd/immediate 3 3 1)  
  19(scm-ref 2 2 3) 
  20(handle-interrupts) 
  21(return-values 2)   ;; 1 value
L1:
  22(throw/value+data 1 201);; #(out-of-range "vector-ref" 
"Argument 2 out of range: ~S")
L2:
  24(throw/value+data 1 225);; #(wrong-type-arg "vector-ref" 
"Wrong type argument in position 2 (expecting small integer): ~S")
L3:
  26(throw/value+data 2 239);; #(wrong-type-arg "vector-ref" 
"Wrong type argument in position 1 (expecting vector): ~S")

So this is a bit horrible and I need to make the disassembler do a
better job, but anyway.  Instructions 1 through 6 check that V is a
vector; instructions 7 and 8 extract the length; 9 and 11 check that the
index is a fixnum, 12 extracts the fixnum value as an untagged 64-bit
integer, and 13 through 16 check that the index is in range.

L1, L2, and L3 are bailouts.  The idea here is that if this vector-ref
is followed by some other operation on the vector, we'll at least get to
elide the vector? checks, and maybe we can reuse the length extraction
too.  Et cetera.

The optimizer makes decisions like when to elide redundant checks based
on flow information.  However for historical reasons unfortunately the
"throw" terms actually did "continue" from the optimizer's perspective;
whereas the information flowing to e.g. L3 shouldn't flow at all to
instruction 7, the IR didn't have a way to denote terms that didn't
continue at all.

To fix this I had to make some changes to the IR.  On the plus side,
$throw is its own term kind now that doesn't have a continuation.  Also,
$branch is a term instead of an expression shoehorned into $continue;
$prompt is a term too.  Maybe one day we'll get a $select term for
proper case compilation.

Finally, the instruction explosion.  I refactored the Tree-IL->CPS
compiler to allow individual primcalls to have custom lowering routines,
and tightened up $primcall so that it now always continues to $kargs.
Then I added custom lowering routines for vector-ref et al, and cons and
other things.  The CPS IR refactors allowed me to remove some useless
passes (elide-values, prune-bailouts).  There were some optimizer bugs
but generally things were already in a good state; e.g. here's a vector
sum routine:

  scheme@(guile-user)> (define (v-sum v)
 (let lp ((n 0) (sum 0))
   (if (< n (vector-length v))
   (lp (+ n 1) (+ sum (vector-ref v n)))
   sum)))
  scheme@(guile-user)> ,x v-sum
  Disassembly of # at #x1fa2750:

 0(assert-nargs-ee/locals 2 3);; 5 slots (1 arg)at (unknown 
file):1:0
 1(make-short-immediate 4 2)  ;; 0
 2(immediate-tag=? 3 7 0) ;; heap-object?   at (unknown 
file):1:51
 4(jne 39);; -> L5
 5(heap-tag=? 3 

Re: guile 3 update: more number unboxing improvements

2017-11-29 Thread Stefan Monnier
>   (define (out-of-range x) (error "out of range" x))
>   (define (not-int x) (error "expected an integer" x))
>   (cond
>((fixnum? x)
> (if (<= -10 x 100)
> (* x 2)
> (out-of-range x)))
>((bignum? x)
> (if (<= -10 x 100)
> (* x 2)
> (out-of-range x)))
>(else
> (not-int x)))

Looks a bit like the result of "splitting tails", in this case,
tho selectively.


Stefan




guile 3 update: more number unboxing improvements

2017-11-29 Thread Andy Wingo
Hi,

In the last update
(https://lists.gnu.org/archive/html/guile-devel/2017-11/msg00010.html) I
talked a lot about fixnums.  This last couple weeks' work is along the
same lines.

Firstly I added support for specialized instructions that compare (in
the < or = sense) s64 and u64 values against 8-bit immediates.  Then I
started to do some "exploding" of the comparison operators: for example,
if a number is a 64-bit integer, maybe first see if it's a fixnum and
take a fast path there, otherwise call out to a scm->u64 primitive.

I was thinking maybe we would be able to inline even scm->u64 entirely,
but this turned out to be a bad idea, and indeed even the speculative
inlining of the fixnum case of e.g. < wasn't that great.  The problem is
that if you replace a simple primcall (define x FOO) with a diamond
control structure like (define x (if FIXNUM? FOO-1 FOO-2)) early in the
compiler, that makes it harder to do code motion like CSE or LICM on X,
as X now has two definitions.  It could make sense to reify a diamond
control flow more towards the compiler back-end, but not in the
beginning.  Instruction explosion only makes sense if some of the
exploded instructions can be eliminated or are themselves subject to
CSE.  If it's just exploding instructions that can never be eliminated,
probably it's better to call out to the runtime (as in the case of
scm->u64, unless it can be reduced to untag-fixnum).

By this time I had backed myself into a little corner with number
specialization, so I had to back out and fix my mess; took a week or so,
but then I was back on track, with a new idea.  

The problem I was looking to solve was to hoist a fixnum? check above
code like (= x (logand x #xff)).  This predicate will only succeed if X
is a fixnum.  I then realized it would be tricky, as it would involve
hoisting the assertion made inside logand that X is an exact integer
"above" (but to where?), and it could be that the assertion doesn't
commute with other effects so it can't be hoisted.

So I modified the problem :)  Instead I wanted to optimize this kind of
code:

(define (t x)
  (unless (exact-integer? x) (error "expected an integer" x))
  (unless (= x (logand x #xff) (error "out of range" x))
  (* x 2))

or

(define (t x)
  (unless (exact-integer? x) (error "expected an integer" x))
  (unless (<= -10 x 100) (error "out of range" x))
  (* x 2))

Here exact-integer? is effectively (or (fixnum? x) (bignum? x)), so we
have a more precise type of X flowing into the predicate.  However it's
not magical; the "=" in the first example still sees that X could be a
bignum, and likewise for the <= operations.

What you want to do here is to "devirtualize" part of this function.
This is a compiler pass that effectively inlines the concrete
implementations of a virtual operation like =, logand, or <=.  But you
don't want to devirtualize just one operation; you want to devirtualize
a trace of operations.  Like this:

  (define (out-of-range x) (error "out of range" x))
  (define (not-int x) (error "expected an integer" x))
  (cond
   ((fixnum? x)
(if (<= -10 x 100)
(* x 2)
(out-of-range x)))
   ((bignum? x)
(if (<= -10 x 100)
(* x 2)
(out-of-range x)))
   (else
(not-int x)))

This causes code growth initially, but probably causes shrinkage later;
concretely this code optimizes to:

  (define (out-of-range x) (error "out of range" x))
  (define (not-int x) (error "expected an integer" x))
  (cond
   ((fixnum? x)
(let ((x* (untag-fixnum x)))
  (if (s64<= -10 x*)
  (if (s64<= x* 100)
  (tag-fixnum (s64+ x* x*))
  (out-of-range x))
  (out-of-range x
   ((bignum? x)
(out-of-range x)
   (else
(error "expected an integer" x)))

which is indeed what we get in code:

  Disassembly of # at #xb437a0:

 0(assert-nargs-ee/locals 2 0);; 2 slots (1 arg)at (unknown 
file):10:0
 1(immediate-tag=? 0 3 2) ;; fixnum?at (unknown 
file):11:10
 3(jne 11);; -> L1
 4(untag-fixnum 1 0)at (unknown 
file):12:10
 5(s64-imm L2
 7(imm-s64 L2
 9(mov 0 1) at (unknown 
file):13:2
10(uadd 1 0 1)
11(tag-fixnum 0 1)
12(handle-interrupts) 
13(return-values 2)   ;; 1 value
  L1:
14(immediate-tag=? 0 7 0) ;; heap-object?   at (unknown 
file):11:10
16(jne 8) ;; -> L3
17(heap-tag=? 0 4095 279) ;; bignum?
19(jne 5) ;; -> L3
  L2:
20(throw/value 0 138) ;; #(misc-error #f "out of range ~S") 
at (unknown file):12:25
22(handle-interrupts) 
23(return-values 1)   ;; 0 values
  L3:
24(throw/value 0 150) ;; #(misc-error #f 

Re: guile 3 update: better reasoning about fixnums

2017-11-14 Thread David Pirotte
Hello Andy,

> An update on Guile 3 hackings over the past couple weeks.
> ...
> In summary, a lot of internal plumbing, for what appears to be preparatory 
> work
> for future stuff.

I do not have the knowledge and background to make any valuable technical 
comment,
but I wanted to thank you for all this great work you are doing on Guile's 
native
AOT compiler: this really is fantastic

Thank you!
David



pgpO2ZJpthBrO.pgp
Description: OpenPGP digital signature


guile 3 update: better reasoning about fixnums

2017-11-13 Thread Andy Wingo
Hi,

An update on Guile 3 hackings over the past couple weeks.

Firstly I made a change to the CPS language.  Before, "primcalls" --
invocations of primitives known to the compiler, some of which
ultimately compile to instructions/bytecode -- took all arguments as CPS
values.  This was even the case for primcalls that took known constant
arguments, for example a primcall that does a vector-ref of a known
index.  I made a change so that all primcalls now have an additional
slot, which is for some constant data associated with the primcall.  (If
you need two pieces of constant data, just cons up a data structure.)

The idea is that constants used by primcalls as immediates don't need to
participate in optimizations in any way -- they should not participate
in CSE, have the same lifetime as the primcall so not part of DCE
either, and don't need slot allocation.  Indirecting them through a
named $const binding is complication for no benefit.  This change should
improve compilation time and memory usage, as the number of labels and
variables goes down.  I didn't measure much though.

I built on that to add set of "throw" primcalls to which all throws and
errors compile, with corresponding VM support.  This reduces the code
size for error branches in functions that do a lot of type checking
using e.g. srfi-9 accessors; for example in Guile 2.2, an internal
procedure in the assembler (encode-X8_S8_S8_S8) compiles to 158 32-bit
words, whereas in 3.0 it is currently 126 words, even after a bit of
instruction explosion, and indeed it's a precondition for facilitating
more instruction explosion.

I also changed the compilation of "ash" to compile to "lsh", "rsh", or a
branch between the two.  This simplifies things later in the compiler.

There are new "tag-fixnum" and "untag-fixnum" instructions, that convert
between uint64_t and SCM values without branches.  Many instances of
scm->u64 etc now compile to these instead.  Some unboxed operations and
comparisons now operate in the signed range, as it's more convenient to
check for fixnums using the fixnum? primcall (which compiles to an
instance of the immediate-tag=? instruction) than it is to e.g. check
ranges of generic integers, and there is now better support throughout
the compiler for s64 values.

I removed the <=, >=, and > comparison operators; they can all be
expressed in terms of <, though looking back on this now I see that I
messed up the behavior with NaN and <= or >=.  Damn.  Will fix.

I added some signed right shift instructions, to do sign-extending right
shifts over unboxed values; and that's about it.  In summary, a lot of
internal plumbing, for what appears to be preparatory work for future
stuff.

My to-do list in the near term has a few more plumbing-type tasks:

 * Remove u64=?, as we can just use s64=? polymorphism

 * Add s64-imm=?, imm-u64= with NaN values

 * Continue with instruction explosion, starting with vector-ref

Cheers,

Andy



Re: guile 3 update

2017-10-30 Thread Christopher Allan Webber
Andy Wingo writes:

> Hi :)
>
> On Sun 22 Oct 2017 15:22, Christopher Allan Webber  
> writes:
>
>>  - Could native code compilation also be a step towards WASM, assuming
>>they lend us their GC?
>
> Regarding this question: yes!  Specifically with the "GC" proposal
> (which in reality is much more:
> https://github.com/WebAssembly/gc/blob/master/proposals/gc/Overview.md)
> I think that we will approach WASM's semantic level.  There are some
> open questions of course, like how to deal with the stack and delimited
> continuations, how to do bignums with overflow, etc; but yeah we're
> getting there.
>
> Andy

This is very exciting!



guile 3 update: lowered conditionals

2017-10-30 Thread Andy Wingo
Hi,

An update on the Guile 3 effort.  Since last week I attacked the
conditional instructions, replacing e.g. "br-if-< A B OFFSET" with a
sequence of "

Re: guile 3 update

2017-10-30 Thread Andy Wingo
Hi :)

On Sun 22 Oct 2017 15:22, Christopher Allan Webber  
writes:

>  - Could native code compilation also be a step towards WASM, assuming
>they lend us their GC?

Regarding this question: yes!  Specifically with the "GC" proposal
(which in reality is much more:
https://github.com/WebAssembly/gc/blob/master/proposals/gc/Overview.md)
I think that we will approach WASM's semantic level.  There are some
open questions of course, like how to deal with the stack and delimited
continuations, how to do bignums with overflow, etc; but yeah we're
getting there.

Andy



Re: guile 3 update

2017-10-22 Thread Christopher Allan Webber
Andy Wingo writes:

> Thoughts welcome if I have forgotten something.  Cheers :)

Super exciting, Andy!  I only have two thoughts:

 - What an exciting future we have ahead of us!
 - Could native code compilation also be a step towards WASM, assuming
   they lend us their GC?

Woo woo, keep up the good work! :)



guile 3 update

2017-10-22 Thread Andy Wingo
Hi!

I have been thinking on how to get from where we are to good native
compilation.  I think the next thing is instruction explosion.  As
described here:

https://wingolog.org/archives/2016/02/04/guile-compiler-tasks

Currently in Guile's VM there are instructions like vector-ref.
This is a little silly: there are also instructions to branch on the
type of an object (br-if-tc7 in this case), to get the vector's
length, and to do a branching integer comparison.  Really we should
replace vector-ref with a combination of these test-and-branches,
with real control flow in the function, and then the actual ref
should use some more primitive unchecked memory reference
instruction.  Optimization could end up hoisting everything but the
primitive unchecked memory reference, while preserving safety, which
would be a win.  But probably in most cases optimization wouldn't
manage to do this, which would be a lose overall because you have
more instruction dispatch.

Well, this transformation is something we need for native
compilation anyway.  I would accept a patch to do this kind of
transformation on the master branch, after version 2.2.0 has forked.
In theory this would remove most all high level instructions from
the VM, making the bytecode closer to a virtual CPU, and likewise
making it easier for the compiler to emit native code as it's
working at a lower level.

I started looking at this and stumbled when figuring out how to
explode struct-ref.  In Guile 2.2, our "struct" data type is a bit
complicated, and when you go to inline it you really realize that it
should be more simple.  So the first thing I did was make some changes
to structs.  I landed some changes in stable-2.2, as described by NEWS:

* New interfaces

** `struct-ref/unboxed' and `struct-set!/unboxed'

These procedures should be used when accessing struct fields with type
`u' (unboxed).  See "Structure Basics" in the manual, for full details.

* New deprecations

** Struct tail arrays deprecated

Guile's structures used to have a facility whereby each instance of a
vtable can contain a variable-length tail array of values.  The length
of the tail array was stored in the structure.  This facility was
originally intended to allow C code to expose raw C structures with
word-sized tail arrays to Scheme.

However, the tail array facility was confusing and doesn't work very
well.  It was very rarely used, but it insinuates itself into all
invocations of `make-struct'.  For this reason the clumsily-named
`make-struct/no-tail' procedure can actually be more elegant in actual
use, because it doesn't have a random `0' argument stuck in the middle.

Tail arrays also inhibit optimization by allowing instances to affect
their shapes.  In the absence of tail arrays, all instances of a given
vtable have the same number and kinds of fields.  This uniformity can be
exploited by the runtime and the optimizer.  The presence of tail arrays
make some of these optimizations more difficult.

Finally, the tail array facility is ad-hoc and does not compose with the
rest of Guile.  If a Guile user wants an array with user-specified
length, it's best to use a vector.  It is more clear in the code, and
the standard optimization techniques will do a good job with it.

For all of these reasons, tail arrays are deprecated in Guile 2.2 and
will be removed from Guile 3.0.  Likewise, `make-struct' /
`scm_make_struct' is deprecated in favor of `make-struct/no-tail' /
`scm_make_struct_no_tail'.  Perhaps one day we will be able to reclaim
the `make-struct' name!

** Struct "self" slots deprecated

It used to be that you could make a structure vtable that had "self"
slots.  Instances of that vtable would have those slots initialized to
the instance itself.  This can be useful in C code where you might have
a pointer to the data array, and want to get the `SCM' handle for the
structure.  However this was a little used complication without any use
vin Scheme code.  To replace it, just use "p" slots and initialize the
slot values manually on initialization.

** Struct fields with opaque ("o") protection deprecated

Struct fields are declared with a "protection", meaning read-only ('r'),
read-write ('w'), or opaque ('o').  There is also "hidden" ('o') which
is read-write but which isn't initialized by arguments passed to
`make-struct/no-tail', but that's a detail.  Opaque struct fields were
used to allocate storage in a struct that could only be accessed by C.
This facility was very rarely used (unused in Guile itself) but now that
we are implementing more and more in Scheme, it is completely useless.

To enforce permissions on struct fields, instead layer on an