; Basic RTL support. ; Copyright (C) 2000, 2001, 2009 Red Hat, Inc. ; This file is part of CGEN. ; See file COPYING.CGEN for details. ; The name for the description language has been changed a couple of times. ; RTL isn't my favorite because of perceived confusion with GCC ; (and perceived misinterpretation of intentions!). ; On the other hand my other choices were taken (and believed to be ; more confusing). ; ; RTL functions are described by class . ; The complete list of rtl functions is defined in doc/rtl.texi. ; Conventions used in this file: ; - procs that perform the basic rtl or semantic expression manipulation that ; is for public use shall be prefixed with "s-" or "rtl-" or "rtx-" ; - no other procs shall be so prefixed ; - rtl globals and other rtx-func object support shall be prefixed with ; "-rtx[-:]" ; - no other procs shall be so prefixed ; Class for defining rtx nodes. ; FIXME: Add new members that are lambda's to perform the argument checking ; specified by `arg-types' and `arg-modes'. This will save a lookup during ; traversing. It will also allow custom versions for oddballs (e.g. for ; `member' we want to verify the 2nd arg is a `number-list' rtx). ; ??? Still useful? (define (class-make ' nil '( ; name as it appears in RTL ; must be accessed via obj:name name ; argument list ; ??? Not used I think, but keep. args ; result mode, or #f if from arg 2 ; (or the containing expression when canonicalizing) result-mode ; types of each argument, as symbols ; This is #f for macros. ; Possible values: ; OPTIONS - optional list of keyword-prefixed options ; ANYINTMODE - any integer mode ; ANYFLOATMODE - any floating point mode ; ANYNUMMODE - any numeric mode ; ANYEXPRMODE - VOID, PTR, or any numeric mode ; EXPLNUMMODE - explicit numeric mode, can't be DFLT or VOID ; VOIDORNUMMODE - VOID or any numeric mode ; VOIDMODE - must be `VOID' ; BIMODE - BI (boolean or bit int) ; INTMODE - must be `INT' ; SYMMODE - must be SYM ; INSNMODE - must be INSN ; MACHMODE - must be MACH ; RTX - any rtx ; SETRTX - any rtx allowed to be `set' ; TESTRTX - the test of an `if' ; CONDRTX - a cond expression ((test) rtx ... rtx) ; CASERTX - a case expression ((symbol .. symbol) rtx ... rtx) ; LOCALS - the locals list of a sequence ; ITERATION - the iteration ; SYMBOLLIST - used for ISA name lists ; ENVSTACK - environment stack ; ATTRS - attribute list ; SYMBOL - arg must be a symbol ; STRING - arg must be a string ; NUMBER - arg must be a number ; SYMORNUM - arg must be a symbol or number ; OBJECT - arg is an object (FIXME: restrict to ?) arg-types ; required mode of each argument ; This is #f for macros. ; Possible values include any mode name and: ; ANY - any mode ; ANYINT - any integer mode ; NA - not applicable ; MATCHEXPR - mode has to match the mode specified in the ; containing expression ; NOTE: This isn't necessarily the mode of the ; result of the expression. E.g. in `set', the ; result always has mode VOID, but the mode ; specified in the expression is the mode of the ; set destination. ; MATCHSEQ - for sequences ; last expression has to match mode of sequence, ; preceding expressions must be VOID ; MATCH2 - must match mode of arg 2 ; MATCH3 - must match mode of arg 3 ; - must match specified mode arg-modes ; arg number of the MATCHEXPR arg, ; or #f if there is none matchexpr-index ; The class of rtx. ; This is #f for macros. ; ARG - operand, local, const ; SET - set, set-quiet ; UNARY - not, inv, etc. ; BINARY - add, sub, etc. ; TRINARY - addc, subc, etc. ; COMPARE - eq, ne, etc. ; IF - if ; COND - cond, case ; SEQUENCE - sequence, parallel ; UNSPEC - c-call ; MISC - everything else class ; A symbol indicating the flavour of rtx node this is. ; FUNCTION - normal function ; SYNTAX - don't pre-eval arguments ; OPERAND - result is an operand ; MACRO - converts one rtx expression to another ; The word "style" was chosen to be sufficiently different ; from "type", "kind", and "class". style ; A function to perform the rtx. evaluator ; Ordinal number of rtx. Used to index into tables. num ) nil) ) ; Predicate. (define (rtx-func? x) (class-instance? x)) ; Accessor fns (define-getters rtx (result-mode arg-types arg-modes matchexpr-index class style evaluator num) ) (define (rtx-style-syntax? rtx) (eq? (rtx-style rtx) 'syntax)) ; Add standard `get-name' method since this isn't a subclass of . (method-make! 'get-name (lambda (self) (elm-get self 'name))) ; List of mode types for arg-types. (define /rtx-valid-mode-types '( ANYINTMODE ANYFLOATMODE ANYNUMMODE ANYEXPRMODE EXPLNUMMODE VOIDORNUMMODE VOIDMODE BIMODE INTMODE SYMMODE INSNMODE MACHMODE ) ) ; List of valid values for arg-types, not including mode names. (define /rtx-valid-types (append '(OPTIONS) /rtx-valid-mode-types '(RTX SETRTX TESTRTX CONDRTX CASERTX) '(LOCALS ITERATION SYMBOLLIST ENVSTACK ATTRS) '(SYMBOL STRING NUMBER SYMORNUM OBJECT) ) ) ; List of valid mode matchers, excluding mode names. (define /rtx-valid-matches '(ANY ANYINT NA MATCHEXPR MATCHSEQ MATCH2 MATCH3) ) ;; Return arg number of MATCHEXPR in ARG-MODES or #f if not present. (define (/rtx-find-matchexpr-index arg-modes) ;; We can't use find-first-index here because arg-modes can be an ;; improper list (a b c . d). ;;(find-first-index 0 (lambda (t) (eq? t 'MATCHEXPR)) arg-modes) (define (improper-find-first-index i pred l) (cond ((null? l) #f) ((pair? l) (cond ((pred (car l)) i) (else (improper-find-first-index (+ 1 i) pred (cdr l))))) ((pred l) i) (else #f))) (improper-find-first-index 0 (lambda (t) (eq? t 'MATCHEXPR)) arg-modes) ) ; List of all defined rtx names. This can be map'd over without having ; to know the innards of /rtx-func-table (which is a hash table). (define /rtx-name-list nil) (define (rtx-name-list) /rtx-name-list) ; Table of rtx function objects. ; This is set in rtl-init!. (define /rtx-func-table nil) ; Look up the object for RTX-KIND. ; Returns the object or #f if not found. ; RTX-KIND is the name of the rtx function. (define (rtx-lookup rtx-kind) (assert (symbol? rtx-kind)) (hashq-ref /rtx-func-table rtx-kind) ) ; Table of rtx macro objects. ; This is set in rtl-init!. (define /rtx-macro-table nil) ; Table of operands, modes, and other non-functional aspects of RTL. ; This is defined in rtl-finish!, after all operands have been read in. (define /rtx-operand-table nil) ; Number of next rtx to be defined. (define /rtx-num-next #f) ; Return the number of rtx's. (define (rtx-max-num) /rtx-num-next ) ; Define Rtx Node ; ; Add an entry to the rtx function table. ; NAME-ARGS is a list of the operation name and arguments. ; The mode of the result must be the first element in `args' (if there are ; any arguments). ; ARG-TYPES is a list of argument types (/rtx-valid-types). ; ARG-MODES is a list of mode matchers (/rtx-valid-matches). ; CLASS is the class of the rtx to be created. ; ACTION is a list of Scheme expressions to perform the operation. ; ; ??? Note that we can support variables. Not sure it should be done. (define (def-rtx-node name-args result-mode arg-types arg-modes class action) (let* ((name (car name-args)) (args (cdr name-args)) (context (make-prefix-context (string-append "defining rtx " (symbol->string name)))) (matchexpr-index (/rtx-find-matchexpr-index arg-modes))) ; (map1-improper (lambda (arg-type) ; (if (not (memq arg-type /rtx-valid-types)) ; (context-error context "While defining rtx functions" ; "invalid arg type" arg-type))) ; arg-types) ; (map1-improper (lambda (arg-mode) ; (if (and (not (memq arg-mode /rtx-valid-matches)) ; (not (symbol? arg-mode))) ;; FIXME: mode-name? ; (context-error context "While defining rtx functions" ; "invalid arg mode match" arg-mode))) ; arg-modes) (let ((rtx (make name args result-mode arg-types arg-modes matchexpr-index class 'function (if action (eval1 (list 'lambda (cons '*estate* args) action)) #f) /rtx-num-next))) ; Add it to the table of rtx handlers. (hashq-set! /rtx-func-table name rtx) (set! /rtx-num-next (+ /rtx-num-next 1)) (set! /rtx-name-list (cons name /rtx-name-list)) *UNSPECIFIED*)) ) (define define-rtx-node ; Written this way so Hobbit can handle it. (defmacro:syntax-transformer (lambda arg-list (apply def-rtx-node arg-list) nil)) ) ; Same as define-rtx-node but don't pre-evaluate the arguments. ; Remember that `mode' must be the first argument. (define (def-rtx-syntax-node name-args result-mode arg-types arg-modes class action) (let ((name (car name-args)) (args (cdr name-args)) (matchexpr-index (/rtx-find-matchexpr-index arg-modes))) (let ((rtx (make name args result-mode arg-types arg-modes matchexpr-index class 'syntax (if action (eval1 (list 'lambda (cons '*estate* args) action)) #f) /rtx-num-next))) ; Add it to the table of rtx handlers. (hashq-set! /rtx-func-table name rtx) (set! /rtx-num-next (+ /rtx-num-next 1)) (set! /rtx-name-list (cons name /rtx-name-list)) *UNSPECIFIED*)) ) (define define-rtx-syntax-node ; Written this way so Hobbit can handle it. (defmacro:syntax-transformer (lambda arg-list (apply def-rtx-syntax-node arg-list) nil)) ) ; Same as define-rtx-node but return an operand (usually an object). ; ??? `mode' must be the first argument? (define (def-rtx-operand-node name-args result-mode arg-types arg-modes class action) ; Operand nodes must specify an action. (assert action) (let ((name (car name-args)) (args (cdr name-args)) (matchexpr-index (/rtx-find-matchexpr-index arg-modes))) (let ((rtx (make name args result-mode arg-types arg-modes matchexpr-index class 'operand (eval1 (list 'lambda (cons '*estate* args) action)) /rtx-num-next))) ; Add it to the table of rtx handlers. (hashq-set! /rtx-func-table name rtx) (set! /rtx-num-next (+ /rtx-num-next 1)) (set! /rtx-name-list (cons name /rtx-name-list)) *UNSPECIFIED*)) ) (define define-rtx-operand-node ; Written this way so Hobbit can handle it. (defmacro:syntax-transformer (lambda arg-list (apply def-rtx-operand-node arg-list) nil)) ) ; Convert one rtx expression into another. ; NAME-ARGS is a list of the operation name and arguments. ; ACTION is a list of Scheme expressions to perform the operation. ; The result of ACTION must be another rtx expression (a list). (define (def-rtx-macro-node name-args action) ; macro nodes must specify an action (assert action) (let ((name (car name-args)) (args (cdr name-args))) (let ((rtx (make name args #f #f #f #f #f ; class 'macro (eval1 (list 'lambda args action)) /rtx-num-next))) ; Add it to the table of rtx macros. (hashq-set! /rtx-macro-table name rtx) (set! /rtx-num-next (+ /rtx-num-next 1)) (set! /rtx-name-list (cons name /rtx-name-list)) *UNSPECIFIED*)) ) (define define-rtx-macro-node ; Written this way so Hobbit can handle it. (defmacro:syntax-transformer (lambda arg-list (apply def-rtx-macro-node arg-list) nil)) ) ; RTL macro expansion. ; RTL macros are different than pmacros. The difference is that the expansion ; happens internally, RTL macros are part of the language. ; Lookup MACRO-NAME and return its object or #f if not found. (define (/rtx-macro-lookup macro-name) (hashq-ref /rtx-macro-table macro-name) ) ; Lookup (car exp) and return the macro's lambda if it is one or #f. (define (/rtx-macro-check exp fn-getter) (let ((macro (hashq-ref /rtx-macro-table (car exp)))) (if macro (fn-getter macro) #f)) ) ; Expand a list. (define (/rtx-macro-expand-list exp fn-getter) (let ((macro (/rtx-macro-check exp fn-getter))) (if macro (apply macro (map (lambda (x) (/rtx-macro-expand x fn-getter)) (cdr exp))) (map (lambda (x) (/rtx-macro-expand x fn-getter)) exp))) ) ; Main entry point to expand a macro invocation. (define (/rtx-macro-expand exp fn-getter) (if (pair? exp) ; pair? -> cheap (and (not (null? exp)) (list? exp)) (let ((result (/rtx-macro-expand-list exp fn-getter))) ; If the result is a new macro invocation, recurse. (if (pair? result) (let ((macro (/rtx-macro-check result fn-getter))) (if macro (/rtx-macro-expand (apply macro (cdr result)) fn-getter) result)) result)) exp) ) ; Publically accessible version. (define rtx-macro-expand /rtx-macro-expand) ; RTX mode support. ; Get implied mode of X, either an operand expression, sequence temp, or ; a hardware reference expression. ; The result is the name of the mode. (define (rtx-lvalue-mode-name estate x) (assert (rtx? x)) (case (car x) ; ((operand) (obj:name (op:mode (current-op-lookup (cadr x) (obj-isa-list (estate-owner estate)))))) ((xop) (obj:name (send (rtx-xop-obj x) 'get-mode))) ; ((opspec) ; (if (eq? (rtx-opspec-mode x) 'VOID) ; (rtx-lvalue-mode-name estate (rtx-opspec-hw-ref x)) ; (rtx-opspec-mode x))) ; ((reg mem) (cadr x)) ((local) ;; (local options mode name) (let* ((name (cadddr x)) (temp (rtx-temp-lookup (estate-env-stack estate) name))) (if (not temp) (estate-error estate "unknown local" name)) (obj:name (rtx-temp-mode temp)))) (else (estate-error error "rtx-lvalue-mode-name: not an operand or hardware reference:" x))) ) ; Lookup the mode to use for semantic operations (unsigned modes aren't ; allowed since we don't have ANDUSI, etc.). ; MODE is a object. ; ??? I have actually implemented both ways (full use of unsigned modes ; and mostly hidden use of unsigned modes). Neither makes me real ; comfortable, though I liked bringing unsigned modes out into the open ; even if it doubled the number of semantic operations. (define (rtx-sem-mode mode) (or (mode:sem-mode mode) mode)) ; Return the mode of object OBJ. (define (rtx-obj-mode obj) (send obj 'get-mode)) ; Return a boolean indicating of modes M1,M2 are compatible. ; M1,M2 are objects. (define (rtx-mode-compatible? m1 m2) ;; ??? This is more permissive than is perhaps proper. (let ((mode1 (rtx-sem-mode m1)) (mode2 (rtx-sem-mode m2))) ;;(eq? (obj:name mode1) (obj:name mode2))) (mode-compatible? 'sameclass mode1 mode2)) ) ; Environments (sequences with local variables). ; Temporaries are created within a sequence. ; MODE is a object. ; VALUE is #f if not set yet. ; e.g. (sequence ((WI tmp)) (set tmp reg0) ...) ; ??? Perhaps what we want here is `let' but for now I prefer `sequence'. ; This isn't exactly `let' either as no initial value is specified. ; Environments are also used to specify incoming values from the top level. (define (class-make ' nil '(name mode value) nil)) ;(define cx-temp:name (elm-make-getter 'name)) ;(define cx-temp:mode (elm-make-getter 'mode)) ;(define cx-temp:value (elm-make-getter 'value)) (define-getters rtx-temp (name mode value)) (method-make! 'make! (lambda (self name mode value) (assert (mode? mode)) (elm-set! self 'name name) (elm-set! self 'mode mode) (elm-set! self 'value (if value value (gen-temp name))) self) ) (define (gen-temp name) ; ??? calls to gen-c-symbol don't belong here (string-append "tmp_" (gen-c-symbol name)) ) ; Return a boolean indicating if X is an . (define (rtx-temp? x) (class-instance? x)) ; Respond to 'get-mode messages. (method-make! 'get-mode (lambda (self) (elm-get self 'mode))) ; Respond to 'get-name messages. (method-make! 'get-name (lambda (self) (elm-get self 'name))) ; An environment is a list of objects. ; An environment stack is a list of environments. (define (rtx-env-stack-empty? env-stack) (null? env-stack)) (define (rtx-env-stack-head env-stack) (car env-stack)) (define (rtx-env-empty-stack) nil) (define (rtx-env-init-stack1 vars-alist) (if (null? vars-alist) nil (cons (rtx-env-make vars-alist) nil)) ) (define (rtx-env-empty? env) (null? env)) ;; Create an environment from VAR-ALIST, ;; an alist of (name -or-mode-name value) elements, ;; or, in the case of /rtx-closure-make, a list of (name . ). (define (rtx-env-make var-alist) ;; Check for an already-compiled environment, for /rtx-closure-make's sake. (if (and (pair? var-alist) (rtx-temp? (cdar var-alist))) var-alist ;; Convert VAR-ALIST to an associative list of objects. (map (lambda (var-spec) (cons (car var-spec) (make (car var-spec) (mode-maybe-lookup (cadr var-spec)) (caddr var-spec)))) var-alist)) ) ; Create an initial environment with local variables. ; VAR-LIST is a list of (mode-name name) elements, i.e. the locals argument to ; `sequence' or equivalent thereof. (define (rtx-env-make-locals var-list) ; Convert VAR-LIST to an associative list of objects. (map (lambda (var-spec) (cons (cadr var-spec) (make (cadr var-spec) (mode:lookup (car var-spec)) #f))) var-list) ) ; Return the symbol name of the limit variable of `do-count' ; given iteration-variable ITER-VAR. ; ??? We don't publish that this variable is available to use, but we could. (define (rtx-make-iteration-limit-var iter-var) (symbol-append iter-var '-limit) ) ; Create an environment with the iteration local variables of `do-count'. (define (rtx-env-make-iteration-locals iter-var) (rtx-env-make-locals (list (list 'INT iter-var) (list 'INT (rtx-make-iteration-limit-var iter-var)))) ) ;; Convert an alist of (name -object-or-name value) to ;; an environment. (define (rtx-var-alist-to-env var-alist) var-alist) ;; Convert an alist of (name -object-or-name value) to ;; an environment stack. (define (rtx-var-alist-to-closure-env-stack var-alist) ;; Preserve emptiness so (null? env-stack) works. (if (null? var-alist) nil (list var-alist)) ) ;; Convert the source form of an env-stack, e.g. as used in a closure, ;; to the internal form, which is (name -object). (define (rtx-make-env-stack closure-env-stack) (map rtx-env-make closure-env-stack) ) ; Push environment ENV onto the front of environment stack ENV-STACK, ; returning a new object. ENV-STACK is not modified. (define (rtx-env-push env-stack env) (cons env env-stack) ) ; Lookup variable NAME in environment stack ENV-STACK. ; The result is the object. (define (rtx-temp-lookup env-stack name) (let loop ((stack env-stack)) (if (null? stack) #f (let ((temp (assq-ref (car stack) name))) (if temp temp (loop (cdr stack)))))) ) ; Create a "closure" of EXPR using the current ISA list and temp stack. ; MODE is the mode name. (define (/rtx-closure-make estate mode expr) ;; NOTE: This records the "compiled" environment stack in the closure. (rtx-make 'closure mode (estate-isas estate) (estate-env-stack estate) expr) ) (define (rtx-env-stack-dump env-stack) (let ((stack env-stack)) (if (rtx-env-stack-empty? stack) (display "rtx-env stack (empty):\n") (let loop ((stack stack) (level 0)) (if (null? stack) #f ; done (begin (display "rtx-env stack, level ") (display level) (display ":\n") (for-each (lambda (var) (display " ") ;(display (obj:name (rtx-temp-mode (cdr var)))) ;(display " ") (display (rtx-temp-name (cdr var))) (newline)) (car stack)) (loop (cdr stack) (+ level 1))))))) ) ; Build, test, and analyze various kinds of rtx's. ; ??? A lot of this could be machine generated except that I don't yet need ; that much. (define (rtx-make kind . args) (cons kind (rtx-munge-mode&options (rtx-lookup kind) 'DFLT kind args)) ) (define rtx-name car) (define (rtx-kind? kind rtx) (eq? kind (rtx-name rtx))) (define (rtx-make-const mode value) (rtx-make 'const mode value)) (define (rtx-make-enum mode value) (rtx-make 'enum mode value)) (define (rtx-constant? rtx) (memq (rtx-name rtx) '(const enum))) ; Return value of constant RTX (either const or enum). (define (rtx-constant-value rtx) (case (rtx-name rtx) ((const) (rtx-const-value rtx)) ((enum) (car (enum-lookup-val (rtx-enum-value rtx)))) (else (error "rtx-constant-value: not const or enum" rtx))) ) (define rtx-options cadr) (define rtx-mode caddr) (define rtx-args cdddr) (define rtx-arg1 cadddr) (define (rtx-arg2 rtx) (car (cddddr rtx))) (define rtx-const-value rtx-arg1) (define rtx-enum-value rtx-arg1) (define rtx-reg-name rtx-arg1) ; Return register number or #f if absent. ; (reg options mode hw-name [regno [selector]]) (define (rtx-reg-number rtx) (list-maybe-ref rtx 4)) ; Return register selector or #f if absent. (define (rtx-reg-selector rtx) (list-maybe-ref rtx 5)) ; Return both register number and selector. (define rtx-reg-index-sel cddddr) ; Return memory address. (define rtx-mem-addr rtx-arg1) ; Return memory selector or #f if absent. (define (rtx-mem-sel mem) (list-maybe-ref mem 4)) ; Return both memory address and selector. (define rtx-mem-index-sel cdddr) ; Return MEM with new address NEW-ADDR. ; ??? Complicate as necessary. (define (rtx-change-address mem new-addr) (rtx-make 'mem (rtx-options mem) (rtx-mode mem) new-addr (rtx-mem-sel mem)) ) ; Return argument to `symbol' rtx. (define rtx-symbol-name rtx-arg1) (define (rtx-make-ifield mode-name ifield-name) (rtx-make 'ifield mode-name ifield-name) ) (define (rtx-ifield? rtx) (eq? 'ifield (rtx-name rtx))) (define (rtx-ifield-name rtx) (let ((ifield (rtx-arg1 rtx))) (if (symbol? ifield) ifield (obj:name ifield))) ) (define (rtx-ifield-obj rtx) (let ((ifield (rtx-arg1 rtx))) (if (symbol? ifield) (current-ifld-lookup ifield) ifield)) ) (define (rtx-make-operand mode-name op-name) (rtx-make 'operand mode-name op-name) ) (define (rtx-operand? rtx) (eq? 'operand (rtx-name rtx))) ;; FIXME: This should just fetch rtx-arg1, ;; operand rtxes shouldn't have objects, that's what xop is for. (define (rtx-operand-name rtx) (let ((operand (rtx-arg1 rtx))) (if (symbol? operand) operand (obj:name operand))) ) ;; Given an operand rtx, return the object. ;; RTX must be canonical rtl. ;; ISA-NAME-LIST is the list of ISAs to look the operand up in. ;; ;; NOTE: op:mode-name can be DFLT, which means use the mode of the type. ;; It is up to the caller to deal with it. (define (rtx-operand-obj rtx isa-name-list) (let ((op (current-op-lookup (rtx-arg1 rtx) isa-name-list)) (mode-name (rtx-mode rtx))) (assert op) (assert (not (eq? mode-name 'DFLT))) ;; Ensure requested mode is supported by the hardware. ;; rtx-canonicalize should have verified this already (I think). (assert (hw-mode-ok? (op:type op) mode-name (op:index op))) op) ) (define (rtx-make-local mode-name local-name) (rtx-make 'local mode-name local-name) ) (define (rtx-local? rtx) (eq? 'local (rtx-name rtx))) (define (rtx-local-name rtx) (let ((local (rtx-arg1 rtx))) (if (symbol? local) local (obj:name local))) ) (define (rtx-local-obj rtx) (let ((local (rtx-arg1 rtx))) (if (symbol? local) (error "can't use rtx-local-obj on local name") local)) ) (define (rtx-make-xop op) (rtx-make 'xop (op:mode-name op) op) ) (define rtx-xop-obj rtx-arg1) ;(define (rtx-opspec? rtx) (eq? 'opspec (rtx-name rtx))) ;(define (rtx-opspec-mode rtx) (rtx-mode rtx)) ;(define (rtx-opspec-hw-ref rtx) (list-ref rtx 5)) ;(define (rtx-opspec-set-op-num! rtx num) (set-car! (cddddr rtx) num)) (define rtx-index-of-value rtx-arg1) (define (rtx-make-set dest src) (rtx-make 'set dest src)) (define rtx-set-dest rtx-arg1) (define rtx-set-src rtx-arg2) (define (rtx-single-set? rtx) (memq (car rtx) '(set set-quiet))) (define rtx-alu-op-mode rtx-mode) (define (rtx-alu-op-arg rtx n) (list-ref rtx (+ n 3))) (define (rtx-boolif-op-arg rtx n) (list-ref rtx (+ n 3))) (define rtx-cmp-op-mode rtx-mode) (define (rtx-cmp-op-arg rtx n) (list-ref rtx (+ n 3))) (define rtx-number-list-values cdddr) (define rtx-member-value rtx-arg1) (define (rtx-member-set rtx) (list-ref rtx 4)) (define rtx-if-mode rtx-mode) (define (rtx-if-test rtx) (rtx-arg1 rtx)) (define (rtx-if-then rtx) (list-ref rtx 4)) ; If `else' clause is missing the result is #f. (define (rtx-if-else rtx) (list-maybe-ref rtx 5)) (define (rtx-eq-attr-owner rtx) (list-ref rtx 3)) (define (rtx-eq-attr-attr rtx) (list-ref rtx 4)) (define (rtx-eq-attr-value rtx) (list-ref rtx 5)) (define (rtx-sequence-locals rtx) (cadddr rtx)) (define (rtx-sequence-exprs rtx) (cddddr rtx)) ; Same as rtx-sequence-locals except return in assq'able form. ; ??? Sometimes I think it should have been (sequence ((name MODE)) ...) ; instead of (sequence ((MODE name)) ...) from the beginning, sigh. (define (rtx-sequence-assq-locals rtx) (let ((locals (rtx-sequence-locals rtx))) (map (lambda (local) (list (cadr local) (car local))) locals)) ) (define (rtx-closure-isas rtx) (list-ref rtx 3)) (define (rtx-closure-env-stack rtx) (list-ref rtx 4)) (define (rtx-closure-expr rtx) (list-ref rtx 5)) ; Return a semi-pretty string describing RTX. ; This is used by hw to include the index in the element's name. (define (rtx-pretty-name rtx) (if (pair? rtx) (case (car rtx) ((const) (number->string (rtx-const-value rtx))) ((operand) (symbol->string (rtx-operand-name rtx))) ((local) (symbol->string (rtx-local-name rtx))) ((xop) (symbol->string (obj:name (rtx-xop-obj rtx)))) (else (if (null? (cdr rtx)) (rtx-pretty-name (car rtx)) (apply stringsym-append (cons (rtx-pretty-name (car rtx)) (map (lambda (elm) (string-append "-" (rtx-pretty-name elm))) (cdr rtx))))))) (stringize rtx "-")) ) ; Various rtx utilities. ; Dump an rtx expression. (define (rtx-dump rtx) (cond ((list? rtx) (map rtx-dump rtx)) ((object? rtx) (string/symbol-append "#")) (else rtx)) ) ; Dump an expression to a string. (define (rtx-strdump rtx) (with-output-to-string (lambda () ;; Use write instead of display, we want strings displayed with quotes. (write (rtx-dump rtx)))) ) ;; Return the pretty-printed from of RTX. (define (rtx-pretty-strdump rtx) (with-output-to-string (lambda () (pretty-print (rtx-dump rtx)))) ) ; Return a boolean indicating if EXPR is known to be a compile-time constant. (define (rtx-compile-time-constant? expr) (cond ((pair? expr) (case (car expr) ((const enum) #t) (else #f))) ((memq expr '(FALSE TRUE)) #t) (else #f)) ) ; Return boolean indicating if EXPR has side-effects. ; FIXME: for now punt. (define (rtx-side-effects? expr) #f ) ; Return a boolean indicating if EXPR is a "true" boolean value. ; ; ??? In RTL, #t is a synonym for (const 1). This is confusing for Schemers, ; so maybe RTL's #t should be renamed to TRUE. (define (rtx-true? expr) (cond ((pair? expr) (case (car expr) ((const enum) (!= (rtx-constant-value expr) 0)) (else #f))) ((eq? expr 'TRUE) #t) (else #f)) ) ; Return a boolean indicating if EXPR is a "false" boolean value. ; ; ??? In RTL, #f is a synonym for (const 0). This is confusing for Schemers, ; so maybe RTL's #f should be renamed to FALSE. (define (rtx-false? expr) (cond ((pair? expr) (case (car expr) ((const enum) (= (rtx-constant-value expr) 0)) (else #f))) ((eq? expr 'FALSE) #t) (else #f)) ) ; Return canonical boolean values. (define (rtx-false) (rtx-make-const 'BI 0)) (define (rtx-true) (rtx-make-const 'BI 1)) ; Convert EXPR to a canonical boolean if possible. (define (rtx-canonical-bool expr) (cond ((rtx-side-effects? expr) expr) ((rtx-false? expr) (rtx-false)) ((rtx-true? expr) (rtx-true)) (else expr)) ) ; Return rtx values for #f/#t. (define (rtx-make-bool value) (if value (rtx-true) (rtx-false)) ) ; Return #t if X is an rtl expression. ; e.g. '(add WI dr simm8); (define (rtx? x) (->bool (and (pair? x) ; pair? -> cheap non-null-list? (or (hashq-ref /rtx-func-table (car x)) (hashq-ref /rtx-macro-table (car x))))) ) ; Instruction field support. ; Return list of ifield names refered to in EXPR. ; Assumes EXPR is more than just (ifield x). (define (rtl-find-ifields expr) (let ((ifields nil)) (letrec ((scan! (lambda (arg-list) (for-each (lambda (arg) (if (pair? arg) (if (eq? (car arg) 'ifield) (set! ifields (cons (rtx-ifield-name arg) ifields)) (scan! (cdr arg))))) arg-list)))) (scan! (cdr expr)) (nub ifields identity))) ) ; Hardware rtx handlers. ; Subroutine of hw to compute the object's name. ; The name of the operand must include the index so that multiple copies ; of a hardware object (e.g. h-gr[0], h-gr[14]) can be distinguished. ; We make some attempt to make the name pretty as it appears in generated ; files. (define (/rtx-hw-name hw hw-name index-arg) (cond ((hw-scalar? hw) hw-name) ((rtx? index-arg) (symbolstr-append hw-name '- (rtx-pretty-name index-arg))) (else (symbolstr-append hw-name ; (obj:name (op:type self)) '- ; (obj:name (op:index self))))) (stringize index-arg "-")))) ) ; Return the object described by ; HW-NAME/MODE-NAME/SELECTOR/INDEX-ARG. ; ; HW-NAME is the name of the hardware element. ; MODE-NAME is the name of the mode. ; INDEX-ARG is an rtx or number of the index. ; In the case of scalar hardware elements, pass 0 for INDEX-ARG. ; In the case of a vector of registers, INDEX-ARG is the vector index. ; SELECTOR is an rtx or number and is passed to HW-NAME to allow selection of a ; particular variant of the hardware. It's kind of like an INDEX, but along ; an atypical axis. An example is memory ASI's on Sparc. Pass ; hw-selector-default if there is no selector. ; ESTATE is the current rtx evaluation state. ; ; *** The index is passed unevaluated because for parallel execution support ; *** a variable is created with a name based on the hardware element and ; *** index, and we want a reasonably simple and stable name. We get this by ; *** stringize-ing it. ; *** ??? Though this needs to be redone anyway. ; ; ??? The specified hardware element must be either a scalar or a vector. ; Maybe in the future allow arrays although there's significant utility in ; allowing only at most a scalar index. (define (/hw estate mode-name hw-name index-arg selector) ; Enforce some rules to keep things in line with the current design. (if (not (symbol? mode-name)) (parse-error (estate-context estate) "invalid mode name" mode-name)) (if (not (symbol? hw-name)) (parse-error (estate-context estate) "invalid hw name" hw-name)) (if (not (or (number? index-arg) (rtx? index-arg))) (parse-error (estate-context estate) "invalid index" index-arg)) (if (not (or (number? selector) (rtx? selector))) (parse-error (estate-context estate) "invalid selector" selector)) (let ((hw (current-hw-sem-lookup-1 hw-name))) (if (not hw) (parse-error (estate-context estate) "invalid hardware element" hw-name)) (let* ((mode (if (eq? mode-name 'DFLT) (hw-mode hw) (mode:lookup mode-name))) (hw-name-with-mode (symbol-append hw-name '- (obj:name mode))) (index-mode (if (eq? hw-name 'h-memory) 'AI 'INT)) (result (if (hw-pc? hw) (new ) (new )))) ; ??? lookup-for-new? (if (not mode) (parse-error (estate-context estate) "invalid mode" mode-name)) ; Record the selector. (elm-xset! result 'selector selector) ; Create the index object. (elm-xset! result 'index (cond ((number? index-arg) (make 'anonymous 'constant UINT index-arg)) ((rtx? index-arg) ; Make sure constant indices are recorded as such. (case (rtx-name index-arg) ((const) (make 'anonymous 'constant UINT (rtx-constant-value index-arg))) ((enum) (make-enum-hw-index 'anonymous (rtx-enum-value index-arg))) (else (make 'anonymous 'rtx (mode:lookup index-mode) (/rtx-closure-make estate index-mode index-arg))))) (else (parse-error (estate-context estate) "invalid index" index-arg)))) (if (not (hw-mode-ok? hw (obj:name mode) (elm-xget result 'index))) (parse-error (estate-context estate) "invalid mode for hardware" mode-name)) (elm-xset! result 'hw-name hw-name) (elm-xset! result 'type hw) (elm-xset! result 'mode-name mode-name) (elm-xset! result 'mode mode) (op:set-pretty-sem-name! result hw-name) ; The name of the operand must include the index so that multiple copies ; of a hardware object (e.g. h-gr[0], h-gr[14]) can be distinguished. (let ((name (if (hw-pc? hw) 'pc (/rtx-hw-name hw hw-name-with-mode index-arg)))) (send result 'set-name! name) (op:set-sem-name! result name)) ; Empty comment and attribute. ; ??? Stick the arguments in the comment for debugging purposes? (send result 'set-comment! "") (send result 'set-atlist! atlist-empty) result)) ) ; This is shorthand for (hw estate mode hw-name regno selector). ; ESTATE is the current rtx evaluation state. ; INDX-SEL is an optional register number and possible selector. ; The register number, if present, is (car indx-sel) and must be a number or ; unevaluated canonical RTX expression. ; The selector, if present, is (cadr indx-sel) and must be a number or ; unevaluated canonical RTX expression. ; ??? A register selector isn't supported yet. It's just an idea that's ; been put down on paper for future reference. (define (reg estate mode-name hw-name . indx-sel) (s-hw estate mode-name hw-name (if (pair? indx-sel) (car indx-sel) 0) (if (and (pair? indx-sel) (pair? (cdr indx-sel))) (cadr indx-sel) hw-selector-default)) ) ; This is shorthand for (hw estate mode-name h-memory addr selector). ; ADDR must be an unevaluated canonical RTX expression. ; If present (car sel) must be a number or unevaluated canonical ; RTX expression. (define (mem estate mode-name addr . sel) (s-hw estate mode-name 'h-memory addr (if (pair? sel) (car sel) hw-selector-default)) ) ; For the rtx nodes to use. (define s-hw /hw) ; The program counter. ; When this code is loaded, global `pc' is nil, it hasn't been set to the ; pc operand yet (see operand-init!). We can't use `pc' inside the drn as the ; value is itself. So we use s-pc. rtl-finish! must be called after ; operand-init!. (define s-pc pc) ; Conditional execution. ; `if' in RTL has a result, like ?: in C. ; We support both: one with a result (non VOID mode), and one without (VOID mode). ; The non-VOID case must have an else part. ; MODE is the mode of the result, not the comparison. ; The comparison is expected to return a zero/non-zero value. ; ??? Perhaps this should be a syntax-expr. Later. (define (e-if estate mode cond then . else) (if (> (length else) 1) (estate-error estate "if: too many elements in `else' part" else)) (if (null? else) (if cond then) (if cond then (car else))) ) ; Subroutines. ; ??? Not sure this should live here. (define (/subr-read context . arg-list) #f ) (define define-subr (lambda arg-list (let ((s (apply /subr-read (cons "define-subr" arg-list)))) (if s (current-subr-add! s)) s)) ) ; Misc. utilities. ; The argument to drn,drmn,drsn must be Scheme code (or a fixed subset ; thereof). .str/.sym are used in pmacros so it makes sense to include them ; in the subset. ; FIXME: Huh? (define .str string-append) (define .sym symbol-append) ; Given (expr1 expr2 expr3 expr4), for example, ; return (fn (fn (fn expr1 expr2) expr3) expr4). (define (rtx-combine fn exprs) (assert (not (null? exprs))) (letrec ((-rtx-combine (lambda (fn exprs result) (if (null? exprs) result (-rtx-combine fn (cdr exprs) (rtx-make fn result (car exprs))))))) (-rtx-combine fn (cdr exprs) (car exprs))) ) ; Called before a .cpu file is read in. (define (rtl-init!) (set! /rtx-func-table (make-hash-table 127)) (set! /rtx-macro-table (make-hash-table 127)) (set! /rtx-num-next 0) (def-rtx-funcs) ; Sanity checks. ; All rtx take options for the first arg and a mode for the second. (for-each (lambda (rtx-name) (let ((rtx (rtx-lookup rtx-name))) (if rtx (begin (if (null? (rtx-arg-types rtx)) #f ; pc is the one exception, blech (begin (assert (eq? (car (rtx-arg-types rtx)) 'OPTIONS)) (assert (memq (cadr (rtx-arg-types rtx)) /rtx-valid-mode-types))))) #f) ; else a macro )) /rtx-name-list) (reader-add-command! 'define-subr "\ Define an rtx subroutine, name/value pair list version. " nil 'arg-list define-subr) *UNSPECIFIED* ) ;; Install builtins (define (rtl-builtin!) (rtx-init-traversal-tables!) *UNSPECIFIED* ) ; Called after cpu files are loaded to add misc. remaining entries to the ; rtx handler table for use during evaluation. ; rtl-finish! must be done before ifmt-compute!, the latter will ; construct hardware objects which is done by rtx evaluation. (define (rtl-finish!) (logit 2 "Building rtx operand table ...\n") ; Update s-pc, must be called after operand-init!. (set! s-pc pc) ; Initialize the operand hash table. (set! /rtx-operand-table (make-hash-table 127)) ; Add the operands to the eval symbol table. (for-each (lambda (op) (hashq-set! /rtx-operand-table (obj:name op) op)) (current-op-list)) ; Add ifields to the eval symbol table. (for-each (lambda (f) (hashq-set! /rtx-operand-table (obj:name f) f)) (non-derived-ifields (current-ifld-list))) *UNSPECIFIED* )