const (
// Regexp to match a single opcode check: optionally begin with "-" (to indicate
- // a negative check), followed by a string literal enclosed in "" or ``. For "",
+ // a negative check) or a positive number (to specify the expected number of
+ // matches), followed by a string literal enclosed in "" or ``. For "",
// backslashes must be handled.
- reMatchCheck = `-?(?:\x60[^\x60]*\x60|"(?:[^"\\]|\\.)*")`
+ reMatchCheck = `(-|[1-9]\d*)?(?:\x60[^\x60]*\x60|"(?:[^"\\]|\\.)*")`
)
var (
fileline string // original source file/line (eg: "/path/foo.go:45")
line int // original source line
opcode *regexp.Regexp // opcode check to be performed on assembly output
+ expected int // expected number of matches
+ actual int // actual number that matched
negative bool // true if the check is supposed to fail rather than pass
found bool // true if the opcode check matched at least one in the output
}
for _, m := range rxAsmCheck.FindAllString(allchecks, -1) {
negative := false
+ expected := 0
if m[0] == '-' {
negative = true
m = m[1:]
+ } else if '1' <= m[0] && m[0] <= '9' {
+ for '0' <= m[0] && m[0] <= '9' {
+ expected *= 10
+ expected += int(m[0] - '0')
+ m = m[1:]
+ }
}
rxsrc, err := strconv.Unquote(m)
ops[env] = make(map[string][]wantedAsmOpcode)
}
ops[env][lnum] = append(ops[env][lnum], wantedAsmOpcode{
+ expected: expected,
negative: negative,
fileline: lnum,
line: i + 1,
// run the checks.
if ops, found := fullops[srcFileLine]; found {
for i := range ops {
- if !ops[i].found && ops[i].opcode.FindString(asm) != "" {
+ if (!ops[i].found || ops[i].expected > 0) && ops[i].opcode.FindString(asm) != "" {
+ ops[i].actual++
ops[i].found = true
}
}
if o.negative == o.found {
failed = append(failed, o)
}
+ if o.expected > 0 && o.expected != o.actual {
+ failed = append(failed, o)
+ }
}
}
if len(failed) == 0 {
if o.negative {
fmt.Fprintf(&errbuf, "%s:%d: %s: wrong opcode found: %q\n", t.goFileName(), o.line, env, o.opcode.String())
+ } else if o.expected > 0 {
+ fmt.Fprintf(&errbuf, "%s:%d: %s: wrong number of opcodes: %q\n", t.goFileName(), o.line, env, o.opcode.String())
} else {
fmt.Fprintf(&errbuf, "%s:%d: %s: opcode not found: %q\n", t.goFileName(), o.line, env, o.opcode.String())
}
--- /dev/null
+// asmcheck
+
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package codegen
+
+func fa(a [2]int) (r [2]int) {
+ // amd64:1`MOVUPS[^,]+, X0$`,1`MOVUPS\sX0,[^\n]+$`
+ return a
+}
+
+func fb(a [4]int) (r [4]int) {
+ // amd64:2`MOVUPS[^,]+, X0$`,2`MOVUPS\sX0,[^\n]+$`
+ return a
+}