-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrtl-xform.scm
562 lines (487 loc) · 17.9 KB
/
rtl-xform.scm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
;; Various RTL transformations.
;;
;; Copyright (C) 2000, 2009, 2010 Red Hat, Inc.
;; This file is part of CGEN.
;; See file COPYING.CGEN for details.
;;
;; In particular:
;; rtx-simplify
;; rtx-solve
;; rtx-trim-for-doc
;; Utility to verify there are no DFLT modes present in EXPR
;; Subroutine of rtx-verify-no-dflt-modes to simplify it.
;; This is the EXPR-FN argument to rtl-traverse.
(define (/rtx-verify-no-dflt-modes-expr-fn rtx-obj expr parent-expr op-pos
tstate appstuff)
(if (eq? (rtx-mode expr) 'DFLT)
(tstate-error tstate "DFLT mode present" expr))
;; Leave EXPR unchanged and continue.
#f
)
;; Entry point. Verify there are no DFLT modes in EXPR.
(define (rtx-verify-no-dflt-modes context expr)
(rtx-traverse context #f expr /rtx-verify-no-dflt-modes-expr-fn #f)
)
;; rtx-simplify (and supporting cast)
; Subroutine of /rtx-simplify-expr-fn to compare two values for equality.
; If both are constants and they're equal return #f/#t.
; INVERT? = #f -> return #t if equal, #t -> return #f if equal.
; Returns 'unknown if either argument is not a constant.
(define (/rtx-const-equal arg0 arg1 invert?)
(if (and (rtx-constant? arg0)
(rtx-constant? arg1))
(if invert?
(!= (rtx-constant-value arg0)
(rtx-constant-value arg1))
(= (rtx-constant-value arg0)
(rtx-constant-value arg1)))
'unknown)
)
; Subroutine of /rtx-simplify-expr-fn to see if MAYBE-CONST is
; an element of NUMBER-LIST.
; NUMBER-LIST is a `number-list' rtx.
; INVERT? is #t if looking for non-membership.
; #f/#t is only returned for definitive answers.
; If INVERT? is #f:
; - return #f if MAYBE-CONST is not in NUMBER-LIST
; - return #t if MAYBE-CONST is in NUMBER-LIST and it has only one member
; - return 'member if MAYBE-CONST is in NUMBER-LIST and it has many members
; - otherwise return 'unknown
; If INVERT? is #t:
; - return #t if MAYBE-CONST is not in NUMBER-LIST
; - return #f if MAYBE-CONST is in NUMBER-LIST and it has only one member
; - return 'member if MAYBE-CONST is in NUMBER-LIST and it has many members
; - otherwise return 'unknown
(define (/rtx-const-list-equal maybe-const number-list invert?)
(assert (rtx-kind? 'number-list number-list))
(if (rtx-constant? maybe-const)
(let ((values (rtx-number-list-values number-list)))
(if invert?
(if (memq (rtx-constant-value maybe-const) values)
(if (= (length values) 1)
#f
'member)
#t)
(if (memq (rtx-constant-value maybe-const) values)
(if (= (length values) 1)
#t
'member)
#f)))
'unknown)
)
; Subroutine of /rtx-simplify-expr-fn to simplify an eq-attr of (current-mach).
; CONTEXT is a <context> object or #f if there is none.
(define (/rtx-simplify-eq-attr-mach rtx context)
(let ((attr (rtx-eq-attr-attr rtx))
(value (rtx-eq-attr-value rtx)))
; If all currently selected machs will yield the same value
; for the attribute, we can simplify.
(let ((values (map (lambda (m)
(obj-attr-value m attr))
(current-mach-list))))
; Ensure at least one mach is selected.
(if (null? values)
(context-error context
"While simplifying rtl"
"no machs selected"
(rtx-strdump rtx)))
; All values equal to the first one?
(if (all-true? (map (lambda (val)
(equal? val (car values)))
values))
(if (equal? value
; Convert internal boolean attribute value
; #f/#t to external value FALSE/TRUE.
; FIXME:revisit.
(case (car values)
((#f) 'FALSE)
((#t) 'TRUE)
(else (car values))))
(rtx-true)
(rtx-false))
; couldn't simplify
rtx)))
)
; Subroutine of /rtx-simplify-expr-fn to simplify an eq-attr of (current-insn).
(define (/rtx-simplify-eq-attr-insn rtx insn context)
(let ((attr (rtx-eq-attr-attr rtx))
(value (rtx-eq-attr-value rtx)))
(if (not (insn? insn))
(context-error context
"While simplifying rtl"
"No current insn for `(current-insn)'"
(rtx-strdump rtx)))
(let ((attr-value (obj-attr-value insn attr)))
(if (eq? value attr-value)
(rtx-true)
(rtx-false))))
)
; Subroutine of rtx-simplify.
; This is the EXPR-FN argument to rtx-traverse.
(define (/rtx-simplify-expr-fn rtx-obj expr parent-expr op-pos
tstate appstuff)
;(display "Processing ") (display (rtx-dump expr)) (newline)
(case (rtx-name expr)
((not)
(let* ((arg (/rtx-traverse (rtx-alu-op-arg expr 0)
'RTX expr 1 tstate appstuff))
(no-side-effects? (not (rtx-side-effects? arg))))
(cond ((and no-side-effects? (rtx-false? arg))
(rtx-true))
((and no-side-effects? (rtx-true? arg))
(rtx-false))
(else (rtx-make 'not (rtx-alu-op-mode expr) arg)))))
((orif)
(let ((arg0 (/rtx-traverse (rtx-boolif-op-arg expr 0)
'RTX expr 0 tstate appstuff))
(arg1 (/rtx-traverse (rtx-boolif-op-arg expr 1)
'RTX expr 1 tstate appstuff)))
(let ((no-side-effects-0? (not (rtx-side-effects? arg0)))
(no-side-effects-1? (not (rtx-side-effects? arg1))))
(cond ((and no-side-effects-0? (rtx-true? arg0))
(rtx-true))
((and no-side-effects-0? (rtx-false? arg0))
(rtx-canonical-bool arg1))
; Value of arg0 is unknown or has side-effects.
((and no-side-effects-1? (rtx-true? arg1))
(if no-side-effects-0?
(rtx-true)
(rtx-make 'orif arg0 (rtx-true))))
((and no-side-effects-1? (rtx-false? arg1))
arg0)
(else
(rtx-make 'orif arg0 arg1))))))
((andif)
(let ((arg0 (/rtx-traverse (rtx-boolif-op-arg expr 0)
'RTX expr 0 tstate appstuff))
(arg1 (/rtx-traverse (rtx-boolif-op-arg expr 1)
'RTX expr 1 tstate appstuff)))
(let ((no-side-effects-0? (not (rtx-side-effects? arg0)))
(no-side-effects-1? (not (rtx-side-effects? arg1))))
(cond ((and no-side-effects-0? (rtx-false? arg0))
(rtx-false))
((and no-side-effects-0? (rtx-true? arg0))
(rtx-canonical-bool arg1))
; Value of arg0 is unknown or has side-effects.
((and no-side-effects-1? (rtx-false? arg1))
(if no-side-effects-0?
(rtx-false)
(rtx-make 'andif arg0 (rtx-false))))
((and no-side-effects-1? (rtx-true? arg1))
arg0)
(else
(rtx-make 'andif arg0 arg1))))))
; Fold if's to their then or else part if we can determine the
; result of the test.
((if)
(let ((test
; ??? Was this but that calls rtx-traverse again which
; resets the temp stack!
; (rtx-simplify context (caddr expr))))
(/rtx-traverse (rtx-if-test expr) 'RTX expr 1 tstate appstuff)))
(cond ((rtx-true? test)
(/rtx-traverse (rtx-if-then expr) 'RTX expr 2 tstate appstuff))
((rtx-false? test)
(if (rtx-if-else expr)
(/rtx-traverse (rtx-if-else expr) 'RTX expr 3 tstate appstuff)
; Sanity check, mode must be VOID.
; FIXME: DFLT can no longer appear
(if (or (mode:eq? 'DFLT (rtx-mode expr))
(mode:eq? 'VOID (rtx-mode expr)))
(rtx-make 'nop 'VOID)
(error "rtx-simplify: non-void-mode `if' missing `else' part" expr))))
; Can't simplify.
; We could traverse the then/else clauses here, but it's simpler
; to have our caller do it (by returning #f).
; The cost is retraversing `test'.
(else #f))))
((eq ne)
(let ((name (rtx-name expr))
(cmp-mode (rtx-cmp-op-mode expr))
(arg0 (/rtx-traverse (rtx-cmp-op-arg expr 0) 'RTX
expr 1 tstate appstuff))
(arg1 (/rtx-traverse (rtx-cmp-op-arg expr 1) 'RTX
expr 2 tstate appstuff)))
(if (or (rtx-side-effects? arg0) (rtx-side-effects? arg1))
(rtx-make name cmp-mode arg0 arg1)
(case (/rtx-const-equal arg0 arg1 (rtx-kind? 'ne expr))
((#f) (rtx-false))
((#t) (rtx-true))
(else
; That didn't work. See if we have an ifield/operand with a
; known range of values. We don't need to check for a known
; single value, that is handled below.
(case (rtx-name arg0)
((ifield)
(let ((known-val (tstate-known-lookup tstate
(rtx-ifield-name arg0))))
(if (and known-val (rtx-kind? 'number-list known-val))
(case (/rtx-const-list-equal arg1 known-val
(rtx-kind? 'ne expr))
((#f) (rtx-false))
((#t) (rtx-true))
(else
(rtx-make name cmp-mode arg0 arg1)))
(rtx-make name cmp-mode arg0 arg1))))
((operand)
(let ((known-val (tstate-known-lookup tstate
(rtx-operand-name arg0))))
(if (and known-val (rtx-kind? 'number-list known-val))
(case (/rtx-const-list-equal arg1 known-val
(rtx-kind? 'ne expr))
((#f) (rtx-false))
((#t) (rtx-true))
(else
(rtx-make name cmp-mode arg0 arg1)))
(rtx-make name cmp-mode arg0 arg1))))
(else
(rtx-make name cmp-mode arg0 arg1))))))))
; Recognize attribute requests of current-insn, current-mach.
((eq-attr)
(cond ((rtx-kind? 'current-mach (rtx-eq-attr-owner expr))
(/rtx-simplify-eq-attr-mach expr (tstate-context tstate)))
((rtx-kind? 'current-insn (rtx-eq-attr-owner expr))
(/rtx-simplify-eq-attr-insn expr (tstate-owner tstate) (tstate-context tstate)))
(else expr)))
((ifield)
(let ((known-val (tstate-known-lookup tstate (rtx-ifield-name expr))))
; If the value is a single number, return that.
; It can be one of several, represented as a number list.
(if (and known-val (rtx-constant? known-val))
known-val ; (rtx-make 'const 'INT known-val)
#f)))
((operand)
(let ((known-val (tstate-known-lookup tstate (rtx-operand-name expr))))
; If the value is a single number, return that.
; It can be one of several, represented as a number list.
(if (and known-val (rtx-constant? known-val))
known-val ; (rtx-make 'const 'INT known-val)
#f)))
((closure)
(let ((simplified-expr (/rtx-traverse (rtx-closure-expr expr)
'RTX expr 2 tstate appstuff)))
simplified-expr))
; Leave EXPR unchanged and continue.
(else #f))
)
; Simplify an rtl expression.
;
; EXPR must be in canonical source form.
; The result is a possibly simplified EXPR, still in source form.
;
; CONTEXT is a <context> object or #f, used for error messages.
; OWNER is the owner of the expression (e.g. <insn>) or #f if there is none.
;
; KNOWN is an alist of known values. Each element is (name . value) where
; NAME is an ifield/operand name and VALUE is a const/number-list rtx.
; FIXME: Need ranges, later.
;
; The following operations are performed:
; - unselected machine dependent code is removed (eq-attr of (current-mach))
; - if's are reduced to either then/else if we can determine that the test is
; a compile-time constant
; - orif/andif
; - eq/ne
; - not
;
; ??? Will become more intelligent as needed.
(define (rtx-simplify context owner expr known)
(/rtx-traverse expr #f #f 0
(tstate-make context owner
/rtx-simplify-expr-fn
#f ;; ok since EXPR is fully canonical
(rtx-env-empty-stack)
#f known 0)
#f)
)
;; Return an insn's semantics simplified.
;; CONTEXT is a <context> object or #f, used for error messages.
(define (rtx-simplify-insn context insn)
(rtx-simplify context insn (insn-canonical-semantics insn)
(insn-build-known-values insn))
)
;; rtx-solve (and supporting cast)
; Utilities for equation solving.
; ??? At the moment this is only focused on ifield assertions.
; ??? That there exist more sophisticated versions than this one can take
; as a given. This works for the task at hand and will evolve or be replaced
; as necessary.
; ??? This makes the simplifying assumption that no expr has side-effects.
; Subroutine of rtx-solve.
; This is the EXPR-FN argument to rtx-traverse.
(define (/solve-expr-fn rtx-obj expr parent-expr op-pos tstate appstuff)
#f ; wip
)
; Return a boolean indicating if {expr} equates to "true".
; If the expression can't be reduced to #f/#t, return '?.
; ??? Use rtx-eval instead of rtx-traverse?
;
; EXPR must be in source form.
; CONTEXT is a <context> object, used for error messages.
; OWNER is the owner of the expression (e.g. <insn>) or #f if there is none.
; KNOWN is an alist of known values. Each element is (name . value) where
; NAME is an ifield/operand name and VALUE is a const/number-list rtx.
; FIXME: Need ranges, later.
;
; This is akin to rtx-simplify except it's geared towards solving ifield
; assertions. It's not unreasonable to combine them. The worry is the
; efficiency lost.
; ??? Will become more intelligent as needed.
(define (rtx-solve context owner expr known)
; First simplify, then solve.
(let* ((simplified-expr (rtx-simplify context owner expr known))
(maybe-solved-expr
simplified-expr) ; FIXME: for now
; (/rtx-traverse simplified-expr #f #f 0
; (tstate-make context owner
; /solve-expr-fn
; #f (rtx-env-empty-stack)
; #f known 0)
; #f))
)
(cond ((rtx-true? maybe-solved-expr) #t)
((rtx-false? maybe-solved-expr) #f)
(else '?)))
)
;; rtx-trim-for-doc (and supporting cast)
;; RTX trimming (removing fluff not normally needed for the human viewer).
;; Subroutine of /rtx-trim-args to simplify it.
;; Trim a list of rtxes.
(define (/rtx-trim-rtx-list rtx-list)
(map /rtx-rtim-for-doc rtx-list)
)
; Subroutine of /rtx-trim-for-doc to simplify it.
; Trim the arguments of rtx NAME.
; ARGS has already had options,mode removed.
(define (/rtx-trim-args name args)
(logit 4 "Trimming args of " name ": " args "\n")
(let* ((rtx-obj (rtx-lookup name))
(arg-types (rtx-arg-types rtx-obj)))
(let loop ((args args)
(types (cddr arg-types)) ; skip options, mode
(result nil))
(if (null? args)
(reverse! result)
(let ((arg (car args))
; Remember, types may be an improper list.
(type (if (pair? types) (car types) types))
(new-arg (car args)))
;(display arg (current-error-port)) (newline (current-error-port))
;(display type (current-error-port)) (newline (current-error-port))
(case type
((OPTIONS)
(assert #f)) ; shouldn't get here
((ANYINTMODE ANYFLOATMODE ANYNUMMODE ANYEXPRMODE ANYCEXPRMODE
EXPLNUMMODE VOIDORNUMMODE VOIDMODE BIMODE INTMODE
SYMMODE INSNMODE MACHMODE)
#f) ; leave arg untouched
((RTX SETRTX TESTRTX)
(set! new-arg (/rtx-trim-for-doc arg)))
((CONDRTX)
(assert (= (length arg) 2))
(if (eq? (car arg) 'else)
(set! new-arg (cons 'else (/rtx-trim-for-doc (cadr arg))))
(set! new-arg (list (/rtx-trim-for-doc (car arg))
(/rtx-trim-for-doc (cadr arg)))))
)
((CASERTX)
(assert (= (length arg) 2))
(set! new-arg (list (car arg) (/rtx-trim-for-doc (cadr arg))))
)
((LOCALS)
#f) ; leave arg untouched
((ITERATION SYMBOLLIST ENVSTACK)
#f) ; leave arg untouched for now
((ATTRS)
#f) ; leave arg untouched for now
((SYMBOL STRING NUMBER SYMORNUM)
#f) ; leave arg untouched
((OBJECT)
(assert #f)) ; hopefully(wip!) shouldn't get here
(else
(assert #f))) ; unknown arg type
(loop (cdr args)
(if (pair? types) (cdr types) types)
(cons new-arg result))))))
)
; Given a canonical rtl expression, usually the result of rtx-simplify,
; remove bits unnecessary for documentation purposes.
; Canonical rtl too verbose for docs.
; Examples of things to remove:
; - empty options list
; - ifield/operand/local/const wrappers
; - modes of operations that don't need them to convey meaning
;
; NOTE: While having to trim the result of rtx-simplify may seem ironic,
; it isn't. You need to keep separate the notions of simplifying "1+1" to "2"
; and trimming the clutter from "(const () BI 0)" yielding "0".
(define (/rtx-trim-for-doc rtx)
(if (pair? rtx) ; ??? cheap rtx?
(let ((name (car rtx))
(options (cadr rtx))
(mode (caddr rtx))
(rest (cdddr rtx)))
(case name
((const ifield operand local)
(if (null? options)
(car rest)
rtx))
((set set-quiet)
(let ((trimmed-args (/rtx-trim-args name rest)))
(if (null? options)
(cons name trimmed-args)
(cons name (cons options (cons mode trimmed-args))))))
((eq ne lt le gt ge ltu leu gtu geu index-of)
(let ((trimmed-args (/rtx-trim-args name rest)))
(if (null? options)
(cons name trimmed-args)
(cons name (cons options (cons mode trimmed-args))))))
((if)
(let ((trimmed-args (/rtx-trim-args name rest)))
(if (null? options)
(if (eq? mode 'VOID)
(cons name trimmed-args)
(cons name (cons mode trimmed-args)))
(cons name (cons options (cons mode trimmed-args))))))
((sequence parallel)
; No special support is needed, except it's nice to remove nop
; statements. These can be created when an `if' get simplified.
(let ((trimmed-args (/rtx-trim-args name rest))
(result nil))
(for-each (lambda (rtx)
(if (equal? rtx '(nop))
#f ; ignore
(set! result (cons rtx result))))
trimmed-args)
(if (null? options)
(if (eq? mode 'VOID)
(cons name (reverse result))
(cons name (cons mode (reverse result))))
(cons name (cons options (cons mode (reverse result)))))))
((nop)
(list 'nop))
((closure)
;; Remove outer closures, they are artificially added, and are
;; basically noise to the human trying to understand the semantics.
;; ??? Since we currently can't distinguish outer closures,
;; just remove them all.
(let ((trimmed-expr (/rtx-trim-for-doc (rtx-closure-expr rtx))))
(if (and (null? options) (null? (rtx-closure-env-stack rtx)))
trimmed-expr
(rtx-make 'closure options mode
(rtx-closure-isas rtx)
(rtx-closure-env-stack rtx)
trimmed-expr))))
(else
(let ((trimmed-args (/rtx-trim-args name rest)))
(if (null? options)
(if (eq? mode 'DFLT) ;; FIXME: DFLT can no longer appear
(cons name trimmed-args)
(cons name (cons mode trimmed-args)))
(cons name (cons options (cons mode trimmed-args))))))))
; Not an rtx expression, must be number, symbol, string.
rtx)
)
(define (rtx-trim-for-doc rtx)
(/rtx-trim-for-doc rtx)
)