Skip to content
This repository was archived by the owner on Sep 13, 2019. It is now read-only.

Commit aaf5b9a

Browse files
committed
expander: track inspector access through cross-linklet inlining
1 parent 40db7af commit aaf5b9a

File tree

13 files changed

+11196
-10298
lines changed

13 files changed

+11196
-10298
lines changed

pkgs/racket-test-core/tests/racket/modprot.rktl

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,4 +418,13 @@
418418

419419
;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
420420

421+
(parameterize ([current-namespace (make-base-namespace)]
422+
[current-code-inspector (make-inspector)])
423+
(eval
424+
;; This compilation is intended to inline a call to `gen-for-each`,
425+
;; and the test is meant to ensure that the reference is allowed
426+
(compile '(lambda (f) (for-each f '(1 2 3 4 5))))))
427+
428+
;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
429+
421430
(report-errs)

racket/src/expander/compile/compiled-in-memory.rkt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
;; variables from that imported linklet; each member of the
2323
;; inner list is #f or an extra inspector that has been carried
2424
;; over from the originally compiled reference
25-
phase-to-link-extra-inspectorsss ; phase -> list of lists
25+
phase-to-link-extra-inspectorsss ; phase -> list of hash tables to "extra inspectors"
2626
;; For using existing values directly, instead of unmarshaling:
2727
mpis
2828
syntax-literals
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
#lang racket/base
2+
(require "../common/set.rkt"
3+
"module-use.rkt"
4+
"../host/linklet.rkt")
5+
6+
(provide extra-inspectors-allow?
7+
8+
module-uses-add-extra-inspectorsss
9+
module-uses-strip-extra-inspectorsss
10+
module-uses-extract-extra-inspectorsss
11+
module-use*-declaration-inspector!
12+
13+
module-use+extra-inspectors
14+
module-use-merge-extra-inspectorss!)
15+
16+
;; Compilation leaves a linklet with some "or" inspectors that apply
17+
;; to the whole linklet plus (potentially) some "and" inspectors for
18+
;; each invdidual binding. Cross-module optimization can move this
19+
;; or+and combination to the "and" part of a different module, so we
20+
;; use functions in general
21+
22+
(define (extra-inspectors-allow? extra-inspectors guard-insp)
23+
(cond
24+
[(not extra-inspectors) #f]
25+
[(set? extra-inspectors)
26+
(for/and ([extra-insp (in-set extra-inspectors)])
27+
(inspector-superior? extra-insp guard-insp))]
28+
[(procedure? extra-inspectors)
29+
(extra-inspectors guard-insp)]
30+
[else
31+
(error 'extra-inspectors-allow?
32+
"unknown representation of extra inspectors: ~e"
33+
extra-inspectors)]))
34+
35+
(define (extra-inspectors-merge extra-inspectors-1 extra-inspectors-2)
36+
(cond
37+
[(or (not extra-inspectors-1)
38+
(not extra-inspectors-2))
39+
#f]
40+
[(and (set? extra-inspectors-1)
41+
(set? extra-inspectors-2))
42+
(set-union extra-inspectors-1 extra-inspectors-2)]
43+
[else
44+
(lambda (guard-insp)
45+
(and (extra-inspectors-allow? extra-inspectors-1 guard-insp)
46+
(extra-inspectors-allow? extra-inspectors-2 guard-insp)))]))
47+
48+
;; ----------------------------------------
49+
50+
;; While compiling a linklet, we start out with parallel lists of
51+
;; module uses and extra inspectors, but it's more convenient to
52+
;; manage inlining if we put those together. We may need to merge
53+
;; extra-inspector sets while preserving `eq?` identity of the
54+
;; `module-use*`, so that field is mutable.
55+
(struct module-use* module-use ([extra-inspectorss #:mutable]
56+
[self-inspector #:mutable]))
57+
58+
;; Parallel lists into one list
59+
(define (module-uses-add-extra-inspectorsss mus extra-inspectorsss)
60+
(cond
61+
[extra-inspectorsss
62+
(for/list ([mu (in-list mus)]
63+
[extra-inspectorss (in-list extra-inspectorsss)])
64+
(module-use* (module-use-module mu)
65+
(module-use-phase mu)
66+
extra-inspectorss
67+
#f))]
68+
[else
69+
(for/list ([mu (in-list mus)])
70+
(module-use* (module-use-module mu)
71+
(module-use-phase mu)
72+
#f
73+
#f))]))
74+
75+
;; Split the list back into one of the parallel lists
76+
(define (module-uses-strip-extra-inspectorsss mu*s)
77+
(for/list ([mu* (in-list mu*s)])
78+
(module-use (module-use-module mu*)
79+
(module-use-phase mu*))))
80+
81+
;; Split the list back into the other parallel list --- but also check
82+
;; for inlining-introduced references that must have formerly been
83+
;; module-internal references (i.e., referenecs that are not already
84+
;; recorded as imports)
85+
(define (module-uses-extract-extra-inspectorsss mu*s linklet check-inlined-reference? skip-n)
86+
(cond
87+
[(not check-inlined-reference?)
88+
(for/list ([mu* (in-list mu*s)])
89+
(module-use*-extra-inspectorss mu*))]
90+
[else
91+
(for/list ([mu* (in-list mu*s)]
92+
[imports (in-list (list-tail (linklet-import-variables linklet) skip-n))])
93+
(define extra-inspectorss (module-use*-extra-inspectorss mu*))
94+
(for/fold ([extra-inspectorss extra-inspectorss]) ([import (in-list imports)])
95+
(cond
96+
[(eq? (hash-ref extra-inspectorss import '#:not-recorded) '#:not-recorded)
97+
(hash-set extra-inspectorss import (set (module-use*-self-inspector mu*)))]
98+
[else extra-inspectorss])))]))
99+
100+
(define (module-use*-declaration-inspector! mu* insp)
101+
(set-module-use*-self-inspector! mu* insp))
102+
103+
;; ----------------------------------------
104+
105+
(define (module-use+extra-inspectors mpi phase imports inspector extra-inspector extra-inspectorss)
106+
;; If `inspector` or `extra-inspector` is not subsumed by the
107+
;; current inspector, then propagate it by adding to each imported
108+
;; variable's set of "or" inspectors
109+
(define now-inspector (current-code-inspector))
110+
(define add-insp? (and inspector (inspector-superior? inspector now-inspector)))
111+
(define add-extra-insp? (and extra-inspector (inspector-superior? extra-inspector now-inspector)))
112+
(define new-extra-inspectorss
113+
(cond
114+
[(or add-insp? add-extra-insp?)
115+
(for/hash ([import (in-list imports)])
116+
(values import
117+
(let ([extra-inspectors (and extra-inspectorss
118+
(hash-ref extra-inspectorss import #f))])
119+
(lambda (guard-insp)
120+
(or (and add-insp? (inspector-superior? inspector guard-insp))
121+
(and add-extra-insp? (inspector-superior? extra-inspector guard-insp))
122+
(extra-inspectors-allow? extra-inspectors guard-insp))))))]
123+
[else
124+
;; Make sure every import is mapped, because w may need to distinguish
125+
;; between "not accessed" and "accessed without extra inspectors"
126+
(for/fold ([extra-inspectorss (or extra-inspectorss (seteq))]) ([import (in-list imports)])
127+
(if (hash-ref extra-inspectorss import #f)
128+
extra-inspectorss
129+
(hash-set extra-inspectorss import #f)))]))
130+
(module-use* mpi phase new-extra-inspectorss #f))
131+
132+
;; Merge inspectors from potentially different paths through imported linklets
133+
(define (module-use-merge-extra-inspectorss! existing-mu* mu*)
134+
(define extra-inspectorss (module-use*-extra-inspectorss mu*))
135+
(define existing-extra-inspectorss (module-use*-extra-inspectorss existing-mu*))
136+
(define new-extra-inspectorss
137+
(for/fold ([new-extra-inspectorss existing-extra-inspectorss]) ([(sym extra-inspectors) (in-hash extra-inspectorss)])
138+
(hash-set new-extra-inspectorss
139+
sym
140+
(extra-inspectors-merge extra-inspectors
141+
(hash-ref new-extra-inspectorss sym (seteq))))))
142+
(set-module-use*-extra-inspectorss! existing-mu* new-extra-inspectorss))

racket/src/expander/compile/form.rkt

Lines changed: 74 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
"instance.rkt"
2323
"namespace-scope.rkt"
2424
"expr.rkt"
25+
"extra-inspector.rkt"
2526
"correlate.rkt")
2627

2728
(provide compile-forms
@@ -230,20 +231,22 @@
230231
(for/hash ([phase (in-list phases-in-order)])
231232
(define header (hash-ref phase-to-header phase #f))
232233
(define-values (link-module-uses imports extra-inspectorsss def-decls)
233-
(generate-links+imports header phase cctx))
234+
(generate-links+imports header phase cctx cross-linklet-inlining?))
234235
(values phase (link-info link-module-uses imports extra-inspectorsss def-decls))))
235236

236237
;; Generate the phase-specific linking units
237-
(define body-linklets+module-uses
238+
(define body-linklets+module-use*s
238239
(for/hasheq ([phase (in-list phases-in-order)])
239240
(define bodys (hash-ref phase-to-body phase))
240241
(define li (hash-ref phase-to-link-info phase))
241242
(define binding-sym-to-define-sym
242243
(header-binding-sym-to-define-sym (hash-ref phase-to-header phase)))
243-
(define module-uses (link-info-link-module-uses li))
244+
(define module-use*s
245+
(module-uses-add-extra-inspectorsss (link-info-link-module-uses li)
246+
(link-info-extra-inspectorsss li)))
244247
;; Compile the linklet with support for cross-module inlining, which
245248
;; means that the set of imports can change:
246-
(define-values (linklet new-module-uses)
249+
(define-values (linklet new-module-use*s)
247250
(performance-region
248251
['compile '_ 'linklet]
249252
((if to-source?
@@ -271,31 +274,37 @@
271274
;; as keys, plus #f or an instance (=> cannot be pruned) for
272275
;; each boilerplate linklet
273276
(list->vector (append body-import-instances
274-
(link-info-link-module-uses li)))
277+
module-use*s))
275278
;; To complete cross-module support, map a key (which is a `module-use`)
276279
;; to a linklet and an optional vector of keys for that linklet's
277280
;; imports:
278281
(make-module-use-to-linklet cross-linklet-inlining?
279282
(compile-context-namespace cctx)
280283
get-module-linklet-info
281-
(link-info-link-module-uses li)))))
282-
(values phase (cons linklet (list-tail (vector->list new-module-uses)
284+
module-use*s))))
285+
(values phase (cons linklet (list-tail (vector->list new-module-use*s)
283286
(length body-imports))))))
284287

285288
(define body-linklets
286-
(for/hasheq ([(phase l+mus) (in-hash body-linklets+module-uses)])
287-
(values phase (car l+mus))))
289+
(for/hasheq ([(phase l+mu*s) (in-hash body-linklets+module-use*s)])
290+
(values phase (car l+mu*s))))
288291

289292
(define phase-to-link-module-uses
290-
(for/hasheq ([(phase l+mus) (in-hash body-linklets+module-uses)])
291-
(values phase (cdr l+mus))))
292-
293+
(for/hasheq ([(phase l+mu*s) (in-hash body-linklets+module-use*s)])
294+
(values phase (module-uses-strip-extra-inspectorsss (cdr l+mu*s)))))
295+
293296
(define phase-to-link-module-uses-expr
294297
(serialize-phase-to-link-module-uses phase-to-link-module-uses mpis))
295298

296299
(define phase-to-link-extra-inspectorsss
297-
(for/hash ([(phase li) (in-hash phase-to-link-info)])
298-
(values phase (link-info-extra-inspectorsss li))))
300+
(for*/hash ([(phase l+mu*s) (in-hash body-linklets+module-use*s)]
301+
[(extra-inspectorsss) (in-value (module-uses-extract-extra-inspectorsss
302+
(cdr l+mu*s)
303+
(car l+mu*s)
304+
cross-linklet-inlining?
305+
(length body-imports)))]
306+
#:when extra-inspectorsss)
307+
(values phase extra-inspectorsss)))
299308

300309
(values body-linklets ; main compilation result
301310
min-phase
@@ -376,51 +385,77 @@
376385

377386
;; ----------------------------------------
378387

379-
(define (make-module-use-to-linklet cross-linklet-inlining? ns get-module-linklet-info init-mus)
388+
(define (make-module-use-to-linklet cross-linklet-inlining? ns get-module-linklet-info init-mu*s)
380389
;; Inlining might reach the same module though different indirections;
381390
;; use a consistent `module-use` value so that the compiler knows to
382391
;; collapse them to a single import
383-
(define mu-intern-table (make-hash))
384-
(define (intern-module-use mu)
385-
(define mod-name (module-path-index-resolve (module-use-module mu)))
386-
(or (hash-ref mu-intern-table (cons mod-name (module-use-phase mu)) #f)
387-
(begin
388-
(hash-set! mu-intern-table (cons mod-name (module-use-phase mu)) mu)
389-
mu)))
390-
(for-each intern-module-use init-mus)
392+
(define mu*-intern-table (make-hash))
393+
(define (intern-module-use* mu*)
394+
(define mod-name (module-path-index-resolve (module-use-module mu*)))
395+
(define existing-mu* (hash-ref mu*-intern-table (cons mod-name (module-use-phase mu*)) #f))
396+
(cond
397+
[existing-mu*
398+
(module-use-merge-extra-inspectorss! existing-mu* mu*)
399+
existing-mu*]
400+
[else
401+
(hash-set! mu*-intern-table (cons mod-name (module-use-phase mu*)) mu*)
402+
mu*]))
403+
(for ([mu* (in-list init-mu*s)])
404+
(intern-module-use* mu*))
391405
;; The callback function supplied to `compile-linklet`:
392-
(lambda (mu)
406+
(lambda (mu*-or-instance)
393407
(cond
394-
[(instance? mu)
408+
[(instance? mu*-or-instance)
395409
;; An instance represents a boilerplate linklet. An instance
396410
;; doesn't enable inlining (and we don't want inlining, since
397411
;; that would change the overall protocol for module or
398-
;; top-level linklets], but it can describe shapes.
399-
(values mu #f)]
412+
;; top-level linklets), but it can describe shapes.
413+
(values mu*-or-instance #f)]
400414
[(not cross-linklet-inlining?)
401415
;; Although we let instances through, because that's cheap,
402416
;; don't track down linklets and allow inlining of functions
403417
(values #f #f)]
404-
[mu
405-
(define mod-name (module-path-index-resolve (module-use-module mu)))
406-
(define mli (or (get-module-linklet-info mod-name (module-use-phase mu))
418+
[mu*-or-instance
419+
(define mu* mu*-or-instance)
420+
(define mod-name (module-path-index-resolve (module-use-module mu*)))
421+
(define mli (or (get-module-linklet-info mod-name (module-use-phase mu*))
407422
(namespace->module-linklet-info ns
408423
mod-name
409-
(module-use-phase mu))))
424+
(module-use-phase mu*))))
425+
(when mli
426+
;; Record the module's declaration-time inspector, for use
427+
;; later recording extra inspectors for inlined referenced
428+
(module-use*-declaration-inspector! mu* (module-linklet-info-inspector mli)))
410429
(if mli
411430
;; Found info for inlining:
412431
(values (module-linklet-info-linklet-or-instance mli)
413-
(and (module-linklet-info-module-uses mli)
432+
(and (module-linklet-info-module-uses mli) ; => linklet
414433
(list->vector
415434
(append
416435
'(#f #f) ; boilerplate imports common to all modules
417-
(for/list ([sub-mu (in-list (module-linklet-info-module-uses mli))])
418-
(intern-module-use
419-
(module-use (module-path-index-shift
420-
(module-use-module sub-mu)
421-
(module-linklet-info-self mli)
422-
(module-use-module mu))
423-
(module-use-phase sub-mu))))))))
436+
(let ([mus (module-linklet-info-module-uses mli)]
437+
[extra-inspectorsss (module-linklet-info-extra-inspectorsss mli)])
438+
(for/list ([sub-mu (in-list mus)]
439+
[imports (in-list
440+
(linklet-import-variables
441+
(module-linklet-info-linklet-or-instance mli)))]
442+
[extra-inspectorss (in-list (or extra-inspectorsss
443+
;; a list of the right length:
444+
mus))])
445+
(intern-module-use*
446+
(module-use+extra-inspectors (module-path-index-shift
447+
(module-use-module sub-mu)
448+
(module-linklet-info-self mli)
449+
(module-use-module mu*))
450+
(module-use-phase sub-mu)
451+
;; The remaining arguments are used to
452+
;; make an `module-use*` instead of a
453+
;; plain `module-use`
454+
imports
455+
(module-linklet-info-inspector mli)
456+
(module-linklet-info-extra-inspector mli)
457+
(and extra-inspectorsss
458+
extra-inspectorss)))))))))
424459
;; Didn't find info, for some reason:
425460
(values #f #f))]
426461
[else

0 commit comments

Comments
 (0)