]> sourceware.org Git - cgen.git/commitdiff
2007-11-05 Dave Brolley <brolley@redhat.com>
authorDave Brolley <brolley@sourceware.org>
Mon, 5 Nov 2007 15:46:19 +0000 (15:46 +0000)
committerDave Brolley <brolley@sourceware.org>
Mon, 5 Nov 2007 15:46:19 +0000 (15:46 +0000)
        * intrinsics.scm,cgen-intrinsics.scm: New application which
        generates code to support intrinsic functions for insns in the
        mep architecture in gcc.

ChangeLog
cgen-intrinsics.scm [new file with mode: 0644]
intrinsics.scm [new file with mode: 0644]

index 891a7563de31e0ac68aa99d26b728dfbd2c5587b..a7c8083d1ffce6f52634bc4ed2c1314f56513563 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2007-11-05  Dave Brolley  <brolley@redhat.com>
+
+        * intrinsics.scm,cgen-intrinsics.scm: New application which
+       generates code to support intrinsic functions for insns in the
+       mep architecture in gcc.
+
 2007-09-21  Dave Brolley  <brolley@redhat.com>
 
        * cpu/mep-core.cpu (rnc, rnuc, rns, rnus, rn3c, rn3uc, rn3s) 
diff --git a/cgen-intrinsics.scm b/cgen-intrinsics.scm
new file mode 100644 (file)
index 0000000..d2710d3
--- /dev/null
@@ -0,0 +1,85 @@
+; GCC "intrinsics" file entry point.
+;
+; This is invoked to build support files for registering intrinsic
+; functions within gcc. this code has a fair bit of target-specific
+; code in it. it's not a general-purpose module yet.
+;
+; Copyright (C) 2000 Red Hat, Inc.
+; This file is part of CGEN.
+;
+; This is a standalone script, we don't load anything until we parse the
+; -s argument (keeps reliance off of environment variables, etc.).
+
+; Load the various support routines.
+
+(define (load-files srcdir)
+  ; Fix up Scheme to be what we use (guile is always in flux).
+  (primitive-load-path (string-append srcdir "/fixup.scm"))
+  (primitive-load-path (string-append srcdir "/guile.scm"))
+
+  (load (string-append srcdir "/read.scm"))
+  (load (string-append srcdir "/intrinsics.scm"))
+)
+
+(define intrinsics-isas '())
+
+(define intrinsics-arguments
+  (list
+   (list "-K" "isa" "keep isa <isa> in intrinsics" #f
+        (lambda (args)
+          (for-each 
+           (lambda (arg) (set! intrinsics-isas (cons (string->symbol arg) intrinsics-isas)))
+           (string-cut args #\,))))
+   (list "-M" "file" "generate insns.md in <file>" #f
+        (lambda (arg) (file-write arg insns.md)))
+   (list "-N" "file" "generate intrinsics.h in <file>" #f
+        (lambda (arg) (file-write arg intrinsics.h)))
+   (list "-P" "file" "generate intrinsic-protos.h in <file>" #f
+        (lambda (arg) (file-write arg intrinsic-protos.h)))
+   (list "-T" "file" "generate intrinsic-testsuite.c in <file>" #f
+        (lambda (arg) (file-write arg intrinsic-testsuite.c)))))
+
+; Kept global so it's available to the other .scm files.
+(define srcdir ".")
+
+; Scan argv for -s srcdir.
+; We can't process any other args until we find the cgen source dir.
+; The result is srcdir.
+; We assume "-s" isn't the argument to another option.  Unwise, yes.
+; Alternatives are to require it to be the first argument or at least preceed
+; any option with a "-s" argument, or to put knowledge of the common argument
+; set and common argument parsing code in every top level file.
+
+(define (find-srcdir argv)
+  (let loop ((argv argv))
+    (if (null? argv)
+       (error "`-s srcdir' not present, can't load cgen"))
+    (if (string=? "-s" (car argv))
+       (begin
+         (if (null? (cdr argv))
+             (error "missing srcdir arg to `-s'"))
+         (cadr argv))
+       (loop (cdr argv))))     
+)
+
+; Main routine, parses options and calls generators.
+
+(define (cgen-intrinsics argv)
+  (let ()
+
+    ; Find and set srcdir, then load all Scheme code.
+    ; Drop the first argument, it is the script name (i.e. argv[0]).
+    (set! srcdir (find-srcdir (cdr argv)))
+    (set! %load-path (cons srcdir %load-path))
+    (load-files srcdir)
+
+    (display-argv argv)
+
+    (cgen #:argv argv
+         #:app-name "intrinsics"
+         #:arg-spec intrinsics-arguments
+         #:analyze intrinsics-analyze!)
+    )
+)
+
+(cgen-intrinsics (program-arguments))
diff --git a/intrinsics.scm b/intrinsics.scm
new file mode 100644 (file)
index 0000000..803e001
--- /dev/null
@@ -0,0 +1,1645 @@
+; intrinsics support generator support routines.
+; 
+; This entire file is deeply littered with mep-specific logic. You have
+; been warned.
+;
+; Copyright (C) 2000, 2001, 2002, 2003 Red Hat, Inc.
+; This file is part of CGEN.
+
+; Specify which application.
+(set! APPLICATION 'INTRINSICS)
+
+(debug-enable 'backtrace)
+
+; String containing copyright text.
+(define CURRENT-COPYRIGHT #f)
+
+; String containing text defining the package we're generating code for.
+(define CURRENT-PACKAGE #f)
+
+; Initialize the options.
+(define (option-init!)
+  (set! CURRENT-COPYRIGHT copyright-fsf)
+  (set! CURRENT-PACKAGE package-gnu-simulators)
+  *UNSPECIFIED*
+  )
+
+(define (intrinsics-analyze!)
+  (arch-analyze-insns! CURRENT-ARCH
+                      #f ; don't include aliases
+                      #t) ; do analyze the semantics
+  )
+
+;; Shortcuts for commonly-used functions.
+(define sa string-append)
+(define (st x) (stringize x " "))
+
+;; HELPER FUNCTIONS
+;; ----------------
+
+;; True if FN returns the same value for FIRST and SECOND.
+(define (same? fn first second)
+  (equal? (fn first) (fn second)))
+
+;; True if predicate FN holds for both FIRST and SECOND.
+(define (both? fn first second)
+  (and (fn first) (fn second)))
+
+;; True if FN holds for every element of LIST.
+(define (for-all? fn list)
+  (let loop ((list list))
+    (or (null? list)
+       (and (fn (car list))
+            (loop (cdr list))))))
+
+;; True if FN holds for one element of LIST.
+(define (exists? fn list)
+  (let loop ((list list))
+    (and (pair? list)
+        (or (fn (car list))
+            (loop (cdr list))))))
+
+;; True if LIST1 and LIST2 are the same length and (FN X Y) holds for
+;; each (X Y) in the zipped list.
+(define (for-all-pairs? fn list1 list2)
+  (let loop ((list1 list1) (list2 list2))
+    (or (both? null? list1 list2)
+       (and (both? pair? list1 list2)
+            (fn (car list1) (car list2))
+            (loop (cdr list1) (cdr list2))))))
+
+;; Use (SETTER ELEM INDEX) to assign some number INDEX to each element
+;; ELEM of LIST.  BASE is the index of the first element; other elements
+;; are numbered incrementally.  Return the first unused index value.
+(define (number-list setter list base)
+  (let loop ((list list) (index base))
+    (if (null? list)
+       index
+       (begin
+         (setter (car list) index)
+         (loop (cdr list) (+ index 1))))))
+
+;; Apply FN to every list of arguments in ARGS.
+(define (apply-list fn args)
+  (for-each (lambda (list) (apply fn list)) args))
+
+;; Sort list ELEMS with partial order FN, where (FN X Y) is true iff X "<=" Y.
+(define (sort-partial elems fn)
+  (if (null? elems)
+      elems
+      (let ((sorted (list (car elems))))
+       (for-each
+        (lambda (elem)
+          (let loop ((pos sorted))
+            (if (fn elem (car pos))
+                (begin
+                  (set-cdr! pos (cons (car pos) (cdr pos)))
+                  (set-car! pos elem))
+                (if (null? (cdr pos))
+                    (set-cdr! pos (list elem))
+                    (loop (cdr pos))))))
+        (cdr elems))
+       sorted)))
+
+;; Generate preprocessor macro names, suitable for use as bitmasks.
+(define (bitmask-name prefix name)
+  (string-upcase (sa prefix "_" (gen-c-symbol name))))
+
+;; Return an inclusive OR of every bitmask member in NAMES.
+(define (bitmask prefix names)
+  (if (null? names)
+      "0"
+      (stringize (map (lambda (x) (bitmask-name prefix x)) names) "|")))
+
+;; Assign values to every bitmask in NAMES.
+(define (define-bitmasks prefix names)
+  (number-list
+   (lambda (name index)
+     (string-write "#define " (bitmask-name prefix name)
+                  " " (st (logsll 1 index)) "\n"))
+   names 0)
+  (string-write "\n"))
+
+;; Convert ISA symbol ISA into a target-frobbed string
+(define (convert-isa isa)
+  (target:frob-isa-name (symbol->string isa)))
+
+;; PRETTY-PRINTER SUPPORT
+;; ----------------------
+
+;; How many spaces to indent the next line.
+(define indentation 0)
+
+;; End the current line and indent the new one.
+(define (line-break)
+  (string-write "\n" (make-string indentation #\space)))
+
+;; Helper functions, useful as arguments to WRITE-LIST.
+(define (comma-break)
+  (string-write ", "))
+
+(define (comma-line-break)
+  (string-write ",")
+  (line-break))
+
+;; Execute BODY so that every call to LINE-BREAK will indent by
+;; INDENT more spaces than it does now.
+(defmacro write-with-indent (indent . body)
+  `(begin
+     (set! indentation (+ indentation ,indent))
+     ,(cons 'begin body)
+     (set! indentation (- indentation ,indent))))
+
+;; Write PREFIX, then execute BODY so that every call to LINE-BREAK
+;; will indent to the end of the prefix.  Write SUFFIX afterwards.
+;;
+;; This function should only be called at the start of a new line.
+(defmacro write-construct (prefix suffix . body)
+  `(begin
+     (string-write ,prefix)
+     (write-with-indent (string-length ,prefix) ,(cons 'begin body))
+     (string-write ,suffix)))
+
+;; Write out each element of LIST individually using WRITE.  Use (BREAK)
+;; to separate the elements.
+(define (write-list break list write)
+  (if (pair? list)
+      (begin
+       (write (car list))
+       (for-each (lambda (x) (break) (write x)) (cdr list)))))
+
+;; Like WRITE-LIST, but write DUMMY if the list is empty.
+(define (write-nonempty-list break list write dummy)
+  (if (null? list)
+      (string-write dummy)
+      (write-list break list write)))
+
+;; MACROS
+;; ------
+
+;; little macro for making assoc tables with nice names
+(defmacro deftable (basename)
+  (let* ((table (symbol-append basename '-table))
+        (initializer (symbol-append 'init- basename '!))
+        (keys (symbol-append basename '-keys))
+        (getter (symbol-append 'get- basename))
+        (setter (symbol-append 'set- basename '!)))
+    `(begin
+       (define ,table '())
+       (define (,initializer) (set! ,table '()))
+       (define (,keys) (map car ,table))
+       (define (,getter k) 
+        (let ((pair (assoc k ,table)))
+          (if pair (cdr pair) pair)))
+       (define (,setter k v) 
+        (let ((pair (assoc k ,table)))
+          (if pair
+              (set-cdr! pair v)
+              (set! ,table (cons (cons k v) ,table))))))))
+
+;; Make a very simple structure interface.  NAME is the structure's name
+;; and FIELDS is a list of its fields.
+;;
+;;    (make-struct foo (f1 f2 f3 ...))
+;;
+;; defines the following functions:
+;;
+;;    (foo:make f1 f2 f3 ...)
+;;       Create a new object with the given values for fields F1, F2, F3...
+;;
+;;    (foo:f1 object)
+;;       Return the value of OBJECT's F1 field, or #f if OBJECT itself is #f.
+;;
+;;    (foo:set-f1! object value)
+;;       Set OBJECT's F1 field to VALUE.
+;;
+;; ... and likewise for the other fields.  Each structure is represented
+;; as a vector of its elements.
+(defmacro make-struct (name fields)
+  (let ((commands (list `(define ,(symbol-append name ':make)
+                          (lambda ,fields ,(cons 'vector fields))))))
+    (number-list
+     (lambda (field index)
+       (let* ((setname (symbol-append name ':set- field '!))
+             (getname (symbol-append name ': field))
+             (setter `(define (,setname x val) (vector-set! x ,index val)))
+             (getter `(define (,getname x) (and x (vector-ref x ,index)))))
+        (set! commands (cons setter (cons getter commands)))))
+     fields
+     0)
+    (cons 'begin commands)))
+
+
+;; MEP-SPECIFIC DETAILS
+;; --------------------
+
+;; Predicates for recognizing coprocessor register set hardware names.
+;; HW is the hardware name: a symbol, or #:unbound in some cases.
+;;
+;; At the moment, we do this by looking at the hardware's name as a
+;; string; it would be more graceful to handle this with an attribute.
+;;
+;; Older MeP .cpu files call the coprocessor register sets h-cr,
+;; h-cr64, and h-ccr.  Newer versions of a2cgen suffix the hardware
+;; names for the coprocessor's registers with the name of the
+;; coprocessor, and the me_module number.  So, for example, if
+;; me_module 3 has an rhcop coprocessor, its register sets will be
+;; called h-cr64-rhcop-3, h-cr-rhcop-3, and h-ccr-rhcop-3.
+
+;; Return a predicate that recognizes hardware names that start with
+;; PREFIX.  PREFIX is a string, like "h-cr"; the returned predicate
+;; will return true if its argument is the symbol whose name is
+;; PREFIX, (e.g. 'h-cr), or any symbol whose name begins with PREFIX
+;; followed by a hyphen (e.g. 'h-cr-rhcop-1).
+(define (suffixed-hardware-recognizer prefix)
+  ;; Precompute some stuff.
+  (let* ((no-hyphen-sym (string->symbol prefix))
+         (hyphenated (string-append prefix "-"))
+         (hyphenated-len (string-length hyphenated)))
+    (lambda (obj)
+      (or (eq? obj no-hyphen-sym)
+          (and (symbol? obj)
+               (let ((name (symbol->string obj)))
+                 (and (>= (string-length name) hyphenated-len)
+                      (string=? (substring name 0 hyphenated-len)
+                                hyphenated))))))))
+
+(define is-h-cr64?    (suffixed-hardware-recognizer "h-cr64"))
+(define is-h-cr?      (suffixed-hardware-recognizer "h-cr"))
+(define is-h-ccr?     (suffixed-hardware-recognizer "h-ccr"))
+
+;; Return the gcc rtl mode that should be used for operand OP.
+;; Return #f to use the default, target-independent choice.
+(define (target:guess-mode op)
+  (cond
+   ((equal? (md-operand:cdata op) 'FMAX_INT) "SI")
+   ((equal? (md-operand:cdata op) 'FMAX_FLOAT) "SF")
+   ((is-h-cr64? (md-operand:hw op)) "COPDI")
+   ((is-h-cr? (md-operand:hw op)) "COPSI")
+   ((not (memory? (md-operand:type op))) "SI")
+   (else #f)))
+
+;; Return the list of arguments for an intrinsic function.  ARGUMENTS is
+;; a list of the operands found in the instruction's syntax string, in the
+;; order they appear.  OUTPUT-OPERANDS is a list of all the instruction's
+;; output operands (no particular order).  Both lists contain md-operands.
+;;
+;; Normally ARGUMENTS itself is the correct return value, but we
+;; need a couple of MeP-specific hacks:
+;;
+;;   - Instructions that write to r0 do not make r0 a syntactic
+;;   operand.  Instead, they embed "\\$0" in the syntax string.
+;;   Cope with this by adding $0 to the beginning of the list
+;;   if written.
+;;
+;;   - $spr and $tpr can appear in the syntax string but are
+;;   not supposed to be treated as arguments to the intrinsic.
+(define (target:frob-arguments arguments output-operands)
+  (set! arguments (find (lambda (op)
+                         (not (member (md-operand:name op) '(tpr spr))))
+                       arguments))
+  (let ((r0-writes (find (lambda (op)
+                          (equal? (md-operand:fixed-register op) 0))
+                        output-operands)))
+    (if (pair? r0-writes)
+       (set! arguments (cons (car r0-writes) arguments))))
+  arguments)
+
+;; Convert the given cgen ISA name into its gcc equivalent.
+;; cgen names such as 'ext_core<X>' and 'ext_cop<X>_YY' become 'ext<X>'.
+(define (target:frob-isa-name isa)
+  (cond
+   ((equal? "ext_cop" (string-take 7 isa))
+    (sa "ext" (string-drop 7 (string-drop -3 isa))))
+
+   ((equal? "ext_core" (string-take 8 isa))
+    (sa "ext" (string-drop 8 isa)))
+
+   (else isa)))
+
+;; Apply FN once for each ISA.  The first argument to FN is a user-readable
+;; string that describes the ISA.  The second argument is the ISA name
+;; returned by frob-isa-name.
+(define (target:for-each-isa! fn)
+  (for-each (lambda (entry)
+             (apply fn (car entry) (sa "ext" (st (cadr entry))) '()))
+           (cdr (attr-values (current-attr-lookup 'CONFIG)))))
+
+;; Return the number of the first register belonging to the given
+;; hardware element.
+(define (target:base-reg hw)
+  (cond
+   ((eq? hw 'h-gpr) 0)             ; core registers
+   ((eq? hw 'h-csr) 16)            ; control registers
+   ((is-h-cr? hw) 48)              ; 32-bit coprocessor registers
+   ((is-h-cr64? hw) 48)            ; 64-bit coprocessor registers (same)
+   ((is-h-ccr? hw) 80)             ; coprocessor control registers
+   (else 0)))
+
+;; Return the constraint string for register operand OP.
+(define (target:reg-constraint op)
+  (case (md-operand:fixed-register op)
+    ((0) "z")
+    ((23) "h") ;; hi
+    ((24) "l") ;; lo
+    (else
+     (cond
+      ;; "tiny" registers, in the range 0..7
+      ((equal? (md-operand:ifield op) 'f-rn3) "t")
+
+      (else
+       (let ((hw (md-operand:hw op)))
+         (cond
+          ((eq? hw 'h-gpr) "r")    ; core registers
+          ((eq? hw 'h-csr) "c")    ; control registers
+         ((or (is-h-cr64? hw)     ; 32-bit coprocessor registers
+              (is-h-cr? hw))      ; 64-bit coprocessor registers
+          (if (equal? (md-operand:length op) 4) "em" "x"))
+          ((is-h-ccr? hw) "y")     ; coprocessor control registers
+          (else "r"))))))))
+
+;; The first hard register available to the intrinsics generator.
+(define target:first-unused-register 97)
+
+;; The instructions mapped to a particular intrinsic can be subdivided
+;; into groups, each representing a particular form of code generation.
+;; In the MeP case, we have one group for __vliw functions and one group
+;; for normal functions.
+(define target:groups '(normal vliw))
+
+;; True if INSN belongs to GROUP, where GROUP is a membmer of TARGET:GROUPS.
+(define (target:belongs-to-group? insn group)
+  (case (obj-attr-value (md-insn:cgen-insn insn) 'SLOT)
+    ((NONE) #t)
+    ((C3) (equal? group 'normal))
+    ((V1 V3) (equal? group 'vliw))))
+
+;; Convert an intrinsic's cgen name into the name of its builtin function.
+(define (target:builtin-name name)
+  (string-append "mep_" (gen-c-symbol name)))
+
+;; Helper functions for getting the values of certain mep-specific gcc
+;; attributes.  In each case INSN is a cgen instruction (not an md-insn).
+(define (-may-trap-attribute insn)
+  (if (obj-has-attr? insn 'MAY_TRAP) "yes" "no"))
+
+(define (-slot-attribute insn)
+  (if (exists? (lambda (isa)
+                (or (equal? isa 'mep)
+                    (equal? (string-take 8 (st isa)) "ext_core")))
+              (bitset-attr->list (obj-attr-value insn 'ISA)))
+      "core"
+      "cop"))
+
+(define (-latency-attribute insn)
+  (if (obj-attr-value insn 'LATENCY)
+      (st (obj-attr-value insn 'LATENCY))
+      "0"))
+
+(define (-length-attribute insn)
+  (st (/ (insn-length insn) 8)))
+
+(define (-stall-attribute insn)
+  (string-downcase (st (obj-attr-value insn 'STALL))))
+
+;; Return the define_insn attributes for INSN as a list of (NAME . VALUE)
+;; pairs.
+(define (target:attributes insn)
+  (let ((cgen-insn (md-insn:cgen-insn insn)))
+    (list (cons 'may_trap (-may-trap-attribute cgen-insn))
+         (cons 'latency (-latency-attribute cgen-insn))
+         (cons 'length (-length-attribute cgen-insn))
+         (cons 'slot (-slot-attribute cgen-insn))
+         (if (eq? (obj-attr-value cgen-insn 'STALL) 'SHIFTI)
+             (cons 'shiftop "operand2")
+             (cons 'stall (-stall-attribute cgen-insn))))))
+
+;; Define target-specific fields of cgen_insn.  In the MeP case, we want
+;; to record how long the intruction is.
+(define (target:declare-fields)
+  (sa "\n"
+      "  /* The length of the instruction, in bytes.  */\n"
+      "  int length;\n"))
+
+;; Initialize the fields described above.
+(define (target:initialize-fields insn)
+  (comma-line-break)
+  (string-write (-length-attribute (md-insn:cgen-insn insn))))
+
+;; Use WELL-KNOWN-INTRINSIC to define the names of builtins that
+;; gcc might treat specially.
+(define (target:add-well-known-intrinsics)
+  (apply-list (lambda args
+               (apply well-known-intrinsic args)
+               (apply well-known-intrinsic (sa (car args) "3") (cdr args))
+               (apply well-known-intrinsic (sa (car args) "i") (cdr args))
+               (apply well-known-intrinsic (sa (car args) "i3") (cdr args)))
+             `(("cadd" plus)
+               ("csub" minus)
+               ("cand" and)
+               ("cor" ior)
+               ("cnor" nor)
+               ("cxor" xor)
+               ("csll" ashift)
+               ("csrl" lshiftrt)
+               ("csra" ashiftrt)))
+
+  (apply-list well-known-intrinsic
+             `(("cmov")
+               ("cmovi" set)
+               ("cmov1")
+               ("cmov2")
+               ("cmovc1")
+               ("cmovc2")
+               ("cmovh1")
+               ("cmovh2")
+               ("cneg" neg)
+               ("cmula0")
+               ("xmula0")
+               ("cextuh")
+               ("cextub")
+               ("cexth")
+               ("cextb")
+               ("fmovs")
+               ("fadds" plus "TARGET_FMAX")
+               ("fsubs" minus "TARGET_FMAX")
+               ("fmuls" mult "TARGET_FMAX")
+               ("fdivs" div "TARGET_FMAX")
+               ("fsqrts" sqrt "TARGET_FMAX")
+               ("fabss" abs "TARGET_FMAX")
+               ("fnegs" neg "TARGET_FMAX")
+               ("ftruncws" fix "TARGET_FMAX")
+               ("fcvtsw" float "TARGET_FMAX")
+               ("fcmpus" unordered "TARGET_FMAX")
+               ("fcmpues" uneq "TARGET_FMAX")
+               ("fcmpuls" unlt "TARGET_FMAX")
+               ("fcmpules" unle "TARGET_FMAX")
+               ("fcmpes" eq "TARGET_FMAX")
+               ("fcmplis" lt "TARGET_FMAX")
+               ("fcmpleis" le "TARGET_FMAX"))))
+
+;; INTRINSIC OPERANDS
+;; ------------------
+;;
+;; Each intrinsic operand is represented by a unique MD-OPERAND.
+;; These objects refer back to normal cgen operands but add the extra
+;; information needed for intrinsics support.  Each MD-OPERAND belongs
+;; to exactly one MD-INSN.
+;;
+;;    OP is the cgen operand
+;;
+;;    IFIELD-VALUE is the constant value that the instruction assigns
+;;    to the operand's field, or #f if the field isn't constant.
+;;
+;;    ARG-INDEX is the position of this operand in the intrinsic's
+;;    argument list, or #f if the operand is not an argument.
+;;
+;;    READ-INDEX is the match_operand number assigned to this operand
+;;    when it appears in a right-hand context.  The value is #f if we
+;;    never generate such a match_operand, either because the operand
+;;    is a strict lvalue or because ARG-INDEX is #f.
+;;
+;;    WRITE-INDEX is like READ-INDEX but is used for left-hand contexts.
+;;
+;;    MODE is the operand's gcc mode (SI, etc.).
+(make-struct md-operand (op ifield-value arg-index
+                        read-index write-index mode))
+
+;; Helper functions to extract commonly-used fields from the
+;; underlying cgen operand.
+(define (md-operand:name op) (op:sem-name (md-operand:op op)))
+(define (md-operand:type op) (op:type (md-operand:op op)))
+(define (md-operand:register? op) (register? (md-operand:type op)))
+(define (md-operand:index op) (op:index (md-operand:op op)))
+(define (md-operand:length op) (op:length (md-operand:op op)))
+(define (md-operand:hw op) (op:hw-name (md-operand:op op)))
+(define (md-operand:ifield op)
+  (let ((ifield (op-ifield (md-operand:op op))))
+    (and ifield (obj:name ifield))))
+
+;; Functions to access well-known operand attributes.
+(define (md-operand:cdata op) (obj-attr-value (md-operand:op op) 'CDATA))
+(define (md-operand:alignment op) (obj-attr-value (md-operand:op op) 'ALIGN))
+(define (md-operand:sem-only? op) (obj-has-attr? (md-operand:op op) 'SEM-ONLY))
+
+;; Return true if operand OP represents the program counter.
+(define (md-operand:pc? op)
+  (or (equal? (md-operand:name op) 'pc)
+      (pc? (md-operand:type op))))
+
+;; Return true if operand OP must be mapped to a label.  This is only
+;; ever true of argument operands.
+(define (md-operand:label? op)
+  (and (class-instance? <hw-immediate> (md-operand:type op))
+       (equal? (md-operand:cdata op) 'LABEL)))
+
+;; Return true if OP is an immediate operand.
+(define (md-operand:immediate? op)
+  (class-instance? <hw-immediate> (md-operand:type op)))
+
+;; Return true if operand OP is an index into a register file.  gcc will
+;; convert them into REG rtxes.
+(define (md-operand:regnum? op)
+  (equal? (md-operand:cdata op) 'REGNUM))
+
+;; If operand OP is a fixed hard register, return the number GCC assigns
+;; to it, otherwise return #f.
+(define (md-operand:fixed-register op)
+  (and (not (md-operand:pc? op))
+       (md-operand:register? op)
+       (let ((constant (if (equal? 'constant
+                                  (hw-index:type (md-operand:index op)))
+                          (hw-index:value (md-operand:index op))
+                          (md-operand:ifield-value op))))
+        (and constant
+             (+ constant (target:base-reg (md-operand:hw op)))))))
+
+;; SPECIFIC TO 32-BIT TARGETS
+;; Guess the gcc rtl mode for operand OP.  First see whether it uses
+;; a known hardware element, then try the CDATA attribute.
+(define (md-operand:guess-mode op)
+  (or (target:guess-mode op)
+      (case (md-operand:cdata op)
+       ((SHORT USHORT) "HI")
+       ((CHAR UCHAR) "QI")
+       (else "SI"))))
+
+;; Return true if operand OP is a signed immediate.
+(define (md-operand:signed? op)
+  (equal? (md-operand:hw op) 'h-sint))
+
+;; If OP accepts only CONST_INTs, return the lowest value it accepts.
+(define (md-operand:lower-bound op)
+  (if (md-operand:signed? op)
+      (- (logsll 1 (+ (md-operand:alignment op)
+                     (md-operand:length op)
+                     -2)))
+      0))
+
+;; Likewise the highest value + 1.
+(define (md-operand:upper-bound op)
+  (logsll 1 (+ (md-operand:alignment op)
+              (md-operand:length op)
+              (if (md-operand:signed? op) -2 -1))))
+
+;; Return the name of an immediate predicate for operand OP, assuming
+;; that OP should accept only CONST_INTs.  We define these predicates
+;; in the gcc include file.
+(define (md-operand:immediate-predicate op)
+  (gen-c-symbol (sa "cgen_" (st (md-operand:hw op)) "_"
+                   (st (md-operand:length op))
+                   "a" (st (md-operand:alignment op))
+                   "_immediate")))
+
+;; Return the match_operand predicate for operand OP.
+(define (md-operand:predicate op lvalue?)
+  (cond
+   ((memory? (md-operand:type op)) "memory_operand")
+   ((md-operand:label? op) "immediate_operand")
+   ((md-operand:immediate? op) (md-operand:immediate-predicate op))
+   (lvalue? "nonimmediate_operand")
+   (else "general_operand")))
+
+
+;; Return the gcc rtx for non-argument operand OP.
+(define (md-operand:fixed-rtx op)
+  (cond
+   ((memory? (md-operand:type op))
+    (sa "(mem:" (md-operand:mode op) " (scratch:SI))"))
+
+   ((md-operand:fixed-register op)
+    (sa "(reg:" (md-operand:mode op) " "
+       (st (md-operand:fixed-register op)) ")"))
+
+   (else
+    (error (sa "bad intrinsic operand \"" (st (md-operand:name op))
+              "\": need constant or ifield indexed register, got "
+              (st (hw-index:type (md-operand:index op))))))))
+
+;; Return the constraint string for operand OP.  LVALUE? is true if the
+;; operand is appearing in a left-hand context.  For read-write operands,
+;; the rvalue operand should have a numerical constraint giving the
+;; number of the lvalue.
+(define (md-operand:constraint lvalue? op)
+  (cond
+   ((and (not lvalue?) (md-operand:write-index op))
+    (st (md-operand:write-index op)))
+   ((md-operand:immediate? op) "")
+   (else (target:reg-constraint op))))
+
+;; Return the rtl pattern for operand OP.  CONTEXT is LHS if the operand
+;; is being used as an lvalue, RHS if it is being used as an rvalue in the
+;; first set of a pattern and RHS-COPY if it is being used as an rvalue
+;; in subsequent sets.
+(define (md-operand:to-string context op)
+  (cond
+   ((md-operand:pc? op) "(pc)")
+   (else
+    (let* ((lvalue? (equal? context 'lhs))
+          (index (if lvalue?
+                     (md-operand:write-index op)
+                     (md-operand:read-index op))))
+      (cond
+       ((not index) (md-operand:fixed-rtx op))
+       ((equal? context 'rhs-copy) (sa "(match_dup " (st index) ")"))
+       (else
+       (sa "(match_operand:"
+           (md-operand:mode op) " " (st index) " \""
+           (md-operand:predicate op lvalue?) "\" \"" (if lvalue? "=" "")
+           (md-operand:constraint lvalue? op) "\")")))))))
+
+
+;; GCC INSTRUCTION PATTERNS
+;; ------------------------
+;;
+;; If we need to generate a define_insn pattern for a particular cgen
+;; instruction, we will create a unique MD-INSN for it.  Each MD-INSN
+;; is associated with a (shared) INTRINSIC object.
+;;
+;;    MD-NAME is the name of the define_insn pattern
+;;
+;;    INDEX is a unique number given to this instruction.  Instructions
+;;    are numbered according to their position in the .md output file,
+;;    the first instruction having index 0.
+;;
+;;    INTRINSIC is the intrinsic object to which this instruction belongs.
+;;
+;;    CGEN-INSN is the underlying cgen insn.
+;;
+;;    SYNTAX is the output of syntax-break-out with cgen operands
+;;    converted to md-operands.
+;;
+;;    ARGUMENTS is a list of the operands that act as formal arguments
+;;    to the intrinsic function.  Usually this is the same as SYNTAX
+;;    with strings removed, but there can be target-specific reasons
+;;    for using a different argument list.
+;;
+;;    INPUTS is a list of the operands that appear in a right-hand
+;;    context within the define_insn pattern.  If a member of this
+;;    list is also in ARGUMENTS, it will have a valid READ-INDEX.
+;;
+;;    OUTPUTS is like INPUTS except that it lists the operands that
+;;    appear in a left-hand context.  Argument operands in this list
+;;    will have a valid WRITE-INDEX.
+;;
+;;    OPERANDS is a concatenation of OUTPUTS and INPUTS.
+(make-struct md-insn (md-name index intrinsic cgen-insn syntax arguments
+                     inputs outputs operands))
+
+;; Return the name of the underlying cgen insn, mostly used for
+;; error reporting.
+(define (md-insn:cgen-name insn) (obj:name (md-insn:cgen-insn insn)))
+
+;; Return true if INSN is inherently volatile, meaning that it has
+;; important effects that are not described by its gcc rtx pattern.
+;; This is true for any instruction with the VOLATILE attribute,
+;; any instruction without output operands (including those with
+;; no semantics at all) and any instruction that reads from or
+;; writes to a REGNUM operand.
+(define (md-insn:volatile? insn)
+  (or (null? (md-insn:outputs insn))
+      (exists? md-operand:regnum? (md-insn:operands insn))
+      (obj-has-attr? (md-insn:cgen-insn insn) 'VOLATILE)))
+
+;; Return the list of ISAs that implement INSN.  Ignore those that
+;; were excluded on the command line.
+(define (md-insn:isas insn)
+  (map convert-isa
+       (find (lambda (isa) (member isa intrinsics-isas))
+            (bitset-attr->list
+             (obj-attr-value (md-insn:cgen-insn insn) 'ISA)))))
+
+;; The full list of instruction groups.  As well target-specific groups,
+;; this includes "known-code", meaning that the instruction uses a specific
+;; rtl code instead of an unspec.
+(define md-insn-groups (cons 'known-code target:groups))
+
+;; Return the list of groups to which INSN belongs.
+(define (md-insn:groups insn)
+  (let ((target-groups (find (lambda (group)
+                              (target:belongs-to-group? insn group))
+                            target:groups)))
+    (if (intrinsic:unspec-version (md-insn:intrinsic insn))
+       (cons 'known-code target-groups)
+       target-groups)))
+
+;; Partial ordering of syntax elements.  Return true if ELEM1 and ELEM2
+;; are compatible and ELEM2's range is a superset of ELEM1's.  The rules
+;; are that:
+;;
+;;    - Identical syntax strings are compatible.
+;;
+;;    - Immediate operands are compatible if the range of one is contained
+;;    within the range of the other.
+;;
+;;    - Other types of operand are compatible if they use the same
+;;    hardware element and have the same length.
+(define (syntax<=? elem1 elem2)
+  (or (and (both? vector? elem1 elem2)
+          (if (both? md-operand:immediate? elem1 elem2)
+              (and (>= (md-operand:alignment elem1)
+                       (md-operand:alignment elem2))
+                   (>= (md-operand:lower-bound elem1)
+                       (md-operand:lower-bound elem2))
+                   (<= (md-operand:upper-bound elem1)
+                       (md-operand:upper-bound elem2)))
+              (and (same? md-operand:hw elem1 elem2)
+                   (same? md-operand:length elem1 elem2))))
+      (and (both? string? elem1 elem2)
+          (string=? elem1 elem2))))
+
+;; Helper functions for comparing lists of operands or lists of syntax
+;; pieces using the above ordering.
+(define (md-insn:operands<=? insn1 insn2)
+  (for-all-pairs? syntax<=?
+                 (md-insn:operands insn1)
+                 (md-insn:operands insn2)))
+
+(define (md-insn:syntax<=? insn1 insn2)
+  (for-all-pairs? syntax<=?
+                 (md-insn:syntax insn1)
+                 (md-insn:syntax insn2)))
+
+
+;; INTRINSICS
+;; ----------
+;;
+;; Intrinsics have two names, the one that appears in the cgen file
+;; and the one that is given to the builtin function.  The former is
+;; its "cgen name" and is only relevant during the analysis phase.
+;;
+;;    NAME is the name of the intrinsic's builtin function.  It is
+;;    generated from the cgen name by TARGET:BUILTIN-NAME.
+;;
+;;    INDEX is the index of this intrinsic in the global INTRINSICS list.
+;;
+;;    UNSPEC is the unspec number to use for the right hand side of the
+;;    first SET pattern.  Add 2 for each subsequent output (so that real
+;;    and shadow registers can use different unspec numbers).
+;;
+;;    HOOK is the gcc-hook object associated with this intrinsic,
+;;    or #f if none.
+;;
+;;    ISAS maps ISA names to the most general implementation of the
+;;    intrinsic for that ISA.  Used for error checking.
+(make-struct intrinsic (name index unspec hook isas))
+
+;; Short-cut functions
+(define (intrinsic:unspec-version intrinsic)
+  (gcc-hook:unspec-version (intrinsic:hook intrinsic)))
+
+;; Return the maximum of HIGHEST and the length of insn property PROPERTY
+;; for any implementation of INSTRINSIC.  PROPERTY can the something
+;; like MD-INSN:INPUTS or MD-INSN:OUTPUTS.
+(define (intrinsic:max highest property intrinsic)
+  (for-each
+   (lambda (isa)
+     (set! highest (max highest (length (apply property (cdr isa) '())))))
+   (intrinsic:isas intrinsic))
+  highest)
+
+;; GLOBAL VARIABLES
+;; ----------------
+
+;; Maps cgen intrinsic names to intrinsic objects.
+(deftable intrinsic)
+
+;; The list of all intrinsics.  After the analysis phase, this list
+;; is in index order.
+(define intrinsics '())
+
+;; The list of all instructions, in the order they appear in the .md file.
+;; When two instructions are compatible, but one is more general than
+;; the other, the more general one will come after the less general one.
+(define md-insns '())
+
+;; Maps fixed hard registers onto shadow global registers.
+(define shadow-registers '())
+
+;; Create an intrinsic with the given cgen name and gcc hook.  Add it to
+;; INTRINSICS and INTRINSIC-TABLE.
+(define (add-intrinsic name hook)
+  (let ((intrinsic (intrinsic:make (target:builtin-name name) #f #f hook '())))
+    (set! intrinsics (cons intrinsic intrinsics))
+    (set-intrinsic! name intrinsic)
+    intrinsic))
+
+;; Return a shadow version of hard register REG.
+(define (get-shadow reg)
+  (or (assoc-ref shadow-registers reg)
+      (let ((retval (+ target:first-unused-register
+                      (length shadow-registers))))
+       (set! shadow-registers
+             (append! shadow-registers (list (cons reg retval))))
+       retval)))
+
+;; WELL-KNOWN INTRINSICS
+;; ---------------------
+
+;; gcc might have a special use for certain intrinsics.  Such intrinsics
+;; have a GCC-HOOK structure attached.
+;;
+;;    RTL-CODE is an rtl code that can be used in the define_insn
+;;    pattern instead of usual unspec or unspec_volatile.  Usually
+;;    the field is an arithmetic or logic code, but it can also be:
+;;
+;;        - 'set': the intrinsic implements a move of some sort.
+;;        - 'nor': represented in gcc as (and (not X) (not Y)).
+;;       - #f: use unspecs as normal.
+;;
+;;    CONDITION is a condition that must be true for the RTL-CODE
+;;    version of the instruction to be available.
+;;
+;;    UNSPEC-VERSION is a version of the same intrinsic that has no
+;;    gcc-hook structure.  It is sometimes useful to have two versions
+;;    of the same instrinsic, one with a specific rtl-code and one
+;;    with a general unspec.  The former will allow more optimisations
+;;    while the latter will act more like an inline asm statement.
+(make-struct gcc-hook (rtl-code condition unspec-version))
+
+;; Declare a well-known intrinsic with the given cgen name and
+;; gcc-hook fields.
+(define (well-known-intrinsic name . args)
+  (let* ((rtl-code (and (> (length args) 0) (car args)))
+        (condition (and (> (length args) 1) (cadr args)))
+        (unspec-version (and rtl-code (add-intrinsic name #f))))
+    (add-intrinsic name (gcc-hook:make rtl-code condition unspec-version))))
+
+(target:add-well-known-intrinsics)
+
+
+;; ANALYSIS PHASE
+;; --------------
+
+;; The next available unspec number.
+(define next-unspec 1000)
+
+;; Given cgen instruction INSN, return the cgen name of its intrinsic.
+(define (intrinsic-name insn)
+  (let ((name (obj-attr-value insn 'INTRINSIC)))
+    (if (equal? name "") (symbol->string (obj:name insn)) name)))
+
+;; Look up an intrinsic by its cgen name.  Create a new intrinsic
+;; if the name hasn't been used yet.
+(define (find-intrinsic name)
+  (or (get-intrinsic name)
+      (add-intrinsic name #f)))
+
+;; If instruction INSN assigns to a constant value to OP's field,
+;; record it in IFIELD-VALUE.
+(define (check-ifield-value op insn)
+  (let* ((name (md-operand:ifield op))
+        (ifield (and name (object-assq name (insn-iflds insn)))))
+    (if (and ifield (ifld-constant? ifield))
+       (md-operand:set-ifield-value! op (ifld-get-value ifield)))))
+
+;; Create an md-insn from the given cgen instruction and add it to MD-INSNS.
+(define (add-md-insn insn intrinsic md-prefix)
+  (let* ((sfmt (insn-sfmt insn))
+        (operands '())
+
+        ;; Create a new md-operand for OP.
+        (new-operand (lambda (op)
+                       (let ((created (md-operand:make op #f #f #f #f #f)))
+                         (set! operands (cons created operands))
+                         (check-ifield-value created insn)
+                         created)))
+
+        ;; Find an md-operand for OP, create a new one if we
+        ;; haven't seen it before.
+        (make-operand (lambda (op)
+                        (let loop ((entry operands))
+                          (if (null? entry)
+                              (new-operand op)
+                              (if (equal? (op:sem-name op)
+                                          (md-operand:name (car entry)))
+                                  (car entry)
+                                  (loop (cdr entry)))))))
+
+        ;; A partial order on md-operands.  Sort them by their position
+        ;; in the argument list, putting non-argument operands last.
+        ;;
+        ;; This ordering is needed when non-commutative intrinsics
+        ;; use a specific gcc rtl code.  For example, if we have
+        ;; an intrinsic:
+        ;;
+        ;;      sub (op0, op1, op2)
+        ;;
+        ;; which is known to do subtraction, we might use the MINUS
+        ;; rtl code in the define_insn pattern.  op1 must then be
+        ;; the first input operand and op2 must be the second:
+        ;;
+        ;;      (set op0 (minus op1 op2))
+        (op<= (lambda (x y)
+                (let ((xpos (md-operand:arg-index x))
+                      (ypos (md-operand:arg-index y)))
+                  (or (not ypos) (and xpos (<= xpos ypos))))))
+
+        ;; Create a version of the broken-out syntax in which
+        ;; each cgen operand is replaced by an md-operand.
+        (syntax (map (lambda (x)
+                       (if (operand? x) (make-operand x) x))
+                     (syntax-break-out (insn-syntax insn))))
+
+        ;; All relevant outputs.
+        (outputs (find (lambda (op)
+                         (or (md-operand:pc? op)
+                             (md-operand:fixed-register op)
+                             (not (md-operand:sem-only? op))))
+                       (map make-operand (sfmt-out-ops sfmt))))
+
+        ;; The arguments to the intrinsic function, represented as
+        ;; a list of operands.  Usually this is taken directly from
+        ;; the assembler syntax, but allow machine-specific hacks
+        ;; to modify the list.
+        (arguments (target:frob-arguments (find vector? syntax) outputs))
+
+        ;; The operands that we know to be inputs.  For tidiness' sake,
+        ;; remove (pc), which was no real meaning inside an unspec or
+        ;; unspec_volatile.
+        (inputs (find (lambda (op)
+                        (and (not (md-operand:pc? op))
+                             (or (md-operand:fixed-register op)
+                                 (not (md-operand:sem-only? op)))))
+                      (map make-operand (sfmt-in-ops sfmt))))
+
+        ;; If an argument has not been classified as an input
+        ;; or an output, treat it as an input.  This helps us to
+        ;; deal with insns whose semantics have not been given.
+        (quiet-inputs (find (lambda (op)
+                              (and (not (memq op inputs))
+                                   (not (memq op outputs))))
+                            arguments)))
+
+    ;; Number each argument operand according to its position in the list.
+    (number-list md-operand:set-arg-index! arguments 0)
+
+    ;; Sort the inputs and outputs as described above.
+    (set! inputs (sort-partial (append inputs quiet-inputs) op<=))
+    (set! outputs (sort-partial outputs op<=))
+
+    ;; Assign match_operand numbers to each argument.  Outputs should
+    ;; have lower numbers than inputs.
+    (number-list md-operand:set-read-index!
+                (find md-operand:arg-index inputs)
+                (number-list md-operand:set-write-index!
+                             (find md-operand:arg-index outputs)
+                             0))
+
+    ;; Assign a mode to each operand.  If we have an output operand,
+    ;; use its mode for all immediate operands.  This is mainly for
+    ;; intrinsics which use rtl codes like 'plus': the source operands
+    ;; are then expected to have the same mode as the destination.
+    (for-each (lambda (op)
+               (if (and (pair? outputs) (md-operand:immediate? op))
+                   (md-operand:set-mode! op (md-operand:mode (car outputs)))
+                   (md-operand:set-mode! op (md-operand:guess-mode op))))
+             (append outputs inputs))
+
+    (set! md-insns
+         (cons (md-insn:make (sa md-prefix (gen-c-symbol (obj:name insn)))
+                             #f intrinsic insn syntax
+                             arguments inputs outputs
+                             (append outputs inputs))
+               md-insns))))
+
+;; Make INSN available when generating code for ISA, updating INSN's
+;; intrinsic structure accordingly.  Insns are passed to this function
+;; in .md file order.
+(define (add-intrinsic-for-isa insn isa)
+  (let* ((intrinsic (md-insn:intrinsic insn))
+        (entry (assoc isa (intrinsic:isas intrinsic))))
+    (if (not entry)
+       ;; We haven't yet seen an implementation of this intrinsic for ISA.
+       (intrinsic:set-isas! intrinsic
+                            (cons (cons isa insn)
+                                  (intrinsic:isas intrinsic)))
+
+       ;; The intrinsic has already been implemented for ISA.
+       ;; Check whether INSN is at least as general as the bellwether
+       ;; implementation.  If it isn't, report an error, otherwise
+       ;; use INSN as the new bellwether.
+       (let ((bellwether (cdr entry)))
+         (if (not (md-insn:syntax<=? bellwether insn))
+             (error (sa "instructions \"" (md-insn:cgen-name insn)
+                        "\" and \"" (md-insn:cgen-name bellwether)
+                        "\" are both mapped to intrinsic \""
+                        (intrinsic:name intrinsic)
+                        "\" but do not have a compatible syntax")))
+
+         (if (not (md-insn:operands<=? bellwether insn))
+             (error (sa "instructions \"" (md-insn:cgen-name insn)
+                        "\" and \"" (md-insn:cgen-name bellwether)
+                        "\" are both mapped to intrinsic \""
+                        (intrinsic:name intrinsic)
+                        "\" but do not have compatible semantics")))
+
+         (set-cdr! entry insn)))))
+
+;; Return true if the given insn should be included in the output files.
+(define (need-insn? insn)
+  (not (member (insn-mnemonic insn) '("--unused--" "--reserved--"))))
+
+;; Set up global variables, if we haven't already.
+(define (analyze-intrinsics!)
+  (if (null? md-insns)
+      (begin
+       (message "Analyzing intrinsics...\n")
+
+       ;; Set up the global lists.
+       (for-each
+        (lambda (insn)
+          (if (need-insn? insn)
+              (let ((intrinsic (find-intrinsic (intrinsic-name insn))))
+                (add-md-insn insn intrinsic "cgen_intrinsic_")
+                (if (intrinsic:unspec-version intrinsic)
+                    (add-md-insn insn (intrinsic:unspec-version intrinsic)
+                                 "cgen_intrinsic_unspec_")))))
+        (current-insn-list))
+
+       (set! md-insns (sort-partial md-insns md-insn:syntax<=?))
+
+       ;; Tell each object what position it has in its respective list.
+       (number-list md-insn:set-index! md-insns 0)
+       (number-list intrinsic:set-index! intrinsics 0)
+
+       ;; Check whether the mapping of instructions to intrinsics is OK.
+       (for-each
+        (lambda (insn)
+          (for-each
+           (lambda (isa) (add-intrinsic-for-isa insn isa))
+           (md-insn:isas insn)))
+        md-insns)
+
+       ;; Assign unspec numbers to each intrinsic.
+       (for-each
+        (lambda (intrinsic)
+          (intrinsic:set-unspec! intrinsic next-unspec)
+          (set! next-unspec
+                (+ next-unspec
+                   (* 2 (intrinsic:max 1 md-insn:outputs intrinsic)))))
+        intrinsics))))
+
+
+;; ITERATION FUNCTIONS
+;; -------------------
+
+(define (for-each-md-insn fn)
+  (for-each fn md-insns))
+
+(define (for-each-argument fn)
+  (for-each-md-insn
+   (lambda (insn)
+     (for-each (lambda (op) (fn insn op))
+              (md-insn:arguments insn)))))
+
+;; .MD GENERATOR
+;; -------------
+
+;; Write the output template for INSN's define_insn.
+;; ??? Still MeP-specific.
+(define (write-syntax insn)
+  (let ((in-mnemonic? #t))
+    (for-each
+     (lambda (part)
+       (cond
+       ((vector? part)
+        (let* ((name (md-operand:name part))
+               (pos (lambda () (st (or (md-operand:read-index part)
+                                       (md-operand:write-index part))))))
+          (cond
+           ((equal? name 'tpr) (string-write "$tp"))
+           ((equal? name 'spr) (string-write "$sp"))
+           ((equal? name 'csrn) (string-write "%" (pos)))
+           ((md-operand:label? part) (string-write "%l" (pos)))
+           (else (string-write "%" (pos))))))
+
+       ((and in-mnemonic? (equal? " " part))
+        (set! in-mnemonic? #f)
+        (string-write "\\\\t"))
+
+       (else (string-write part))))
+     (md-insn:syntax insn))))
+
+;; Write the inputs to INSN, wrapped in an unspec, unspec_volatile,
+;; or intrinsic-specific rtl code.  MODE is the mode should go after
+;; the wrapper's rtl-code, such as "" or ":SI".  UNSPEC is the unspec
+;; number to use, if an unspec is needed, and CONTEXT is as for
+;; MD-OPERAND:TO-STRING.
+(define (write-inputs context insn mode unspec)
+  (let* ((code (gcc-hook:rtl-code (intrinsic:hook (md-insn:intrinsic insn))))
+        (inputs (map (lambda (op)
+                       (md-operand:to-string context op))
+                     (md-insn:inputs insn))))
+    (if (not code)
+       (begin
+         (string-write (if (md-insn:volatile? insn)
+                           "(unspec_volatile"
+                           "(unspec")
+                       mode " [")
+         (write-with-indent 2
+           (line-break)
+           (if (null? inputs)
+               (string-write "(const_int 0)")
+               (write-list line-break inputs string-write)))
+         (line-break)
+         (string-write "] " (st unspec) ")"))
+       (cond
+        ((equal? code 'set)
+         (string-write (car inputs)))
+
+        ((equal? code 'nor)
+         (write-construct (sa "(and" mode " ") ")"
+           (write-list line-break inputs
+                       (lambda (op)
+                         (string-write "(not" mode " " op ")")))))
+
+        (else
+         (write-construct (sa "(" (st code) mode " ") ")"
+           (write-list line-break inputs string-write)))))))
+
+;; Write a "(set ...)" pattern for the given output.  CONTEXT is RHS
+;; for the first output and RHS-COPY for the rest.  UNSPEC is an unspec
+;; number to use for this output.
+(define (write-to-one-output context insn output unspec)
+  (write-construct "(set " ")"
+    (string-write (md-operand:to-string 'lhs output))
+    (line-break)
+    (let ((branch-labels (and (md-operand:pc? output)
+                             (find md-operand:label?
+                                   (md-insn:inputs insn)))))
+      (if (pair? branch-labels)
+         (write-construct "(if_then_else " ")"
+           (write-construct "(eq " ")"
+             (write-inputs context insn "" unspec)
+             (line-break)
+             (string-write "(const_int 0)"))
+           (line-break)
+           (string-write "(match_dup "
+                         (st (md-operand:read-index (car branch-labels)))
+                         ")")
+           (line-break)
+           (string-write "(pc)"))
+         (let ((mode (md-operand:mode output)))
+           (write-inputs context insn (sa ":" mode) unspec)))))
+  ;; If this instruction is used for expanding intrinsics, and if the
+  ;; output is a fixed register that is not mapped to an intrinsic
+  ;; argument, treat the instruction as setting a global register.
+  ;; This isn't necessary for volatile instructions since gcc will
+  ;; not try to second-guess what they do.
+  (if (and (not (intrinsic:unspec-version (md-insn:intrinsic insn)))
+          (not (md-insn:volatile? insn))
+          (not (md-operand:write-index output))
+          (md-operand:fixed-register output))
+      (let ((reg (get-shadow (md-operand:fixed-register output))))
+       (line-break)
+       (write-construct "(set " ")"
+         (string-write "(reg:SI " (st reg) ")")
+         (line-break)
+         (write-inputs 'rhs-copy insn ":SI" (+ unspec 1))))))
+
+
+;; Write a define_insn for INSN.
+(define (write-insn insn)
+  (string-write "\n\n(define_insn \"" (md-insn:md-name insn) "\"\n")
+  (write-construct "  [" "]"
+    (let ((outputs (md-insn:outputs insn))
+         (unspec (intrinsic:unspec (md-insn:intrinsic insn))))
+      (if (null? outputs)
+         (write-inputs 'rhs insn "" unspec)
+         (begin
+           (write-to-one-output 'rhs insn (car outputs) unspec)
+           (number-list
+            (lambda (output index)
+              (line-break)
+              (write-to-one-output 'rhs-copy insn output
+                                   (+ unspec (* 2 index))))
+            (cdr outputs) 1)))))
+  (line-break)
+
+  ;; C predicate.
+  (string-write "  \"CGEN_ENABLE_INSN_P (" (st (md-insn:index insn)) ")")
+  (let ((hook (intrinsic:hook (md-insn:intrinsic insn))))
+    (if (gcc-hook:condition hook)
+       (string-write " && (" (gcc-hook:condition hook) ")")))
+  (string-write "\"\n")
+
+  ;; assembly syntax
+  (string-write "  \"")
+  (write-syntax insn)
+  (string-write "\"\n")
+
+  ;; attributes
+  (write-construct "  [" "]"
+    (write-list line-break (target:attributes insn)
+               (lambda (attribute)
+                 (string-write "(set_attr \"" (car attribute)
+                               "\" \"" (cdr attribute) "\")"))))
+  (string-write ")\n"))
+       
+(define (insns.md) 
+  (string-write 
+   "\n\n"
+   ";; DO NOT EDIT: This file is automatically generated by CGEN.\n"
+   ";; Any changes you make will be discarded when it is next regenerated.\n"
+   "\n\n")
+  (analyze-intrinsics!)
+  (message "Generating .md file...\n")
+  (for-each-md-insn write-insn)
+  (string-write "\n")
+  "")
+
+
+;; GCC SOURCE CODE GENERATOR
+;; -------------------------
+
+;; Maps the names of immediate predicates to an example of an operand
+;; which needs it.
+(deftable immediate-predicate)
+
+;; If OP is an immediate predicate, make sure that it has an entry
+;; in IMMEDIATE-PREDICATES.
+(define (note-immediates insn op)
+  (if (and (md-operand:immediate? op)
+          (not (md-operand:label? op)))
+      (let ((name (md-operand:immediate-predicate op)))
+       (if (not (get-immediate-predicate name))
+           (set-immediate-predicate! name op)))))
+
+(define (enum-type op)
+  (cond
+   ((is-h-cr64? (md-operand:hw op)) "cgen_regnum_operand_type_COPDI")
+   ((is-h-cr?   (md-operand:hw op)) "cgen_regnum_operand_type_COPSI")
+   (else
+    (case (md-operand:cdata op)
+      ((POINTER)         "cgen_regnum_operand_type_POINTER") 
+      ((LABEL)                  "cgen_regnum_operand_type_LABEL") 
+      ((LONG)           "cgen_regnum_operand_type_LONG") 
+      ((ULONG)                  "cgen_regnum_operand_type_ULONG") 
+      ((SHORT)                  "cgen_regnum_operand_type_SHORT") 
+      ((USHORT)          "cgen_regnum_operand_type_USHORT") 
+      ((CHAR)           "cgen_regnum_operand_type_CHAR") 
+      ((UCHAR)                  "cgen_regnum_operand_type_UCHAR") 
+      (else              "cgen_regnum_operand_type_DEFAULT")))))
+
+;; Write out the cgen_insn initialiser for INSN.
+(define (write-cgen-insn insn)
+  (write-construct "  { " " }"
+    (string-write (st (intrinsic:index (md-insn:intrinsic insn))))
+
+    (comma-line-break)
+    (string-write (bitmask "ISA" (md-insn:isas insn)))
+
+    (comma-line-break)
+    (string-write (bitmask "GROUP" (md-insn:groups insn)))
+
+    (comma-line-break)
+    (string-write "CODE_FOR_" (md-insn:md-name insn))
+
+    (comma-line-break)
+    (string-write (st (length (md-insn:arguments insn))))
+
+    (comma-line-break)
+    (write-construct "{ " " }"
+      (write-nonempty-list
+       comma-break
+       (find md-operand:arg-index (md-insn:operands insn))
+       (lambda (op) (string-write (st (md-operand:arg-index op))))
+       "0"))
+
+   (comma-line-break)
+   (write-construct "{ " " }"
+     (write-nonempty-list
+        comma-break
+       (md-insn:arguments insn)
+       (lambda (op)
+         (if (md-operand:regnum? op)
+             (string-write
+              "{ " (st (md-operand:upper-bound op))
+              ", " (st (target:base-reg (md-operand:hw op))))
+             (string-write "{ 0, 0"))
+         (string-write ", " (enum-type op)
+                       ", " (if (and (not (equal? (md-operand:cdata op) 'REGNUM))
+                                     (md-operand:write-index op))
+                                "1" "0")
+                       " }"))
+       "{ 0, 0, cgen_regnum_operand_type_DEFAULT, 0}"))
+
+    (target:initialize-fields insn)))
+
+(define (intrinsics.h) ; i.e., mep-intrin.h
+  (string-write 
+   "\n\n"
+   "/* DO NOT EDIT: This file is automatically generated by CGEN.\n"
+   "   Any changes you make will be discarded when it is next regenerated. */\n"
+   "\n")
+  (analyze-intrinsics!)
+  (message "Generating gcc include file...\n")
+  (init-immediate-predicate!)
+  (for-each-argument note-immediates)
+
+  (string-write "#ifdef WANT_GCC_DECLARATIONS\n")
+
+  ;; Declare the range of shadow registers
+  (string-write "#define FIRST_SHADOW_REGISTER "
+               (st target:first-unused-register) "\n")
+  (string-write "#define LAST_SHADOW_REGISTER "
+               (st (+ target:first-unused-register
+                      (length shadow-registers)
+                      -1)) "\n")
+  (string-write "#define FIXED_SHADOW_REGISTERS \\\n  ")
+  (write-list comma-break
+             shadow-registers
+             (lambda (entry) (string-write "1")))
+  (string-write "\n")
+  (string-write "#define CALL_USED_SHADOW_REGISTERS FIXED_SHADOW_REGISTERS\n")
+  (string-write "#define SHADOW_REG_ALLOC_ORDER \\\n  ")
+  (write-list comma-break
+             shadow-registers
+             (lambda (entry) (string-write (st (cdr entry)))))
+  (string-write "\n")
+  (string-write "#define SHADOW_REGISTER_NAMES \\\n  ")
+  (write-list comma-break
+             shadow-registers
+             (lambda (entry)
+               (string-write "\"$shadow" (st (car entry)) "\"")))
+  (string-write "\n\n")
+
+  ;; Declare the immediate predicates in a form suitable for PREDICATE_CODES.
+  (string-write "#define IMMEDIATE_PREDICATE_CODES")
+  (for-each
+   (lambda (entry)
+     (string-write "  \\\n  {\"" (car entry) "\", {CONST_INT}},"))
+   immediate-predicate-table)
+
+  ;; Declare the index values for well-known intrinsics.
+  (string-write "\n\n#ifndef __MEP__\n")
+  (string-write "enum {\n")
+  (write-list comma-line-break
+             (find intrinsic:hook intrinsics)
+             (lambda (intrinsic)
+               (string-write "  " (intrinsic:name intrinsic)
+                             " = " (st (intrinsic:index intrinsic)))))
+  (string-write "\n};\n")
+  (string-write "#endif /* ! defined (__MEP__) */\n")
+
+  ;; Define the structure used to describe intrinsic insns.
+  (string-write
+   "\n\n"
+   "enum cgen_regnum_operand_type {\n"
+   "  cgen_regnum_operand_type_POINTER,         /* long *          */\n"
+   "  cgen_regnum_operand_type_LABEL,           /* void *          */\n"
+   "  cgen_regnum_operand_type_LONG,            /* long            */\n"
+   "  cgen_regnum_operand_type_ULONG,           /* unsigned long   */\n"
+   "  cgen_regnum_operand_type_SHORT,           /* short           */\n"
+   "  cgen_regnum_operand_type_USHORT,          /* unsigned short  */\n"
+   "  cgen_regnum_operand_type_CHAR,            /* char            */\n"
+   "  cgen_regnum_operand_type_UCHAR,           /* unsigned char   */\n"
+   "  cgen_regnum_operand_type_COPSI,           /* __cop long      */\n"
+   "  cgen_regnum_operand_type_COPDI,           /* __cop long long */\n"
+   "  cgen_regnum_operand_type_CP_DATA_BUS_INT, /* cp_data_bus_int */\n"
+   "  cgen_regnum_operand_type_DEFAULT = cgen_regnum_operand_type_LONG\n"
+   "};\n"
+   "\n"
+   "struct cgen_regnum_operand {\n"
+   "  /* The number of addressable registers, 0 for non-regnum operands.  */\n"
+   "  unsigned char count;\n"
+   "\n"
+   "  /* The first register.  */\n"
+   "  unsigned char base;\n"
+   "\n"
+   "  /* The type of the operand.  */\n"
+   "  enum cgen_regnum_operand_type type;\n"
+   "\n"
+   "  /* Is it passed by reference?  */\n"
+   "  int reference_p;\n"
+   "};\n\n"
+   "struct cgen_insn {\n"
+   "  /* An index into cgen_intrinsics[].  */\n"
+   "  unsigned int intrinsic;\n"
+   "\n"
+   "  /* A bitmask of the ISAs which include this instruction.  */\n"
+   "  unsigned int isas;\n"
+   "\n"
+   "  /* A bitmask of the target-specific groups to which this instruction\n"
+   "     belongs.  */\n"
+   "  unsigned int groups;\n"
+   "\n"
+   "  /* The insn_code for this instruction.  */\n"
+   "  int icode;\n"
+   "\n"
+   "  /* The number of arguments to the intrinsic function.  */\n"
+   "  unsigned int num_args;\n"
+   "\n"
+   "  /* Maps operand numbers to argument numbers.  */\n"
+   "  unsigned int op_mapping[10];\n"
+   "\n"
+   "  /* Array of regnum properties, indexed by argument number.  */\n"
+   "  struct cgen_regnum_operand regnums[10];\n"
+   (target:declare-fields)
+   "};\n")
+
+  ;; Declare the arrays that we define later.
+  (string-write
+   "\n"
+   "extern const struct cgen_insn cgen_insns[];\n"
+   "extern const char *const cgen_intrinsics[];\n")
+
+  ;; Macro used by the .md file.
+  (string-write
+   "\n"
+   "/* Is the instruction described by cgen_insns[INDEX] enabled?  */\n"
+   "#define CGEN_ENABLE_INSN_P(INDEX) \\\n"
+   "  ((CGEN_CURRENT_ISAS & cgen_insns[INDEX].isas) != 0 \\\n"
+   "   && (CGEN_CURRENT_GROUP & cgen_insns[INDEX].groups) != 0)\n\n")
+
+  (define-bitmasks "ISA"
+    (remove-duplicates (sort (map convert-isa intrinsics-isas) string<?)))
+
+  (define-bitmasks "GROUP" md-insn-groups)
+
+  (string-write "#endif\n")
+
+  (string-write "#ifdef WANT_GCC_DEFINITIONS\n")
+
+  ;; Define the immediate predicates.
+  (for-each
+   (lambda (entry)
+     (let* ((op (cdr entry))
+           (align-mask (- (md-operand:alignment op) 1)))
+       (string-write
+       "int\n"
+       (car entry) " (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)\n"
+       "{\n"
+       "  return (GET_CODE (op) == CONST_INT\n"
+       "          && (INTVAL (op) & " (st align-mask) ") == 0\n"
+       "          && INTVAL (op) >= " (st (md-operand:lower-bound op)) "\n"
+       "          && INTVAL (op) < " (st (md-operand:upper-bound op)) ");\n"
+       "}\n\n")))
+   immediate-predicate-table)
+
+  ;; Create an array describing the range and alignment of immediate
+  ;; predicates.
+  (string-write
+   "struct cgen_immediate_predicate {\n"
+   "  insn_operand_predicate_fn predicate;\n"
+   "  int lower, upper, align;\n"
+   "};\n\n"
+   "const struct cgen_immediate_predicate cgen_immediate_predicates[] = {\n")
+
+  (write-list comma-line-break immediate-predicate-table
+             (lambda (entry)
+               (let ((op (cdr entry)))
+                 (string-write
+                  "  { " (car entry)
+                  ", " (st (md-operand:lower-bound op))
+                  ", " (st (md-operand:upper-bound op))
+                  ", " (st (md-operand:alignment op)) " }"))))
+
+  (string-write "\n};\n\n")
+
+  ;; Create an array containing the names of all the available intrinsinics.
+  (string-write "const char *const cgen_intrinsics[] = {\n")
+  (write-list comma-line-break intrinsics
+             (lambda (intrinsic)
+               (string-write "  \"" (intrinsic:name intrinsic) "\"")))
+  (string-write "\n};\n\n")
+
+  ;; Create an array describing each .md file instruction.
+  (string-write "const struct cgen_insn cgen_insns[] = {\n")
+  (write-list comma-line-break md-insns write-cgen-insn)
+  (string-write "\n};\n")
+
+  (string-write "#endif\n"))
+
+
+;; PROTOTYPE GENERATOR
+;; -------------------
+
+(define (runtime-type op)
+  (sa (case (md-operand:cdata op)
+       ((POINTER) "long *")
+       ((LABEL) "void *")
+       ((LONG) "long")
+       ((ULONG) "unsigned long")
+       ((SHORT) "short")
+       ((USHORT) "unsigned short")
+       ((CHAR) "char")
+       ((UCHAR) "unsigned char")
+       ((CP_DATA_BUS_INT) "cp_data_bus_int")
+       (else "long"))
+      (if (and (not (equal? (md-operand:cdata op) 'REGNUM))
+              (md-operand:write-index op))
+         "&" "")))
+
+(define (intrinsic-protos.h) ; i.e., intrinsics.h
+  (string-write 
+   "\n\n"
+   "/* DO NOT EDIT: This file is automatically generated by CGEN.\n"
+   "   Any changes you make will be discarded when it is next regenerated.\n"
+   "\n"
+   "   FURTHERMORE: this file serves only as documentation and must not be included\n "
+   "   in any application code which wishes to use intrinsics. */\n"
+   "\n\n"
+   "#error \"This file must not be included in any application code which uses intrinsics.\"\n"
+   "#error \"It is here for documentation purposes ONLY.\"\n"
+   "\n\n"
+   "#if __MEP_CONFIG_CP_DATA_BUS_WIDTH == 64\n"
+   "  typedef long long cp_data_bus_int;\n"
+   "#else\n"
+   "  typedef long cp_data_bus_int;\n"
+   "#endif\n")
+  (analyze-intrinsics!)
+  (message "Generating prototype file...\n")
+  (target:for-each-isa!
+   (lambda (name isa)
+     (string-write "\n// " name "\n")
+     (for-each
+      (lambda (intrinsic)
+       (let ((entry (assoc isa (intrinsic:isas intrinsic))))
+         (if entry
+             (let* ((insn (cdr entry))
+                    (proto (sa "void " (intrinsic:name intrinsic)
+                               " (" (stringize (map runtime-type
+                                                    (md-insn:arguments insn))
+                                               ", ")
+                               ");"))
+                    (proto-len (string-length proto))
+                    (attrs '()))
+
+               (if (md-insn:volatile? insn)
+                   (set! attrs (cons "volatile" attrs)))
+
+               (string-write proto)
+               (if (pair? attrs)
+                   (string-write (make-string (max 1 (- 40 proto-len))
+                                              #\space)
+                                 "// " (stringize attrs " ")))
+               (string-write "\n")))))
+      intrinsics)))
+  "")
+
+
+;; The rest of this file has not been converted to use the INTRINSICS
+;; attribute.  The code isn't used at the moment anyway.
+
+(define (intrinsic-testsuite.c)
+  (map-intrinsics!)
+  (for-each (maybe-do-all declare-intrinsic-test) intrinsic-insns)
+  (string-write "\n")
+  "")
+
+(define (test-val is-retval? op vbase)
+  (let ((mode (op:mode op))
+       (cdata (obj-attr-value op 'CDATA)))
+    (cond 
+     ((equal? cdata 'REGNUM) "7")
+     ((equal? cdata 'LABEL) "&&lab")
+     ((treat-op-as-immediate? op)             
+      (let* ((field (fetch-ifield-for-op-in-current-insn op))
+            (align-bits (case (obj:name field) 
+                          ((f-8s8a2 f-12s4a2 f-17s16a2 f-24s5a2n f-24u5a2n f-7u9a2 f-8s24a2) 1)
+                          ((f-7u9a4 f-8s24a4 f-24u8a4n) 2)
+                          ((f-8s24a8) 3)
+                          (else 0)))
+            (val (ash (send field 'max-value) align-bits)))
+       (string-append "0x" (number->string val 16))))
+     (else (let* ((expr-suffix (if is-retval? "" 
+                                  (if (get-gcc-write-index op) "" " + 1")))
+                 (val
+                  (case cdata
+                    ((POINTER) "p")
+                    ((LONG) "l")
+                    ((ULONG) "ul")
+                    ((SHORT) "s")
+                    ((USHORT) "us")
+                    ((CHAR) "c")
+                    ((UCHAR) "uc")
+                    ((CP_DATA_BUS_INT) "cpdbi")
+                    (else "l"))))
+            (sa vbase val expr-suffix))))))
+  
+(define (declare-intrinsic-test name insn others)
+  (set! curr-insn insn)
+  (scan-syntax insn)
+  (scan-read-write insn)
+
+  (let* ((mnem (insn-mnemonic insn))
+        (syntax (insn-syntax insn))
+        (first #t)
+        (comma-not-first (lambda () (if first (begin (set! first #f) "") ", ")))
+        (vars '("x" "y" "z" "t" "w"))
+        (operands syntactic-operands))
+    
+    (cond ((equal? mnem "--unused--") '())
+         ((equal? mnem "--reserved--") '())
+         (else
+          (begin         
+            (string-write (target:builtin-name (intrinsic-name insn)) " (")
+            (for-each (lambda (operand) 
+                        (string-write (sa (comma-not-first) 
+                                          (test-val #f operand (car vars))
+                                          ))
+                        (set! vars (cdr vars))) operands)
+            (string-write ");\n")))
+         )))
This page took 0.069109 seconds and 5 git commands to generate.