internal/simdgen: introduce instruction variants and use for masked ops

In AVX-512, nearly all operations take a mask, but it's optional.

Currently, the XED loader produces only the masked form of these
instructions. Since this mask is one of the input operands, the Go
definitions YAML needs a separate entry to match the masked form. To
generate both the masked and unmasked forms, we unify only the masked
form and then when generating the Go API we recognize this and
duplicate the operation in unmasked form. Unfortunately, since
pre-AVX-512 operations never have this mask input, we wind up
duplicating many unifier definitions to match both the pre-AVX-512 and
AVX-512 forms, even though the unmasked operation produced during API
generation looks like a pre-AVX-512 definition.

To fix all this, we flip things around. Instead of generating the
masked and unmasked variants at API generation, we generate them much
earlier, during XED loading. The XED data already contains a clear
marker for which masks are optional (it's wrong in a few cases, which
we work around). For instructions with an optional mask, the XED
loader now generates both the masked and unmasked forms.

Then, to make both easy to match, we put the mask operand into a new
top-level tuple called "inVariant". This way, a single unifier def can
match the pre-AVX-512 instruction, and the masked and unmasked AVX-512
instructions.

When we load the results of unification for generating the API, we do
some light canonicalization of the operation. We append any inVariant
operands to the input operands list; and if there's a mask in
inVariant, we append "Masked" to the Go method name.

With all of this done, we can delete all of the "*Masked" forms of
operations from the YAML. In a few cases, we have to merge some
information from the masked form into the unmasked form. For
operations that were introduced in AVX-512, we currently *only* have
the Masked form; so for these we keep the definition but strip out the
masking.

This ultimately has very little effect on the generated API. In a few
cases it fills in holes that let us pick a better instruction. It
fixes some doc strings that got duplicated incorrectly between masked
and unmasked forms.

This change makes it much easier to write other tools besides the Go
API generator because it moves nearly all masking logic out of
writeGoDefs. It also eliminates some fragile Operation duplication
logic from writeGoDefs. I plan to move a few more things out of the Go
API generator, but this is definitely the big one.

Change-Id: I17ee70cff15a80e8025eec96a7286266233546d9
Reviewed-on: https://go-review.googlesource.com/c/arch/+/691341
Auto-Submit: Austin Clements <austin@google.com>
Reviewed-by: David Chase <drchase@google.com>
Reviewed-by: Junyang Shao <shaojunyang@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
29 files changed
tree: 3cf5536c70abec4abbbb96aaeb1c60dae741c244
  1. arm/
  2. arm64/
  3. internal/
  4. loong64/
  5. ppc64/
  6. riscv64/
  7. s390x/
  8. x86/
  9. codereview.cfg
  10. CONTRIBUTING.md
  11. go.mod
  12. go.sum
  13. LICENSE
  14. PATENTS
  15. README.md
README.md

arch

Go Reference

This repository holds machine architecture information used by the Go toolchain. The parts needed in the main Go repository are copied in.

Report Issues / Send Patches

This repository uses Gerrit for code changes. To learn how to submit changes to this repository, see https://go.dev/doc/contribute.

The git repository is https://go.googlesource.com/arch.

The main issue tracker for the arch repository is located at https://go.dev/issues. Prefix your issue with “x/arch:” in the subject line, so it is easy to find.