Make the optimizer recognize and track `make-struct-property-type`
values, and use that information to recognize `make-struct-type`
calls that will defnitely succeed because a property that hs no
guard is given a value in the list of properties.
Combined with the change to require-keyword expansion, this
change allows the optimizer to inline `f` in
(define (g y)
(f #:x y))
(define (f #:x x)
(list x))
because the `make-struct-type` that appears between `g` and `f`
is determined to have no side-effect that would prevent `f` from
having its expected value.
Make the definition of a function with a required keyword expand in a
way that allows the optimizer to recognize it as a form that has no
errors or externally visible side effects.
The old expansion of
(define (f #:x x) ...)
included
(define lifted-constructor (make-required ....))
(define f (lifted-constructor (lambda ....) ....))
where `make-required` calls `make-struct-type` and returns just the
constructor.
The new expansion instead has
(define-values (_ lifted-constructor _ _ _)
(make-struct-type ....))
(define f (lifted-constructor (lambda ....) ....))
In other words, `make-required` is inlined by macro expansion,
so that the optimizer will be able to see it and eventually
conclude that no side effects have taken place.
When a module defines and exports an identifier at two phases,
and when another module imports both of them at the same phase,
an error was not reported as it should have been.
With this option, FFI calls always block until scheme_check_foreign_work
is called by the program embedding Racket.
This is needed for embedding Racket into contexts where you do not
control the event loop, need Racket to make FFI calls, and those FFI
calls must occur on a thread within the event loop. A good example of
this is with OpenGL FFI calls that require the current thread to hold
the OpenGL/EGL context.
An important point about this is that scheme_check_foreign_work will
only execute a single FFI call. So if this is used for OpenGL rendering,
you'll want to run it a lot.
Some expressions are omittable only when the arguments have certain types.
In this case the application is marked with APPN_FLAG_OMITTABLE instead of relaying on the flags of the primitive.
The optimizer can't use this flag to move the expression inside a lamba or across a potential continuation capture, unlike other omittable expressions. They can be moved
only in more restricted conditions.
For example, in this program
#lang racket/base
(define n 10000)
(define m 10000)
(time
(define xs (build-list n (lambda (x) 0)))
(length xs)
(define ws (list->vector xs)) ; <-- omittable
(for ([i (in-range m)])
(vector-ref ws 0))) ; <-- ws is used once
If the optimizer moves the expression in the definition of ws inside the recursive
lambda that is created by the for, then the code is equivalent to:
#lang racket/base
(define n 10000)
(define m 10000)
(time
(define xs (build-list n (lambda (x) 0)))
(length xs)
(for ([i (in-range m)])
(vector-ref (list->vector xs) 0))) ; <-- moved here
And the new code is O(n*m) instead of O(n+m). This example is a minimized version
of the function kde from the plot package, where n=m and the bug changed the run
time from linear to quadratic.
The application of some procedures are omittables when the arguments have
certain properties. Check the arity of the procedure before marking the application as omittable.
The only case that appears to be relevant is the expression (-).
The relevant predicates are almost disjoint. The superposition
is solved with predicate_implies and predicate_implies_not.
This is also valid considering the equivalence classes modulo
eqv? and equal?. So if the optimizer knows that two expressions
X and Y have different relevant types, then it can reduce
(equal? X Y) ==> (begin X Y #f).
Changes signatures in `syntax/modcode` to accept `path-string?` arguments
instead of `path?`.
Before, the docs listed `path-string?` but the contracts used `path?`.
Now they agree.
The optimizer now makes more choices based on imported structure-type
info that thet validator needs to reconstruct, so pass that
information all the way through.
Allow a `struct` form to be recognized when it provides
a number as the 8th argument to `make-struct-type`. In
particular, that change allows the construction of
optional-keyword functions to be recognized as a
purely functional operation.
Also, allow the optimizer to use information about imports
when deciding whether a module-level form is functional.
It's ok to use that information, because the validator has
it, too.
This combination of changes allows something like
(define (f #:optional [x #f])
(later))
(define (later) ....)
to compile to a reference to `later` wihout a check.
Fixing dead-code cleanup in the letrec-check pass exposed
a bug in a part of the letrec check interpretation that is
analogous to copy propagation. The copy's representation
now refers to the original variable, instead of copying
the current set of deferrals (which is wrong if the original
is a `letrec`-bound variable that hasn't yet accumulated
its closures).
Due to an obvious problem in the setup, the letrec-check pass wasn't
running an intended dead-code pruning pass. Correcting the problem
cuases one test in "optimize.rktl" to change, because the letrec-check
pass can see more in one case than thanother.
(Problem discovered by accidentally fixing the setup in a Racket
branch based on "linklets".)
Along with the `PLT_COMPILED_FILE_CHECK` environment variable, allows
the timestamp check to be disabled when deciding whether to use a
compiled bytecode file.
In accomodating this change, `raco make` and `raco setup` in all modes
check whether the SHA1 hash of a module source matches the one
recorded in its ".dep" file, even if the timestamp on the bytecode
file is newer. (If the compile-file check mode is 'exists, the
timestamp is completely ignored.)