Add tests for iformat description errors.
* dev.scm (cload): New option #:diag.
* read.scm (<reader>): New member verify-iformat?.
(/parse-diagnostic, parse-warning): New functions.
(parse-error): Guts moved to /parse-diagnostic.
(/set-diagnostic-options!): New function.
(cpu-load): New arg diagnostic-options, all callers updated.
Recognize -w diagnostic-option-list.
* ifield.scm (ifields-base-ifields): Move here from iformat.scm.
(ifld-simple-ifields, ifields-simple-ifields): New function.
* insn.scm (/parse-insn-format-iflds): New function.
(/parse-insn-format): Guts moved to /parse-insn-format-iflds.
New arg isa, all callers updated. Do some basic validation of the
ifield list if requested.
* mach.scm (/sanity-check-insns): Improve error message text.
* doc/running.text: Document -w option.
* ifield.scm (/multi-ifield-parse): Initialize bitrange.
2009-09-09 Doug Evans <dje@sebabeach.org>
+ Add support for controlling warnings/errors.
+ Add tests for iformat description errors.
+ * dev.scm (cload): New option #:diag.
+ * read.scm (<reader>): New member verify-iformat?.
+ (/parse-diagnostic, parse-warning): New functions.
+ (parse-error): Guts moved to /parse-diagnostic.
+ (/set-diagnostic-options!): New function.
+ (cpu-load): New arg diagnostic-options, all callers updated.
+ Recognize -w diagnostic-option-list.
+ * ifield.scm (ifields-base-ifields): Move here from iformat.scm.
+ (ifld-simple-ifields, ifields-simple-ifields): New function.
+ * insn.scm (/parse-insn-format-iflds): New function.
+ (/parse-insn-format): Guts moved to /parse-insn-format-iflds.
+ New arg isa, all callers updated. Do some basic validation of the
+ ifield list if requested.
+ * mach.scm (/sanity-check-insns): Improve error message text.
+ * doc/running.text: Document -w option.
+
+ * ifield.scm (/multi-ifield-parse): Initialize bitrange.
+
* dev.scm (*): Change default verbosity level to 2.
* ifield.scm (<ifield> pretty-print): New method.
(dnh h-pc "program counter" (PC PROFILE) (pc) () () ())
+; get the real index of a gpr on the circular register queue.
+
+(define-pmacro (real-gpr-index index)
+ (if WI (lt index 20)
+ ; global reg
+ index
+ ; local reg
+ (sequence WI ((WI pr))
+ (set pr (add index (reg h-gr 0)))
+ (if WI (ge pr 256)
+ (sub pr 236)
+ pr
+ )
+ )
+ )
+)
+
+; All accesses to gprs in instructions use {get|set}-{gpr|vgpr}
+; functions. These functions handle register 0 being 0. This cannot
+; be handled without understanding the context of the get or set since
+; regs 1-3 are 0 for vector instructions but not for scalar
+; instructions.
+;
+; general purpose registers.
+
+;(define-hardware
+; (name h-gpr)
+; (comment "General Purpose Registers")
+; (type register DI (256))
+; (indices keyword "" (gen-register-names "r" 256 0 1))
+; (get (index)
+; (raw-reg DI h-gpr (real-gpr-index index))
+; )
+; (set (index newval)
+; (set (raw-reg DI h-gpr (real-gpr-index index)) newval)
+; )
+;)
+
+
(define-hardware
(name h-gr)
(comment "general registers")
(r0 0) (r1 1) (r2 2) (r3 3) (r4 4) (r5 5) (r6 6) (r7 7)
(r8 8) (r9 9) (r10 10) (r11 11) (r12 12) (r13 13) (r14 14) (r15 15)
))
+ (get (index)
+ (raw-reg WI h-gr (real-gpr-index index))
+ )
+ (set (index newval)
+ (set (raw-reg WI h-gr (real-gpr-index index)) newval)
+ )
)
(define-hardware
(dsh h-df "df test" () (register DF))
(dsh h-tf "tf test" () (register TF))
+
+(define-hardware
+ (name accum)
+ (type register (INT 64))
+)
\f
; Operand attributes.
(keep-mach "all")
(keep-isa "all")
(options "")
- (trace-options ""))
+ (trace-options "")
+ (diagnostic-options ""))
; Doesn't check if (cadr args) exists or if #:arch was specified, but
; this is a debugging tool!
((#:isas) (set! keep-isa (cadr args)))
((#:options) (set! options (cadr args)))
((#:trace) (set! trace-options (cadr args)))
+ ((#:diag) (set! diagnostic-options (cadr args)))
(else (error "unknown option:" (car args))))
(loop (cddr args)))))
(case APPLICATION
((UNKNOWN) (error "application not loaded"))
((DESC) (cpu-load cpu-file
- keep-mach keep-isa options trace-options
+ keep-mach keep-isa options
+ trace-options diagnostic-options
desc-init!
desc-finish!
desc-analyze!))
((DOC) (cpu-load cpu-file
- keep-mach keep-isa options trace-options
+ keep-mach keep-isa options
+ trace-options diagnostic-options
doc-init!
doc-finish!
doc-analyze!))
((OPCODES) (cpu-load cpu-file
- keep-mach keep-isa options trace-options
+ keep-mach keep-isa options
+ trace-options diagnostic-options
opcodes-init!
opcodes-finish!
opcodes-analyze!))
((GAS-TEST) (cpu-load cpu-file
- keep-mach keep-isa options trace-options
+ keep-mach keep-isa options
+ trace-options diagnostic-options
gas-test-init!
gas-test-finish!
gas-test-analyze!))
((SIMULATOR) (cpu-load cpu-file
- keep-mach keep-isa options trace-options
+ keep-mach keep-isa options
+ trace-options diagnostic-options
sim-init!
sim-finish!
sim-analyze!))
((SID-SIMULATOR) (cpu-load cpu-file
- keep-mach keep-isa options trace-options
+ keep-mach keep-isa options
+ trace-options diagnostic-options
sim-init!
sim-finish!
sim-analyze!))
((SIM-TEST) (cpu-load cpu-file
- keep-mach keep-isa options trace-options
+ keep-mach keep-isa options
+ trace-options diagnostic-options
sim-test-init!
sim-test-finish!
sim-test-analyze!))
((TESTSUITE) (cpu-load cpu-file
- keep-mach keep-isa options trace-options
+ keep-mach keep-isa options
+ trace-options diagnostic-options
testsuite-init!
testsuite-finish!
testsuite-analyze!))
* s:: -s Specify the source directory.
* t:: -t Specify tracing of various things.
* v:: -v Increment the verbosity level.
+* w:: -w Enable various diagnostics.
* version:: --version Print version info.
* opcodes:: Opcodes generator arguments.
Specifying multiple @code{-v} options will increase the verbosity.
+@node w
+@section Enable various diagnostics. @option{-w} @var{diagnostic-list}
+
+Use this to turn on warnings or errors of various things.
+The argument is a comma-separated list.
+At present the following diagnostics are supported.
+
+@itemize @bullet
+
+@item @option{iformat}
+
+Turn on verification of the instruction format.
+If an instruction's field list has missing bits or too many bits
+then a warning is issued.
+
+@emph{NOTE:} The checking is incomplete, but it does catch most
+common forms of errors.
+
+@item @option{all}
+
+Turn on diagnostics for everything.
+
+@end itemize
+
+Each application will invoke CGEN in its own way, so the details of
+enabling diagnostics may vary from application to application.
+Generally though, each application has a CGENFLAGS makefile variable
+for passing flags to CGEN.
+
+Binutils example:
+
+@smallexample
+# Turn on verification of instruction formats while generating
+# the m32r port's opcodes files in the binutils package.
+cd obj/opcodes
+rm stamp-m32r
+make stamp-m32r CGENFLAGS="-v -b -w iformat"
+@end smallexample
+
@node version
@section Print version info. @option{--version}
(elm-xset! result 'mode (parse-mode-name context mode))
(elm-xset! result 'encode (/ifld-parse-encode context encode))
(elm-xset! result 'decode (/ifld-parse-encode context decode))
+ (elm-xset! result 'bitrange "multi-ifields don't have bitranges") ;; FIXME
(if insert
(elm-xset! result 'insert insert)
(elm-xset! result 'insert
(derived-ifield-subfields self))))
)
-; Traverse the ifield to collect all base (non-derived) ifields used in it.
+; Return a list of all base (non-derived) ifields in IFLD.
+; NOTE: multi-ifields are *not* reduced to their sub-ifields.
(define (ifld-base-ifields ifld)
(cond ((derived-ifield? ifld) (ifields-base-ifields (derived-ifield-subfields ifld)))
;;((multi-ifield? ifld) (ifields-base-ifields (multi-ifld-subfields ifld)))
(else (list ifld)))
)
+
+; Collect all base (non-derived) ifields in IFLD-LIST.
+; NOTE: multi-ifields are *not* reduced to their sub-ifields.
+
+(define (ifields-base-ifields ifld-list)
+ (collect ifld-base-ifields ifld-list)
+)
+
+; Return a list of all simple ifields in IFLD.
+; NOTE: multi-ifields *are* reduced to their sub-ifields.
+
+(define (ifld-simple-ifields ifld)
+ (cond ((derived-ifield? ifld) (ifields-simple-ifields (derived-ifield-subfields ifld)))
+ ((multi-ifield? ifld) (ifields-simple-ifields (multi-ifld-subfields ifld)))
+ (else (list ifld)))
+)
+
+; Collect all simple ifields in IFLD-LIST.
+; NOTE: multi-ifields *are* reduced to their sub-ifields.
+
+(define (ifields-simple-ifields ifld-list)
+ (collect ifld-simple-ifields ifld-list)
+)
\f
; Misc. utilities.
(number key ifields mask-length length mask eg-insn)
)
-; Traverse the ifield list to collect all base (non-derived) ifields
-; used in it.
-
-(define (ifields-base-ifields ifld-list)
- (collect ifld-base-ifields ifld-list)
-)
-
; Return enum cgen_fmt_type value for FMT.
; ??? Not currently used.
; intelligent processing of it later.
(for-each (lambda (insn)
- (logit 3 "Scanning operands of " (obj:name insn) ": "
+ (logit 2 "Scanning operands of " (obj:name insn) ": "
(insn-syntax insn) " ...\n")
(let ((sem-ops (ifmt-analyze insn compute-sformat?)))
(insn-set-fmt-desc! insn (car sem-ops))
)
(for-each (lambda (insn)
- (logit 3 "Processing format for " (obj:name insn) ": "
+ (logit 2 "Processing format for " (obj:name insn) ": "
(insn-syntax insn) " ...\n")
(let ((fmt-desc (insn-fmt-desc insn)))
;; Pick out name first to augment the error context.
(let* ((name (parse-name context name))
(context (context-append-name context name))
- (atlist-obj (atlist-parse context attrs "cgen_insn")))
+ (atlist-obj (atlist-parse context attrs "cgen_insn"))
+ (isas (bitset-attr->list (atlist-attr-value atlist-obj 'ISA #f))))
(if (keep-atlist? atlist-obj #f)
semantics
#f))
(format (/parse-insn-format (context-append context " format")
+ ;; Just pick the first, the base len
+ ;; for each should be the same.
+ ;; If not this is caught by
+ ;; compute-insn-base-mask-length.
+ (current-isa-lookup (car isas))
fmt))
(comment (parse-comment context comment))
; If there are no semantics, mark this as an alias.
(parse-error context "unknown ifield" spec)))
)
-; Given an insn format field from a .cpu file, replace it with a list of
-; ifield objects with the values assigned.
-;
-; An insn format field is a list of ifields that make up the instruction.
-; All bits must be specified, including reserved bits
-; [at present no checking is made of this, but the rule still holds].
-;
-; A normal entry begins with `+' and then consist of the following:
-; - operand name
-; - (ifield-name [options] value)
-; - (operand-name [options] [value])
-; - insn ifield enum
-;
-; Example: (+ OP1_ADD (f-res2 0) dr src1 (f-src2 1) (f-res1 #xea))
-;
-; where OP1_ADD is an enum, dr and src1 are operands, and f-src2 and f-res1
-; are ifield's. The `+' allows for future extension.
-;
-; The other form of entry begins with `=' and is followed by an instruction
-; name that has the same format. The specified instruction must already be
-; defined. Instructions with this form typically also include an
-; `ifield-assertion' spec to keep them separate.
-;
-; An empty field list is ok. This means it's unspecified.
-; VIRTUAL insns have this.
-;
-; This is one of the more important routines to be efficient.
-; It's called for each instruction, and is one of the more expensive routines
-; in insn parsing.
+; Subroutine of /parse-insn-format to simplify it.
+; Parse the provided iformat spec and return the list of ifields.
-(define (/parse-insn-format context fld-list)
+(define (/parse-insn-iformat-iflds context fld-list)
(if (null? fld-list)
nil ; field list unspecified
(case (car fld-list)
))
)
+; Given an insn format field from a .cpu file, replace it with a list of
+; ifield objects with the values assigned.
+; If ISA is non-#f, it is an <isa> object, and we perform various checks
+; on the format (which require an isa).
+;
+; An insn format field is a list of ifields that make up the instruction.
+; All bits must be specified, including reserved bits
+; [at present no checking is made of this, but the rule still holds].
+;
+; A normal entry begins with `+' and then consist of the following:
+; - operand name
+; - (ifield-name [options] value)
+; - (operand-name [options] [value])
+; - insn ifield enum
+;
+; Example: (+ OP1_ADD (f-res2 0) dr src1 (f-src2 1) (f-res1 #xea))
+;
+; where OP1_ADD is an enum, dr and src1 are operands, and f-src2 and f-res1
+; are ifield's. The `+' allows for future extension.
+;
+; The other form of entry begins with `=' and is followed by an instruction
+; name that has the same format. The specified instruction must already be
+; defined. Instructions with this form typically also include an
+; `ifield-assertion' spec to keep them separate.
+;
+; An empty field list is ok. This means it's unspecified.
+; VIRTUAL insns have this.
+;
+; This is one of the more important routines to be efficient.
+; It's called for each instruction, and is one of the more expensive routines
+; in insn parsing.
+
+(define (/parse-insn-format context isa ifld-list)
+ (let* ((parsed-ifld-list (/parse-insn-iformat-iflds context ifld-list)))
+
+ ;; NOTE: We could sort the fields here, but it introduces differences
+ ;; in the generated opcodes files. Later it might be a good thing to do
+ ;; but keeping the output consistent is important right now.
+ ;; (sorted-ifld-list (sort-ifield-list parsed-ifld-list
+ ;; (not (current-arch-insn-lsb0?))))
+ ;; The rest of the code assumes the list isn't sorted.
+ ;; Is there a benefit to removing this assumption? Note that
+ ;; multi-ifields can be discontiguous, so the sorting isn't perfect.
+
+ (if (and isa
+ (reader-verify-iformat? CURRENT-READER))
+ (let ((base-len (isa-base-insn-bitsize isa)))
+
+ ;; Perform some error checking.
+ ;; Look for overlapping ifields and missing bits.
+ ;; With derived ifields this is really hard, so only do the base insn
+ ;; for now. Do the simple test for now, it doesn't catch everything,
+ ;; but it should catch a lot.
+
+ (let* ((base-iflds (find (lambda (f)
+ (not (ifld-beyond-base? f)))
+ (ifields-simple-ifields parsed-ifld-list)))
+ (base-iflds-length (apply + (map ifld-length base-iflds))))
+ ;; FIXME: We don't use parse-error here because some existing ports
+ ;; have problems, and I don't have time to fix them right now.
+ (cond ((< base-iflds-length base-len)
+ (parse-warning context
+ "insufficient number of bits specified in base insn"
+ ifld-list))
+ ((> base-iflds-length base-len)
+ (parse-warning context
+ "too many bits specified in base insn"
+ ifld-list)))
+ )
+ ))
+
+ parsed-ifld-list)
+)
+
; Return a boolean indicating if IFLD-LIST contains anyof operands.
(define (anyof-operand-format? ifld-list)
;; Ensure instruction base values agree with their masks.
;; Errors can come from bad .cpu files, bugs, or both.
;; It's better to catch such errors early.
+ ;; If it is an error in the .cpu file, we don't want to crash
+ ;; on a Guile error.
(for-each
"This usually means some kind of error in the instruction's ifield list.\n"
"base mask: 0x" (number->hex base-mask)
", base value: 0x" (number->hex base-value)
+ "\nfield list:"
+ (string-map (lambda (f)
+ (string-append " "
+ (ifld-pretty-print f)))
+ (insn-iflds insn))
)))
;; Insert more checks here.
; ??? Calling /parse-insn-format is a quick hack.
; It's an internal routine of some other file.
- (let ((iflds (/parse-insn-format context encoding)))
+ (let ((iflds (/parse-insn-format context #f encoding)))
(make <derived-ifield>
operand-name
'derived-ifield ; (string-append "<derived-ifield> for " operand-name)
; Boolean indicating if pmacro tracing is on.
(cons 'trace-pmacros? #f)
+ ; Issue diagnostics for instruction format issues.
+ (cons 'verify-iformat? #f)
+
; Currently select cpu family, computed from `keep-mach'.
; Some applications don't care, and this is moderately
; expensive to compute so we use delay/force.
(define-getters <reader> reader
(keep-mach keep-isa
- trace-commands? trace-pmacros?
+ trace-commands? trace-pmacros? verify-iformat?
current-cpu commands location))
(define-setters <reader> reader
(keep-mach keep-isa
- trace-commands? trace-pmacros?
+ trace-commands? trace-pmacros? verify-iformat?
current-cpu commands location))
(define (reader-add-command! name comment attrs arg-spec handler)
":")))
)
-;;; Signal a parse error while reading a .cpu file.
-;;; If CONTEXT is #f, use a default context of the current reader location
-;;; and an empty prefix.
-;;; If MAYBE-HELP-TEXT is specified, elide the last trailing \n.
-;;; Multiple lines of help text need embedded newlines, and should be no longer
-;;; than 79 characters.
+;;; Subroutine of parse-error, parse-warning to simplify them.
+;;; Flag an error or a warning.
+;;; EMITTER is a function of one argument, the message to print.
-(define (parse-error context message expr . maybe-help-text)
+(define (/parse-diagnostic emitter context message expr maybe-help-text)
(if (not context)
(set! context (make <context> (current-reader-location) #f)))
(prefix (or (context-prefix context) "Error"))
(text (string-append prefix ": " message)))
- (error
+ (emitter
(simple-format
#f
"\n~A:\n@ ~A:\n\n~A: ~A: ~S~A"
(single-location->simple-string top-sloc)
text
expr
- (if (null? maybe-help-text)
- ""
- (string-append "\n\n" (car maybe-help-text))))))
+ (if maybe-help-text
+ (string-append "\n\n" maybe-help-text)
+ ""))))
+)
+
+;;; Signal a parse error while reading a .cpu file.
+;;; If CONTEXT is #f, use a default context of the current reader location
+;;; and an empty prefix.
+;;; If MAYBE-HELP-TEXT is specified, elide the last trailing \n.
+;;; Multiple lines of help text need embedded newlines, and should be no longer
+;;; than 79 characters.
+
+(define (parse-error context errmsg expr . maybe-help-text)
+ (/parse-diagnostic error
+ context
+ errmsg
+ expr
+ (if (null? maybe-help-text) "" (car maybe-help-text)))
+)
+
+;;; Signal a parse warning while reading a .cpu file.
+;;; If CONTEXT is #f, use a default context of the current reader location
+;;; and an empty prefix.
+;;; If MAYBE-HELP-TEXT is specified, elide the last trailing \n.
+;;; Multiple lines of help text need embedded newlines, and should be no longer
+;;; than 79 characters.
+
+(define (parse-warning context errmsg expr . maybe-help-text)
+ (/parse-diagnostic (lambda (text) (message "Warning: " text "\n"))
+ context
+ errmsg
+ expr
+ (if (null? maybe-help-text) #f (car maybe-help-text)))
)
; Return the current source location.
*UNSPECIFIED*
)
\f
+;; Diagnostic support.
+
+;;; Enable the specified diagnostics.
+;;; DIAGNOSTIC-OPTIONS is a comma-separated list of things to trace.
+;;;
+;;; Currently supported diagnostics:
+;;; iformat - issue diagnostics for iformat issues
+;;; all - turn on all diagnostics
+;;;
+;;; [If we later need to support disabling some diagnostic, one way is to
+;;; recognize an "-" in front of an option.]
+
+(define (/set-diagnostic-options! diagnostic-options)
+ (let ((all (list "iformat"))
+ (requests (string-cut diagnostic-options #\,)))
+ (if (member "all" requests)
+ (append! requests all))
+ (for-each (lambda (item)
+ (cond ((string=? "iformat" item)
+ (reader-set-verify-iformat?! CURRENT-READER #t))
+ ((string=? "all" item)
+ #t) ;; handled above
+ (else
+ (cgen-usage 'unknown (string-append "-w " item)
+ common-arguments))))
+ requests))
+
+ *UNSPECIFIED*
+)
+\f
; If #f, treat reserved fields as operands and extract them with the insn.
; Code can then be emitted in the extraction routines to validate them.
; If #t, treat reserved fields as part of the opcode.
; KEEP-ISA is a string of comma separated isas to keep.
; OPTIONS is the OPTIONS argument to -init-parse-cpu!.
; TRACE-OPTIONS is a random list of things to trace.
+; DIAGNOSTIC-OPTIONS is a random list of things to warn/error about.
; APP-INITER! is an application specific zero argument proc (thunk)
; to call after -init-parse-cpu!
; APP-FINISHER! is an application specific zero argument proc to call after
;
; This function isn't local because it's used by dev.scm.
-(define (cpu-load file keep-mach keep-isa options trace-options
+(define (cpu-load file keep-mach keep-isa options
+ trace-options diagnostic-options
app-initer! app-finisher! analyzer!)
(/init-reader!)
(/init-parse-cpu! keep-mach keep-isa options)
(/set-trace-options! trace-options)
+ (/set-diagnostic-options! diagnostic-options)
(app-initer!)
(logit 1 "Loading cpu description " file " ...\n")
(set! arch-path (dirname file))
"pmacros - trace pmacro expansion"
"all - trace everything")
("-v" #f "increment verbosity level")
+ ("-w" "diagnostic-options" "specify list of things to issue diagnostics about"
+ "Options:"
+ "iformat - verify instruction formats are valid"
+ "all - turn on all diagnostics")
+
("--version" #f "print version info")
)
)
(moreopts? #t)
(debugging #f) ; default is off, for speed
(trace-options "")
+ (diagnostic-options "")
(cep (current-error-port))
(str=? string=?)
)
((str=? "-v" (car opt))
(verbose-inc!)
)
+ ((str=? "-w" (car opt))
+ (set! diagnostic-options arg)
+ )
((str=? "--version" (car opt))
(begin
(display "Cpu tools GENerator version ")
(debug-repl nil))
(cpu-load arch-file
- keep-mach keep-isa flags trace-options
+ keep-mach keep-isa flags
+ trace-options diagnostic-options
app-init! app-finish! app-analyze!)
;; Start another repl loop if -d.