Compare commits
15
Commits
95025be1bc
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cf6bc6987e | ||
|
|
ff7b1452b1 | ||
|
|
517c1cea25 | ||
|
|
a3e3010e0f | ||
|
|
057c4eb545 | ||
|
|
f720381d43 | ||
|
|
2c9042d62c | ||
|
|
82ef289d3a | ||
|
|
7246b0e002 | ||
|
|
8cfd40aac8 | ||
|
|
5382c9a8e4 | ||
|
|
53de91b2df | ||
|
|
8a36af7c7d | ||
|
|
e9789ce3f4 | ||
|
|
837231c068 |
@@ -342,7 +342,10 @@ jobs:
|
|||||||
my @cmd = (q{curl}, q{-sS}, q{-o}, q{/dev/null}, q{-w}, q{%{http_code}},
|
my @cmd = (q{curl}, q{-sS}, q{-o}, q{/dev/null}, q{-w}, q{%{http_code}},
|
||||||
q{-H}, qq{Authorization: token $ENV{GITEA_TOKEN}},
|
q{-H}, qq{Authorization: token $ENV{GITEA_TOKEN}},
|
||||||
q{-H}, q{Content-Type: application/octet-stream},
|
q{-H}, q{Content-Type: application/octet-stream},
|
||||||
q{-X}, q{POST}, q{--data-binary}, qq{@$path},
|
# The @ must not sit inside a qq{} string: there it starts an
|
||||||
|
# array interpolation and the upload body collapses to empty,
|
||||||
|
# which Gitea stores as a 201-created zero-byte attachment.
|
||||||
|
q{-X}, q{POST}, q{--data-binary}, q{@} . $path,
|
||||||
qq{$ENV{GITEA_SERVER_URL}/api/v1/repos/$ENV{GITEA_REPOSITORY}/releases/$id/assets?name=$name});
|
qq{$ENV{GITEA_SERVER_URL}/api/v1/repos/$ENV{GITEA_REPOSITORY}/releases/$id/assets?name=$name});
|
||||||
open(my $curl, q{-|}, @cmd) or die qq{curl: $!};
|
open(my $curl, q{-|}, @cmd) or die qq{curl: $!};
|
||||||
my $code = <$curl>;
|
my $code = <$curl>;
|
||||||
|
|||||||
+96
-23
@@ -9,14 +9,31 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- **GOOBJ format specification.** [docs/GOOBJ.md](docs/GOOBJ.md)
|
-
|
||||||
documents the Go object file format in full: both containers, the 96
|
|
||||||
byte header and all 19 blocks, every structure with its byte
|
## [0.35.0] - 2026-09-22
|
||||||
offsets, symbol kinds and flag bits, all 106 relocation types with
|
|
||||||
the weak variants, aux symbols, the FuncInfo payload, the pc-value
|
### Added
|
||||||
table encoding, the content hashes and the builtin table, all
|
|
||||||
verified byte for byte against objects produced by Go 1.27.1's own
|
- **The go_asm.h generator.** `gasm asm` generates the package's go_asm.h
|
||||||
tools.
|
itself when an assembly file includes it: the Go files beside the source
|
||||||
|
are type-checked for the target architecture and the constants and field
|
||||||
|
offsets become assembler defines, so package-context files assemble with
|
||||||
|
no compiler and no `go build` in the loop. `-GOOS` selects the
|
||||||
|
type-checking GOOS for GOOS-specific files, and the corpus audit derives
|
||||||
|
the GOOS from the file name.
|
||||||
|
- **ELF data relocations on arm64, riscv64 and loong64.** `gasm asm
|
||||||
|
--format elf` emits `.rela.data` for symbol-valued DATA initialisers on
|
||||||
|
every architecture (amd64 carried them already), so standalone ELF
|
||||||
|
objects link on all four targets.
|
||||||
|
- **Corpus failure listing.** `gasm audit-instructions --corpus --list`
|
||||||
|
prints every failing file with its failure reason, per architecture,
|
||||||
|
instead of one representative file per reason.
|
||||||
|
- **DATA with symbol values and relaxed symbol spellings.** DATA
|
||||||
|
initialisers accept `$symbol(SB)` values, laid down as an absolute
|
||||||
|
relocation at the data field (GOOBJ on all four architectures and ELF
|
||||||
|
on all four as of this release), and U+2215 is accepted inside symbol
|
||||||
|
package paths.
|
||||||
- **Macro expansion and include splicing.** `gasm asm`, `gasm diff` and
|
- **Macro expansion and include splicing.** `gasm asm`, `gasm diff` and
|
||||||
`gasm audit-instructions` now preprocess assembly the way the
|
`gasm audit-instructions` now preprocess assembly the way the
|
||||||
toolchain does: object and parameterised `#define` macros expand at
|
toolchain does: object and parameterised `#define` macros expand at
|
||||||
@@ -57,21 +74,77 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
ranges, index-only VSIB memory operands and bare trailing immediates;
|
ranges, index-only VSIB memory operands and bare trailing immediates;
|
||||||
macro substitution reaches parameters used with element suffixes
|
macro substitution reaches parameters used with element suffixes
|
||||||
(`A.S4`), and `;` separates statements in plain files.
|
(`A.S4`), and `;` separates statements in plain files.
|
||||||
- **`gasm asm -GOOS`.** The go_asm.h generator type-checks per target
|
- **Per-architecture reference pages.** [docs/asm/](docs/asm/README.md)
|
||||||
GOOS, so the darwin-only and windows-only runtime files assemble with
|
gains AMD64, ARM64, RISCV64 and LOONG64: the register files and the
|
||||||
their own defines; the corpus audit derives the GOOS from the file
|
roles the ABI fixes, addressing, operand order with every special form,
|
||||||
name. DATA initialisers accept `$symbol(SB)` values (an absolute
|
constants and materialisation, alignment, fences and the relocations
|
||||||
relocation at the data field, GOOBJ on all four architectures and ELF
|
each target emits. An instruction inventory appendix per architecture
|
||||||
on amd64, arm64, riscv64 and loong64), and U+2215 is accepted inside
|
is generated from the toolchain's own tables by `just gen`, and the
|
||||||
symbol package paths.
|
regenerated tables recognise 147 more mnemonics than the previous
|
||||||
- **The corpus audit measures honestly.** Files named for Go ports gasm
|
release carried (arm64 107, riscv64 31, loong64 9).
|
||||||
does not target (arm, 386, s390x, ...) are no longer attempted for the
|
- **The Plan 9 assembly language reference.** [docs/asm/](docs/asm/README.md)
|
||||||
four supported architectures (no supported build compiles them), and
|
opens the complete language reference with its common core: the lexicon,
|
||||||
the headline rate is reported over attemptable files: 136 of 433 on
|
statement structure and constant expressions, the operand grammar with
|
||||||
the full corpus (31.4 %), 135 of 383 on real code (35.2 %), from the
|
the pseudo-registers and symbol naming, the directives and the function
|
||||||
127 that the previous release measured. The probe battery that
|
flag vocabulary, preprocessing with `#define` and `#include`, and the
|
||||||
decides encodability gained the operand shapes the new families use.
|
Go-embedded layer (ABI0, prototypes, `go_asm.h`, `funcdata.h` and the
|
||||||
-
|
runtime contract). Every claim is verified against `go tool asm` of
|
||||||
|
Go 1.27.1 and gasm's differential tests; the per-architecture pages and
|
||||||
|
generated instruction appendices follow.
|
||||||
|
- **GOOBJ format specification.** [docs/GOOBJ.md](docs/GOOBJ.md)
|
||||||
|
documents the Go object file format in full: both containers, the 96
|
||||||
|
byte header and all 19 blocks, every structure with its byte
|
||||||
|
offsets, symbol kinds and flag bits, all 106 relocation types with
|
||||||
|
the weak variants, aux symbols, the FuncInfo payload, the pc-value
|
||||||
|
table encoding, the content hashes and the builtin table, all
|
||||||
|
verified byte for byte against objects produced by Go 1.27.1's own
|
||||||
|
tools.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- **The corpus audit measures like a build.** Files named for a Go port
|
||||||
|
gasm does not target (arm, 386, s390x, ...) are never attempted, because
|
||||||
|
no supported build compiles them; the GOOS comes from the file name; and
|
||||||
|
each target's go_asm.h is generated on the fly. The headline is reported
|
||||||
|
over attemptable files: 291 of 353 on the full corpus (82.4 %) assemble
|
||||||
|
for every target architecture and 295 of 303 on real code (97.4 %),
|
||||||
|
against 108 of 627 over all files (17.2 %) that the previous release
|
||||||
|
measured.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- **The operand forms GOROOT writes.** Numeric PC-relative jumps
|
||||||
|
(`JEQ 2(PC)`, the park loop `JMP 0(PC)`) resolve with the toolchain's
|
||||||
|
own instruction counting and fold jump-to-jump chains exactly as its
|
||||||
|
branch optimiser does; symbol immediates (`MOVQ $sym(SB), AX`)
|
||||||
|
assemble to the toolchain's RIP-relative LEA with an R_PCREL
|
||||||
|
relocation; negated constant expressions in operands (`ADJSP
|
||||||
|
$-(REGS - 8)`, the shape the cgo ABI macros write) fold; the immediate
|
||||||
|
multiply (`IMULQ $1000000000, AX`) encodes with the toolchain's
|
||||||
|
0x69/0x6B selection; the TLS access pair assembles as the toolchain's
|
||||||
|
one-instruction form (the bare `MOVQ TLS, r` load nops out and
|
||||||
|
`off(r)(TLS*1)` folds to the segment-prefixed absolute whose disp32
|
||||||
|
carries the R_TLSLE relocation, per-GOOS); arm64 accepts the
|
||||||
|
bare-register indirect branch (`BL R9` beside `BL (R9)`, both BLR) and
|
||||||
|
the zero-immediate store (`MOVD $0, mem` through the zero register,
|
||||||
|
rejecting non-zero immediates as the toolchain does); `PCALIGN` now
|
||||||
|
aligns on amd64, padding with the toolchain's greedy
|
||||||
|
single-instruction NOPs; the segment-absolute forms (`MOVQ 0x30(GS),
|
||||||
|
AX` and the store direction) and the absolute crash-store
|
||||||
|
(`MOVL $0xf1, 0xf1`) encode; and `gasm asm` predefines the
|
||||||
|
`GOARCH_<arch>` and `GOOS_<goos>` macros the go command passes to
|
||||||
|
`go tool asm`, so GOROOT headers' `#ifdef GOARCH_amd64` platform
|
||||||
|
blocks (`go_tls.h`'s `get_tls` and friends) select as intended. The
|
||||||
|
GOROOT corpus measure moves to 291 of 353 files assembling for every
|
||||||
|
target architecture (82.4 %), 97.4 % of the real-code corpus, from
|
||||||
|
70.8 % and 82.2 %.
|
||||||
|
- **Tool corrections across the pipeline.** The formatter keeps square
|
||||||
|
brackets in SIMD operands, statement separators and canonical macro
|
||||||
|
bodies; the linter drops false positives on shift counts, SETcc
|
||||||
|
spellings and ABIInternal references; the lexer treats a trailing
|
||||||
|
carriage return as a line end so comment text stays idempotent; and
|
||||||
|
arm64 rejects bare BTI with a diagnostic while accepting the full
|
||||||
|
family.
|
||||||
|
|
||||||
## [0.34.0] - 2026-09-20
|
## [0.34.0] - 2026-09-20
|
||||||
|
|
||||||
|
|||||||
@@ -81,7 +81,10 @@ to give that syntax the tooling it deserves.
|
|||||||
GOOBJ format, which needs the installed toolchain and which `go build`
|
GOOBJ format, which needs the installed toolchain and which `go build`
|
||||||
consumes in place of the toolchain's output. Framed functions get the
|
consumes in place of the toolchain's output. Framed functions get the
|
||||||
stack-split guard and the morestack block, byte-identical to the
|
stack-split guard and the morestack block, byte-identical to the
|
||||||
toolchain's, so split functions link too.
|
toolchain's, so split functions link too. The assembler preprocesses
|
||||||
|
like the toolchain (`#define`, `#include` with `-I`, `#ifdef`), generates
|
||||||
|
`go_asm.h` from the package's Go files, and carries `PCALIGN`, the
|
||||||
|
`LOCK`/`REP` prefixes and the literal-data pseudo-ops.
|
||||||
- **Disassembler.** `gasm dis` lists a `.s` file's functions at their real
|
- **Disassembler.** `gasm dis` lists a `.s` file's functions at their real
|
||||||
offsets after assembling, or disassembles raw bytes from a file or stdin.
|
offsets after assembling, or disassembles raw bytes from a file or stdin.
|
||||||
- **Dynamic verification.** `gasm verify` JIT-loads assembled functions into
|
- **Dynamic verification.** `gasm verify` JIT-loads assembled functions into
|
||||||
@@ -110,9 +113,9 @@ Four architectures, the four that matter in practice:
|
|||||||
| Architecture | GOARCH | File suffix | Instructions recognised |
|
| Architecture | GOARCH | File suffix | Instructions recognised |
|
||||||
|--------------|-------------|--------------|---------------------------------------------|
|
|--------------|-------------|--------------|---------------------------------------------|
|
||||||
| AMD64 | `amd64` | `_amd64.s` | 1600 + common opcodes + traditional aliases |
|
| AMD64 | `amd64` | `_amd64.s` | 1600 + common opcodes + traditional aliases |
|
||||||
| ARM64 | `arm64` | `_arm64.s` | 538 + common opcodes |
|
| ARM64 | `arm64` | `_arm64.s` | 645 + common opcodes |
|
||||||
| RISC-V | `riscv64` | `_riscv64.s` | 961 + common opcodes |
|
| RISC-V | `riscv64` | `_riscv64.s` | 992 + common opcodes |
|
||||||
| LoongArch | `loong64` | `_loong64.s` | 799 + common opcodes |
|
| LoongArch | `loong64` | `_loong64.s` | 808 + common opcodes |
|
||||||
|
|
||||||
"Common opcodes" are the instructions shared by every architecture (`RET`,
|
"Common opcodes" are the instructions shared by every architecture (`RET`,
|
||||||
`JMP`, `NOP`, `CALL`, `TEXT`, `FUNCDATA`, `PCDATA`, ...). AMD64 additionally
|
`JMP`, `NOP`, `CALL`, `TEXT`, `FUNCDATA`, `PCDATA`, ...). AMD64 additionally
|
||||||
@@ -124,8 +127,8 @@ can emit today is narrower, and a recognised but unencodable instruction is
|
|||||||
reported as an explicit error, never as a wrong byte.
|
reported as an explicit error, never as a wrong byte.
|
||||||
|
|
||||||
The same measurement runs over GOROOT's whole assembly corpus:
|
The same measurement runs over GOROOT's whole assembly corpus:
|
||||||
`gasm audit-instructions --corpus` reports 136 of 433 attemptable files
|
`gasm audit-instructions --corpus` reports 291 of 353 attemptable files
|
||||||
(31.4 %) assembling for every target architecture today (files named for
|
(82.4 %) assembling for every target architecture today (files named for
|
||||||
other Go ports are counted but never attempted), with the top failure
|
other Go ports are counted but never attempted), with the top failure
|
||||||
reasons per architecture; the number moves with every release.
|
reasons per architecture; the number moves with every release.
|
||||||
|
|
||||||
@@ -186,8 +189,9 @@ verified the way the code is verified: an encoding documented here is
|
|||||||
one that differential tests against `go tool asm` confirm
|
one that differential tests against `go tool asm` confirm
|
||||||
byte-for-byte, and a format field documented here is one the linker
|
byte-for-byte, and a format field documented here is one the linker
|
||||||
demonstrably reads. The work has begun: [docs/GOOBJ.md](docs/GOOBJ.md)
|
demonstrably reads. The work has begun: [docs/GOOBJ.md](docs/GOOBJ.md)
|
||||||
specifies the object file format completely. The assembler language
|
specifies the object file format completely, and
|
||||||
reference follows.
|
[docs/asm/README.md](docs/asm/README.md) opens the language reference
|
||||||
|
with its common core. The per-architecture pages follow.
|
||||||
|
|
||||||
## Direction
|
## Direction
|
||||||
|
|
||||||
@@ -315,6 +319,7 @@ recipe.
|
|||||||
them
|
them
|
||||||
- [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md): components and data flow
|
- [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md): components and data flow
|
||||||
- [docs/GOOBJ.md](docs/GOOBJ.md): the GOOBJ object file format specification
|
- [docs/GOOBJ.md](docs/GOOBJ.md): the GOOBJ object file format specification
|
||||||
|
- [docs/asm/](docs/asm/README.md): the Plan 9 assembly language reference
|
||||||
- [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md): development setup and recipes
|
- [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md): development setup and recipes
|
||||||
- [CHANGELOG.md](CHANGELOG.md): release history
|
- [CHANGELOG.md](CHANGELOG.md): release history
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -7,7 +7,7 @@ releases do not receive them.
|
|||||||
|
|
||||||
| Version | Supported |
|
| Version | Supported |
|
||||||
|---|---|
|
|---|---|
|
||||||
| 0.34.0 | yes |
|
| 0.35.0 | yes |
|
||||||
| older releases | no |
|
| older releases | no |
|
||||||
|
|
||||||
## Reporting a vulnerability
|
## Reporting a vulnerability
|
||||||
|
|||||||
+101
-3
@@ -8,6 +8,10 @@
|
|||||||
// names so gasm-devkit supports every instruction the real assembler does,
|
// names so gasm-devkit supports every instruction the real assembler does,
|
||||||
// with no hand-maintained (and therefore inevitably incomplete) lists.
|
// with no hand-maintained (and therefore inevitably incomplete) lists.
|
||||||
//
|
//
|
||||||
|
// The same data feeds the generated instruction appendices of the assembly
|
||||||
|
// language reference, docs/asm/INSTRUCTIONS-<ARCH>.md, so that the reference
|
||||||
|
// cannot drift from the tables it documents.
|
||||||
|
//
|
||||||
// Usage (via the justfile):
|
// Usage (via the justfile):
|
||||||
//
|
//
|
||||||
// just gen
|
// just gen
|
||||||
@@ -26,6 +30,9 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"sourcedock.dev/petrbalvin/gasm-devkit/arch"
|
||||||
|
"sourcedock.dev/petrbalvin/gasm-devkit/asm"
|
||||||
)
|
)
|
||||||
|
|
||||||
// archDirs maps a gasm-devkit architecture name to its obj sub-directory.
|
// archDirs maps a gasm-devkit architecture name to its obj sub-directory.
|
||||||
@@ -39,11 +46,30 @@ var archDirs = []struct {
|
|||||||
{"loong64", "loong64"},
|
{"loong64", "loong64"},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// docPages maps an architecture to its generated appendix in the language
|
||||||
|
// reference. The amd64 page carries a per-mnemonic encodability column,
|
||||||
|
// decided by asm.Encodable, which mirrors the encoder's own dispatch; the
|
||||||
|
// other targets have no single cheap predicate, so their pages carry the
|
||||||
|
// inventory and point at the live measurement instead.
|
||||||
|
var docPages = []struct {
|
||||||
|
arch arch.Arch
|
||||||
|
title string
|
||||||
|
file string
|
||||||
|
anames string
|
||||||
|
encodable bool
|
||||||
|
}{
|
||||||
|
{arch.AMD64, "AMD64", "INSTRUCTIONS-AMD64.md", "cmd/internal/obj/x86/anames.go", true},
|
||||||
|
{arch.ARM64, "ARM64", "INSTRUCTIONS-ARM64.md", "cmd/internal/obj/arm64/anames.go", false},
|
||||||
|
{arch.RISCV, "RISC-V 64", "INSTRUCTIONS-RISCV64.md", "cmd/internal/obj/riscv/anames.go", false},
|
||||||
|
{arch.LOONG64, "LoongArch 64", "INSTRUCTIONS-LOONG64.md", "cmd/internal/obj/loong64/anames.go", false},
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
goroot := strings.TrimSpace(runGoEnvGOROOT())
|
goroot := strings.TrimSpace(runGoEnvGOROOT())
|
||||||
if goroot == "" {
|
if goroot == "" {
|
||||||
fatal("could not determine GOROOT")
|
fatal("could not determine GOROOT")
|
||||||
}
|
}
|
||||||
|
version := strings.TrimSpace(runGoEnv("GOVERSION"))
|
||||||
// The common opcodes shared by every architecture (RET, JMP, NOP, CALL,
|
// The common opcodes shared by every architecture (RET, JMP, NOP, CALL,
|
||||||
// TEXT, FUNCDATA, …) live in cmd/internal/obj/util.go.
|
// TEXT, FUNCDATA, …) live in cmd/internal/obj/util.go.
|
||||||
commonPath := filepath.Join(goroot, "src", "cmd", "internal", "obj", "util.go")
|
commonPath := filepath.Join(goroot, "src", "cmd", "internal", "obj", "util.go")
|
||||||
@@ -57,16 +83,24 @@ func main() {
|
|||||||
}
|
}
|
||||||
fmt.Printf("%-8s %4d instructions -> arch/common_gen.go\n", "common", len(common))
|
fmt.Printf("%-8s %4d instructions -> arch/common_gen.go\n", "common", len(common))
|
||||||
|
|
||||||
|
names := map[string][]string{}
|
||||||
for _, a := range archDirs {
|
for _, a := range archDirs {
|
||||||
path := filepath.Join(goroot, "src", "cmd", "internal", "obj", a.sub, "anames.go")
|
path := filepath.Join(goroot, "src", "cmd", "internal", "obj", a.sub, "anames.go")
|
||||||
names, err := extractInstrs(path)
|
names[a.arch], err = extractInstrs(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fatal("extract %s: %v", a.arch, err)
|
fatal("extract %s: %v", a.arch, err)
|
||||||
}
|
}
|
||||||
if err := writeGen(a.arch, a.sub, names); err != nil {
|
if err := writeGen(a.arch, a.sub, names[a.arch]); err != nil {
|
||||||
fatal("write %s: %v", a.arch, err)
|
fatal("write %s: %v", a.arch, err)
|
||||||
}
|
}
|
||||||
fmt.Printf("%-8s %4d instructions -> arch/%s_gen.go\n", a.arch, len(names), a.arch)
|
fmt.Printf("%-8s %4d instructions -> arch/%s_gen.go\n", a.arch, len(names[a.arch]), a.arch)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, p := range docPages {
|
||||||
|
if err := writeDocPage(p.arch, p.title, p.file, p.anames, version, p.encodable); err != nil {
|
||||||
|
fatal("write %s: %v", p.file, err)
|
||||||
|
}
|
||||||
|
fmt.Printf("%-8s -> docs/asm/%s\n", p.arch, p.file)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,6 +206,61 @@ func writeGen(arch, sub string, names []string) error {
|
|||||||
return os.WriteFile(filepath.Join("arch", arch+"_gen.go"), []byte(b.String()), 0o644)
|
return os.WriteFile(filepath.Join("arch", arch+"_gen.go"), []byte(b.String()), 0o644)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// writeDocPage emits docs/asm/<file>, the generated instruction appendix of
|
||||||
|
// the language reference for one architecture: every mnemonic the toolchain
|
||||||
|
// accepts, with the curated summary where the architecture table carries one
|
||||||
|
// and, on amd64, a per-mnemonic encodability column.
|
||||||
|
func writeDocPage(a arch.Arch, title, file, anames, version string, encodable bool) error {
|
||||||
|
table := arch.ForArch(a)
|
||||||
|
instrs := table.Instructions()
|
||||||
|
|
||||||
|
var b strings.Builder
|
||||||
|
b.WriteString("# " + title + ": instruction inventory\n\n")
|
||||||
|
b.WriteString("Generated by gasm-devkit's `_gen` from the Go toolchain's instruction table\n")
|
||||||
|
b.WriteString("(`" + anames + "`, " + version + "); DO NOT EDIT. This page lists every mnemonic\n")
|
||||||
|
b.WriteString("`go tool asm` accepts on this target, which is the upper bound of the\n")
|
||||||
|
b.WriteString("language on it: a name absent here is not an instruction of the target,\n")
|
||||||
|
b.WriteString("and a name present here may still be one gasm's encoder cannot emit yet.\n\n")
|
||||||
|
|
||||||
|
encodableCount := 0
|
||||||
|
if encodable {
|
||||||
|
b.WriteString("The `gasm encodes` column reports whether gasm's encoder can emit the\n")
|
||||||
|
b.WriteString("mnemonic today; the gap is the encoder backlog, measured live by\n")
|
||||||
|
b.WriteString("`gasm audit-instructions`.\n\n")
|
||||||
|
b.WriteString("| Mnemonic | gasm encodes | Notes |\n")
|
||||||
|
b.WriteString("|---|---|---|\n")
|
||||||
|
for _, in := range instrs {
|
||||||
|
ok := asm.Encodable(in.Name)
|
||||||
|
if ok {
|
||||||
|
encodableCount++
|
||||||
|
}
|
||||||
|
b.WriteString("| `" + in.Name + "` | " + yesNo(ok) + " | " + in.Summary + " |\n")
|
||||||
|
}
|
||||||
|
b.WriteString("\n")
|
||||||
|
fmt.Fprintf(&b, "Recognised: %d mnemonics. gasm encodes: %d.\n", len(instrs), encodableCount)
|
||||||
|
} else {
|
||||||
|
b.WriteString("The inventory carries no per-mnemonic encoder column: on this target\n")
|
||||||
|
b.WriteString("encodability is decided per operand shape, and the live measured\n")
|
||||||
|
b.WriteString("coverage is reported by `gasm audit-instructions`.\n\n")
|
||||||
|
b.WriteString("| Mnemonic | Notes |\n")
|
||||||
|
b.WriteString("|---|---|\n")
|
||||||
|
for _, in := range instrs {
|
||||||
|
b.WriteString("| `" + in.Name + "` | " + in.Summary + " |\n")
|
||||||
|
}
|
||||||
|
b.WriteString("\n")
|
||||||
|
fmt.Fprintf(&b, "Recognised: %d mnemonics.\n", len(instrs))
|
||||||
|
}
|
||||||
|
return os.WriteFile(filepath.Join("docs", "asm", file), []byte(b.String()), 0o644)
|
||||||
|
}
|
||||||
|
|
||||||
|
// yesNo renders a boolean as the word the appendix tables use.
|
||||||
|
func yesNo(v bool) string {
|
||||||
|
if v {
|
||||||
|
return "yes"
|
||||||
|
}
|
||||||
|
return "no"
|
||||||
|
}
|
||||||
|
|
||||||
func runGoEnvGOROOT() string {
|
func runGoEnvGOROOT() string {
|
||||||
out, err := exec.Command("go", "env", "GOROOT").Output()
|
out, err := exec.Command("go", "env", "GOROOT").Output()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -180,6 +269,15 @@ func runGoEnvGOROOT() string {
|
|||||||
return string(out)
|
return string(out)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// runGoEnv runs `go env` for a single variable.
|
||||||
|
func runGoEnv(name string) string {
|
||||||
|
out, err := exec.Command("go", "env", name).Output()
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return string(out)
|
||||||
|
}
|
||||||
|
|
||||||
func fatal(format string, args ...any) {
|
func fatal(format string, args ...any) {
|
||||||
fmt.Fprintf(os.Stderr, "gen: "+format+"\n", args...)
|
fmt.Fprintf(os.Stderr, "gen: "+format+"\n", args...)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
|||||||
@@ -364,6 +364,8 @@ var arm64GeneratedInstrs = []string{
|
|||||||
"REVW",
|
"REVW",
|
||||||
"ROR",
|
"ROR",
|
||||||
"RORW",
|
"RORW",
|
||||||
|
"RPRFM",
|
||||||
|
"SB",
|
||||||
"SBC",
|
"SBC",
|
||||||
"SBCS",
|
"SBCS",
|
||||||
"SBCSW",
|
"SBCSW",
|
||||||
@@ -477,23 +479,68 @@ var arm64GeneratedInstrs = []string{
|
|||||||
"UXTH",
|
"UXTH",
|
||||||
"UXTHW",
|
"UXTHW",
|
||||||
"UXTW",
|
"UXTW",
|
||||||
|
"VABS",
|
||||||
"VADD",
|
"VADD",
|
||||||
"VADDP",
|
"VADDP",
|
||||||
"VADDV",
|
"VADDV",
|
||||||
"VAND",
|
"VAND",
|
||||||
"VBCAX",
|
"VBCAX",
|
||||||
|
"VBIC",
|
||||||
"VBIF",
|
"VBIF",
|
||||||
"VBIT",
|
"VBIT",
|
||||||
"VBSL",
|
"VBSL",
|
||||||
|
"VCLS",
|
||||||
|
"VCLZ",
|
||||||
"VCMEQ",
|
"VCMEQ",
|
||||||
|
"VCMGE",
|
||||||
|
"VCMGT",
|
||||||
|
"VCMHI",
|
||||||
|
"VCMHS",
|
||||||
|
"VCMLE",
|
||||||
|
"VCMLT",
|
||||||
"VCMTST",
|
"VCMTST",
|
||||||
"VCNT",
|
"VCNT",
|
||||||
"VDUP",
|
"VDUP",
|
||||||
"VEOR",
|
"VEOR",
|
||||||
"VEOR3",
|
"VEOR3",
|
||||||
"VEXT",
|
"VEXT",
|
||||||
|
"VFABS",
|
||||||
|
"VFADD",
|
||||||
|
"VFADDP",
|
||||||
|
"VFCMEQ",
|
||||||
|
"VFCMGE",
|
||||||
|
"VFCMGT",
|
||||||
|
"VFCMLE",
|
||||||
|
"VFCMLT",
|
||||||
|
"VFCVTL",
|
||||||
|
"VFCVTL2",
|
||||||
|
"VFCVTN",
|
||||||
|
"VFCVTN2",
|
||||||
|
"VFCVTZS",
|
||||||
|
"VFCVTZU",
|
||||||
|
"VFDIV",
|
||||||
|
"VFMAX",
|
||||||
|
"VFMAXNM",
|
||||||
|
"VFMAXNMP",
|
||||||
|
"VFMAXNMV",
|
||||||
|
"VFMAXP",
|
||||||
|
"VFMAXV",
|
||||||
|
"VFMIN",
|
||||||
|
"VFMINNM",
|
||||||
|
"VFMINNMP",
|
||||||
|
"VFMINNMV",
|
||||||
|
"VFMINP",
|
||||||
|
"VFMINV",
|
||||||
"VFMLA",
|
"VFMLA",
|
||||||
"VFMLS",
|
"VFMLS",
|
||||||
|
"VFMUL",
|
||||||
|
"VFNEG",
|
||||||
|
"VFRINTM",
|
||||||
|
"VFRINTN",
|
||||||
|
"VFRINTP",
|
||||||
|
"VFRINTZ",
|
||||||
|
"VFSQRT",
|
||||||
|
"VFSUB",
|
||||||
"VLD1",
|
"VLD1",
|
||||||
"VLD1R",
|
"VLD1R",
|
||||||
"VLD2",
|
"VLD2",
|
||||||
@@ -502,11 +549,17 @@ var arm64GeneratedInstrs = []string{
|
|||||||
"VLD3R",
|
"VLD3R",
|
||||||
"VLD4",
|
"VLD4",
|
||||||
"VLD4R",
|
"VLD4R",
|
||||||
|
"VMLA",
|
||||||
|
"VMLS",
|
||||||
"VMOV",
|
"VMOV",
|
||||||
"VMOVD",
|
"VMOVD",
|
||||||
"VMOVI",
|
"VMOVI",
|
||||||
"VMOVQ",
|
"VMOVQ",
|
||||||
"VMOVS",
|
"VMOVS",
|
||||||
|
"VMUL",
|
||||||
|
"VNEG",
|
||||||
|
"VNOT",
|
||||||
|
"VORN",
|
||||||
"VORR",
|
"VORR",
|
||||||
"VPMULL",
|
"VPMULL",
|
||||||
"VPMULL2",
|
"VPMULL2",
|
||||||
@@ -515,14 +568,47 @@ var arm64GeneratedInstrs = []string{
|
|||||||
"VREV16",
|
"VREV16",
|
||||||
"VREV32",
|
"VREV32",
|
||||||
"VREV64",
|
"VREV64",
|
||||||
|
"VSCVTF",
|
||||||
|
"VSHADD",
|
||||||
"VSHL",
|
"VSHL",
|
||||||
|
"VSHRN",
|
||||||
|
"VSHRN2",
|
||||||
"VSLI",
|
"VSLI",
|
||||||
|
"VSMAX",
|
||||||
|
"VSMAXP",
|
||||||
|
"VSMAXV",
|
||||||
|
"VSMIN",
|
||||||
|
"VSMINP",
|
||||||
|
"VSMINV",
|
||||||
|
"VSMLAL",
|
||||||
|
"VSMLAL2",
|
||||||
|
"VSMLSL",
|
||||||
|
"VSMLSL2",
|
||||||
|
"VSMULL",
|
||||||
|
"VSMULL2",
|
||||||
|
"VSQABS",
|
||||||
|
"VSQADD",
|
||||||
|
"VSQNEG",
|
||||||
|
"VSQSHL",
|
||||||
|
"VSQSUB",
|
||||||
|
"VSQXTN",
|
||||||
|
"VSQXTN2",
|
||||||
|
"VSQXTUN",
|
||||||
|
"VSQXTUN2",
|
||||||
|
"VSRHADD",
|
||||||
"VSRI",
|
"VSRI",
|
||||||
|
"VSRSHR",
|
||||||
|
"VSSHL",
|
||||||
|
"VSSHLL",
|
||||||
|
"VSSHLL2",
|
||||||
|
"VSSHR",
|
||||||
"VST1",
|
"VST1",
|
||||||
"VST2",
|
"VST2",
|
||||||
"VST3",
|
"VST3",
|
||||||
"VST4",
|
"VST4",
|
||||||
"VSUB",
|
"VSUB",
|
||||||
|
"VSXTL",
|
||||||
|
"VSXTL2",
|
||||||
"VTBL",
|
"VTBL",
|
||||||
"VTBX",
|
"VTBX",
|
||||||
"VTRN1",
|
"VTRN1",
|
||||||
@@ -530,8 +616,27 @@ var arm64GeneratedInstrs = []string{
|
|||||||
"VUADDLV",
|
"VUADDLV",
|
||||||
"VUADDW",
|
"VUADDW",
|
||||||
"VUADDW2",
|
"VUADDW2",
|
||||||
|
"VUCVTF",
|
||||||
|
"VUHADD",
|
||||||
"VUMAX",
|
"VUMAX",
|
||||||
|
"VUMAXP",
|
||||||
|
"VUMAXV",
|
||||||
"VUMIN",
|
"VUMIN",
|
||||||
|
"VUMINP",
|
||||||
|
"VUMINV",
|
||||||
|
"VUMLAL",
|
||||||
|
"VUMLAL2",
|
||||||
|
"VUMLSL",
|
||||||
|
"VUMLSL2",
|
||||||
|
"VUMULL",
|
||||||
|
"VUMULL2",
|
||||||
|
"VUQADD",
|
||||||
|
"VUQSHL",
|
||||||
|
"VUQSUB",
|
||||||
|
"VUQXTN",
|
||||||
|
"VUQXTN2",
|
||||||
|
"VURHADD",
|
||||||
|
"VUSHL",
|
||||||
"VUSHLL",
|
"VUSHLL",
|
||||||
"VUSHLL2",
|
"VUSHLL2",
|
||||||
"VUSHR",
|
"VUSHR",
|
||||||
@@ -541,6 +646,8 @@ var arm64GeneratedInstrs = []string{
|
|||||||
"VUZP1",
|
"VUZP1",
|
||||||
"VUZP2",
|
"VUZP2",
|
||||||
"VXAR",
|
"VXAR",
|
||||||
|
"VXTN",
|
||||||
|
"VXTN2",
|
||||||
"VZIP1",
|
"VZIP1",
|
||||||
"VZIP2",
|
"VZIP2",
|
||||||
"WFE",
|
"WFE",
|
||||||
|
|||||||
@@ -152,6 +152,8 @@ var loong64GeneratedInstrs = []string{
|
|||||||
"FNMADDF",
|
"FNMADDF",
|
||||||
"FNMSUBD",
|
"FNMSUBD",
|
||||||
"FNMSUBF",
|
"FNMSUBF",
|
||||||
|
"FRINTD",
|
||||||
|
"FRINTF",
|
||||||
"FSCALEBD",
|
"FSCALEBD",
|
||||||
"FSCALEBF",
|
"FSCALEBF",
|
||||||
"FSEL",
|
"FSEL",
|
||||||
@@ -177,7 +179,10 @@ var loong64GeneratedInstrs = []string{
|
|||||||
"FTINTWF",
|
"FTINTWF",
|
||||||
"JIRL",
|
"JIRL",
|
||||||
"LL",
|
"LL",
|
||||||
|
"LLACQV",
|
||||||
|
"LLACQW",
|
||||||
"LLV",
|
"LLV",
|
||||||
|
"LLW",
|
||||||
"LU12IW",
|
"LU12IW",
|
||||||
"LU32ID",
|
"LU32ID",
|
||||||
"LU52ID",
|
"LU52ID",
|
||||||
@@ -248,7 +253,11 @@ var loong64GeneratedInstrs = []string{
|
|||||||
"ROTR",
|
"ROTR",
|
||||||
"ROTRV",
|
"ROTRV",
|
||||||
"SC",
|
"SC",
|
||||||
|
"SCQ",
|
||||||
|
"SCRELV",
|
||||||
|
"SCRELW",
|
||||||
"SCV",
|
"SCV",
|
||||||
|
"SCW",
|
||||||
"SGT",
|
"SGT",
|
||||||
"SGTU",
|
"SGTU",
|
||||||
"SLL",
|
"SLL",
|
||||||
|
|||||||
@@ -81,6 +81,9 @@ var riscvGeneratedInstrs = []string{
|
|||||||
"CLD",
|
"CLD",
|
||||||
"CLDSP",
|
"CLDSP",
|
||||||
"CLI",
|
"CLI",
|
||||||
|
"CLMUL",
|
||||||
|
"CLMULH",
|
||||||
|
"CLMULR",
|
||||||
"CLUI",
|
"CLUI",
|
||||||
"CLW",
|
"CLW",
|
||||||
"CLWSP",
|
"CLWSP",
|
||||||
@@ -95,13 +98,20 @@ var riscvGeneratedInstrs = []string{
|
|||||||
"CSDSP",
|
"CSDSP",
|
||||||
"CSLLI",
|
"CSLLI",
|
||||||
"CSRAI",
|
"CSRAI",
|
||||||
|
"CSRC",
|
||||||
|
"CSRCI",
|
||||||
"CSRLI",
|
"CSRLI",
|
||||||
|
"CSRR",
|
||||||
"CSRRC",
|
"CSRRC",
|
||||||
"CSRRCI",
|
"CSRRCI",
|
||||||
"CSRRS",
|
"CSRRS",
|
||||||
"CSRRSI",
|
"CSRRSI",
|
||||||
"CSRRW",
|
"CSRRW",
|
||||||
"CSRRWI",
|
"CSRRWI",
|
||||||
|
"CSRS",
|
||||||
|
"CSRSI",
|
||||||
|
"CSRW",
|
||||||
|
"CSRWI",
|
||||||
"CSUB",
|
"CSUB",
|
||||||
"CSUBW",
|
"CSUBW",
|
||||||
"CSW",
|
"CSW",
|
||||||
@@ -259,6 +269,7 @@ var riscvGeneratedInstrs = []string{
|
|||||||
"ORCB",
|
"ORCB",
|
||||||
"ORI",
|
"ORI",
|
||||||
"ORN",
|
"ORN",
|
||||||
|
"PAUSE",
|
||||||
"RDCYCLE",
|
"RDCYCLE",
|
||||||
"RDINSTRET",
|
"RDINSTRET",
|
||||||
"RDTIME",
|
"RDTIME",
|
||||||
@@ -322,6 +333,8 @@ var riscvGeneratedInstrs = []string{
|
|||||||
"VADDVI",
|
"VADDVI",
|
||||||
"VADDVV",
|
"VADDVV",
|
||||||
"VADDVX",
|
"VADDVX",
|
||||||
|
"VANDNVV",
|
||||||
|
"VANDNVX",
|
||||||
"VANDVI",
|
"VANDVI",
|
||||||
"VANDVV",
|
"VANDVV",
|
||||||
"VANDVX",
|
"VANDVX",
|
||||||
@@ -329,8 +342,17 @@ var riscvGeneratedInstrs = []string{
|
|||||||
"VASUBUVX",
|
"VASUBUVX",
|
||||||
"VASUBVV",
|
"VASUBVV",
|
||||||
"VASUBVX",
|
"VASUBVX",
|
||||||
|
"VBREV8V",
|
||||||
|
"VBREVV",
|
||||||
|
"VCLMULHVV",
|
||||||
|
"VCLMULHVX",
|
||||||
|
"VCLMULVV",
|
||||||
|
"VCLMULVX",
|
||||||
|
"VCLZV",
|
||||||
"VCOMPRESSVM",
|
"VCOMPRESSVM",
|
||||||
"VCPOPM",
|
"VCPOPM",
|
||||||
|
"VCPOPV",
|
||||||
|
"VCTZV",
|
||||||
"VDIVUVV",
|
"VDIVUVV",
|
||||||
"VDIVUVX",
|
"VDIVUVX",
|
||||||
"VDIVVV",
|
"VDIVVV",
|
||||||
@@ -743,10 +765,16 @@ var riscvGeneratedInstrs = []string{
|
|||||||
"VREMUVX",
|
"VREMUVX",
|
||||||
"VREMVV",
|
"VREMVV",
|
||||||
"VREMVX",
|
"VREMVX",
|
||||||
|
"VREV8V",
|
||||||
"VRGATHEREI16VV",
|
"VRGATHEREI16VV",
|
||||||
"VRGATHERVI",
|
"VRGATHERVI",
|
||||||
"VRGATHERVV",
|
"VRGATHERVV",
|
||||||
"VRGATHERVX",
|
"VRGATHERVX",
|
||||||
|
"VROLVV",
|
||||||
|
"VROLVX",
|
||||||
|
"VRORVI",
|
||||||
|
"VRORVV",
|
||||||
|
"VRORVX",
|
||||||
"VRSUBVI",
|
"VRSUBVI",
|
||||||
"VRSUBVX",
|
"VRSUBVX",
|
||||||
"VS1RV",
|
"VS1RV",
|
||||||
@@ -950,6 +978,9 @@ var riscvGeneratedInstrs = []string{
|
|||||||
"VWMULVX",
|
"VWMULVX",
|
||||||
"VWREDSUMUVS",
|
"VWREDSUMUVS",
|
||||||
"VWREDSUMVS",
|
"VWREDSUMVS",
|
||||||
|
"VWSLLVI",
|
||||||
|
"VWSLLVV",
|
||||||
|
"VWSLLVX",
|
||||||
"VWSUBUVV",
|
"VWSUBUVV",
|
||||||
"VWSUBUVX",
|
"VWSUBUVX",
|
||||||
"VWSUBUWV",
|
"VWSUBUWV",
|
||||||
|
|||||||
@@ -637,6 +637,20 @@ func encodeARM64Branch(mnem string, ops []*ast.Operand, pc int, offsets map[stri
|
|||||||
return a64wordLE(a64UncondBranch(opc, uint32(rn), 0)), nil
|
return a64wordLE(a64UncondBranch(opc, uint32(rn), 0)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The bare spelling BL R9 is the same indirect branch: the parser reads
|
||||||
|
// a bare identifier as a symbol, and one named for a register is an
|
||||||
|
// indirect branch through it, which the toolchain accepts alongside the
|
||||||
|
// parenthesised form (BL (R3) and BL R3 both encode BLR R3).
|
||||||
|
if op.Addr.Sym != nil && op.Addr.Sym.Pseudo == "" && op.Addr.Base == "" && op.Addr.Index == "" {
|
||||||
|
if rn := arm64RegNum(op.Addr.Sym.Name); rn >= 0 {
|
||||||
|
opc := uint32(0) // BR
|
||||||
|
if link {
|
||||||
|
opc = 1 // BLR
|
||||||
|
}
|
||||||
|
return a64wordLE(a64UncondBranch(opc, uint32(rn), 0)), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Symbol reference: BL sym(SB), or B sym(SB) for a tail call, against a
|
// Symbol reference: BL sym(SB), or B sym(SB) for a tail call, against a
|
||||||
// relocation (R_CALLARM64 either way).
|
// relocation (R_CALLARM64 either way).
|
||||||
if op.Addr.Sym != nil && op.Addr.Sym.Pseudo == "SB" {
|
if op.Addr.Sym != nil && op.Addr.Sym.Pseudo == "SB" {
|
||||||
@@ -1454,6 +1468,15 @@ func encodeARM64Mov(instr *ast.Instr, mnem string, wb string, fi arm64FrameInfo,
|
|||||||
}
|
}
|
||||||
return encodeARM64SBAddr(src.Imm.Sym, rd, relocs), nil
|
return encodeARM64SBAddr(src.Imm.Sym, rd, relocs), nil
|
||||||
}
|
}
|
||||||
|
// Immediate → memory: only storing zero is encodable (the ZR
|
||||||
|
// register); the toolchain rejects any other immediate-to-memory
|
||||||
|
// combination ("illegal combination").
|
||||||
|
if isMemOperand(dst) {
|
||||||
|
if arm64Imm64(src) != 0 {
|
||||||
|
return nil, fmt.Errorf("%s: illegal combination: an immediate store must be zero", mnem)
|
||||||
|
}
|
||||||
|
return encodeARM64MemOp(mnem, dst, 31, false, fi, "")
|
||||||
|
}
|
||||||
rd := arm64RegNum(operandRegName(dst))
|
rd := arm64RegNum(operandRegName(dst))
|
||||||
if rd < 0 {
|
if rd < 0 {
|
||||||
return nil, fmt.Errorf("%s $imm: invalid destination register", mnem)
|
return nil, fmt.Errorf("%s $imm: invalid destination register", mnem)
|
||||||
|
|||||||
+348
-15
@@ -38,10 +38,25 @@ func Assemble(t *ast.Text) ([]byte, map[string]int, error) {
|
|||||||
// rejects SB operands outright (single-function assembly cannot resolve
|
// rejects SB operands outright (single-function assembly cannot resolve
|
||||||
// them). When allowExternal is set, a reference to a symbol no GLOBL in the
|
// them). When allowExternal is set, a reference to a symbol no GLOBL in the
|
||||||
// file defines is recorded as an external relocation instead of failing
|
// file defines is recorded as an external relocation instead of failing
|
||||||
// the object-file emitters resolve it at link time.
|
// the object-file emitters resolve it at link time. goos selects the TLS
|
||||||
|
// access form: the empty default behaves as linux.
|
||||||
type linkInfo struct {
|
type linkInfo struct {
|
||||||
symbols map[string]bool
|
symbols map[string]bool
|
||||||
allowExternal bool
|
allowExternal bool
|
||||||
|
goos string
|
||||||
|
}
|
||||||
|
|
||||||
|
// tlsOneInsn reports the one-instruction TLS form, obj6.go's
|
||||||
|
// CanUse1InsnTLS for the GOOS gasm supports: the bare TLS load nops out and
|
||||||
|
// the (TLS*1) index folds to a segment-absolute access. Windows and plan9
|
||||||
|
// keep the two-instruction form; shared linux does too, which gasm's raw
|
||||||
|
// path does not model and therefore does not select.
|
||||||
|
func (l *linkInfo) tlsOneInsn() bool {
|
||||||
|
switch l.goos {
|
||||||
|
case "", "linux", "freebsd":
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// sbPatch is a function-relative static-symbol relocation: the disp32 field
|
// sbPatch is a function-relative static-symbol relocation: the disp32 field
|
||||||
@@ -86,6 +101,10 @@ func assemble(t *ast.Text, link *linkInfo) ([]byte, []sbPatch, map[string]int, [
|
|||||||
// outgrows the short form.
|
// outgrows the short form.
|
||||||
long := make([]bool, len(t.Body))
|
long := make([]bool, len(t.Body))
|
||||||
sizes := make([]int, len(t.Body))
|
sizes := make([]int, len(t.Body))
|
||||||
|
numTargets := make([]int, len(t.Body))
|
||||||
|
for i := range numTargets {
|
||||||
|
numTargets[i] = -1
|
||||||
|
}
|
||||||
offsets := map[string]int{}
|
offsets := map[string]int{}
|
||||||
pcs := make([]int, len(t.Body))
|
pcs := make([]int, len(t.Body))
|
||||||
var guardJBlong, guardJBElong, moreJMPlong bool
|
var guardJBlong, guardJBElong, moreJMPlong bool
|
||||||
@@ -94,23 +113,106 @@ func assemble(t *ast.Text, link *linkInfo) ([]byte, []sbPatch, map[string]int, [
|
|||||||
for {
|
for {
|
||||||
guard := fi.guardLen(guardJBlong, guardJBElong)
|
guard := fi.guardLen(guardJBlong, guardJBElong)
|
||||||
pos := guard + len(fi.prologue)
|
pos := guard + len(fi.prologue)
|
||||||
|
for i := range numTargets {
|
||||||
|
numTargets[i] = -1
|
||||||
|
}
|
||||||
|
idxAtPc := map[int]int{}
|
||||||
for i, stmt := range t.Body {
|
for i, stmt := range t.Body {
|
||||||
switch s := stmt.(type) {
|
switch s := stmt.(type) {
|
||||||
case *ast.Label:
|
case *ast.Label:
|
||||||
offsets[s.Name.Text] = pos
|
offsets[s.Name.Text] = pos
|
||||||
case *ast.Instr:
|
case *ast.Instr:
|
||||||
|
if strings.ToUpper(s.Mnemonic.Text) == "PCALIGN" {
|
||||||
|
// The alignment pseudo-statement: its size is the
|
||||||
|
// padding to the next boundary at this very position,
|
||||||
|
// filled with NOPs at emission.
|
||||||
|
pad, err := pcAlignPad(pcAlignValue(s), pos)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, nil, nil, nil, fmt.Errorf("PCALIGN: %w", err)
|
||||||
|
}
|
||||||
|
sizes[i] = pad
|
||||||
|
pcs[i] = pos
|
||||||
|
pos += pad
|
||||||
|
continue
|
||||||
|
}
|
||||||
sz, err := instrSize(s, fi, long[i], link)
|
sz, err := instrSize(s, fi, long[i], link)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, nil, nil, nil, fmt.Errorf("%s: %w", s.Mnemonic.Text, err)
|
return nil, nil, nil, nil, nil, nil, fmt.Errorf("%s: %w", s.Mnemonic.Text, err)
|
||||||
}
|
}
|
||||||
sizes[i] = sz
|
sizes[i] = sz
|
||||||
pcs[i] = pos
|
pcs[i] = pos
|
||||||
|
idxAtPc[pos] = i
|
||||||
pos += sz
|
pos += sz
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
bodyLen := pos - (guard + len(fi.prologue))
|
bodyLen := pos - (guard + len(fi.prologue))
|
||||||
// Expand any short jump whose displacement no longer fits rel8.
|
// Expand any short jump whose displacement no longer fits rel8.
|
||||||
changed := false
|
changed := false
|
||||||
|
// Numeric ±N(PC) jumps resolve against this iteration's layout; the
|
||||||
|
// emission pass reads the same table after the loop converges. A
|
||||||
|
// target that is itself an unconditional local JMP is chased to the
|
||||||
|
// ultimate target: the toolchain's brloop pass collapses branch-to-
|
||||||
|
// branch chains before it encodes, so matching its bytes requires
|
||||||
|
// the same redirection.
|
||||||
|
for i := range numTargets {
|
||||||
|
numTargets[i] = -1
|
||||||
|
}
|
||||||
|
for i, stmt := range t.Body {
|
||||||
|
s, ok := stmt.(*ast.Instr)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if len(s.Operands) == 1 {
|
||||||
|
if n, isNum := pcJumpOffset(s.Operands[0]); isNum {
|
||||||
|
if target, okT := pcJumpTarget(t, i, n, pcs); okT {
|
||||||
|
numTargets[i] = target
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i := range numTargets {
|
||||||
|
if numTargets[i] < 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
tgt := numTargets[i]
|
||||||
|
for hop := 0; hop < len(t.Body); hop++ {
|
||||||
|
idx, ok := idxAtPc[tgt]
|
||||||
|
if !ok {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
in, ok := t.Body[idx].(*ast.Instr)
|
||||||
|
if !ok || strings.ToUpper(in.Mnemonic.Text) != "JMP" || len(in.Operands) != 1 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if name, isLabel := labelName(in.Operands[0]); isLabel {
|
||||||
|
tgt = offsets[resolve(name)]
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if n, isNum := pcJumpOffset(in.Operands[0]); isNum {
|
||||||
|
next, okT := pcJumpTarget(t, idx, n, pcs)
|
||||||
|
if !okT {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
tgt = next
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
break // JMP through a register or memory: the chain ends
|
||||||
|
}
|
||||||
|
numTargets[i] = tgt
|
||||||
|
}
|
||||||
|
for i, stmt := range t.Body {
|
||||||
|
s, ok := stmt.(*ast.Instr)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if numTargets[i] >= 0 && !long[i] {
|
||||||
|
rel := int64(numTargets[i] - (pcs[i] + jumpSize(strings.ToUpper(s.Mnemonic.Text), false)))
|
||||||
|
if !fits8(rel) {
|
||||||
|
long[i] = true
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
for i, stmt := range t.Body {
|
for i, stmt := range t.Body {
|
||||||
s, ok := stmt.(*ast.Instr)
|
s, ok := stmt.(*ast.Instr)
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -232,7 +334,7 @@ func assemble(t *ast.Text, link *linkInfo) ([]byte, []sbPatch, map[string]int, [
|
|||||||
spadjStep{pos + epi, 0},
|
spadjStep{pos + epi, 0},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
code, ps, pool, err := encodeInstr(s, pos, offsets, fi, long[i], resolve, link)
|
code, ps, pool, err := encodeInstr(s, pos, offsets, fi, long[i], resolve, link, numTargets[i])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, nil, nil, nil, fmt.Errorf("%s: %w", s.Mnemonic.Text, err)
|
return nil, nil, nil, nil, nil, nil, fmt.Errorf("%s: %w", s.Mnemonic.Text, err)
|
||||||
}
|
}
|
||||||
@@ -428,6 +530,83 @@ func computeFrame(t *ast.Text) frameInfo {
|
|||||||
return fi
|
return fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// pcJumpOffset recognises the numeric relative jump operand ±N(PC) and
|
||||||
|
// returns N: the toolchain counts instructions, not bytes, so +2(PC) targets
|
||||||
|
// the second instruction boundary after the branch.
|
||||||
|
func pcJumpOffset(op *ast.Operand) (int, bool) {
|
||||||
|
if op.Kind != ast.OpAddr || op.Addr.Base != "PC" {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
return int(op.Addr.Offset), true
|
||||||
|
}
|
||||||
|
|
||||||
|
// pcJumpTarget resolves a numeric jump at statement index j: N counts the
|
||||||
|
// instruction statements after the jump itself (N = 0 is the jump's own
|
||||||
|
// address, the classic park loop), and the target is the start of the Nth
|
||||||
|
// one. It reports false when the count runs past the end of the function.
|
||||||
|
func pcJumpTarget(t *ast.Text, j, n int, pcs []int) (int, bool) {
|
||||||
|
if n == 0 {
|
||||||
|
return pcs[j], true
|
||||||
|
}
|
||||||
|
seen := 0
|
||||||
|
for k := j + 1; k < len(t.Body); k++ {
|
||||||
|
if _, ok := t.Body[k].(*ast.Instr); !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
seen++
|
||||||
|
if seen == n {
|
||||||
|
return pcs[k], true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// x86 NOP encodings, single-instruction no-ops of lengths 1 to 9 (the
|
||||||
|
// toolchain's asm6.go nop table); longer padding repeats the largest that
|
||||||
|
// fits, greedy from the end.
|
||||||
|
var x86Nops = [][]byte{
|
||||||
|
{0x90},
|
||||||
|
{0x66, 0x90},
|
||||||
|
{0x0F, 0x1F, 0x00},
|
||||||
|
{0x0F, 0x1F, 0x40, 0x00},
|
||||||
|
{0x0F, 0x1F, 0x44, 0x00, 0x00},
|
||||||
|
{0x66, 0x0F, 0x1F, 0x44, 0x00, 0x00},
|
||||||
|
{0x0F, 0x1F, 0x80, 0x00, 0x00, 0x00, 0x00},
|
||||||
|
{0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00},
|
||||||
|
{0x66, 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00},
|
||||||
|
}
|
||||||
|
|
||||||
|
// fillNOPs fills p with the greedy largest single-instruction NOPs, exactly
|
||||||
|
// the toolchain's fillnop.
|
||||||
|
func fillNOPs(p []byte) {
|
||||||
|
for len(p) > 0 {
|
||||||
|
m := min(len(p), len(x86Nops))
|
||||||
|
copy(p[:m], x86Nops[m-1])
|
||||||
|
p = p[m:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// pcAlignPad computes the padding PCALIGN $align inserts at pos: the
|
||||||
|
// alignment must be a power of two in [8, 2048] and the padding runs to the
|
||||||
|
// next boundary (zero when the position is already aligned).
|
||||||
|
func pcAlignPad(align, pos int) (int, error) {
|
||||||
|
if align <= 0 || align&(align-1) != 0 || align < 8 || align > 2048 {
|
||||||
|
return 0, fmt.Errorf("alignment value of an instruction must be a power of two and in the range [8, 2048], got %d", align)
|
||||||
|
}
|
||||||
|
if lob := pos & (align - 1); lob != 0 {
|
||||||
|
return align - lob, nil
|
||||||
|
}
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// pcAlignValue reads a PCALIGN statement's alignment operand.
|
||||||
|
func pcAlignValue(s *ast.Instr) int {
|
||||||
|
if len(s.Operands) == 1 && s.Operands[0].Kind == ast.OpImmediate && s.Operands[0].Imm.HasVal {
|
||||||
|
return int(s.Operands[0].Imm.Val)
|
||||||
|
}
|
||||||
|
return 0 // rejected by pcAlignPad's range check
|
||||||
|
}
|
||||||
|
|
||||||
// hasCall reports whether the function body contains a CALL instruction.
|
// hasCall reports whether the function body contains a CALL instruction.
|
||||||
func hasCall(t *ast.Text) bool {
|
func hasCall(t *ast.Text) bool {
|
||||||
for _, stmt := range t.Body {
|
for _, stmt := range t.Body {
|
||||||
@@ -602,7 +781,7 @@ func instrSize(s *ast.Instr, fi frameInfo, long bool, link *linkInfo) (int, erro
|
|||||||
}
|
}
|
||||||
return jumpSize(mnem, long), nil
|
return jumpSize(mnem, long), nil
|
||||||
}
|
}
|
||||||
code, _, _, err := encodeInstr(s, 0, nil, fi, false, nil, link)
|
code, _, _, err := encodeInstr(s, 0, nil, fi, false, nil, link, -1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
@@ -637,9 +816,21 @@ func jumpSize(mnem string, long bool) int {
|
|||||||
// (relative to pc, the instruction's own offset). A RET in a frame-pointer
|
// (relative to pc, the instruction's own offset). A RET in a frame-pointer
|
||||||
// function is prefixed with the epilogue. resolve, when non-nil, redirects a
|
// function is prefixed with the epilogue. resolve, when non-nil, redirects a
|
||||||
// jump label through the jump-to-jump chain before the offset lookup.
|
// jump label through the jump-to-jump chain before the offset lookup.
|
||||||
func encodeInstr(s *ast.Instr, pc int, offsets map[string]int, fi frameInfo, long bool, resolve func(string) string, link *linkInfo) ([]byte, []sbPatch, []floatPoolEntry, error) {
|
func encodeInstr(s *ast.Instr, pc int, offsets map[string]int, fi frameInfo, long bool, resolve func(string) string, link *linkInfo, numTarget int) ([]byte, []sbPatch, []floatPoolEntry, error) {
|
||||||
mnem := strings.ToUpper(s.Mnemonic.Text)
|
mnem := strings.ToUpper(s.Mnemonic.Text)
|
||||||
|
|
||||||
|
if mnem == "PCALIGN" {
|
||||||
|
// The layout pass already accounted the padding; emit the same
|
||||||
|
// amount of NOP bytes for the statement's own position.
|
||||||
|
pad, err := pcAlignPad(pcAlignValue(s), pc)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, err
|
||||||
|
}
|
||||||
|
out := make([]byte, pad)
|
||||||
|
fillNOPs(out)
|
||||||
|
return out, nil, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
var prefix []byte
|
var prefix []byte
|
||||||
if mnem == "RET" && fi.useFP {
|
if mnem == "RET" && fi.useFP {
|
||||||
prefix = fi.epilogue
|
prefix = fi.epilogue
|
||||||
@@ -677,7 +868,7 @@ func encodeInstr(s *ast.Instr, pc int, offsets map[string]int, fi frameInfo, lon
|
|||||||
}
|
}
|
||||||
return append(prefix, code...), nil, nil, nil
|
return append(prefix, code...), nil, nil, nil
|
||||||
}
|
}
|
||||||
code, err = encodeJump(s, mnem, pc+len(prefix), offsets, long, resolve)
|
code, err = encodeJump(s, mnem, pc+len(prefix), offsets, long, resolve, numTarget)
|
||||||
} else {
|
} else {
|
||||||
code, ps, pool, err = encodeNormal(s, fi, link)
|
code, ps, pool, err = encodeNormal(s, fi, link)
|
||||||
}
|
}
|
||||||
@@ -703,6 +894,41 @@ func encodeNormal(s *ast.Instr, fi frameInfo, link *linkInfo) ([]byte, []sbPatch
|
|||||||
}
|
}
|
||||||
return code, nil, nil, nil
|
return code, nil, nil, nil
|
||||||
}
|
}
|
||||||
|
// MOVQ $sym±off(SB), r64: the toolchain assembles a symbol immediate as
|
||||||
|
// LEAQ disp32(RIP), r64 with an R_PCREL relocation at the disp32 field,
|
||||||
|
// never as a 64-bit absolute immediate (verified against go tool asm).
|
||||||
|
// MOVD is the MOVQ alias; the narrower widths reject the form outright.
|
||||||
|
if (mnemUpper == "MOVQ" || mnemUpper == "MOVD") && len(s.Operands) == 2 &&
|
||||||
|
s.Operands[0].Kind == ast.OpImmediate && s.Operands[0].Imm.Sym != nil &&
|
||||||
|
s.Operands[0].Imm.Sym.Pseudo == "SB" {
|
||||||
|
mem := &ast.Operand{Kind: ast.OpAddr, Addr: ast.Address{Sym: s.Operands[0].Imm.Sym}}
|
||||||
|
src, err := operandFromAST(mnemUpper, mem, 8, fi, link)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, err
|
||||||
|
}
|
||||||
|
dst, err := operandFromAST(mnemUpper, s.Operands[1], 8, fi, link)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, err
|
||||||
|
}
|
||||||
|
e := &enc{}
|
||||||
|
if err := e.encodeLea([]Operand{src, dst}, 8); err != nil {
|
||||||
|
return nil, nil, nil, err
|
||||||
|
}
|
||||||
|
ps := make([]sbPatch, len(e.patches))
|
||||||
|
for i, p := range e.patches {
|
||||||
|
ps[i] = sbPatch{off: p.off, name: p.name, addend: p.addend}
|
||||||
|
}
|
||||||
|
return e.out, ps, nil, nil
|
||||||
|
}
|
||||||
|
// MOVQ/MOVL TLS, r: the bare TLS load. The toolchain's progedit nops
|
||||||
|
// it out on the one-instruction TLS systems (linux and freebsd, not
|
||||||
|
// shared) and encodes the segment-prefixed load elsewhere; get_tls(r),
|
||||||
|
// the macro GOROOT's go_tls.h defines, expands to exactly this
|
||||||
|
// statement, and the toolchain's pairing pass removes it whenever the
|
||||||
|
// following instruction's (TLS*1) index folds.
|
||||||
|
if (mnemUpper == "MOVQ" || mnemUpper == "MOVL") && len(s.Operands) == 2 && isBareTLS(s.Operands[0]) {
|
||||||
|
return encodeTLSBaseLoad(s, fi, link)
|
||||||
|
}
|
||||||
_, size := splitSize(mnemUpper)
|
_, size := splitSize(mnemUpper)
|
||||||
if size == 0 {
|
if size == 0 {
|
||||||
size = 8
|
size = 8
|
||||||
@@ -722,10 +948,67 @@ func encodeNormal(s *ast.Instr, fi frameInfo, link *linkInfo) ([]byte, []sbPatch
|
|||||||
ps := make([]sbPatch, len(e.patches))
|
ps := make([]sbPatch, len(e.patches))
|
||||||
for i, p := range e.patches {
|
for i, p := range e.patches {
|
||||||
ps[i] = sbPatch{off: p.off, name: p.name, addend: p.addend}
|
ps[i] = sbPatch{off: p.off, name: p.name, addend: p.addend}
|
||||||
|
if p.tls {
|
||||||
|
ps[i].kind = RelTLSLE
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return e.out, ps, e.floatPoolList(), nil
|
return e.out, ps, e.floatPoolList(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isBareTLS reports whether the operand is the bare TLS pseudo-register
|
||||||
|
// load source, the expansion of go_tls.h's get_tls(r) macro.
|
||||||
|
func isBareTLS(op *ast.Operand) bool {
|
||||||
|
return op.Kind == ast.OpAddr && op.Addr.Sym != nil &&
|
||||||
|
op.Addr.Sym.Pseudo == "" && op.Addr.Sym.Name == "TLS" &&
|
||||||
|
op.Addr.Base == "" && op.Addr.Index == ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// encodeTLSBaseLoad assembles MOVQ/MOVL TLS, r. On the one-instruction TLS
|
||||||
|
// systems (linux and freebsd outside -shared, obj6.go's CanUse1InsnTLS) the
|
||||||
|
// statement nops out: the following (TLS*1) access folds to a direct
|
||||||
|
// segment-absolute load. The two-instruction systems keep the segment load,
|
||||||
|
// nine bytes with the R_TLSLE patch site at the disp32.
|
||||||
|
func encodeTLSBaseLoad(s *ast.Instr, fi frameInfo, link *linkInfo) ([]byte, []sbPatch, []floatPoolEntry, error) {
|
||||||
|
_, size := splitSize(strings.ToUpper(s.Mnemonic.Text))
|
||||||
|
if size == 0 {
|
||||||
|
size = 8
|
||||||
|
}
|
||||||
|
dst, err := operandFromAST("MOVQ", s.Operands[1], 8, fi, link)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, err
|
||||||
|
}
|
||||||
|
reg, ok := dst.(Reg)
|
||||||
|
if !ok || reg.isVec() {
|
||||||
|
return nil, nil, nil, fmt.Errorf("TLS: destination must be a general register")
|
||||||
|
}
|
||||||
|
if link == nil || link.tlsOneInsn() {
|
||||||
|
return nil, nil, nil, nil // noped out
|
||||||
|
}
|
||||||
|
seg := byte(0x64) // FS
|
||||||
|
if link.goos == "windows" {
|
||||||
|
seg = 0x65 // GS
|
||||||
|
}
|
||||||
|
e := &enc{}
|
||||||
|
i := &instr{
|
||||||
|
prefix: seg,
|
||||||
|
rexW: size == 8,
|
||||||
|
rexR: reg.idx >= 8,
|
||||||
|
opcode: []byte{0x8B},
|
||||||
|
modrm: 0x04 | (reg.idx&7)<<3,
|
||||||
|
sib: 0x25,
|
||||||
|
disp: le32(0),
|
||||||
|
tls: true,
|
||||||
|
}
|
||||||
|
if err := e.emit(i); err != nil {
|
||||||
|
return nil, nil, nil, err
|
||||||
|
}
|
||||||
|
ps := make([]sbPatch, len(e.patches))
|
||||||
|
for i, p := range e.patches {
|
||||||
|
ps[i] = sbPatch{off: p.off, name: p.name, addend: p.addend, kind: RelTLSLE}
|
||||||
|
}
|
||||||
|
return e.out, ps, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
// encodeBookkeeping accepts-and-ignores FUNCDATA and PCDATA at the statement
|
// encodeBookkeeping accepts-and-ignores FUNCDATA and PCDATA at the statement
|
||||||
// level, before operand conversion: the toolchain's shapes are FUNCDATA
|
// level, before operand conversion: the toolchain's shapes are FUNCDATA
|
||||||
// $n, sym(SB) and PCDATA $n, $m, and neither contributes a byte to the
|
// $n, sym(SB) and PCDATA $n, $m, and neither contributes a byte to the
|
||||||
@@ -753,21 +1036,29 @@ func encodeBookkeeping(upper string, s *ast.Instr) ([]byte, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// encodeJump encodes a JMP/CALL/Jcc with a relative offset resolved from the
|
// encodeJump encodes a JMP/CALL/Jcc with a relative offset resolved from the
|
||||||
// target label, in the short (rel8) or long (rel32) form.
|
// target label or from a numeric ±N(PC) instruction count, in the short
|
||||||
func encodeJump(s *ast.Instr, mnem string, pc int, offsets map[string]int, long bool, resolve func(string) string) ([]byte, error) {
|
// (rel8) or long (rel32) form. numTarget is the resolved byte offset of a
|
||||||
|
// numeric operand, negative when the operand is not one.
|
||||||
|
func encodeJump(s *ast.Instr, mnem string, pc int, offsets map[string]int, long bool, resolve func(string) string, numTarget int) ([]byte, error) {
|
||||||
if len(s.Operands) != 1 {
|
if len(s.Operands) != 1 {
|
||||||
return nil, fmt.Errorf("jump expects 1 operand, got %d", len(s.Operands))
|
return nil, fmt.Errorf("jump expects 1 operand, got %d", len(s.Operands))
|
||||||
}
|
}
|
||||||
name, ok := labelName(s.Operands[0])
|
name, isLabel := labelName(s.Operands[0])
|
||||||
if !ok {
|
if !isLabel && numTarget < 0 {
|
||||||
return nil, fmt.Errorf("jump target must be a local label")
|
return nil, fmt.Errorf("jump target must be a local label")
|
||||||
}
|
}
|
||||||
if resolve != nil && mnem != "CALL" {
|
var target int
|
||||||
name = resolve(name)
|
if isLabel {
|
||||||
}
|
if resolve != nil && mnem != "CALL" {
|
||||||
target, ok := offsets[name]
|
name = resolve(name)
|
||||||
if !ok {
|
}
|
||||||
return nil, fmt.Errorf("undefined label %q", name)
|
t, ok := offsets[name]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("undefined label %q", name)
|
||||||
|
}
|
||||||
|
target = t
|
||||||
|
} else {
|
||||||
|
target = numTarget
|
||||||
}
|
}
|
||||||
rel := int64(target - (pc + jumpSize(mnem, long)))
|
rel := int64(target - (pc + jumpSize(mnem, long)))
|
||||||
|
|
||||||
@@ -841,6 +1132,11 @@ func indirectJumpTarget(s *ast.Instr) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
a := s.Operands[0].Addr
|
a := s.Operands[0].Addr
|
||||||
|
// ±N(PC) is the numeric relative form, the PC counts instructions from
|
||||||
|
// the branch: relative, not indirect.
|
||||||
|
if a.Base == "PC" || a.Index == "PC" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
if a.Base != "" || a.Index != "" {
|
if a.Base != "" || a.Index != "" {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -958,12 +1254,44 @@ func operandFromAST(mnemUpper string, op *ast.Operand, size int, fi frameInfo, l
|
|||||||
|
|
||||||
// Memory with a real base register: (base), off(base), (base)(index*scale).
|
// Memory with a real base register: (base), off(base), (base)(index*scale).
|
||||||
if a.Base != "" {
|
if a.Base != "" {
|
||||||
|
// Segment-absolute: 0x30(GS) and 0x28(FS), the windows TLS
|
||||||
|
// spellings. The segment override prefixes a disp32 absolute
|
||||||
|
// reference with no relocation.
|
||||||
|
if a.Base == "GS" || a.Base == "FS" {
|
||||||
|
seg := byte(0x64)
|
||||||
|
if a.Base == "GS" {
|
||||||
|
seg = 0x65
|
||||||
|
}
|
||||||
|
return SegAbs{Disp: a.Offset, Size: size, Seg: seg}, nil
|
||||||
|
}
|
||||||
base, ok := ParseReg(a.Base)
|
base, ok := ParseReg(a.Base)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("unknown base register %q", a.Base)
|
return nil, fmt.Errorf("unknown base register %q", a.Base)
|
||||||
}
|
}
|
||||||
m := Mem{Base: base, Disp: a.Offset, HasBase: true, Size: size}
|
m := Mem{Base: base, Disp: a.Offset, HasBase: true, Size: size}
|
||||||
if a.Index != "" {
|
if a.Index != "" {
|
||||||
|
if a.Index == "TLS" {
|
||||||
|
// off(base)(TLS*1): the thread-local annotation. The
|
||||||
|
// one-instruction TLS form folds it to off(TLS), the
|
||||||
|
// segment-prefixed absolute whose disp32 carries an
|
||||||
|
// R_TLS_LE patch site; the base register disappears
|
||||||
|
// from the encoding, exactly as the toolchain's
|
||||||
|
// progedit rewrites the address.
|
||||||
|
seg := byte(0x64) // FS on linux, freebsd, plan9
|
||||||
|
if link != nil && link.goos == "windows" {
|
||||||
|
seg = 0x65 // GS
|
||||||
|
}
|
||||||
|
return TLSMem{Disp: a.Offset, Size: size, Seg: seg}, nil
|
||||||
|
}
|
||||||
|
if a.Index == "GS" || a.Index == "FS" {
|
||||||
|
// 0(CX)(GS): the segment annotation rides the base
|
||||||
|
// access as the override prefix.
|
||||||
|
m.Seg = 0x64
|
||||||
|
if a.Index == "GS" {
|
||||||
|
m.Seg = 0x65
|
||||||
|
}
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
idx, ok := ParseReg(a.Index)
|
idx, ok := ParseReg(a.Index)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("unknown index register %q", a.Index)
|
return nil, fmt.Errorf("unknown index register %q", a.Index)
|
||||||
@@ -984,6 +1312,11 @@ func operandFromAST(mnemUpper string, op *ast.Operand, size int, fi frameInfo, l
|
|||||||
}
|
}
|
||||||
return Mem{Index: idx, Scale: a.Scale, Disp: a.Offset, HasIndex: true, Size: size}, nil
|
return Mem{Index: idx, Scale: a.Scale, Disp: a.Offset, HasIndex: true, Size: size}, nil
|
||||||
}
|
}
|
||||||
|
// A bare displacement with no base: the absolute address form,
|
||||||
|
// MOVL $0xf1, 0xf1. No segment and no relocation.
|
||||||
|
if a.Sym == nil && a.Base == "" && a.Index == "" && a.HasOff {
|
||||||
|
return SegAbs{Disp: a.Offset, Size: size}, nil
|
||||||
|
}
|
||||||
// Bare register.
|
// Bare register.
|
||||||
if a.Sym != nil && a.Sym.Pseudo == "" && a.Sym.Name != "" {
|
if a.Sym != nil && a.Sym.Pseudo == "" && a.Sym.Name != "" {
|
||||||
if r, ok := ParseReg(a.Sym.Name); ok {
|
if r, ok := ParseReg(a.Sym.Name); ok {
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ type encPatch struct {
|
|||||||
off int
|
off int
|
||||||
name string
|
name string
|
||||||
addend int64
|
addend int64
|
||||||
|
tls bool // a TLS slot offset: the patch is R_TLSLE with no symbol
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *enc) encode(mnem string, ops []Operand) error {
|
func (e *enc) encode(mnem string, ops []Operand) error {
|
||||||
@@ -578,6 +579,7 @@ type instr struct {
|
|||||||
disp []byte
|
disp []byte
|
||||||
imm []byte
|
imm []byte
|
||||||
sb *sbRef // static-symbol displacement in disp, awaiting resolution
|
sb *sbRef // static-symbol displacement in disp, awaiting resolution
|
||||||
|
tls bool // the displacement is a TLS slot offset, patched R_TLSLE
|
||||||
}
|
}
|
||||||
|
|
||||||
// sbRef records that an instruction's displacement refers to a static symbol
|
// sbRef records that an instruction's displacement refers to a static symbol
|
||||||
@@ -620,6 +622,9 @@ func (e *enc) emit(i *instr) error {
|
|||||||
if i.sb != nil {
|
if i.sb != nil {
|
||||||
e.patches = append(e.patches, encPatch{off: len(e.out), name: i.sb.name, addend: i.sb.addend})
|
e.patches = append(e.patches, encPatch{off: len(e.out), name: i.sb.name, addend: i.sb.addend})
|
||||||
}
|
}
|
||||||
|
if i.tls {
|
||||||
|
e.patches = append(e.patches, encPatch{off: len(e.out), tls: true})
|
||||||
|
}
|
||||||
e.out = append(e.out, i.disp...)
|
e.out = append(e.out, i.disp...)
|
||||||
e.out = append(e.out, i.imm...)
|
e.out = append(e.out, i.imm...)
|
||||||
return nil
|
return nil
|
||||||
@@ -674,12 +679,30 @@ func setRMReg(i *instr, regField int, rexR, regForced bool, rm Operand, opSize i
|
|||||||
i.disp = le32(0)
|
i.disp = le32(0)
|
||||||
i.sb = &sbRef{name: r.name, addend: r.addend}
|
i.sb = &sbRef{name: r.name, addend: r.addend}
|
||||||
return nil
|
return nil
|
||||||
|
case TLSMem:
|
||||||
|
// off(TLS): the segment-prefixed absolute access, mod=00 with the
|
||||||
|
// SIB escape's disp32 absolute form. The displacement is the TLS
|
||||||
|
// slot offset, patched by the linker's TLS relocation.
|
||||||
|
i.prefix = r.Seg
|
||||||
|
i.modrm = 0x04 | regField<<3
|
||||||
|
i.sib = 0x25
|
||||||
|
i.disp = le32(r.Disp)
|
||||||
|
i.tls = true
|
||||||
|
return nil
|
||||||
|
case SegAbs:
|
||||||
|
// 0x30(GS): the segment override with the SIB escape's disp32
|
||||||
|
// absolute form, no relocation.
|
||||||
|
setSegAbs(i, regField, r)
|
||||||
|
return nil
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("invalid r/m operand %T", rm)
|
return fmt.Errorf("invalid r/m operand %T", rm)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func setMem(i *instr, regField int, m Mem) error {
|
func setMem(i *instr, regField int, m Mem) error {
|
||||||
|
if m.Seg != 0 {
|
||||||
|
i.prefix = m.Seg
|
||||||
|
}
|
||||||
modrm, sib, disp, xBit, bBit, err := memComponents(regField, m)
|
modrm, sib, disp, xBit, bBit, err := memComponents(regField, m)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -692,6 +715,16 @@ func setMem(i *instr, regField int, m Mem) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// setSegAbs assembles a segment-absolute operand, 0x30(GS): the segment
|
||||||
|
// override with the mod=00 SIB escape's disp32 absolute form and no
|
||||||
|
// relocation.
|
||||||
|
func setSegAbs(i *instr, regField int, m SegAbs) {
|
||||||
|
i.prefix = m.Seg
|
||||||
|
i.modrm = 0x04 | regField<<3
|
||||||
|
i.sib = 0x25
|
||||||
|
i.disp = le32(m.Disp)
|
||||||
|
}
|
||||||
|
|
||||||
// memComponents computes the ModR/M byte (with the given reg field), the SIB
|
// memComponents computes the ModR/M byte (with the given reg field), the SIB
|
||||||
// byte (-1 if none), the displacement bytes, and the high index/base bits, for
|
// byte (-1 if none), the displacement bytes, and the high index/base bits, for
|
||||||
// a memory operand. It is shared by the REX (scalar) and VEX (vector) paths.
|
// a memory operand. It is shared by the REX (scalar) and VEX (vector) paths.
|
||||||
|
|||||||
+4
-4
@@ -307,12 +307,12 @@ func TestStackGuardBytesLOONG64(t *testing.T) {
|
|||||||
func TestStackGuardGOObjInternalCall(t *testing.T) {
|
func TestStackGuardGOObjInternalCall(t *testing.T) {
|
||||||
for _, tt := range []struct {
|
for _, tt := range []struct {
|
||||||
src string
|
src string
|
||||||
assemble func(*ast.File) (*Image, error)
|
assemble func(*ast.File, ...AssembleOption) (*Image, error)
|
||||||
}{
|
}{
|
||||||
{"g_amd64.s", AssembleFile},
|
{"g_amd64.s", AssembleFile},
|
||||||
{"g_arm64.s", AssembleFileARM64},
|
{"g_arm64.s", func(f *ast.File, _ ...AssembleOption) (*Image, error) { return AssembleFileARM64(f) }},
|
||||||
{"g_riscv64.s", AssembleFileRISCV},
|
{"g_riscv64.s", func(f *ast.File, _ ...AssembleOption) (*Image, error) { return AssembleFileRISCV(f) }},
|
||||||
{"g_loong64.s", AssembleFileLOONG64},
|
{"g_loong64.s", func(f *ast.File, _ ...AssembleOption) (*Image, error) { return AssembleFileLOONG64(f) }},
|
||||||
} {
|
} {
|
||||||
f, errs := parser.Parse(tt.src, "TEXT \u00b7callsmall(SB), $16-0\n\tCALL \u00b7other(SB)\n\tRET\nTEXT \u00b7other(SB), NOSPLIT, $0\n\tRET\n")
|
f, errs := parser.Parse(tt.src, "TEXT \u00b7callsmall(SB), $16-0\n\tCALL \u00b7other(SB)\n\tRET\nTEXT \u00b7other(SB), NOSPLIT, $0\n\tRET\n")
|
||||||
if len(errs) > 0 {
|
if len(errs) > 0 {
|
||||||
|
|||||||
+74
-21
@@ -192,6 +192,30 @@ func (e *enc) encodeMov(ops []Operand, size int) error {
|
|||||||
}
|
}
|
||||||
return e.emit(i)
|
return e.emit(i)
|
||||||
|
|
||||||
|
case TLSMem:
|
||||||
|
if !dstIsReg {
|
||||||
|
return fmt.Errorf("MOV: two memory operands")
|
||||||
|
}
|
||||||
|
// MOV r, off(TLS): the segment-prefixed absolute load, reg=dst,
|
||||||
|
// rm=src(tlsMem) through the SIB escape; the disp32 is the TLS slot
|
||||||
|
// offset with its R_TLSLE patch site.
|
||||||
|
i := newInstr(size, []byte{movRR(size)})
|
||||||
|
if err := setRM(i, dstReg, src, size); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return e.emit(i)
|
||||||
|
|
||||||
|
case SegAbs:
|
||||||
|
if !dstIsReg {
|
||||||
|
return fmt.Errorf("MOV: two memory operands")
|
||||||
|
}
|
||||||
|
// MOV r, 0x30(GS): the segment-absolute load.
|
||||||
|
i := newInstr(size, []byte{movRR(size)})
|
||||||
|
if err := setRM(i, dstReg, src, size); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return e.emit(i)
|
||||||
|
|
||||||
case Imm:
|
case Imm:
|
||||||
if dstIsReg {
|
if dstIsReg {
|
||||||
v := int64(src)
|
v := int64(src)
|
||||||
@@ -232,11 +256,24 @@ func (e *enc) encodeMov(ops []Operand, size int) error {
|
|||||||
i.imm = imm
|
i.imm = imm
|
||||||
return e.emit(i)
|
return e.emit(i)
|
||||||
}
|
}
|
||||||
// MOV r/m, imm: 0xC6 (8-bit) / 0xC7 /0.
|
// MOV r/m, imm: 0xC6 (8-bit) / 0xC7 /0. An immediate in the
|
||||||
|
// destination slot is the absolute-address crash-store spelling,
|
||||||
|
// MOVL $0xf1, 0xf1: the parser reads the trailing bare constant
|
||||||
|
// as an immediate, and the store's disp32 carries the address.
|
||||||
op := byte(0xC7)
|
op := byte(0xC7)
|
||||||
if size == 1 {
|
if size == 1 {
|
||||||
op = 0xC6
|
op = 0xC6
|
||||||
}
|
}
|
||||||
|
if d, ok := dst.(Imm); ok {
|
||||||
|
i := newInstr(size, []byte{op})
|
||||||
|
setSegAbs(i, 0, SegAbs{Disp: int64(d)})
|
||||||
|
immBytes, err := immediate(int64(src), size, false)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
i.imm = immBytes
|
||||||
|
return e.emit(i)
|
||||||
|
}
|
||||||
i := newInstr(size, []byte{op})
|
i := newInstr(size, []byte{op})
|
||||||
if err := setRMDigit(i, 0, dst, size); err != nil {
|
if err := setRMDigit(i, 0, dst, size); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -627,7 +664,16 @@ func (e *enc) encodeDoubleShift(base string, ops []Operand, size int) error {
|
|||||||
func (e *enc) encodeImul(ops []Operand, size int) error {
|
func (e *enc) encodeImul(ops []Operand, size int) error {
|
||||||
switch len(ops) {
|
switch len(ops) {
|
||||||
case 2:
|
case 2:
|
||||||
// IMUL r, r/m: 0x0F 0xAF.
|
// Two shapes. The leading-immediate spelling IMUL $imm, r multiplies
|
||||||
|
// r in place (dst = rm = r): the shape GOROOT's clock code writes.
|
||||||
|
// Otherwise IMUL r, r/m: 0x0F 0xAF.
|
||||||
|
if imm, ok := ops[0].(Imm); ok {
|
||||||
|
dstReg, isReg := ops[1].(Reg)
|
||||||
|
if !isReg {
|
||||||
|
return fmt.Errorf("IMUL: destination must be a register")
|
||||||
|
}
|
||||||
|
return e.encodeImulImm(imm, dstReg, dstReg, size)
|
||||||
|
}
|
||||||
dstReg, ok := ops[1].(Reg)
|
dstReg, ok := ops[1].(Reg)
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("IMUL: destination must be a register")
|
return fmt.Errorf("IMUL: destination must be a register")
|
||||||
@@ -647,29 +693,36 @@ func (e *enc) encodeImul(ops []Operand, size int) error {
|
|||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("IMUL: immediate operand expected first")
|
return fmt.Errorf("IMUL: immediate operand expected first")
|
||||||
}
|
}
|
||||||
// Plan 9 order: IMUL $imm, src, dst.
|
// Plan 9 order: IMUL $imm, src, dst; the source stays a general
|
||||||
if fits8(int64(imm)) {
|
// r/m operand (setRM takes registers and memory alike).
|
||||||
i := newInstr(size, []byte{0x6B})
|
return e.encodeImulImm(imm, ops[1], dstReg, size)
|
||||||
if err := setRM(i, dstReg, ops[1], size); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
i.imm = []byte{byte(int8(imm))}
|
|
||||||
return e.emit(i)
|
|
||||||
}
|
|
||||||
i := newInstr(size, []byte{0x69})
|
|
||||||
if err := setRM(i, dstReg, ops[1], size); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
immBytes, err := immediate(int64(imm), size, false)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
i.imm = immBytes
|
|
||||||
return e.emit(i)
|
|
||||||
}
|
}
|
||||||
return fmt.Errorf("IMUL expects 2 or 3 operands, got %d", len(ops))
|
return fmt.Errorf("IMUL expects 2 or 3 operands, got %d", len(ops))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// encodeImulImm emits the immediate multiply: 0x6B with a sign-extended imm8
|
||||||
|
// when the value fits, 0x69 with a 32-bit immediate otherwise.
|
||||||
|
func (e *enc) encodeImulImm(imm Imm, rm Operand, dst Reg, size int) error {
|
||||||
|
if fits8(int64(imm)) {
|
||||||
|
i := newInstr(size, []byte{0x6B})
|
||||||
|
if err := setRM(i, dst, rm, size); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
i.imm = []byte{byte(int8(imm))}
|
||||||
|
return e.emit(i)
|
||||||
|
}
|
||||||
|
i := newInstr(size, []byte{0x69})
|
||||||
|
if err := setRM(i, dst, rm, size); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
immBytes, err := immediate(int64(imm), size, false)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
i.imm = immBytes
|
||||||
|
return e.emit(i)
|
||||||
|
}
|
||||||
|
|
||||||
// --- PUSH / POP -------------------------------------------------------------
|
// --- PUSH / POP -------------------------------------------------------------
|
||||||
|
|
||||||
func (e *enc) encodePushPop(ops []Operand, size int, push bool) error {
|
func (e *enc) encodePushPop(ops []Operand, size int, push bool) error {
|
||||||
|
|||||||
@@ -130,6 +130,7 @@ func TestDifferentialKernels(t *testing.T) {
|
|||||||
{filepath.Join("..", "testdata", "verify", "quadreg_amd64.s"), "", false},
|
{filepath.Join("..", "testdata", "verify", "quadreg_amd64.s"), "", false},
|
||||||
{filepath.Join("..", "testdata", "verify", "floatimm_amd64.s"), "", false},
|
{filepath.Join("..", "testdata", "verify", "floatimm_amd64.s"), "", false},
|
||||||
{filepath.Join("..", "testdata", "verify", "bookkeep_amd64.s"), "", false},
|
{filepath.Join("..", "testdata", "verify", "bookkeep_amd64.s"), "", false},
|
||||||
|
{filepath.Join("..", "testdata", "verify", "forms_amd64.s"), "", false},
|
||||||
{filepath.Join("..", "testdata", "verify", "datarel_arm64.s"), "arm64", true},
|
{filepath.Join("..", "testdata", "verify", "datarel_arm64.s"), "arm64", true},
|
||||||
{filepath.Join("..", "testdata", "verify", "divslash_arm64.s"), "arm64", true},
|
{filepath.Join("..", "testdata", "verify", "divslash_arm64.s"), "arm64", true},
|
||||||
} {
|
} {
|
||||||
|
|||||||
+23
-1
@@ -150,6 +150,18 @@ func (img *Image) Bytes() []byte {
|
|||||||
return append(out, img.Data...)
|
return append(out, img.Data...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AssembleOption adjusts the file-level assembly context.
|
||||||
|
type AssembleOption func(*linkInfo)
|
||||||
|
|
||||||
|
// WithGOOS selects the target operating system for the forms that depend on
|
||||||
|
// it, the TLS access shape above all: linux and freebsd take the
|
||||||
|
// one-instruction form, windows and plan9 keep the two-instruction load.
|
||||||
|
func WithGOOS(goos string) AssembleOption {
|
||||||
|
return func(l *linkInfo) {
|
||||||
|
l.goos = goos
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// AssembleFile assembles every TEXT function of a parsed file and lays out
|
// AssembleFile assembles every TEXT function of a parsed file and lays out
|
||||||
// its static symbols (GLOBL/DATA) in a data section behind the code. Each
|
// its static symbols (GLOBL/DATA) in a data section behind the code. Each
|
||||||
// reference to a file-local static symbol becomes a RIP-relative load whose
|
// reference to a file-local static symbol becomes a RIP-relative load whose
|
||||||
@@ -157,7 +169,7 @@ func (img *Image) Bytes() []byte {
|
|||||||
// GLOBL defines is recorded as an external relocation (Externals) with its
|
// GLOBL defines is recorded as an external relocation (Externals) with its
|
||||||
// displacement left zero, the object-file emitters resolve it at link
|
// displacement left zero, the object-file emitters resolve it at link
|
||||||
// time, while the raw image (Bytes) cannot represent it.
|
// time, while the raw image (Bytes) cannot represent it.
|
||||||
func AssembleFile(f *ast.File) (*Image, error) {
|
func AssembleFile(f *ast.File, opts ...AssembleOption) (*Image, error) {
|
||||||
dataSyms, err := collectData(f)
|
dataSyms, err := collectData(f)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -166,7 +178,17 @@ func AssembleFile(f *ast.File) (*Image, error) {
|
|||||||
for _, d := range dataSyms {
|
for _, d := range dataSyms {
|
||||||
known[d.name] = true
|
known[d.name] = true
|
||||||
}
|
}
|
||||||
|
// TEXT symbols are file-level definitions too: a symbol immediate
|
||||||
|
// ($fn(SB)) may name one, exactly as a data reference names a GLOBL.
|
||||||
|
for _, d := range f.Decls {
|
||||||
|
if t, ok := d.(*ast.Text); ok {
|
||||||
|
known[t.Name.Name] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
link := &linkInfo{symbols: known, allowExternal: true}
|
link := &linkInfo{symbols: known, allowExternal: true}
|
||||||
|
for _, o := range opts {
|
||||||
|
o(link)
|
||||||
|
}
|
||||||
poolSeen := map[string]bool{}
|
poolSeen := map[string]bool{}
|
||||||
|
|
||||||
img := &Image{Symbols: map[string]int{}, SourcePath: f.Path}
|
img := &Image{Symbols: map[string]int{}, SourcePath: f.Path}
|
||||||
|
|||||||
@@ -36,6 +36,29 @@ type FloatImm struct {
|
|||||||
|
|
||||||
func (FloatImm) isOperand() {}
|
func (FloatImm) isOperand() {}
|
||||||
|
|
||||||
|
// TLSMem is a thread-local access, the source form off(base)(TLS*1) with the
|
||||||
|
// base dropped: the toolchain's one-instruction TLS rewrite assembles it as
|
||||||
|
// the segment-prefixed absolute whose disp32 carries an R_TLS_LE patch site
|
||||||
|
// (the linker fills the TLS slot offset).
|
||||||
|
type TLSMem struct {
|
||||||
|
Disp int64
|
||||||
|
Size int
|
||||||
|
Seg byte // the segment override: FS (0x64) or GS (0x65) on windows
|
||||||
|
}
|
||||||
|
|
||||||
|
func (TLSMem) isOperand() {}
|
||||||
|
|
||||||
|
// SegAbs is a segment-absolute access, 0x30(GS): the segment override
|
||||||
|
// prefixes a disp32 absolute reference with no relocation. The base
|
||||||
|
// register spellings GS and FS produce it.
|
||||||
|
type SegAbs struct {
|
||||||
|
Disp int64
|
||||||
|
Size int
|
||||||
|
Seg byte // 0x64 FS, 0x65 GS
|
||||||
|
}
|
||||||
|
|
||||||
|
func (SegAbs) isOperand() {}
|
||||||
|
|
||||||
// Mem is a memory operand of the form disp(base)(index*scale).
|
// Mem is a memory operand of the form disp(base)(index*scale).
|
||||||
type Mem struct {
|
type Mem struct {
|
||||||
Base Reg
|
Base Reg
|
||||||
@@ -45,6 +68,7 @@ type Mem struct {
|
|||||||
Size int // operand width in bytes
|
Size int // operand width in bytes
|
||||||
HasBase bool
|
HasBase bool
|
||||||
HasIndex bool
|
HasIndex bool
|
||||||
|
Seg byte // segment override prefix (0x64 FS, 0x65 GS); 0 = none
|
||||||
}
|
}
|
||||||
|
|
||||||
func (Mem) isOperand() {}
|
func (Mem) isOperand() {}
|
||||||
|
|||||||
+11
-3
@@ -5,6 +5,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -393,13 +394,20 @@ func TestRunCorpusAuditGOOS(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TestGenerateGoAsmHeaderRuntime pins the generator against the real thing:
|
// TestGenerateGoAsmHeaderRuntime pins the generator against the real thing:
|
||||||
// the runtime package, whose header the toolchain's own -asmhdr output was
|
// the runtime package of the ambient toolchain, whose header the toolchain's
|
||||||
// sampled from. Skipped in short mode: it type-checks the whole package.
|
// own -asmhdr output was sampled from. Skipped in short mode: it type-checks
|
||||||
|
// the whole package. The GOROOT comes from the go command itself, so the
|
||||||
|
// test follows whatever toolchain the host provides.
|
||||||
func TestGenerateGoAsmHeaderRuntime(t *testing.T) {
|
func TestGenerateGoAsmHeaderRuntime(t *testing.T) {
|
||||||
if testing.Short() {
|
if testing.Short() {
|
||||||
t.Skip("type-checks the whole runtime package")
|
t.Skip("type-checks the whole runtime package")
|
||||||
}
|
}
|
||||||
dir, err := generateGoAsmHeader("/usr/local/go/src/runtime", "", "amd64", t.TempDir())
|
out, err := exec.Command("go", "env", "GOROOT").Output()
|
||||||
|
if err != nil {
|
||||||
|
t.Skipf("no Go toolchain: %v", err)
|
||||||
|
}
|
||||||
|
runtimeDir := filepath.Join(strings.TrimSpace(string(out)), "src", "runtime")
|
||||||
|
dir, err := generateGoAsmHeader(runtimeDir, "", "amd64", t.TempDir())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("generateGoAsmHeader(runtime): %v", err)
|
t.Fatalf("generateGoAsmHeader(runtime): %v", err)
|
||||||
}
|
}
|
||||||
|
|||||||
+43
-15
@@ -38,7 +38,7 @@ import (
|
|||||||
// construction and are excluded from the diff; the other architectures list
|
// construction and are excluded from the diff; the other architectures list
|
||||||
// their conditional branches outright.
|
// their conditional branches outright.
|
||||||
func cmdAuditInstructions(args []string) error {
|
func cmdAuditInstructions(args []string) error {
|
||||||
fs := newCommand("audit-instructions", "gasm audit-instructions [--corpus [dir]] [-I dir] [amd64|arm64|riscv64|loong64]", `
|
fs := newCommand("audit-instructions", "gasm audit-instructions [--corpus [dir]] [--list] [-I dir] [amd64|arm64|riscv64|loong64]", `
|
||||||
Compare the gasm encoder for the given architecture (default amd64) against
|
Compare the gasm encoder for the given architecture (default amd64) against
|
||||||
go tool asm and print the diff: superset encodings (gasm-only, shippable via
|
go tool asm and print the diff: superset encodings (gasm-only, shippable via
|
||||||
gasm asm --format goobj) and known-but-unencodable names (the backlog). The
|
gasm asm --format goobj) and known-but-unencodable names (the backlog). The
|
||||||
@@ -55,16 +55,19 @@ toolchain probing. A file whose name carries a recognisable _arch suffix is
|
|||||||
attempted for that architecture; a file without one is attempted for all
|
attempted for that architecture; a file without one is attempted for all
|
||||||
four, exactly as a GOARCH build would compile it. The report gives the
|
four, exactly as a GOARCH build would compile it. The report gives the
|
||||||
per-architecture pass rates and the most common failure reasons, which drive
|
per-architecture pass rates and the most common failure reasons, which drive
|
||||||
the encodability backlog by frequency rather than by table order.
|
the encodability backlog by frequency rather than by table order. With
|
||||||
|
-list the report also prints every failing file with its reason, per
|
||||||
|
architecture.
|
||||||
`)
|
`)
|
||||||
corpus := fs.Bool("corpus", false, "assemble a corpus of .s files and report pass rates and failure reasons")
|
corpus := fs.Bool("corpus", false, "assemble a corpus of .s files and report pass rates and failure reasons")
|
||||||
|
list := fs.Bool("list", false, "with --corpus, list every failing file with its reason, per architecture")
|
||||||
var dirs includeDirs
|
var dirs includeDirs
|
||||||
fs.Var(&dirs, "I", "directory to search for #include files (may be repeated)")
|
fs.Var(&dirs, "I", "directory to search for #include files (may be repeated)")
|
||||||
if err := fs.Parse(args); err != nil {
|
if err := fs.Parse(args); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if *corpus {
|
if *corpus {
|
||||||
return cmdAuditCorpus(fs.Args(), dirs)
|
return cmdAuditCorpus(fs.Args(), dirs, *list)
|
||||||
}
|
}
|
||||||
archName := "amd64"
|
archName := "amd64"
|
||||||
switch n := len(fs.Args()); {
|
switch n := len(fs.Args()); {
|
||||||
@@ -403,20 +406,30 @@ type corpusTally struct {
|
|||||||
assembled int
|
assembled int
|
||||||
reasons map[string]int // failure reason → count
|
reasons map[string]int // failure reason → count
|
||||||
example map[string]string // failure reason → one representative file
|
example map[string]string // failure reason → one representative file
|
||||||
|
fails []corpusFailure // every failure, in file order, for --list
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *corpusTally) fail(path, reason string) {
|
// corpusFailure is one failed attempt, recorded for the --list report.
|
||||||
|
type corpusFailure struct {
|
||||||
|
path string
|
||||||
|
reason string
|
||||||
|
detail string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *corpusTally) fail(path string, err error) {
|
||||||
|
reason := corpusReason(err)
|
||||||
t.reasons[reason]++
|
t.reasons[reason]++
|
||||||
if t.example[reason] == "" {
|
if t.example[reason] == "" {
|
||||||
t.example[reason] = path
|
t.example[reason] = path
|
||||||
}
|
}
|
||||||
|
t.fails = append(t.fails, corpusFailure{path: path, reason: reason, detail: firstLine(err.Error())})
|
||||||
}
|
}
|
||||||
|
|
||||||
// cmdAuditCorpus implements audit-instructions --corpus. The include
|
// cmdAuditCorpus implements audit-instructions --corpus. The include
|
||||||
// directories carry #include resolution over a corpus whose files refer to
|
// directories carry #include resolution over a corpus whose files refer to
|
||||||
// headers such as GOROOT/pkg/include, the same -I a toolchain comparison
|
// headers such as GOROOT/pkg/include, the same -I a toolchain comparison
|
||||||
// needs.
|
// needs.
|
||||||
func cmdAuditCorpus(args []string, dirs includeDirs) error {
|
func cmdAuditCorpus(args []string, dirs includeDirs, list bool) error {
|
||||||
if len(args) > 1 {
|
if len(args) > 1 {
|
||||||
return &usageError{fmt.Errorf("audit-instructions --corpus takes at most one directory argument")}
|
return &usageError{fmt.Errorf("audit-instructions --corpus takes at most one directory argument")}
|
||||||
}
|
}
|
||||||
@@ -454,7 +467,7 @@ func cmdAuditCorpus(args []string, dirs includeDirs) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
printCorpusStats(stats)
|
printCorpusStats(stats, list)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -645,21 +658,22 @@ func runCorpusAudit(root string, dirs includeDirs) (*corpusStats, error) {
|
|||||||
hdrDir, err := hdr.dirFor(pkgDir, goos, goarchName(tg.a))
|
hdrDir, err := hdr.dirFor(pkgDir, goos, goarchName(tg.a))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ok = false
|
ok = false
|
||||||
t.fail(path, corpusReason(err))
|
t.fail(path, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
f, errs := parser.ParseWithOptions(path, src, parser.Options{
|
f, errs := parser.ParseWithOptions(path, src, parser.Options{
|
||||||
Expand: true,
|
Expand: true,
|
||||||
IncludeDirs: append(slices.Clone(dirs), hdrDir),
|
IncludeDirs: append(slices.Clone(dirs), hdrDir),
|
||||||
|
Predefines: platformPredefinesFor(goarchName(tg.a), goos),
|
||||||
})
|
})
|
||||||
if len(errs) > 0 {
|
if len(errs) > 0 {
|
||||||
ok = false
|
ok = false
|
||||||
t.fail(path, corpusReason(errs[0]))
|
t.fail(path, errs[0])
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if _, err := assembleFile(tg.a, f); err != nil {
|
if _, err := assembleFile(tg.a, f, goos); err != nil {
|
||||||
ok = false
|
ok = false
|
||||||
t.fail(path, corpusReason(err))
|
t.fail(path, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
t.assembled++
|
t.assembled++
|
||||||
@@ -670,21 +684,28 @@ func runCorpusAudit(root string, dirs includeDirs) (*corpusStats, error) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
f, errs := parser.ParseWithOptions(path, src, parser.Options{Expand: true, IncludeDirs: dirs})
|
|
||||||
|
|
||||||
ok := true
|
ok := true
|
||||||
for _, i := range wanted {
|
for _, i := range wanted {
|
||||||
tg, t := targets[i], tallies[i]
|
tg, t := targets[i], tallies[i]
|
||||||
t.attempted++
|
t.attempted++
|
||||||
|
// The parse carries the target's platform predefines, so it
|
||||||
|
// cannot be shared across targets the way a header-free file's
|
||||||
|
// could: a #ifdef GOARCH_arm block must be live on arm64 and
|
||||||
|
// dead everywhere else.
|
||||||
|
f, errs := parser.ParseWithOptions(path, src, parser.Options{
|
||||||
|
Expand: true,
|
||||||
|
IncludeDirs: dirs,
|
||||||
|
Predefines: platformPredefinesFor(goarchName(tg.a), goos),
|
||||||
|
})
|
||||||
var err error
|
var err error
|
||||||
if len(errs) > 0 {
|
if len(errs) > 0 {
|
||||||
err = errs[0] // a parse failure is a failure for every target
|
err = errs[0] // a parse failure is a failure for every target
|
||||||
} else {
|
} else {
|
||||||
_, err = assembleFile(tg.a, f)
|
_, err = assembleFile(tg.a, f, goos)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ok = false
|
ok = false
|
||||||
t.fail(path, corpusReason(err))
|
t.fail(path, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
t.assembled++
|
t.assembled++
|
||||||
@@ -706,7 +727,7 @@ func runCorpusAudit(root string, dirs includeDirs) (*corpusStats, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// printCorpusStats renders the corpus audit report.
|
// printCorpusStats renders the corpus audit report.
|
||||||
func printCorpusStats(s *corpusStats) {
|
func printCorpusStats(s *corpusStats, list bool) {
|
||||||
fmt.Printf("corpus %s: %d files (%d generic, attempted for all architectures; %d named for other Go ports, never attempted)\n", s.root, s.files, s.generic, s.otherPort)
|
fmt.Printf("corpus %s: %d files (%d generic, attempted for all architectures; %d named for other Go ports, never attempted)\n", s.root, s.files, s.generic, s.otherPort)
|
||||||
// The rate is over the files a supported build would attempt: the
|
// The rate is over the files a supported build would attempt: the
|
||||||
// other ports' files sit in the count for completeness but can never
|
// other ports' files sit in the count for completeness but can never
|
||||||
@@ -721,6 +742,13 @@ func printCorpusStats(s *corpusStats) {
|
|||||||
fmt.Printf(" %4d %s\n", t.reasons[r], r)
|
fmt.Printf(" %4d %s\n", t.reasons[r], r)
|
||||||
fmt.Printf(" e.g. %s\n", t.example[r])
|
fmt.Printf(" e.g. %s\n", t.example[r])
|
||||||
}
|
}
|
||||||
|
if !list {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, f := range t.fails {
|
||||||
|
fmt.Printf(" FAIL %s\n", f.path)
|
||||||
|
fmt.Printf(" %s: %s\n", f.reason, f.detail)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -84,7 +84,7 @@ func disSource(path string, target arch.Arch) int {
|
|||||||
if len(errs) > 0 {
|
if len(errs) > 0 {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
img, err := assembleFile(target, f)
|
img, err := assembleFile(target, f, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "gasm dis: %v\n", err)
|
fmt.Fprintf(os.Stderr, "gasm dis: %v\n", err)
|
||||||
return 1
|
return 1
|
||||||
|
|||||||
+34
-11
@@ -574,7 +574,7 @@ naming the package.
|
|||||||
defer cleanup()
|
defer cleanup()
|
||||||
dirs = append(dirs, hdrDir)
|
dirs = append(dirs, hdrDir)
|
||||||
}
|
}
|
||||||
f, errs := parser.ParseWithOptions(path, src, parser.Options{Expand: true, IncludeDirs: dirs})
|
f, errs := parser.ParseWithOptions(path, src, parser.Options{Expand: true, IncludeDirs: dirs, Predefines: platformPredefinesFor(string(targetArch), goos)})
|
||||||
for _, e := range errs {
|
for _, e := range errs {
|
||||||
fmt.Fprintf(os.Stderr, "%s: %v\n", path, e)
|
fmt.Fprintf(os.Stderr, "%s: %v\n", path, e)
|
||||||
}
|
}
|
||||||
@@ -582,7 +582,7 @@ naming the package.
|
|||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
img, err := assembleFile(targetArch, f)
|
img, err := assembleFile(targetArch, f, goos)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "%s: %v\n", path, err)
|
fmt.Fprintf(os.Stderr, "%s: %v\n", path, err)
|
||||||
return 1
|
return 1
|
||||||
@@ -789,11 +789,34 @@ e.g. --map wideCopyAVX2=wideCopyAVX512 pairs the two regardless of suffix.
|
|||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// platformPredefines mirrors the go command's assembler invocation, which
|
||||||
|
// defines GOOS_<goos> and GOARCH_<arch> as -D macros: GOROOT headers
|
||||||
|
// (go_tls.h, asm_riscv64.h) select their platform blocks with #ifdef on
|
||||||
|
// exactly those names, so an assembler without them cannot see the platform
|
||||||
|
// definitions at all.
|
||||||
|
func platformPredefines(goarch, goos string) map[string]string {
|
||||||
|
return map[string]string{
|
||||||
|
"GOARCH_" + goarch: "1",
|
||||||
|
"GOOS_" + goos: "1",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// platformPredefinesFor resolves the ambient GOOS the way a build would: a
|
||||||
|
// file whose name carries one (sys_darwin_arm64.s) is compiled for that GOOS
|
||||||
|
// and nothing else.
|
||||||
|
func platformPredefinesFor(goarch string, fileGoos string) map[string]string {
|
||||||
|
goos := fileGoos
|
||||||
|
if goos == "" {
|
||||||
|
goos = runtime.GOOS
|
||||||
|
}
|
||||||
|
return platformPredefines(goarch, goos)
|
||||||
|
}
|
||||||
|
|
||||||
// assembleFile assembles a parsed file for the given architecture and returns the image.
|
// assembleFile assembles a parsed file for the given architecture and returns the image.
|
||||||
func assembleFile(targetArch arch.Arch, f *ast.File) (*asm.Image, error) {
|
func assembleFile(targetArch arch.Arch, f *ast.File, goos string) (*asm.Image, error) {
|
||||||
switch targetArch {
|
switch targetArch {
|
||||||
case arch.AMD64:
|
case arch.AMD64:
|
||||||
return asm.AssembleFile(f)
|
return asm.AssembleFile(f, asm.WithGOOS(goos))
|
||||||
case arch.RISCV:
|
case arch.RISCV:
|
||||||
return asm.AssembleFileRISCV(f)
|
return asm.AssembleFileRISCV(f)
|
||||||
case arch.ARM64:
|
case arch.ARM64:
|
||||||
@@ -813,18 +836,18 @@ func assemblePath(path string, forced arch.Arch, dirs includeDirs) (*asm.Image,
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
f, errs := parser.ParseWithOptions(path, src, parser.Options{Expand: true, IncludeDirs: dirs})
|
target := forced
|
||||||
|
if target == arch.Unknown {
|
||||||
|
target = arch.FromFilename(path)
|
||||||
|
}
|
||||||
|
f, errs := parser.ParseWithOptions(path, src, parser.Options{Expand: true, IncludeDirs: dirs, Predefines: platformPredefinesFor(string(target), "")})
|
||||||
for _, e := range errs {
|
for _, e := range errs {
|
||||||
fmt.Fprintf(os.Stderr, "%s: %v\n", path, e)
|
fmt.Fprintf(os.Stderr, "%s: %v\n", path, e)
|
||||||
}
|
}
|
||||||
if len(errs) > 0 {
|
if len(errs) > 0 {
|
||||||
return nil, fmt.Errorf("parse errors")
|
return nil, fmt.Errorf("parse errors")
|
||||||
}
|
}
|
||||||
target := forced
|
return assembleFile(target, f, "")
|
||||||
if target == arch.Unknown {
|
|
||||||
target = arch.FromFilename(path)
|
|
||||||
}
|
|
||||||
return assembleFile(target, f)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// printByteDiff shows the first few byte differences between two code blocks.
|
// printByteDiff shows the first few byte differences between two code blocks.
|
||||||
@@ -920,7 +943,7 @@ func cmdVerifyNonJIT(path string, targetArch arch.Arch, groundTruth, profile boo
|
|||||||
if len(errs) > 0 {
|
if len(errs) > 0 {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
img, err := assembleFile(targetArch, f)
|
img, err := assembleFile(targetArch, f, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "gasm verify: %v\n", err)
|
fmt.Fprintf(os.Stderr, "gasm verify: %v\n", err)
|
||||||
return 1
|
return 1
|
||||||
|
|||||||
@@ -119,6 +119,20 @@ identifier is a register or a label is an *architecture* question, so it is
|
|||||||
left to `arch` and resolved in the lint/lsp layers. This keeps the parser
|
left to `arch` and resolved in the lint/lsp layers. This keeps the parser
|
||||||
arch-agnostic and its output deterministic.
|
arch-agnostic and its output deterministic.
|
||||||
|
|
||||||
|
### Optional preprocessing
|
||||||
|
|
||||||
|
With `Options{Expand: true}` the parser runs a pre-parse pass
|
||||||
|
(`preproc.go`) that splices `#include` files (the source directory, then the
|
||||||
|
`-I` directories), expands object and parameterised `#define` macros,
|
||||||
|
applies `#undef` and the `#ifdef`/`#ifndef`/`#else`/`#endif` family, and
|
||||||
|
folds constant expressions left in operands. The go command's platform
|
||||||
|
macros (`GOARCH_<arch>`, `GOOS_<goos>`) arrive through `Options.Predefines`.
|
||||||
|
The assembly path (`asm`, `diff`, `audit`) expands; `lint`, `fmt` and the
|
||||||
|
language server read the raw file. The command layer adds the go_asm.h
|
||||||
|
generator (`asmhdr.go`): a file that includes go_asm.h gets the package's
|
||||||
|
defines type-checked out of its Go files for the target architecture and
|
||||||
|
GOOS, with no compiler in the loop.
|
||||||
|
|
||||||
### `arch`
|
### `arch`
|
||||||
|
|
||||||
Register files are generated programmatically (the regular `R8`-`R15`,
|
Register files are generated programmatically (the regular `R8`-`R15`,
|
||||||
|
|||||||
+4
-2
@@ -365,7 +365,7 @@ add: 16 bytes, args=24, frame=0 NOSPLIT
|
|||||||
## audit-instructions
|
## audit-instructions
|
||||||
|
|
||||||
```text
|
```text
|
||||||
Usage: gasm audit-instructions [--corpus [dir]] [-I dir] [amd64|arm64|riscv64|loong64]
|
Usage: gasm audit-instructions [--corpus [dir]] [--list] [-I dir] [amd64|arm64|riscv64|loong64]
|
||||||
```
|
```
|
||||||
|
|
||||||
Compare the gasm encoder for the given architecture (default amd64) against the
|
Compare the gasm encoder for the given architecture (default amd64) against the
|
||||||
@@ -403,7 +403,9 @@ architecture; a file without one is attempted for all four, exactly as a
|
|||||||
The report gives the headline number (files
|
The report gives the headline number (files
|
||||||
that assemble for every target architecture), the per-architecture pass rates
|
that assemble for every target architecture), the per-architecture pass rates
|
||||||
and the most common failure reasons with one representative file each, which
|
and the most common failure reasons with one representative file each, which
|
||||||
drive the encodability backlog by frequency rather than by table order. A run
|
drive the encodability backlog by frequency rather than by table order. With
|
||||||
|
`--list` the report additionally prints every failing file with its failure
|
||||||
|
reason, per architecture. A run
|
||||||
over GOROOT takes under a second.
|
over GOROOT takes under a second.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
|
|||||||
@@ -0,0 +1,124 @@
|
|||||||
|
# AMD64
|
||||||
|
|
||||||
|
Layer 1, target page. Verified against `go tool asm` of Go 1.27.1 and against
|
||||||
|
gasm's encoder, whose output is compared byte for byte with the toolchain's
|
||||||
|
and executed on real hardware (`gasm verify`). The complete mnemonic
|
||||||
|
inventory lives in the generated appendix
|
||||||
|
[INSTRUCTIONS-AMD64.md](INSTRUCTIONS-AMD64.md); this page is the grammar and
|
||||||
|
the conventions.
|
||||||
|
|
||||||
|
## Registers
|
||||||
|
|
||||||
|
| Group | Names | Notes |
|
||||||
|
|---|---|---|
|
||||||
|
| General purpose, 64-bit | `AX` `BX` `CX` `DX` `SI` `DI` `BP` `SP` `R8` to `R15` | bare names, no prefix |
|
||||||
|
| Sub-registers | `AL` `CL` `DL` `BL` `AH` family; `R8B` `R8W` `R8D` for the byte, word and double word of `R8` | width rides the mnemonic as well |
|
||||||
|
| Vector | `X0` to `X15` (128-bit), `Y0` to `Y15` (256-bit), `Z0` to `Z31` (512-bit) | SSE, AVX and AVX-512 |
|
||||||
|
| Mask | `K0` to `K7` | AVX-512 opmask |
|
||||||
|
| System | `TLS` | the thread pointer, see below |
|
||||||
|
|
||||||
|
Roles the calling convention fixes, which assembly must respect and can rely
|
||||||
|
on:
|
||||||
|
|
||||||
|
- `SP` is the hardware stack pointer; the virtual frame pointer of the
|
||||||
|
common language is the pseudo-register SP of OPERANDS.md, a different
|
||||||
|
spelling with a different meaning.
|
||||||
|
- `BP` is callee-save. The assembler inserts the save and restore whenever
|
||||||
|
the function has a non-zero frame, so using BP as a general register
|
||||||
|
interferes with sampling profilers that walk the frame chain.
|
||||||
|
- `R14` holds `g`, the goroutine pointer, in the register ABI; `RDX` holds
|
||||||
|
the closure context; `R12` and `R13` are the register ABI's scratch pair
|
||||||
|
and `R15` its GOT temporary; `X15` is the zeroing register the compiler
|
||||||
|
uses. An ABI0 assembly function called from Go sees none of these live
|
||||||
|
across the call, but runtime assembly reads them directly.
|
||||||
|
- The legacy spellings for the goroutine pointer are the macros of
|
||||||
|
`runtime/go_tls.h`: `get_tls(r)` expands to `MOVQ TLS, r` and `g(r)` to
|
||||||
|
`0(r)(TLS*1)`, the segment base riding the index field.
|
||||||
|
|
||||||
|
## Addressing
|
||||||
|
|
||||||
|
The common forms of OPERANDS.md, with the amd64 specifics:
|
||||||
|
|
||||||
|
```text
|
||||||
|
offset(base) MOVQ 16(BX), AX
|
||||||
|
offset(base)(index*scale) MOVL foo+32(SP)(R9*8), CX
|
||||||
|
scale is 1, 2, 4 or 8
|
||||||
|
name±offset(SB) MOVQ ·table(SB), CX
|
||||||
|
```
|
||||||
|
|
||||||
|
- Global references assemble as absolute addresses and produce R_ADDR
|
||||||
|
relocations; branch targets produce R_PCREL.
|
||||||
|
- Vector indexed memory, the VSIB form with an X, Y or Z register in the
|
||||||
|
index position, exists for the gather and scatter families.
|
||||||
|
- There are no segment overrides in source; the one segment-flavoured form
|
||||||
|
is the TLS base in the index field shown above.
|
||||||
|
|
||||||
|
## The frame and the split check
|
||||||
|
|
||||||
|
The assembler manages the frame, not the programmer:
|
||||||
|
|
||||||
|
- It inserts the `BP` save and restore for any non-zero frame.
|
||||||
|
- It inserts the stack-split check for any function that is not NoSplit:
|
||||||
|
the check compares SP against the guard, and on exhaustion calls
|
||||||
|
`runtime.morestack_noctxt`. Frames at or below 128 bytes, StackSmall, use
|
||||||
|
the small compare; frames at or below 4096 bytes, StackBig, use the
|
||||||
|
adjusted form; larger frames compare in two steps.
|
||||||
|
- On amd64 the assembler marks a function NoSplit itself when the frame is
|
||||||
|
under StackSmall and the body calls nothing that needs stack: such a
|
||||||
|
function carries the NoSplit flag in the object without the source ever
|
||||||
|
writing NOSPLIT.
|
||||||
|
|
||||||
|
Results and arguments are stack-only in ABI0: the caller's frame carries
|
||||||
|
them at FP offsets, per the Go prototype.
|
||||||
|
|
||||||
|
## Instructions
|
||||||
|
|
||||||
|
The inventory counts 1654 recognised mnemonics today, of which the encoder
|
||||||
|
emits 1113; both numbers are generated in the appendix, and the gap is the
|
||||||
|
encoder backlog that `gasm audit-instructions` measures. The families:
|
||||||
|
|
||||||
|
- **Integer base.** The ALU and move set with width suffixes, `MOVB`,
|
||||||
|
`MOVW`, `MOVL`, `MOVQ`; the extension moves `MOVBLZX`, `MOVWLSX`,
|
||||||
|
`MOVLQSX` and their siblings, which the compiler's output leans on;
|
||||||
|
`LEA`; `PUSH` and `POP`; the shifts and rotates; the bit operations `BT`
|
||||||
|
through `BTC`, `BSF`, `BSR`, `LZCNT`, `TZCNT`, `POPCNT`, `BSWAP`; the
|
||||||
|
string primitives `MOVS` and `STOS`.
|
||||||
|
- **Exchange and atomics.** `XCHG`, `CMPXCHG`, `XADD`; the extended-carry
|
||||||
|
pair `ADCX` and `ADOX`; `CRC32`.
|
||||||
|
- **Scalar floating point.** The SSE2 scalar moves and arithmetic
|
||||||
|
(`MOVSD`, `MOVSS`, `ADDSD`, and the `CVT` family). Floating-point
|
||||||
|
immediates are not encodable on this target, so the assembler
|
||||||
|
materialises them: the constant lands in a synthesised read-only pool,
|
||||||
|
and a positive zero collapses to `XORPS` of the register with itself,
|
||||||
|
exactly as the toolchain does.
|
||||||
|
- **Legacy SIMD, SSE.** The `MOVO`, `MOVOU`, `MOVAPS` family and the packed
|
||||||
|
integer and floating operations, shuffles, lane extracts and inserts and
|
||||||
|
the imm8-controlled forms.
|
||||||
|
- **VEX and EVEX.** The `V`-prefixed forms for 256 and 512-bit work,
|
||||||
|
opmask operations on `K0` to `K7`, gathers and scatters, and the
|
||||||
|
quad-register families 4FMAPS, 4FNMADD, 4VNNIW, VP4DPWSSD and VP4DPWSSDS,
|
||||||
|
whose register list rides the inverted V′VVV field. Mixing VEX and legacy
|
||||||
|
SSE in one loop pays the AVX-SSE transition penalty on every switch: keep
|
||||||
|
a loop in one dialect.
|
||||||
|
- **Cryptographic and counting extensions.** AES-NI, SHA-1 and SHA-256,
|
||||||
|
PCLMULQDQ, GFNI.
|
||||||
|
- **System.** `CPUID`, `RDTSC`, `SYSCALL`, the fences, `LDMXCSR` and
|
||||||
|
`STMXCSR`, the prefetch family.
|
||||||
|
- **Pseudo-operations.** `BYTE`, `WORD`, `LONG`, `QUAD` lay raw bytes or
|
||||||
|
words into the stream for encodings the assembler does not know; `ADJSP`
|
||||||
|
adjusts the stack pointer; `DUFFCOPY` and `DUFFZERO` and `GETCALLERPC`
|
||||||
|
are compiler-side names the table recognises but an encoder need not
|
||||||
|
emit.
|
||||||
|
|
||||||
|
A mnemonic the appendix lists with `gasm encodes: no` assembles nowhere:
|
||||||
|
gasm reports it as an explicit error, never as wrong bytes, and the
|
||||||
|
`unencodable-instruction` lint flags it at edit time.
|
||||||
|
|
||||||
|
## Relocations
|
||||||
|
|
||||||
|
The relocations an amd64 object carries, all specified in
|
||||||
|
[GOOBJ.md](../GOOBJ.md): `R_ADDR` for absolute globals, `R_PCREL` for
|
||||||
|
relative addresses, `R_CALL` for direct calls, `R_TLS_LE` and `R_TLS_IE` for
|
||||||
|
thread local access and `R_GOTPCREL` for GOT relative sequences, plus
|
||||||
|
`R_DWTXTADDR_U4` inside the DWARF records, which the assembler always
|
||||||
|
emits in the four-byte flavour.
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
# ARM64
|
||||||
|
|
||||||
|
Layer 1, target page. Verified against `go tool asm` of Go 1.27.1, against the
|
||||||
|
toolchain's own arm64 assembler manual (`cmd/internal/obj/arm64/doc.go`) and
|
||||||
|
against gasm's encoder, whose output is compared byte for byte with the
|
||||||
|
toolchain's. The complete mnemonic inventory lives in the generated appendix
|
||||||
|
[INSTRUCTIONS-ARM64.md](INSTRUCTIONS-ARM64.md).
|
||||||
|
|
||||||
|
## Registers
|
||||||
|
|
||||||
|
- General purpose: `R0` to `R30`, plus `ZR`, the zero register, and `RSP`,
|
||||||
|
the stack pointer. There is no R31: thirty-one names and ZR.
|
||||||
|
- Floating-point and SIMD share one file written `Vn`; where an instruction
|
||||||
|
is scalar floating point the operand may be written `Fn` (`F0` to `F31`).
|
||||||
|
- SVE register names (`Z0` to `Z31`, `P0` to `P15`) exist in the assembler's
|
||||||
|
tables.
|
||||||
|
- Roles the convention fixes: `RSP` is the stack pointer, `R29` the frame
|
||||||
|
pointer, `R30` the link register, `R26` the closure context and `R27` the
|
||||||
|
assembler's scratch register. The goroutine pointer lives in `R28` and is
|
||||||
|
written `g` in source, its fields as `g_m(g)`, `g_sched(g)`; `R18` is the
|
||||||
|
platform-reserved register and the Go toolchain never addresses it.
|
||||||
|
|
||||||
|
## Loads, stores and the width suffixes
|
||||||
|
|
||||||
|
The MOV series is the load and store interface, with the width in the
|
||||||
|
mnemonic rather than the register name:
|
||||||
|
|
||||||
|
| Mnemonic | Machine instruction |
|
||||||
|
|---|---|
|
||||||
|
| `MOVD` | ldr, str, stur, 64-bit |
|
||||||
|
| `MOVW` | ldrsw, str, stur, 32-bit sign extending |
|
||||||
|
| `MOVWU` | ldr, 32-bit zero extending |
|
||||||
|
| `MOVH` | ldrsh, strh, sturh |
|
||||||
|
| `MOVHU` | ldrh |
|
||||||
|
| `MOVB` | ldrsb, strb, sturb |
|
||||||
|
| `MOVBU` | ldrb |
|
||||||
|
|
||||||
|
Post-index and pre-index addressing take the `.P` and `.W` suffixes on the
|
||||||
|
mnemonic: `MOVD.P -8(R10), R8` is `ldr x8, [x10],#-8`, and `MOVB.W
|
||||||
|
16(R16), R10` is `ldrsb x10, [x16,#16]!`.
|
||||||
|
|
||||||
|
## Addressing
|
||||||
|
|
||||||
|
```text
|
||||||
|
imm(Rn|RSP) 28(R17)
|
||||||
|
(Rn|RSP) (R22)
|
||||||
|
(Rn)(Rm) (R27)(R23)
|
||||||
|
(Rn)(Rm<<scale) (R4)(R12<<2)
|
||||||
|
(Rn)(Rm.UXTW<<3) extended and shifted index
|
||||||
|
(Rt1, Rt2) register pair for LDP, STP and the exclusive pair forms
|
||||||
|
```
|
||||||
|
|
||||||
|
Branch targets are labels, `(R3)` for indirect, `name(SB)` for static.
|
||||||
|
|
||||||
|
## Operand order and the special forms
|
||||||
|
|
||||||
|
Most instructions appear in left-to-right assignment order: `ADD R11,
|
||||||
|
RSP, R25` computes into R25. The exceptions the toolchain's manual lists,
|
||||||
|
each with its own order:
|
||||||
|
|
||||||
|
- stores and `CBZ`, `CBNZ` keep the GNU order: `MOVD R29, 384(R19)`.
|
||||||
|
- The multiply-accumulate family `MADD`, `MSUB`, `SMADDL` and friends are
|
||||||
|
`<Rm>, <Ra>, <Rn>, <Rd>`.
|
||||||
|
- The scalar FMA family `FMADDD` and friends are `<Fm>, <Fa>, <Fn>, <Fd>`.
|
||||||
|
- The bitfield family `BFI`, `BFXIL`, `SBFIZ`, `SBFX`, `UBFIZ`, `UBFX` is
|
||||||
|
`$<lsb>, <Rn>, $<width>, <Rd>`.
|
||||||
|
- The conditional compare and select families carry the condition as the
|
||||||
|
**first** operand: `CSEL GT, R0, R19, R1`, `CCMP MI, R22, $12, $13`,
|
||||||
|
`FCCMPD AL, F8, F26, $0`.
|
||||||
|
- The exclusive stores are `<Rf>, (<Rn>), <Rs>` with the status register
|
||||||
|
last: `STLXR ZR, (R15), R16`.
|
||||||
|
- `TBZ` and `TBNZ` are `$<imm>, <Rt>, <label>`.
|
||||||
|
|
||||||
|
Shifted and extended register operands ride the register: `R19>>30`,
|
||||||
|
`R26->24` for arithmetic right shift, `@>` for rotate, and the extend forms
|
||||||
|
`R19.UXTB<<4`, `R14.SXTX` with extend operators UXTB, UXTH, UXTW, UXTX,
|
||||||
|
SXTB, SXTH, SXTW, SXTX.
|
||||||
|
|
||||||
|
## Conditions, branches and names
|
||||||
|
|
||||||
|
- Conditions ride the branch mnemonic: `B.EQ`, or the canonical
|
||||||
|
per-condition names such as `BEQ`. Both spellings exist; the canonical
|
||||||
|
names are what the generated inventory lists.
|
||||||
|
- `br` is `JMP` and `blr` is `CALL` in this dialect; indirect branches are
|
||||||
|
`JMP (R3)` and `CALL (R17)`.
|
||||||
|
- `NOP` is a zero-width pseudo-instruction; the hardware nop is `NOOP`,
|
||||||
|
an alias of `HINT $0`.
|
||||||
|
- `umov` is written as `VMOV`.
|
||||||
|
|
||||||
|
## Constants
|
||||||
|
|
||||||
|
- A 16-bit immediate optionally shifted: `MOVK $(10<<32), R20`, with
|
||||||
|
`MOVZ`, `MOVN` and their W variants; a zero shift is rejected by the
|
||||||
|
assembler.
|
||||||
|
- Large integer constants: `MOV` materialises any 64-bit constant, the
|
||||||
|
closest-instruction way.
|
||||||
|
- Vector constants: `VMOVS`, `VMOVD` and `VMOVQ`, the last taking two
|
||||||
|
64-bit halves for a 128-bit value:
|
||||||
|
`VMOVQ $0x1122334455667788, $0x99aabbccddeeff00, V2`.
|
||||||
|
|
||||||
|
## SIMD
|
||||||
|
|
||||||
|
Floating-point and SIMD instructions mostly carry a `V` prefix
|
||||||
|
(`VADD`, `VFMLA`), the cryptographic extensions (`AESD`, `SHA256H`) and the
|
||||||
|
scalar floating-point instructions being the exceptions. Operands carry an
|
||||||
|
arrangement suffix, `V5.H8`, and structure loads and stores use bracket
|
||||||
|
lists, `[V21.B16]`, with element selection as `V9.S[1]`.
|
||||||
|
|
||||||
|
## Alignment
|
||||||
|
|
||||||
|
`PCALIGN $n` pads to a power-of-two boundary between 8 and 2048 and also
|
||||||
|
raises the function's alignment to the coarsest boundary any of its PCALIGN
|
||||||
|
directives asks for. Functions default to 16-byte alignment on this target.
|
||||||
|
|
||||||
|
## Relocations
|
||||||
|
|
||||||
|
`R_ADDRARM64` for the adrp-plus-add pair, `R_ARM64_PCREL` and the
|
||||||
|
`R_ARM64_PCREL_LDST` family for PC relative addressing, `R_ARM64_LDST` for
|
||||||
|
the load and store immediates, `R_ARM64_GOTPCREL` and `R_ARM64_GOT` for the
|
||||||
|
GOT, `R_ARM64_TLS_LE` and `R_ARM64_TLS_IE` for thread local storage and
|
||||||
|
`R_CALLARM64` for direct calls, all specified in
|
||||||
|
[GOOBJ.md](../GOOBJ.md).
|
||||||
@@ -0,0 +1,148 @@
|
|||||||
|
# Directives: TEXT, DATA, GLOBL and the annotations
|
||||||
|
|
||||||
|
Layer 1, the common language, with the flag vocabulary both layers share.
|
||||||
|
Verified against `go tool asm` of Go 1.27.1, against the shipped headers
|
||||||
|
`textflag.h` and `funcdata.h` in `$GOROOT/pkg/include`, and against gasm's
|
||||||
|
parser. Where gasm extends a directive, the extension says so and is marked.
|
||||||
|
|
||||||
|
Six directives exist. Three define things: TEXT, DATA, GLOBL. Three
|
||||||
|
annotate: FUNCDATA, PCDATA, PCALIGN.
|
||||||
|
|
||||||
|
## TEXT
|
||||||
|
|
||||||
|
```text
|
||||||
|
// func Add(a, b int64) int64
|
||||||
|
TEXT ·Add(SB), NOSPLIT, $0-24
|
||||||
|
...instructions...
|
||||||
|
RET
|
||||||
|
```
|
||||||
|
|
||||||
|
```text
|
||||||
|
TEXT symbol(SB), [flags,] $framesize[-argsize]
|
||||||
|
```
|
||||||
|
|
||||||
|
- The symbol is an `·Name(SB)` reference into the current package, or a
|
||||||
|
fully qualified name.
|
||||||
|
- The optional flag argument is a constant expression, normally an OR of the
|
||||||
|
names from `textflag.h`, the table below. Without `#include "textflag.h"`
|
||||||
|
the names are not macros and the assembler reports the misleading error
|
||||||
|
`illegal or missing addressing mode for symbol NOSPLIT`: include the
|
||||||
|
header first.
|
||||||
|
- `$framesize-argsize` is two constants, not a subtraction: the local frame
|
||||||
|
size in bytes, and the caller's argument area in bytes. The argument size
|
||||||
|
may be omitted entirely, `$16`, which marks the argument size unknown
|
||||||
|
(0x80000000 in the object, the value of `ArgsSizeUnknown` from
|
||||||
|
`funcdata.h`); a frame size may be negative only in the generated ABI
|
||||||
|
wrappers.
|
||||||
|
- A function whose last instruction is not a branch cannot fall through into
|
||||||
|
the next TEXT: the toolchain appends a jump to itself, so end functions
|
||||||
|
with `RET` deliberately.
|
||||||
|
- One TEXT per symbol; redeclaring is an error. The TEXT line also fixes the
|
||||||
|
function's source line for traceback: it is the line number that pcln
|
||||||
|
reports for the function's start.
|
||||||
|
|
||||||
|
The framesize and argsize fields do real work: the framesize drives the
|
||||||
|
stack-split preamble (RUNTIME.md carries the contract), and both travel into
|
||||||
|
the FuncInfo record of the object (GOOBJ.md carries its layout).
|
||||||
|
|
||||||
|
### The flag table
|
||||||
|
|
||||||
|
Values from `textflag.h`, in agreement with `cmd/internal/obj/textflag.go`:
|
||||||
|
|
||||||
|
| Name | Value | Applies to | Meaning |
|
||||||
|
|---|---|---|---|
|
||||||
|
| NOPROF | 1 | both | do not profile; deprecated |
|
||||||
|
| DUPOK | 2 | both | the linker may keep one of several duplicates |
|
||||||
|
| NOSPLIT | 4 | TEXT | no stack-split preamble |
|
||||||
|
| RODATA | 8 | data | put the data in a read-only section |
|
||||||
|
| NOPTR | 16 | data | the data contains no pointers |
|
||||||
|
| WRAPPER | 32 | TEXT | a wrapper; must not disable `recover` |
|
||||||
|
| NEEDCTXT | 64 | TEXT | a closure consuming the context register |
|
||||||
|
| TLSBSS | 256 | data | a thread local word in BSS |
|
||||||
|
| NOFRAME | 512 | TEXT | no frame setup; only valid with a frame size of 0 |
|
||||||
|
| REFLECTMETHOD | 1024 | TEXT | the function calls `reflect.Type.Method` or `MethodByName` |
|
||||||
|
| TOPFRAME | 2048 | TEXT | the outermost frame; unwinders stop here |
|
||||||
|
| ABIWRAPPER | 4096 | TEXT | an ABI transition wrapper |
|
||||||
|
|
||||||
|
Rules with teeth:
|
||||||
|
|
||||||
|
- `NOSPLIT` removes the split check, so the frame plus everything the
|
||||||
|
function calls must fit in the stack segment that remains. It exists to
|
||||||
|
protect the splitting code itself; reaching for it to save two instructions
|
||||||
|
is how stack overflows corrupt memory. On amd64 the assembler additionally
|
||||||
|
marks small leaf functions NoSplit itself and omits the check, so the
|
||||||
|
absence of the preamble is not proof the flag was written.
|
||||||
|
- A TEXT whose symbol is declared `ABIInternal` must carry NOSPLIT: the
|
||||||
|
assembler rejects it otherwise, because it cannot generate
|
||||||
|
the split path for a register-ABI function.
|
||||||
|
- `RODATA` implies NOPTR for the garbage collector.
|
||||||
|
|
||||||
|
## DATA
|
||||||
|
|
||||||
|
```text
|
||||||
|
DATA ·table+0(SB)/8, $0x0102030405060708
|
||||||
|
DATA ·msg+0(SB)/14, $"hello, world\n"
|
||||||
|
GLOBL ·msg(SB), RODATA, $14
|
||||||
|
```
|
||||||
|
|
||||||
|
```text
|
||||||
|
DATA symbol+offset(SB)/width, value
|
||||||
|
```
|
||||||
|
|
||||||
|
- `width` is exactly 1, 2, 4 or 8: the initialiser is written into the data
|
||||||
|
image at `symbol+offset` in that many bytes.
|
||||||
|
- The value is an integer or character constant of the width, or a string
|
||||||
|
literal whose byte length equals the width exactly; escapes count. Long
|
||||||
|
data is written as successive DATA lines at increasing offsets; bytes the
|
||||||
|
directives never name are zero.
|
||||||
|
- Every symbol initialised with DATA ends with a GLOBL line declaring its
|
||||||
|
total size, after all of its DATA lines.
|
||||||
|
|
||||||
|
A symbol containing pointers cannot be defined in assembly, because the
|
||||||
|
collector cannot see into it: define it in Go and refer to it by name. As a
|
||||||
|
rule, data that is not read-only belongs in Go.
|
||||||
|
|
||||||
|
Extension, gasm only: a DATA initialiser may name a symbol,
|
||||||
|
`DATA ·fn+0(SB)/8, $·handler(SB)`, which gasm lays down as an absolute
|
||||||
|
relocation on that field. The toolchain offers no ground truth for this
|
||||||
|
form; gasm's behaviour is verified by linking and execution.
|
||||||
|
|
||||||
|
## GLOBL
|
||||||
|
|
||||||
|
```text
|
||||||
|
GLOBL symbol(SB), [flags,] $size
|
||||||
|
```
|
||||||
|
|
||||||
|
Declares the symbol global with its total size in bytes. The useful flags
|
||||||
|
are RODATA, NOPTR, DUPOK and TLSBSS from the table above. Uninitialised
|
||||||
|
bytes are zero, which makes GLOBL with no DATA the language's BSS.
|
||||||
|
|
||||||
|
## FUNCDATA and PCDATA
|
||||||
|
|
||||||
|
```text
|
||||||
|
FUNCDATA $functypeid, symbol(SB)
|
||||||
|
PCDATA $pctypeid, $value
|
||||||
|
```
|
||||||
|
|
||||||
|
The compiler's annotations for the garbage collector and traceback, named by
|
||||||
|
the ids in `funcdata.h`: FUNCDATA 0 to 7 (args pointer maps, locals pointer
|
||||||
|
maps, stack objects, inline tree, open-coded defer info, argument info,
|
||||||
|
argument liveness, wrap info), PCDATA 0 to 4 (unsafe point, stack map index,
|
||||||
|
inline tree index, argument liveness index, panic bounds). Assembly code
|
||||||
|
normally reaches them only through the macro forms in `funcdata.h`, which
|
||||||
|
RUNTIME.md explains. Outside the macros, hand-written PCDATA is meaningless:
|
||||||
|
the values are pc-value tables the compiler builds from its own view of the
|
||||||
|
program.
|
||||||
|
|
||||||
|
## PCALIGN
|
||||||
|
|
||||||
|
```text
|
||||||
|
PCALIGN $32
|
||||||
|
```
|
||||||
|
|
||||||
|
Pads the code so that the next instruction lands on the given boundary,
|
||||||
|
which must be a power of two and at least the target's instruction
|
||||||
|
alignment. Supported on amd64, arm64, ppc64, loong64 and riscv64. The
|
||||||
|
padding instructions are the target's NOP encoding, so the bytes between
|
||||||
|
functions differ from what the instruction stream alone would produce, which
|
||||||
|
matters to anyone comparing encodings byte for byte.
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,570 @@
|
|||||||
|
# ARM64: instruction inventory
|
||||||
|
|
||||||
|
Generated by gasm-devkit's `_gen` from the Go toolchain's instruction table
|
||||||
|
(`cmd/internal/obj/arm64/anames.go`, go1.27.1); DO NOT EDIT. This page lists every mnemonic
|
||||||
|
`go tool asm` accepts on this target, which is the upper bound of the
|
||||||
|
language on it: a name absent here is not an instruction of the target,
|
||||||
|
and a name present here may still be one gasm's encoder cannot emit yet.
|
||||||
|
|
||||||
|
The inventory carries no per-mnemonic encoder column: on this target
|
||||||
|
encodability is decided per operand shape, and the live measured
|
||||||
|
coverage is reported by `gasm audit-instructions`.
|
||||||
|
|
||||||
|
| Mnemonic | Notes |
|
||||||
|
|---|---|
|
||||||
|
| `CALL` | |
|
||||||
|
| `DUFFCOPY` | |
|
||||||
|
| `DUFFZERO` | |
|
||||||
|
| `END` | |
|
||||||
|
| `FUNCDATA` | |
|
||||||
|
| `GETCALLERPC` | |
|
||||||
|
| `JMP` | |
|
||||||
|
| `NOP` | No operation |
|
||||||
|
| `PCALIGN` | |
|
||||||
|
| `PCALIGNMAX` | |
|
||||||
|
| `PCDATA` | |
|
||||||
|
| `RET` | Return |
|
||||||
|
| `TEXT` | |
|
||||||
|
| `UNDEF` | |
|
||||||
|
| `ADC` | ADC (64-bit) |
|
||||||
|
| `ADCS` | ADCS (64-bit) |
|
||||||
|
| `ADCSW` | ADCS (32-bit) |
|
||||||
|
| `ADCW` | ADC (32-bit) |
|
||||||
|
| `ADD` | ADD (64-bit) |
|
||||||
|
| `ADDS` | ADDS (64-bit) |
|
||||||
|
| `ADDSW` | ADDS (32-bit) |
|
||||||
|
| `ADDW` | ADD (32-bit) |
|
||||||
|
| `ADR` | Address of label/page |
|
||||||
|
| `ADRP` | Address of label/page |
|
||||||
|
| `AESD` | AES round |
|
||||||
|
| `AESE` | AES round |
|
||||||
|
| `AESIMC` | AES round |
|
||||||
|
| `AESMC` | AES round |
|
||||||
|
| `AND` | AND (64-bit) |
|
||||||
|
| `ANDS` | ANDS (64-bit) |
|
||||||
|
| `ANDSW` | ANDS (32-bit) |
|
||||||
|
| `ANDW` | AND (32-bit) |
|
||||||
|
| `ASR` | ASR shift |
|
||||||
|
| `ASRW` | ASR shift (32-bit) |
|
||||||
|
| `AT` | |
|
||||||
|
| `AUTIA1716` | |
|
||||||
|
| `AUTIASP` | |
|
||||||
|
| `AUTIB1716` | |
|
||||||
|
| `AUTIBSP` | |
|
||||||
|
| `BCC` | Conditional branch |
|
||||||
|
| `BCS` | Conditional branch |
|
||||||
|
| `BEQ` | Conditional branch |
|
||||||
|
| `BFI` | |
|
||||||
|
| `BFIW` | |
|
||||||
|
| `BFM` | |
|
||||||
|
| `BFMW` | |
|
||||||
|
| `BFXIL` | Bitfield extract |
|
||||||
|
| `BFXILW` | |
|
||||||
|
| `BGE` | Conditional branch |
|
||||||
|
| `BGT` | Conditional branch |
|
||||||
|
| `BHI` | Conditional branch |
|
||||||
|
| `BHS` | Conditional branch |
|
||||||
|
| `BIC` | BIC (64-bit) |
|
||||||
|
| `BICS` | BICS (64-bit) |
|
||||||
|
| `BICSW` | BICS (32-bit) |
|
||||||
|
| `BICW` | BIC (32-bit) |
|
||||||
|
| `BLE` | Conditional branch |
|
||||||
|
| `BLO` | Conditional branch |
|
||||||
|
| `BLS` | Conditional branch |
|
||||||
|
| `BLT` | Conditional branch |
|
||||||
|
| `BMI` | Conditional branch |
|
||||||
|
| `BNE` | Conditional branch |
|
||||||
|
| `BPL` | Conditional branch |
|
||||||
|
| `BRK` | Breakpoint |
|
||||||
|
| `BTI` | |
|
||||||
|
| `BVC` | Conditional branch |
|
||||||
|
| `BVS` | Conditional branch |
|
||||||
|
| `CASAD` | |
|
||||||
|
| `CASALB` | |
|
||||||
|
| `CASALD` | |
|
||||||
|
| `CASALH` | |
|
||||||
|
| `CASALW` | |
|
||||||
|
| `CASAW` | |
|
||||||
|
| `CASB` | |
|
||||||
|
| `CASD` | |
|
||||||
|
| `CASH` | |
|
||||||
|
| `CASLD` | |
|
||||||
|
| `CASLW` | |
|
||||||
|
| `CASPD` | |
|
||||||
|
| `CASPW` | |
|
||||||
|
| `CASW` | |
|
||||||
|
| `CBNZ` | Compare/test and branch |
|
||||||
|
| `CBNZW` | Compare/test and branch (32-bit) |
|
||||||
|
| `CBZ` | Compare/test and branch |
|
||||||
|
| `CBZW` | Compare/test and branch (32-bit) |
|
||||||
|
| `CCMN` | Conditional compare |
|
||||||
|
| `CCMNW` | Conditional compare |
|
||||||
|
| `CCMP` | Conditional compare |
|
||||||
|
| `CCMPW` | Conditional compare |
|
||||||
|
| `CINC` | Conditional select |
|
||||||
|
| `CINCW` | Conditional select (32-bit) |
|
||||||
|
| `CINV` | Conditional select |
|
||||||
|
| `CINVW` | Conditional select (32-bit) |
|
||||||
|
| `CLREX` | |
|
||||||
|
| `CLS` | Bit manipulation |
|
||||||
|
| `CLSW` | Bit manipulation |
|
||||||
|
| `CLZ` | Bit manipulation |
|
||||||
|
| `CLZW` | Bit manipulation |
|
||||||
|
| `CMN` | CMN (64-bit) |
|
||||||
|
| `CMNW` | CMN (32-bit) |
|
||||||
|
| `CMP` | CMP (64-bit) |
|
||||||
|
| `CMPW` | CMP (32-bit) |
|
||||||
|
| `CNEG` | Conditional select |
|
||||||
|
| `CNEGW` | Conditional select (32-bit) |
|
||||||
|
| `CRC32B` | |
|
||||||
|
| `CRC32CB` | |
|
||||||
|
| `CRC32CH` | |
|
||||||
|
| `CRC32CW` | |
|
||||||
|
| `CRC32CX` | |
|
||||||
|
| `CRC32H` | |
|
||||||
|
| `CRC32W` | |
|
||||||
|
| `CRC32X` | |
|
||||||
|
| `CSEL` | Conditional select |
|
||||||
|
| `CSELW` | Conditional select (32-bit) |
|
||||||
|
| `CSET` | Conditional select |
|
||||||
|
| `CSETM` | Conditional select |
|
||||||
|
| `CSETMW` | Conditional select (32-bit) |
|
||||||
|
| `CSETW` | Conditional select (32-bit) |
|
||||||
|
| `CSINC` | Conditional select |
|
||||||
|
| `CSINCW` | Conditional select (32-bit) |
|
||||||
|
| `CSINV` | Conditional select |
|
||||||
|
| `CSINVW` | Conditional select (32-bit) |
|
||||||
|
| `CSNEG` | Conditional select |
|
||||||
|
| `CSNEGW` | Conditional select (32-bit) |
|
||||||
|
| `DC` | Data cache maintenance |
|
||||||
|
| `DCPS1` | |
|
||||||
|
| `DCPS2` | |
|
||||||
|
| `DCPS3` | |
|
||||||
|
| `DMB` | Barrier |
|
||||||
|
| `DRPS` | |
|
||||||
|
| `DSB` | Barrier |
|
||||||
|
| `DWORD` | |
|
||||||
|
| `EON` | EON (64-bit) |
|
||||||
|
| `EONW` | EON (32-bit) |
|
||||||
|
| `EOR` | EOR (64-bit) |
|
||||||
|
| `EORW` | EOR (32-bit) |
|
||||||
|
| `ERET` | |
|
||||||
|
| `EXTR` | Bitfield extract |
|
||||||
|
| `EXTRW` | |
|
||||||
|
| `FABSD` | |
|
||||||
|
| `FABSS` | |
|
||||||
|
| `FADDD` | |
|
||||||
|
| `FADDS` | |
|
||||||
|
| `FCCMPD` | |
|
||||||
|
| `FCCMPED` | |
|
||||||
|
| `FCCMPES` | |
|
||||||
|
| `FCCMPS` | |
|
||||||
|
| `FCMPD` | |
|
||||||
|
| `FCMPED` | |
|
||||||
|
| `FCMPES` | |
|
||||||
|
| `FCMPS` | |
|
||||||
|
| `FCSELD` | |
|
||||||
|
| `FCSELS` | |
|
||||||
|
| `FCVTDH` | |
|
||||||
|
| `FCVTDS` | |
|
||||||
|
| `FCVTHD` | |
|
||||||
|
| `FCVTHS` | |
|
||||||
|
| `FCVTSD` | |
|
||||||
|
| `FCVTSH` | |
|
||||||
|
| `FCVTZSD` | |
|
||||||
|
| `FCVTZSDW` | |
|
||||||
|
| `FCVTZSS` | |
|
||||||
|
| `FCVTZSSW` | |
|
||||||
|
| `FCVTZUD` | |
|
||||||
|
| `FCVTZUDW` | |
|
||||||
|
| `FCVTZUS` | |
|
||||||
|
| `FCVTZUSW` | |
|
||||||
|
| `FDIVD` | |
|
||||||
|
| `FDIVS` | |
|
||||||
|
| `FLDPD` | Register-pair load or store |
|
||||||
|
| `FLDPQ` | |
|
||||||
|
| `FLDPS` | |
|
||||||
|
| `FMADDD` | |
|
||||||
|
| `FMADDS` | |
|
||||||
|
| `FMAXD` | |
|
||||||
|
| `FMAXNMD` | |
|
||||||
|
| `FMAXNMS` | |
|
||||||
|
| `FMAXS` | |
|
||||||
|
| `FMIND` | |
|
||||||
|
| `FMINNMD` | |
|
||||||
|
| `FMINNMS` | |
|
||||||
|
| `FMINS` | |
|
||||||
|
| `FMOVD` | Move / load / store |
|
||||||
|
| `FMOVQ` | |
|
||||||
|
| `FMOVS` | Move / load / store |
|
||||||
|
| `FMSUBD` | |
|
||||||
|
| `FMSUBS` | |
|
||||||
|
| `FMULD` | |
|
||||||
|
| `FMULS` | |
|
||||||
|
| `FNEGD` | |
|
||||||
|
| `FNEGS` | |
|
||||||
|
| `FNMADDD` | |
|
||||||
|
| `FNMADDS` | |
|
||||||
|
| `FNMSUBD` | |
|
||||||
|
| `FNMSUBS` | |
|
||||||
|
| `FNMULD` | |
|
||||||
|
| `FNMULS` | |
|
||||||
|
| `FRINTAD` | |
|
||||||
|
| `FRINTAS` | |
|
||||||
|
| `FRINTID` | |
|
||||||
|
| `FRINTIS` | |
|
||||||
|
| `FRINTMD` | |
|
||||||
|
| `FRINTMS` | |
|
||||||
|
| `FRINTND` | |
|
||||||
|
| `FRINTNS` | |
|
||||||
|
| `FRINTPD` | |
|
||||||
|
| `FRINTPS` | |
|
||||||
|
| `FRINTXD` | |
|
||||||
|
| `FRINTXS` | |
|
||||||
|
| `FRINTZD` | |
|
||||||
|
| `FRINTZS` | |
|
||||||
|
| `FSQRTD` | |
|
||||||
|
| `FSQRTS` | |
|
||||||
|
| `FSTPD` | Register-pair load or store |
|
||||||
|
| `FSTPQ` | |
|
||||||
|
| `FSTPS` | |
|
||||||
|
| `FSUBD` | |
|
||||||
|
| `FSUBS` | |
|
||||||
|
| `HINT` | |
|
||||||
|
| `HLT` | |
|
||||||
|
| `HVC` | Exception generation |
|
||||||
|
| `IC` | |
|
||||||
|
| `ISB` | Barrier |
|
||||||
|
| `LDADDAB` | |
|
||||||
|
| `LDADDAD` | |
|
||||||
|
| `LDADDAH` | |
|
||||||
|
| `LDADDALB` | |
|
||||||
|
| `LDADDALD` | |
|
||||||
|
| `LDADDALH` | |
|
||||||
|
| `LDADDALW` | |
|
||||||
|
| `LDADDAW` | |
|
||||||
|
| `LDADDB` | |
|
||||||
|
| `LDADDD` | |
|
||||||
|
| `LDADDH` | |
|
||||||
|
| `LDADDLB` | |
|
||||||
|
| `LDADDLD` | |
|
||||||
|
| `LDADDLH` | |
|
||||||
|
| `LDADDLW` | |
|
||||||
|
| `LDADDW` | |
|
||||||
|
| `LDAR` | Atomic memory operation |
|
||||||
|
| `LDARB` | Atomic memory operation |
|
||||||
|
| `LDARH` | Atomic memory operation |
|
||||||
|
| `LDARW` | Atomic memory operation |
|
||||||
|
| `LDAXP` | |
|
||||||
|
| `LDAXPW` | |
|
||||||
|
| `LDAXR` | Atomic memory operation |
|
||||||
|
| `LDAXRB` | Atomic memory operation |
|
||||||
|
| `LDAXRH` | Atomic memory operation |
|
||||||
|
| `LDAXRW` | Atomic memory operation |
|
||||||
|
| `LDCLRAB` | |
|
||||||
|
| `LDCLRAD` | |
|
||||||
|
| `LDCLRAH` | |
|
||||||
|
| `LDCLRALB` | |
|
||||||
|
| `LDCLRALD` | |
|
||||||
|
| `LDCLRALH` | |
|
||||||
|
| `LDCLRALW` | |
|
||||||
|
| `LDCLRAW` | |
|
||||||
|
| `LDCLRB` | |
|
||||||
|
| `LDCLRD` | |
|
||||||
|
| `LDCLRH` | |
|
||||||
|
| `LDCLRLB` | |
|
||||||
|
| `LDCLRLD` | |
|
||||||
|
| `LDCLRLH` | |
|
||||||
|
| `LDCLRLW` | |
|
||||||
|
| `LDCLRW` | |
|
||||||
|
| `LDEORAB` | |
|
||||||
|
| `LDEORAD` | |
|
||||||
|
| `LDEORAH` | |
|
||||||
|
| `LDEORALB` | |
|
||||||
|
| `LDEORALD` | |
|
||||||
|
| `LDEORALH` | |
|
||||||
|
| `LDEORALW` | |
|
||||||
|
| `LDEORAW` | |
|
||||||
|
| `LDEORB` | |
|
||||||
|
| `LDEORD` | |
|
||||||
|
| `LDEORH` | |
|
||||||
|
| `LDEORLB` | |
|
||||||
|
| `LDEORLD` | |
|
||||||
|
| `LDEORLH` | |
|
||||||
|
| `LDEORLW` | |
|
||||||
|
| `LDEORW` | |
|
||||||
|
| `LDORAB` | |
|
||||||
|
| `LDORAD` | |
|
||||||
|
| `LDORAH` | |
|
||||||
|
| `LDORALB` | |
|
||||||
|
| `LDORALD` | |
|
||||||
|
| `LDORALH` | |
|
||||||
|
| `LDORALW` | |
|
||||||
|
| `LDORAW` | |
|
||||||
|
| `LDORB` | |
|
||||||
|
| `LDORD` | |
|
||||||
|
| `LDORH` | |
|
||||||
|
| `LDORLB` | |
|
||||||
|
| `LDORLD` | |
|
||||||
|
| `LDORLH` | |
|
||||||
|
| `LDORLW` | |
|
||||||
|
| `LDORW` | |
|
||||||
|
| `LDP` | Register-pair load or store |
|
||||||
|
| `LDPSW` | |
|
||||||
|
| `LDPW` | Register-pair load or store |
|
||||||
|
| `LDXP` | |
|
||||||
|
| `LDXPW` | |
|
||||||
|
| `LDXR` | |
|
||||||
|
| `LDXRB` | |
|
||||||
|
| `LDXRH` | |
|
||||||
|
| `LDXRW` | |
|
||||||
|
| `LSL` | LSL shift |
|
||||||
|
| `LSLW` | LSL shift (32-bit) |
|
||||||
|
| `LSR` | LSR shift |
|
||||||
|
| `LSRW` | LSR shift (32-bit) |
|
||||||
|
| `MADD` | Multiply / multiply-accumulate |
|
||||||
|
| `MADDW` | |
|
||||||
|
| `MNEG` | Multiply / multiply-accumulate |
|
||||||
|
| `MNEGW` | |
|
||||||
|
| `MOVB` | Move / load / store |
|
||||||
|
| `MOVBU` | Move / load / store |
|
||||||
|
| `MOVD` | Move / load / store |
|
||||||
|
| `MOVH` | Move / load / store |
|
||||||
|
| `MOVHU` | Move / load / store |
|
||||||
|
| `MOVK` | Move wide constant |
|
||||||
|
| `MOVKW` | Move wide constant |
|
||||||
|
| `MOVN` | Move wide constant |
|
||||||
|
| `MOVNW` | Move wide constant |
|
||||||
|
| `MOVP` | |
|
||||||
|
| `MOVPD` | |
|
||||||
|
| `MOVPQ` | |
|
||||||
|
| `MOVPS` | |
|
||||||
|
| `MOVPSW` | |
|
||||||
|
| `MOVPW` | |
|
||||||
|
| `MOVW` | Move / load / store |
|
||||||
|
| `MOVWU` | Move / load / store |
|
||||||
|
| `MOVZ` | Move wide constant |
|
||||||
|
| `MOVZW` | Move wide constant |
|
||||||
|
| `MRS` | System register access |
|
||||||
|
| `MSR` | System register access |
|
||||||
|
| `MSUB` | Multiply / multiply-accumulate |
|
||||||
|
| `MSUBW` | |
|
||||||
|
| `MUL` | Multiply / multiply-accumulate |
|
||||||
|
| `MULW` | |
|
||||||
|
| `MVN` | MVN (64-bit) |
|
||||||
|
| `MVNW` | MVN (32-bit) |
|
||||||
|
| `NEG` | NEG (64-bit) |
|
||||||
|
| `NEGS` | |
|
||||||
|
| `NEGSW` | |
|
||||||
|
| `NEGW` | NEG (32-bit) |
|
||||||
|
| `NGC` | NGC (64-bit) |
|
||||||
|
| `NGCS` | |
|
||||||
|
| `NGCSW` | |
|
||||||
|
| `NGCW` | NGC (32-bit) |
|
||||||
|
| `NOOP` | |
|
||||||
|
| `ORN` | ORN (64-bit) |
|
||||||
|
| `ORNW` | ORN (32-bit) |
|
||||||
|
| `ORR` | ORR (64-bit) |
|
||||||
|
| `ORRW` | ORR (32-bit) |
|
||||||
|
| `PACIASP` | |
|
||||||
|
| `PACIBSP` | |
|
||||||
|
| `PRFM` | Memory prefetch |
|
||||||
|
| `PRFUM` | |
|
||||||
|
| `RBIT` | Bit manipulation |
|
||||||
|
| `RBITW` | Bit manipulation |
|
||||||
|
| `REM` | |
|
||||||
|
| `REMW` | |
|
||||||
|
| `REV` | Bit manipulation |
|
||||||
|
| `REV16` | Bit manipulation |
|
||||||
|
| `REV16W` | |
|
||||||
|
| `REV32` | Bit manipulation |
|
||||||
|
| `REVW` | Bit manipulation |
|
||||||
|
| `ROR` | ROR shift |
|
||||||
|
| `RORW` | ROR shift (32-bit) |
|
||||||
|
| `SBC` | SBC (64-bit) |
|
||||||
|
| `SBCS` | SBCS (64-bit) |
|
||||||
|
| `SBCSW` | SBCS (32-bit) |
|
||||||
|
| `SBCW` | SBC (32-bit) |
|
||||||
|
| `SBFIZ` | |
|
||||||
|
| `SBFIZW` | |
|
||||||
|
| `SBFM` | Bitfield extract |
|
||||||
|
| `SBFMW` | |
|
||||||
|
| `SBFX` | Bitfield extract |
|
||||||
|
| `SBFXW` | |
|
||||||
|
| `SCVTFD` | |
|
||||||
|
| `SCVTFS` | |
|
||||||
|
| `SCVTFWD` | |
|
||||||
|
| `SCVTFWS` | |
|
||||||
|
| `SDIV` | Divide |
|
||||||
|
| `SDIVW` | Divide |
|
||||||
|
| `SEV` | |
|
||||||
|
| `SEVL` | |
|
||||||
|
| `SHA1C` | SHA round |
|
||||||
|
| `SHA1H` | SHA round |
|
||||||
|
| `SHA1M` | SHA round |
|
||||||
|
| `SHA1P` | SHA round |
|
||||||
|
| `SHA1SU0` | SHA round |
|
||||||
|
| `SHA1SU1` | SHA round |
|
||||||
|
| `SHA256H` | SHA round |
|
||||||
|
| `SHA256H2` | SHA round |
|
||||||
|
| `SHA256SU0` | SHA round |
|
||||||
|
| `SHA256SU1` | SHA round |
|
||||||
|
| `SHA512H` | SHA round |
|
||||||
|
| `SHA512H2` | SHA round |
|
||||||
|
| `SHA512SU0` | SHA round |
|
||||||
|
| `SHA512SU1` | SHA round |
|
||||||
|
| `SMADDL` | Multiply / multiply-accumulate |
|
||||||
|
| `SMC` | Exception generation |
|
||||||
|
| `SMNEGL` | |
|
||||||
|
| `SMSUBL` | Multiply / multiply-accumulate |
|
||||||
|
| `SMULH` | Multiply / multiply-accumulate |
|
||||||
|
| `SMULL` | Multiply / multiply-accumulate |
|
||||||
|
| `STLR` | Atomic memory operation |
|
||||||
|
| `STLRB` | Atomic memory operation |
|
||||||
|
| `STLRH` | Atomic memory operation |
|
||||||
|
| `STLRW` | Atomic memory operation |
|
||||||
|
| `STLXP` | |
|
||||||
|
| `STLXPW` | |
|
||||||
|
| `STLXR` | |
|
||||||
|
| `STLXRB` | |
|
||||||
|
| `STLXRH` | |
|
||||||
|
| `STLXRW` | |
|
||||||
|
| `STP` | Register-pair load or store |
|
||||||
|
| `STPW` | Register-pair load or store |
|
||||||
|
| `STXP` | |
|
||||||
|
| `STXPW` | |
|
||||||
|
| `STXR` | Atomic memory operation |
|
||||||
|
| `STXRB` | Atomic memory operation |
|
||||||
|
| `STXRH` | Atomic memory operation |
|
||||||
|
| `STXRW` | Atomic memory operation |
|
||||||
|
| `SUB` | SUB (64-bit) |
|
||||||
|
| `SUBS` | SUBS (64-bit) |
|
||||||
|
| `SUBSW` | SUBS (32-bit) |
|
||||||
|
| `SUBW` | SUB (32-bit) |
|
||||||
|
| `SVC` | Exception generation |
|
||||||
|
| `SWPAB` | |
|
||||||
|
| `SWPAD` | |
|
||||||
|
| `SWPAH` | |
|
||||||
|
| `SWPALB` | |
|
||||||
|
| `SWPALD` | |
|
||||||
|
| `SWPALH` | |
|
||||||
|
| `SWPALW` | |
|
||||||
|
| `SWPAW` | |
|
||||||
|
| `SWPB` | |
|
||||||
|
| `SWPD` | |
|
||||||
|
| `SWPH` | |
|
||||||
|
| `SWPLB` | |
|
||||||
|
| `SWPLD` | |
|
||||||
|
| `SWPLH` | |
|
||||||
|
| `SWPLW` | |
|
||||||
|
| `SWPW` | |
|
||||||
|
| `SXTB` | |
|
||||||
|
| `SXTBW` | |
|
||||||
|
| `SXTH` | |
|
||||||
|
| `SXTHW` | |
|
||||||
|
| `SXTW` | |
|
||||||
|
| `SYS` | |
|
||||||
|
| `SYSL` | |
|
||||||
|
| `TBNZ` | Compare/test and branch |
|
||||||
|
| `TBZ` | Compare/test and branch |
|
||||||
|
| `TLBI` | |
|
||||||
|
| `TST` | TST (64-bit) |
|
||||||
|
| `TSTW` | TST (32-bit) |
|
||||||
|
| `UBFIZ` | |
|
||||||
|
| `UBFIZW` | |
|
||||||
|
| `UBFM` | Bitfield extract |
|
||||||
|
| `UBFMW` | |
|
||||||
|
| `UBFX` | Bitfield extract |
|
||||||
|
| `UBFXW` | |
|
||||||
|
| `UCVTFD` | |
|
||||||
|
| `UCVTFS` | |
|
||||||
|
| `UCVTFWD` | |
|
||||||
|
| `UCVTFWS` | |
|
||||||
|
| `UDIV` | Divide |
|
||||||
|
| `UDIVW` | Divide |
|
||||||
|
| `UMADDL` | Multiply / multiply-accumulate |
|
||||||
|
| `UMNEGL` | |
|
||||||
|
| `UMSUBL` | Multiply / multiply-accumulate |
|
||||||
|
| `UMULH` | Multiply / multiply-accumulate |
|
||||||
|
| `UMULL` | Multiply / multiply-accumulate |
|
||||||
|
| `UREM` | |
|
||||||
|
| `UREMW` | |
|
||||||
|
| `UXTB` | |
|
||||||
|
| `UXTBW` | |
|
||||||
|
| `UXTH` | |
|
||||||
|
| `UXTHW` | |
|
||||||
|
| `UXTW` | |
|
||||||
|
| `VADD` | NEON SIMD vector operation |
|
||||||
|
| `VADDP` | |
|
||||||
|
| `VADDV` | NEON SIMD vector operation |
|
||||||
|
| `VAND` | NEON SIMD vector operation |
|
||||||
|
| `VBCAX` | Three-way XOR / rotate crypto vector operation |
|
||||||
|
| `VBIF` | NEON SIMD vector operation |
|
||||||
|
| `VBIT` | |
|
||||||
|
| `VBSL` | NEON SIMD vector operation |
|
||||||
|
| `VCMEQ` | |
|
||||||
|
| `VCMTST` | |
|
||||||
|
| `VCNT` | NEON SIMD vector operation |
|
||||||
|
| `VDUP` | NEON SIMD vector operation |
|
||||||
|
| `VEOR` | NEON SIMD vector operation |
|
||||||
|
| `VEOR3` | Three-way XOR / rotate crypto vector operation |
|
||||||
|
| `VEXT` | NEON SIMD vector operation |
|
||||||
|
| `VFMLA` | NEON SIMD vector operation |
|
||||||
|
| `VFMLS` | NEON SIMD vector operation |
|
||||||
|
| `VLD1` | NEON SIMD vector operation |
|
||||||
|
| `VLD1R` | |
|
||||||
|
| `VLD2` | NEON SIMD vector operation |
|
||||||
|
| `VLD2R` | |
|
||||||
|
| `VLD3` | NEON SIMD vector operation |
|
||||||
|
| `VLD3R` | |
|
||||||
|
| `VLD4` | NEON SIMD vector operation |
|
||||||
|
| `VLD4R` | |
|
||||||
|
| `VMOV` | NEON SIMD vector operation |
|
||||||
|
| `VMOVD` | |
|
||||||
|
| `VMOVI` | NEON SIMD vector operation |
|
||||||
|
| `VMOVQ` | NEON SIMD vector operation |
|
||||||
|
| `VMOVS` | |
|
||||||
|
| `VORR` | NEON SIMD vector operation |
|
||||||
|
| `VPMULL` | |
|
||||||
|
| `VPMULL2` | |
|
||||||
|
| `VRAX1` | Three-way XOR / rotate crypto vector operation |
|
||||||
|
| `VRBIT` | |
|
||||||
|
| `VREV16` | NEON SIMD vector operation |
|
||||||
|
| `VREV32` | NEON SIMD vector operation |
|
||||||
|
| `VREV64` | NEON SIMD vector operation |
|
||||||
|
| `VSHL` | NEON SIMD vector operation |
|
||||||
|
| `VSLI` | |
|
||||||
|
| `VSRI` | |
|
||||||
|
| `VST1` | NEON SIMD vector operation |
|
||||||
|
| `VST2` | NEON SIMD vector operation |
|
||||||
|
| `VST3` | NEON SIMD vector operation |
|
||||||
|
| `VST4` | NEON SIMD vector operation |
|
||||||
|
| `VSUB` | NEON SIMD vector operation |
|
||||||
|
| `VTBL` | NEON SIMD vector operation |
|
||||||
|
| `VTBX` | NEON SIMD vector operation |
|
||||||
|
| `VTRN1` | NEON SIMD vector operation |
|
||||||
|
| `VTRN2` | NEON SIMD vector operation |
|
||||||
|
| `VUADDLV` | |
|
||||||
|
| `VUADDW` | |
|
||||||
|
| `VUADDW2` | |
|
||||||
|
| `VUMAX` | |
|
||||||
|
| `VUMIN` | |
|
||||||
|
| `VUSHLL` | |
|
||||||
|
| `VUSHLL2` | |
|
||||||
|
| `VUSHR` | NEON SIMD vector operation |
|
||||||
|
| `VUSRA` | |
|
||||||
|
| `VUXTL` | |
|
||||||
|
| `VUXTL2` | |
|
||||||
|
| `VUZP1` | NEON SIMD vector operation |
|
||||||
|
| `VUZP2` | NEON SIMD vector operation |
|
||||||
|
| `VXAR` | Three-way XOR / rotate crypto vector operation |
|
||||||
|
| `VZIP1` | NEON SIMD vector operation |
|
||||||
|
| `VZIP2` | NEON SIMD vector operation |
|
||||||
|
| `WFE` | |
|
||||||
|
| `WFI` | |
|
||||||
|
| `WORD` | |
|
||||||
|
| `YIELD` | |
|
||||||
|
| `B` | Unconditional branch |
|
||||||
|
| `BL` | Branch with link |
|
||||||
|
|
||||||
|
Recognised: 554 mnemonics.
|
||||||
@@ -0,0 +1,830 @@
|
|||||||
|
# LoongArch 64: instruction inventory
|
||||||
|
|
||||||
|
Generated by gasm-devkit's `_gen` from the Go toolchain's instruction table
|
||||||
|
(`cmd/internal/obj/loong64/anames.go`, go1.27.1); DO NOT EDIT. This page lists every mnemonic
|
||||||
|
`go tool asm` accepts on this target, which is the upper bound of the
|
||||||
|
language on it: a name absent here is not an instruction of the target,
|
||||||
|
and a name present here may still be one gasm's encoder cannot emit yet.
|
||||||
|
|
||||||
|
The inventory carries no per-mnemonic encoder column: on this target
|
||||||
|
encodability is decided per operand shape, and the live measured
|
||||||
|
coverage is reported by `gasm audit-instructions`.
|
||||||
|
|
||||||
|
| Mnemonic | Notes |
|
||||||
|
|---|---|
|
||||||
|
| `CALL` | |
|
||||||
|
| `DUFFCOPY` | |
|
||||||
|
| `DUFFZERO` | |
|
||||||
|
| `END` | |
|
||||||
|
| `FUNCDATA` | |
|
||||||
|
| `GETCALLERPC` | |
|
||||||
|
| `JMP` | |
|
||||||
|
| `NOP` | No operation |
|
||||||
|
| `PCALIGN` | |
|
||||||
|
| `PCALIGNMAX` | |
|
||||||
|
| `PCDATA` | |
|
||||||
|
| `RET` | Return |
|
||||||
|
| `TEXT` | |
|
||||||
|
| `UNDEF` | |
|
||||||
|
| `ABSD` | |
|
||||||
|
| `ABSF` | |
|
||||||
|
| `ADD` | Integer add (word) |
|
||||||
|
| `ADDD` | Add doubleword |
|
||||||
|
| `ADDF` | |
|
||||||
|
| `ADDV` | |
|
||||||
|
| `ADDV16` | |
|
||||||
|
| `ADDVU` | |
|
||||||
|
| `ADDW` | Add word |
|
||||||
|
| `ALSLV` | |
|
||||||
|
| `ALSLW` | |
|
||||||
|
| `ALSLWU` | |
|
||||||
|
| `AMADDDBV` | |
|
||||||
|
| `AMADDDBW` | |
|
||||||
|
| `AMADDV` | |
|
||||||
|
| `AMADDW` | |
|
||||||
|
| `AMANDDBV` | |
|
||||||
|
| `AMANDDBW` | |
|
||||||
|
| `AMANDV` | |
|
||||||
|
| `AMANDW` | |
|
||||||
|
| `AMCASB` | |
|
||||||
|
| `AMCASDBB` | |
|
||||||
|
| `AMCASDBH` | |
|
||||||
|
| `AMCASDBV` | |
|
||||||
|
| `AMCASDBW` | |
|
||||||
|
| `AMCASH` | |
|
||||||
|
| `AMCASV` | |
|
||||||
|
| `AMCASW` | |
|
||||||
|
| `AMMAXDBV` | |
|
||||||
|
| `AMMAXDBVU` | |
|
||||||
|
| `AMMAXDBW` | |
|
||||||
|
| `AMMAXDBWU` | |
|
||||||
|
| `AMMAXV` | |
|
||||||
|
| `AMMAXVU` | |
|
||||||
|
| `AMMAXW` | |
|
||||||
|
| `AMMAXWU` | |
|
||||||
|
| `AMMINDBV` | |
|
||||||
|
| `AMMINDBVU` | |
|
||||||
|
| `AMMINDBW` | |
|
||||||
|
| `AMMINDBWU` | |
|
||||||
|
| `AMMINV` | |
|
||||||
|
| `AMMINVU` | |
|
||||||
|
| `AMMINW` | |
|
||||||
|
| `AMMINWU` | |
|
||||||
|
| `AMORDBV` | |
|
||||||
|
| `AMORDBW` | |
|
||||||
|
| `AMORV` | |
|
||||||
|
| `AMORW` | |
|
||||||
|
| `AMSWAPB` | |
|
||||||
|
| `AMSWAPDBB` | |
|
||||||
|
| `AMSWAPDBH` | |
|
||||||
|
| `AMSWAPDBV` | |
|
||||||
|
| `AMSWAPDBW` | |
|
||||||
|
| `AMSWAPH` | |
|
||||||
|
| `AMSWAPV` | |
|
||||||
|
| `AMSWAPW` | |
|
||||||
|
| `AMXORDBV` | |
|
||||||
|
| `AMXORDBW` | |
|
||||||
|
| `AMXORV` | |
|
||||||
|
| `AMXORW` | |
|
||||||
|
| `AND` | Bitwise AND |
|
||||||
|
| `ANDN` | |
|
||||||
|
| `BEQ` | Branch if equal |
|
||||||
|
| `BFPF` | |
|
||||||
|
| `BFPT` | |
|
||||||
|
| `BGE` | Branch if greater or equal |
|
||||||
|
| `BGEU` | Branch if greater or equal unsigned |
|
||||||
|
| `BGEZ` | |
|
||||||
|
| `BGTZ` | |
|
||||||
|
| `BITREV4B` | |
|
||||||
|
| `BITREV8B` | |
|
||||||
|
| `BITREVV` | |
|
||||||
|
| `BITREVW` | |
|
||||||
|
| `BLEZ` | |
|
||||||
|
| `BLT` | Branch if less than |
|
||||||
|
| `BLTU` | Branch if less than unsigned |
|
||||||
|
| `BLTZ` | |
|
||||||
|
| `BNE` | Branch if not equal |
|
||||||
|
| `BREAK` | Breakpoint |
|
||||||
|
| `BSTRINSV` | |
|
||||||
|
| `BSTRINSW` | |
|
||||||
|
| `BSTRPICKV` | |
|
||||||
|
| `BSTRPICKW` | |
|
||||||
|
| `CLOV` | |
|
||||||
|
| `CLOW` | |
|
||||||
|
| `CLZV` | |
|
||||||
|
| `CLZW` | |
|
||||||
|
| `CMPEQD` | |
|
||||||
|
| `CMPEQF` | |
|
||||||
|
| `CMPGED` | |
|
||||||
|
| `CMPGEF` | |
|
||||||
|
| `CMPGTD` | |
|
||||||
|
| `CMPGTF` | |
|
||||||
|
| `CPUCFG` | |
|
||||||
|
| `CRCCWBW` | |
|
||||||
|
| `CRCCWHW` | |
|
||||||
|
| `CRCCWVW` | |
|
||||||
|
| `CRCCWWW` | |
|
||||||
|
| `CRCWBW` | |
|
||||||
|
| `CRCWHW` | |
|
||||||
|
| `CRCWVW` | |
|
||||||
|
| `CRCWWW` | |
|
||||||
|
| `CTOV` | |
|
||||||
|
| `CTOW` | |
|
||||||
|
| `CTZV` | |
|
||||||
|
| `CTZW` | |
|
||||||
|
| `DBAR` | Barrier |
|
||||||
|
| `DIV` | Divide (word) |
|
||||||
|
| `DIVD` | Divide doubleword |
|
||||||
|
| `DIVF` | |
|
||||||
|
| `DIVU` | |
|
||||||
|
| `DIVV` | |
|
||||||
|
| `DIVVU` | |
|
||||||
|
| `DIVW` | Divide word |
|
||||||
|
| `DIVWU` | |
|
||||||
|
| `EXTWB` | |
|
||||||
|
| `EXTWH` | |
|
||||||
|
| `FCLASSD` | |
|
||||||
|
| `FCLASSF` | |
|
||||||
|
| `FCOPYSGD` | |
|
||||||
|
| `FCOPYSGF` | |
|
||||||
|
| `FFINTDV` | |
|
||||||
|
| `FFINTDW` | |
|
||||||
|
| `FFINTFV` | |
|
||||||
|
| `FFINTFW` | |
|
||||||
|
| `FLOGBD` | |
|
||||||
|
| `FLOGBF` | |
|
||||||
|
| `FMADDD` | |
|
||||||
|
| `FMADDF` | |
|
||||||
|
| `FMAXAD` | |
|
||||||
|
| `FMAXAF` | |
|
||||||
|
| `FMAXD` | |
|
||||||
|
| `FMAXF` | |
|
||||||
|
| `FMINAD` | |
|
||||||
|
| `FMINAF` | |
|
||||||
|
| `FMIND` | |
|
||||||
|
| `FMINF` | |
|
||||||
|
| `FMSUBD` | |
|
||||||
|
| `FMSUBF` | |
|
||||||
|
| `FNMADDD` | |
|
||||||
|
| `FNMADDF` | |
|
||||||
|
| `FNMSUBD` | |
|
||||||
|
| `FNMSUBF` | |
|
||||||
|
| `FSCALEBD` | |
|
||||||
|
| `FSCALEBF` | |
|
||||||
|
| `FSEL` | |
|
||||||
|
| `FTINTRMVD` | |
|
||||||
|
| `FTINTRMVF` | |
|
||||||
|
| `FTINTRMWD` | |
|
||||||
|
| `FTINTRMWF` | |
|
||||||
|
| `FTINTRNEVD` | |
|
||||||
|
| `FTINTRNEVF` | |
|
||||||
|
| `FTINTRNEWD` | |
|
||||||
|
| `FTINTRNEWF` | |
|
||||||
|
| `FTINTRPVD` | |
|
||||||
|
| `FTINTRPVF` | |
|
||||||
|
| `FTINTRPWD` | |
|
||||||
|
| `FTINTRPWF` | |
|
||||||
|
| `FTINTRZVD` | |
|
||||||
|
| `FTINTRZVF` | |
|
||||||
|
| `FTINTRZWD` | |
|
||||||
|
| `FTINTRZWF` | |
|
||||||
|
| `FTINTVD` | |
|
||||||
|
| `FTINTVF` | |
|
||||||
|
| `FTINTWD` | |
|
||||||
|
| `FTINTWF` | |
|
||||||
|
| `JIRL` | Jump indirect with link |
|
||||||
|
| `LL` | |
|
||||||
|
| `LLV` | |
|
||||||
|
| `LU12IW` | |
|
||||||
|
| `LU32ID` | |
|
||||||
|
| `LU52ID` | |
|
||||||
|
| `LUI` | |
|
||||||
|
| `MASKEQZ` | |
|
||||||
|
| `MASKNEZ` | |
|
||||||
|
| `MOVB` | |
|
||||||
|
| `MOVBU` | |
|
||||||
|
| `MOVD` | |
|
||||||
|
| `MOVDF` | |
|
||||||
|
| `MOVDV` | |
|
||||||
|
| `MOVDW` | |
|
||||||
|
| `MOVF` | |
|
||||||
|
| `MOVFD` | |
|
||||||
|
| `MOVFV` | |
|
||||||
|
| `MOVFW` | |
|
||||||
|
| `MOVH` | |
|
||||||
|
| `MOVHU` | |
|
||||||
|
| `MOVV` | |
|
||||||
|
| `MOVVD` | |
|
||||||
|
| `MOVVF` | |
|
||||||
|
| `MOVVP` | |
|
||||||
|
| `MOVW` | |
|
||||||
|
| `MOVWD` | |
|
||||||
|
| `MOVWF` | |
|
||||||
|
| `MOVWP` | |
|
||||||
|
| `MOVWU` | |
|
||||||
|
| `MUL` | Multiply (word) |
|
||||||
|
| `MULD` | Multiply doubleword |
|
||||||
|
| `MULF` | |
|
||||||
|
| `MULH` | |
|
||||||
|
| `MULHU` | |
|
||||||
|
| `MULHV` | |
|
||||||
|
| `MULHVU` | |
|
||||||
|
| `MULV` | |
|
||||||
|
| `MULVU` | |
|
||||||
|
| `MULW` | Multiply word |
|
||||||
|
| `MULWVW` | |
|
||||||
|
| `MULWVWU` | |
|
||||||
|
| `NEGD` | |
|
||||||
|
| `NEGF` | |
|
||||||
|
| `NEGV` | |
|
||||||
|
| `NEGW` | |
|
||||||
|
| `NOOP` | |
|
||||||
|
| `NOR` | Bitwise NOR |
|
||||||
|
| `OR` | Bitwise OR |
|
||||||
|
| `ORN` | |
|
||||||
|
| `PCADDU12I` | |
|
||||||
|
| `PCALAU12I` | |
|
||||||
|
| `PRELD` | |
|
||||||
|
| `PRELDX` | |
|
||||||
|
| `RDTIMED` | |
|
||||||
|
| `RDTIMEHW` | |
|
||||||
|
| `RDTIMELW` | |
|
||||||
|
| `REM` | |
|
||||||
|
| `REMU` | |
|
||||||
|
| `REMV` | |
|
||||||
|
| `REMVU` | |
|
||||||
|
| `REMW` | |
|
||||||
|
| `REMWU` | |
|
||||||
|
| `REVB2H` | |
|
||||||
|
| `REVB2W` | |
|
||||||
|
| `REVB4H` | |
|
||||||
|
| `REVBV` | |
|
||||||
|
| `REVH2W` | |
|
||||||
|
| `REVHV` | |
|
||||||
|
| `RFE` | |
|
||||||
|
| `ROTR` | Rotate right |
|
||||||
|
| `ROTRV` | |
|
||||||
|
| `SC` | |
|
||||||
|
| `SCV` | |
|
||||||
|
| `SGT` | |
|
||||||
|
| `SGTU` | |
|
||||||
|
| `SLL` | Shift left logical |
|
||||||
|
| `SLLV` | |
|
||||||
|
| `SQRTD` | |
|
||||||
|
| `SQRTF` | |
|
||||||
|
| `SRA` | Shift right arithmetic |
|
||||||
|
| `SRAV` | |
|
||||||
|
| `SRL` | Shift right logical |
|
||||||
|
| `SRLV` | |
|
||||||
|
| `SUB` | Subtract (word) |
|
||||||
|
| `SUBD` | Subtract doubleword |
|
||||||
|
| `SUBF` | |
|
||||||
|
| `SUBV` | |
|
||||||
|
| `SUBVU` | |
|
||||||
|
| `SUBW` | Subtract word |
|
||||||
|
| `SYSCALL` | System call |
|
||||||
|
| `TEQ` | |
|
||||||
|
| `TNE` | |
|
||||||
|
| `TRUNCDV` | |
|
||||||
|
| `TRUNCDW` | |
|
||||||
|
| `TRUNCFV` | |
|
||||||
|
| `TRUNCFW` | |
|
||||||
|
| `VADDB` | |
|
||||||
|
| `VADDBU` | |
|
||||||
|
| `VADDD` | |
|
||||||
|
| `VADDF` | |
|
||||||
|
| `VADDH` | |
|
||||||
|
| `VADDHU` | |
|
||||||
|
| `VADDQ` | |
|
||||||
|
| `VADDV` | |
|
||||||
|
| `VADDVU` | |
|
||||||
|
| `VADDW` | |
|
||||||
|
| `VADDWEVHB` | |
|
||||||
|
| `VADDWEVHBU` | |
|
||||||
|
| `VADDWEVQV` | |
|
||||||
|
| `VADDWEVQVU` | |
|
||||||
|
| `VADDWEVVW` | |
|
||||||
|
| `VADDWEVVWU` | |
|
||||||
|
| `VADDWEVWH` | |
|
||||||
|
| `VADDWEVWHU` | |
|
||||||
|
| `VADDWODHB` | |
|
||||||
|
| `VADDWODHBU` | |
|
||||||
|
| `VADDWODQV` | |
|
||||||
|
| `VADDWODQVU` | |
|
||||||
|
| `VADDWODVW` | |
|
||||||
|
| `VADDWODVWU` | |
|
||||||
|
| `VADDWODWH` | |
|
||||||
|
| `VADDWODWHU` | |
|
||||||
|
| `VADDWU` | |
|
||||||
|
| `VANDB` | |
|
||||||
|
| `VANDNV` | |
|
||||||
|
| `VANDV` | |
|
||||||
|
| `VBITCLRB` | |
|
||||||
|
| `VBITCLRH` | |
|
||||||
|
| `VBITCLRV` | |
|
||||||
|
| `VBITCLRW` | |
|
||||||
|
| `VBITREVB` | |
|
||||||
|
| `VBITREVH` | |
|
||||||
|
| `VBITREVV` | |
|
||||||
|
| `VBITREVW` | |
|
||||||
|
| `VBITSETB` | |
|
||||||
|
| `VBITSETH` | |
|
||||||
|
| `VBITSETV` | |
|
||||||
|
| `VBITSETW` | |
|
||||||
|
| `VDIVB` | |
|
||||||
|
| `VDIVBU` | |
|
||||||
|
| `VDIVD` | |
|
||||||
|
| `VDIVF` | |
|
||||||
|
| `VDIVH` | |
|
||||||
|
| `VDIVHU` | |
|
||||||
|
| `VDIVV` | |
|
||||||
|
| `VDIVVU` | |
|
||||||
|
| `VDIVW` | |
|
||||||
|
| `VDIVWU` | |
|
||||||
|
| `VEXTRINSB` | |
|
||||||
|
| `VEXTRINSH` | |
|
||||||
|
| `VEXTRINSV` | |
|
||||||
|
| `VEXTRINSW` | |
|
||||||
|
| `VFCLASSD` | |
|
||||||
|
| `VFCLASSF` | |
|
||||||
|
| `VFRECIPD` | |
|
||||||
|
| `VFRECIPF` | |
|
||||||
|
| `VFRINTD` | |
|
||||||
|
| `VFRINTF` | |
|
||||||
|
| `VFRINTRMD` | |
|
||||||
|
| `VFRINTRMF` | |
|
||||||
|
| `VFRINTRNED` | |
|
||||||
|
| `VFRINTRNEF` | |
|
||||||
|
| `VFRINTRPD` | |
|
||||||
|
| `VFRINTRPF` | |
|
||||||
|
| `VFRINTRZD` | |
|
||||||
|
| `VFRINTRZF` | |
|
||||||
|
| `VFRSQRTD` | |
|
||||||
|
| `VFRSQRTF` | |
|
||||||
|
| `VFSQRTD` | |
|
||||||
|
| `VFSQRTF` | |
|
||||||
|
| `VILVHB` | |
|
||||||
|
| `VILVHH` | |
|
||||||
|
| `VILVHV` | |
|
||||||
|
| `VILVHW` | |
|
||||||
|
| `VILVLB` | |
|
||||||
|
| `VILVLH` | |
|
||||||
|
| `VILVLV` | |
|
||||||
|
| `VILVLW` | |
|
||||||
|
| `VMADDB` | |
|
||||||
|
| `VMADDH` | |
|
||||||
|
| `VMADDV` | |
|
||||||
|
| `VMADDW` | |
|
||||||
|
| `VMADDWEVHB` | |
|
||||||
|
| `VMADDWEVHBU` | |
|
||||||
|
| `VMADDWEVHBUB` | |
|
||||||
|
| `VMADDWEVQV` | |
|
||||||
|
| `VMADDWEVQVU` | |
|
||||||
|
| `VMADDWEVQVUV` | |
|
||||||
|
| `VMADDWEVVW` | |
|
||||||
|
| `VMADDWEVVWU` | |
|
||||||
|
| `VMADDWEVVWUW` | |
|
||||||
|
| `VMADDWEVWH` | |
|
||||||
|
| `VMADDWEVWHU` | |
|
||||||
|
| `VMADDWEVWHUH` | |
|
||||||
|
| `VMADDWODHB` | |
|
||||||
|
| `VMADDWODHBU` | |
|
||||||
|
| `VMADDWODHBUB` | |
|
||||||
|
| `VMADDWODQV` | |
|
||||||
|
| `VMADDWODQVU` | |
|
||||||
|
| `VMADDWODQVUV` | |
|
||||||
|
| `VMADDWODVW` | |
|
||||||
|
| `VMADDWODVWU` | |
|
||||||
|
| `VMADDWODVWUW` | |
|
||||||
|
| `VMADDWODWH` | |
|
||||||
|
| `VMADDWODWHU` | |
|
||||||
|
| `VMADDWODWHUH` | |
|
||||||
|
| `VMODB` | |
|
||||||
|
| `VMODBU` | |
|
||||||
|
| `VMODH` | |
|
||||||
|
| `VMODHU` | |
|
||||||
|
| `VMODV` | |
|
||||||
|
| `VMODVU` | |
|
||||||
|
| `VMODW` | |
|
||||||
|
| `VMODWU` | |
|
||||||
|
| `VMOVQ` | |
|
||||||
|
| `VMSUBB` | |
|
||||||
|
| `VMSUBH` | |
|
||||||
|
| `VMSUBV` | |
|
||||||
|
| `VMSUBW` | |
|
||||||
|
| `VMUHB` | |
|
||||||
|
| `VMUHBU` | |
|
||||||
|
| `VMUHH` | |
|
||||||
|
| `VMUHHU` | |
|
||||||
|
| `VMUHV` | |
|
||||||
|
| `VMUHVU` | |
|
||||||
|
| `VMUHW` | |
|
||||||
|
| `VMUHWU` | |
|
||||||
|
| `VMULB` | |
|
||||||
|
| `VMULD` | |
|
||||||
|
| `VMULF` | |
|
||||||
|
| `VMULH` | |
|
||||||
|
| `VMULV` | |
|
||||||
|
| `VMULW` | |
|
||||||
|
| `VMULWEVHB` | |
|
||||||
|
| `VMULWEVHBU` | |
|
||||||
|
| `VMULWEVHBUB` | |
|
||||||
|
| `VMULWEVQV` | |
|
||||||
|
| `VMULWEVQVU` | |
|
||||||
|
| `VMULWEVQVUV` | |
|
||||||
|
| `VMULWEVVW` | |
|
||||||
|
| `VMULWEVVWU` | |
|
||||||
|
| `VMULWEVVWUW` | |
|
||||||
|
| `VMULWEVWH` | |
|
||||||
|
| `VMULWEVWHU` | |
|
||||||
|
| `VMULWEVWHUH` | |
|
||||||
|
| `VMULWODHB` | |
|
||||||
|
| `VMULWODHBU` | |
|
||||||
|
| `VMULWODHBUB` | |
|
||||||
|
| `VMULWODQV` | |
|
||||||
|
| `VMULWODQVU` | |
|
||||||
|
| `VMULWODQVUV` | |
|
||||||
|
| `VMULWODVW` | |
|
||||||
|
| `VMULWODVWU` | |
|
||||||
|
| `VMULWODVWUW` | |
|
||||||
|
| `VMULWODWH` | |
|
||||||
|
| `VMULWODWHU` | |
|
||||||
|
| `VMULWODWHUH` | |
|
||||||
|
| `VNEGB` | |
|
||||||
|
| `VNEGH` | |
|
||||||
|
| `VNEGV` | |
|
||||||
|
| `VNEGW` | |
|
||||||
|
| `VNORB` | |
|
||||||
|
| `VNORV` | |
|
||||||
|
| `VORB` | |
|
||||||
|
| `VORNV` | |
|
||||||
|
| `VORV` | |
|
||||||
|
| `VPCNTB` | |
|
||||||
|
| `VPCNTH` | |
|
||||||
|
| `VPCNTV` | |
|
||||||
|
| `VPCNTW` | |
|
||||||
|
| `VPERMIW` | |
|
||||||
|
| `VROTRB` | |
|
||||||
|
| `VROTRH` | |
|
||||||
|
| `VROTRV` | |
|
||||||
|
| `VROTRW` | |
|
||||||
|
| `VSADDB` | |
|
||||||
|
| `VSADDBU` | |
|
||||||
|
| `VSADDH` | |
|
||||||
|
| `VSADDHU` | |
|
||||||
|
| `VSADDV` | |
|
||||||
|
| `VSADDVU` | |
|
||||||
|
| `VSADDW` | |
|
||||||
|
| `VSADDWU` | |
|
||||||
|
| `VSEQB` | |
|
||||||
|
| `VSEQH` | |
|
||||||
|
| `VSEQV` | |
|
||||||
|
| `VSEQW` | |
|
||||||
|
| `VSETALLNEB` | |
|
||||||
|
| `VSETALLNEH` | |
|
||||||
|
| `VSETALLNEV` | |
|
||||||
|
| `VSETALLNEW` | |
|
||||||
|
| `VSETANYEQB` | |
|
||||||
|
| `VSETANYEQH` | |
|
||||||
|
| `VSETANYEQV` | |
|
||||||
|
| `VSETANYEQW` | |
|
||||||
|
| `VSETEQV` | |
|
||||||
|
| `VSETNEV` | |
|
||||||
|
| `VSHUF4IB` | |
|
||||||
|
| `VSHUF4IH` | |
|
||||||
|
| `VSHUF4IV` | |
|
||||||
|
| `VSHUF4IW` | |
|
||||||
|
| `VSHUFB` | |
|
||||||
|
| `VSHUFH` | |
|
||||||
|
| `VSHUFV` | |
|
||||||
|
| `VSHUFW` | |
|
||||||
|
| `VSLLB` | |
|
||||||
|
| `VSLLH` | |
|
||||||
|
| `VSLLV` | |
|
||||||
|
| `VSLLW` | |
|
||||||
|
| `VSLTB` | |
|
||||||
|
| `VSLTBU` | |
|
||||||
|
| `VSLTH` | |
|
||||||
|
| `VSLTHU` | |
|
||||||
|
| `VSLTV` | |
|
||||||
|
| `VSLTVU` | |
|
||||||
|
| `VSLTW` | |
|
||||||
|
| `VSLTWU` | |
|
||||||
|
| `VSRAB` | |
|
||||||
|
| `VSRAH` | |
|
||||||
|
| `VSRAV` | |
|
||||||
|
| `VSRAW` | |
|
||||||
|
| `VSRLB` | |
|
||||||
|
| `VSRLH` | |
|
||||||
|
| `VSRLV` | |
|
||||||
|
| `VSRLW` | |
|
||||||
|
| `VSSUBB` | |
|
||||||
|
| `VSSUBBU` | |
|
||||||
|
| `VSSUBH` | |
|
||||||
|
| `VSSUBHU` | |
|
||||||
|
| `VSSUBV` | |
|
||||||
|
| `VSSUBVU` | |
|
||||||
|
| `VSSUBW` | |
|
||||||
|
| `VSSUBWU` | |
|
||||||
|
| `VSUBB` | |
|
||||||
|
| `VSUBBU` | |
|
||||||
|
| `VSUBD` | |
|
||||||
|
| `VSUBF` | |
|
||||||
|
| `VSUBH` | |
|
||||||
|
| `VSUBHU` | |
|
||||||
|
| `VSUBQ` | |
|
||||||
|
| `VSUBV` | |
|
||||||
|
| `VSUBVU` | |
|
||||||
|
| `VSUBW` | |
|
||||||
|
| `VSUBWEVHB` | |
|
||||||
|
| `VSUBWEVHBU` | |
|
||||||
|
| `VSUBWEVQV` | |
|
||||||
|
| `VSUBWEVQVU` | |
|
||||||
|
| `VSUBWEVVW` | |
|
||||||
|
| `VSUBWEVVWU` | |
|
||||||
|
| `VSUBWEVWH` | |
|
||||||
|
| `VSUBWEVWHU` | |
|
||||||
|
| `VSUBWODHB` | |
|
||||||
|
| `VSUBWODHBU` | |
|
||||||
|
| `VSUBWODQV` | |
|
||||||
|
| `VSUBWODQVU` | |
|
||||||
|
| `VSUBWODVW` | |
|
||||||
|
| `VSUBWODVWU` | |
|
||||||
|
| `VSUBWODWH` | |
|
||||||
|
| `VSUBWODWHU` | |
|
||||||
|
| `VSUBWU` | |
|
||||||
|
| `VXORB` | |
|
||||||
|
| `VXORV` | |
|
||||||
|
| `WORD` | |
|
||||||
|
| `XOR` | Bitwise XOR |
|
||||||
|
| `XVADDB` | |
|
||||||
|
| `XVADDBU` | |
|
||||||
|
| `XVADDD` | |
|
||||||
|
| `XVADDF` | |
|
||||||
|
| `XVADDH` | |
|
||||||
|
| `XVADDHU` | |
|
||||||
|
| `XVADDQ` | |
|
||||||
|
| `XVADDV` | |
|
||||||
|
| `XVADDVU` | |
|
||||||
|
| `XVADDW` | |
|
||||||
|
| `XVADDWEVHB` | |
|
||||||
|
| `XVADDWEVHBU` | |
|
||||||
|
| `XVADDWEVQV` | |
|
||||||
|
| `XVADDWEVQVU` | |
|
||||||
|
| `XVADDWEVVW` | |
|
||||||
|
| `XVADDWEVVWU` | |
|
||||||
|
| `XVADDWEVWH` | |
|
||||||
|
| `XVADDWEVWHU` | |
|
||||||
|
| `XVADDWODHB` | |
|
||||||
|
| `XVADDWODHBU` | |
|
||||||
|
| `XVADDWODQV` | |
|
||||||
|
| `XVADDWODQVU` | |
|
||||||
|
| `XVADDWODVW` | |
|
||||||
|
| `XVADDWODVWU` | |
|
||||||
|
| `XVADDWODWH` | |
|
||||||
|
| `XVADDWODWHU` | |
|
||||||
|
| `XVADDWU` | |
|
||||||
|
| `XVANDB` | |
|
||||||
|
| `XVANDNV` | |
|
||||||
|
| `XVANDV` | |
|
||||||
|
| `XVBITCLRB` | |
|
||||||
|
| `XVBITCLRH` | |
|
||||||
|
| `XVBITCLRV` | |
|
||||||
|
| `XVBITCLRW` | |
|
||||||
|
| `XVBITREVB` | |
|
||||||
|
| `XVBITREVH` | |
|
||||||
|
| `XVBITREVV` | |
|
||||||
|
| `XVBITREVW` | |
|
||||||
|
| `XVBITSETB` | |
|
||||||
|
| `XVBITSETH` | |
|
||||||
|
| `XVBITSETV` | |
|
||||||
|
| `XVBITSETW` | |
|
||||||
|
| `XVDIVB` | |
|
||||||
|
| `XVDIVBU` | |
|
||||||
|
| `XVDIVD` | |
|
||||||
|
| `XVDIVF` | |
|
||||||
|
| `XVDIVH` | |
|
||||||
|
| `XVDIVHU` | |
|
||||||
|
| `XVDIVV` | |
|
||||||
|
| `XVDIVVU` | |
|
||||||
|
| `XVDIVW` | |
|
||||||
|
| `XVDIVWU` | |
|
||||||
|
| `XVEXTRINSB` | |
|
||||||
|
| `XVEXTRINSH` | |
|
||||||
|
| `XVEXTRINSV` | |
|
||||||
|
| `XVEXTRINSW` | |
|
||||||
|
| `XVFCLASSD` | |
|
||||||
|
| `XVFCLASSF` | |
|
||||||
|
| `XVFRECIPD` | |
|
||||||
|
| `XVFRECIPF` | |
|
||||||
|
| `XVFRINTD` | |
|
||||||
|
| `XVFRINTF` | |
|
||||||
|
| `XVFRINTRMD` | |
|
||||||
|
| `XVFRINTRMF` | |
|
||||||
|
| `XVFRINTRNED` | |
|
||||||
|
| `XVFRINTRNEF` | |
|
||||||
|
| `XVFRINTRPD` | |
|
||||||
|
| `XVFRINTRPF` | |
|
||||||
|
| `XVFRINTRZD` | |
|
||||||
|
| `XVFRINTRZF` | |
|
||||||
|
| `XVFRSQRTD` | |
|
||||||
|
| `XVFRSQRTF` | |
|
||||||
|
| `XVFSQRTD` | |
|
||||||
|
| `XVFSQRTF` | |
|
||||||
|
| `XVILVHB` | |
|
||||||
|
| `XVILVHH` | |
|
||||||
|
| `XVILVHV` | |
|
||||||
|
| `XVILVHW` | |
|
||||||
|
| `XVILVLB` | |
|
||||||
|
| `XVILVLH` | |
|
||||||
|
| `XVILVLV` | |
|
||||||
|
| `XVILVLW` | |
|
||||||
|
| `XVMADDB` | |
|
||||||
|
| `XVMADDH` | |
|
||||||
|
| `XVMADDV` | |
|
||||||
|
| `XVMADDW` | |
|
||||||
|
| `XVMADDWEVHB` | |
|
||||||
|
| `XVMADDWEVHBU` | |
|
||||||
|
| `XVMADDWEVHBUB` | |
|
||||||
|
| `XVMADDWEVQV` | |
|
||||||
|
| `XVMADDWEVQVU` | |
|
||||||
|
| `XVMADDWEVQVUV` | |
|
||||||
|
| `XVMADDWEVVW` | |
|
||||||
|
| `XVMADDWEVVWU` | |
|
||||||
|
| `XVMADDWEVVWUW` | |
|
||||||
|
| `XVMADDWEVWH` | |
|
||||||
|
| `XVMADDWEVWHU` | |
|
||||||
|
| `XVMADDWEVWHUH` | |
|
||||||
|
| `XVMADDWODHB` | |
|
||||||
|
| `XVMADDWODHBU` | |
|
||||||
|
| `XVMADDWODHBUB` | |
|
||||||
|
| `XVMADDWODQV` | |
|
||||||
|
| `XVMADDWODQVU` | |
|
||||||
|
| `XVMADDWODQVUV` | |
|
||||||
|
| `XVMADDWODVW` | |
|
||||||
|
| `XVMADDWODVWU` | |
|
||||||
|
| `XVMADDWODVWUW` | |
|
||||||
|
| `XVMADDWODWH` | |
|
||||||
|
| `XVMADDWODWHU` | |
|
||||||
|
| `XVMADDWODWHUH` | |
|
||||||
|
| `XVMODB` | |
|
||||||
|
| `XVMODBU` | |
|
||||||
|
| `XVMODH` | |
|
||||||
|
| `XVMODHU` | |
|
||||||
|
| `XVMODV` | |
|
||||||
|
| `XVMODVU` | |
|
||||||
|
| `XVMODW` | |
|
||||||
|
| `XVMODWU` | |
|
||||||
|
| `XVMOVQ` | |
|
||||||
|
| `XVMSUBB` | |
|
||||||
|
| `XVMSUBH` | |
|
||||||
|
| `XVMSUBV` | |
|
||||||
|
| `XVMSUBW` | |
|
||||||
|
| `XVMUHB` | |
|
||||||
|
| `XVMUHBU` | |
|
||||||
|
| `XVMUHH` | |
|
||||||
|
| `XVMUHHU` | |
|
||||||
|
| `XVMUHV` | |
|
||||||
|
| `XVMUHVU` | |
|
||||||
|
| `XVMUHW` | |
|
||||||
|
| `XVMUHWU` | |
|
||||||
|
| `XVMULB` | |
|
||||||
|
| `XVMULD` | |
|
||||||
|
| `XVMULF` | |
|
||||||
|
| `XVMULH` | |
|
||||||
|
| `XVMULV` | |
|
||||||
|
| `XVMULW` | |
|
||||||
|
| `XVMULWEVHB` | |
|
||||||
|
| `XVMULWEVHBU` | |
|
||||||
|
| `XVMULWEVHBUB` | |
|
||||||
|
| `XVMULWEVQV` | |
|
||||||
|
| `XVMULWEVQVU` | |
|
||||||
|
| `XVMULWEVQVUV` | |
|
||||||
|
| `XVMULWEVVW` | |
|
||||||
|
| `XVMULWEVVWU` | |
|
||||||
|
| `XVMULWEVVWUW` | |
|
||||||
|
| `XVMULWEVWH` | |
|
||||||
|
| `XVMULWEVWHU` | |
|
||||||
|
| `XVMULWEVWHUH` | |
|
||||||
|
| `XVMULWODHB` | |
|
||||||
|
| `XVMULWODHBU` | |
|
||||||
|
| `XVMULWODHBUB` | |
|
||||||
|
| `XVMULWODQV` | |
|
||||||
|
| `XVMULWODQVU` | |
|
||||||
|
| `XVMULWODQVUV` | |
|
||||||
|
| `XVMULWODVW` | |
|
||||||
|
| `XVMULWODVWU` | |
|
||||||
|
| `XVMULWODVWUW` | |
|
||||||
|
| `XVMULWODWH` | |
|
||||||
|
| `XVMULWODWHU` | |
|
||||||
|
| `XVMULWODWHUH` | |
|
||||||
|
| `XVNEGB` | |
|
||||||
|
| `XVNEGH` | |
|
||||||
|
| `XVNEGV` | |
|
||||||
|
| `XVNEGW` | |
|
||||||
|
| `XVNORB` | |
|
||||||
|
| `XVNORV` | |
|
||||||
|
| `XVORB` | |
|
||||||
|
| `XVORNV` | |
|
||||||
|
| `XVORV` | |
|
||||||
|
| `XVPCNTB` | |
|
||||||
|
| `XVPCNTH` | |
|
||||||
|
| `XVPCNTV` | |
|
||||||
|
| `XVPCNTW` | |
|
||||||
|
| `XVPERMIQ` | |
|
||||||
|
| `XVPERMIV` | |
|
||||||
|
| `XVPERMIW` | |
|
||||||
|
| `XVROTRB` | |
|
||||||
|
| `XVROTRH` | |
|
||||||
|
| `XVROTRV` | |
|
||||||
|
| `XVROTRW` | |
|
||||||
|
| `XVSADDB` | |
|
||||||
|
| `XVSADDBU` | |
|
||||||
|
| `XVSADDH` | |
|
||||||
|
| `XVSADDHU` | |
|
||||||
|
| `XVSADDV` | |
|
||||||
|
| `XVSADDVU` | |
|
||||||
|
| `XVSADDW` | |
|
||||||
|
| `XVSADDWU` | |
|
||||||
|
| `XVSEQB` | |
|
||||||
|
| `XVSEQH` | |
|
||||||
|
| `XVSEQV` | |
|
||||||
|
| `XVSEQW` | |
|
||||||
|
| `XVSETALLNEB` | |
|
||||||
|
| `XVSETALLNEH` | |
|
||||||
|
| `XVSETALLNEV` | |
|
||||||
|
| `XVSETALLNEW` | |
|
||||||
|
| `XVSETANYEQB` | |
|
||||||
|
| `XVSETANYEQH` | |
|
||||||
|
| `XVSETANYEQV` | |
|
||||||
|
| `XVSETANYEQW` | |
|
||||||
|
| `XVSETEQV` | |
|
||||||
|
| `XVSETNEV` | |
|
||||||
|
| `XVSHUF4IB` | |
|
||||||
|
| `XVSHUF4IH` | |
|
||||||
|
| `XVSHUF4IV` | |
|
||||||
|
| `XVSHUF4IW` | |
|
||||||
|
| `XVSHUFB` | |
|
||||||
|
| `XVSHUFH` | |
|
||||||
|
| `XVSHUFV` | |
|
||||||
|
| `XVSHUFW` | |
|
||||||
|
| `XVSLLB` | |
|
||||||
|
| `XVSLLH` | |
|
||||||
|
| `XVSLLV` | |
|
||||||
|
| `XVSLLW` | |
|
||||||
|
| `XVSLTB` | |
|
||||||
|
| `XVSLTBU` | |
|
||||||
|
| `XVSLTH` | |
|
||||||
|
| `XVSLTHU` | |
|
||||||
|
| `XVSLTV` | |
|
||||||
|
| `XVSLTVU` | |
|
||||||
|
| `XVSLTW` | |
|
||||||
|
| `XVSLTWU` | |
|
||||||
|
| `XVSRAB` | |
|
||||||
|
| `XVSRAH` | |
|
||||||
|
| `XVSRAV` | |
|
||||||
|
| `XVSRAW` | |
|
||||||
|
| `XVSRLB` | |
|
||||||
|
| `XVSRLH` | |
|
||||||
|
| `XVSRLV` | |
|
||||||
|
| `XVSRLW` | |
|
||||||
|
| `XVSSUBB` | |
|
||||||
|
| `XVSSUBBU` | |
|
||||||
|
| `XVSSUBH` | |
|
||||||
|
| `XVSSUBHU` | |
|
||||||
|
| `XVSSUBV` | |
|
||||||
|
| `XVSSUBVU` | |
|
||||||
|
| `XVSSUBW` | |
|
||||||
|
| `XVSSUBWU` | |
|
||||||
|
| `XVSUBB` | |
|
||||||
|
| `XVSUBBU` | |
|
||||||
|
| `XVSUBD` | |
|
||||||
|
| `XVSUBF` | |
|
||||||
|
| `XVSUBH` | |
|
||||||
|
| `XVSUBHU` | |
|
||||||
|
| `XVSUBQ` | |
|
||||||
|
| `XVSUBV` | |
|
||||||
|
| `XVSUBVU` | |
|
||||||
|
| `XVSUBW` | |
|
||||||
|
| `XVSUBWEVHB` | |
|
||||||
|
| `XVSUBWEVHBU` | |
|
||||||
|
| `XVSUBWEVQV` | |
|
||||||
|
| `XVSUBWEVQVU` | |
|
||||||
|
| `XVSUBWEVVW` | |
|
||||||
|
| `XVSUBWEVVWU` | |
|
||||||
|
| `XVSUBWEVWH` | |
|
||||||
|
| `XVSUBWEVWHU` | |
|
||||||
|
| `XVSUBWODHB` | |
|
||||||
|
| `XVSUBWODHBU` | |
|
||||||
|
| `XVSUBWODQV` | |
|
||||||
|
| `XVSUBWODQVU` | |
|
||||||
|
| `XVSUBWODVW` | |
|
||||||
|
| `XVSUBWODVWU` | |
|
||||||
|
| `XVSUBWODWH` | |
|
||||||
|
| `XVSUBWODWHU` | |
|
||||||
|
| `XVSUBWU` | |
|
||||||
|
| `XVXORB` | |
|
||||||
|
| `XVXORV` | |
|
||||||
|
| `JAL` | |
|
||||||
|
|
||||||
|
Recognised: 814 mnemonics.
|
||||||
@@ -0,0 +1,991 @@
|
|||||||
|
# RISC-V 64: instruction inventory
|
||||||
|
|
||||||
|
Generated by gasm-devkit's `_gen` from the Go toolchain's instruction table
|
||||||
|
(`cmd/internal/obj/riscv/anames.go`, go1.27.1); DO NOT EDIT. This page lists every mnemonic
|
||||||
|
`go tool asm` accepts on this target, which is the upper bound of the
|
||||||
|
language on it: a name absent here is not an instruction of the target,
|
||||||
|
and a name present here may still be one gasm's encoder cannot emit yet.
|
||||||
|
|
||||||
|
The inventory carries no per-mnemonic encoder column: on this target
|
||||||
|
encodability is decided per operand shape, and the live measured
|
||||||
|
coverage is reported by `gasm audit-instructions`.
|
||||||
|
|
||||||
|
| Mnemonic | Notes |
|
||||||
|
|---|---|
|
||||||
|
| `CALL` | Call subroutine |
|
||||||
|
| `DUFFCOPY` | |
|
||||||
|
| `DUFFZERO` | |
|
||||||
|
| `END` | |
|
||||||
|
| `FUNCDATA` | |
|
||||||
|
| `GETCALLERPC` | |
|
||||||
|
| `JMP` | Unconditional jump |
|
||||||
|
| `NOP` | |
|
||||||
|
| `PCALIGN` | |
|
||||||
|
| `PCALIGNMAX` | |
|
||||||
|
| `PCDATA` | |
|
||||||
|
| `RET` | Return |
|
||||||
|
| `TEXT` | |
|
||||||
|
| `UNDEF` | |
|
||||||
|
| `ADD` | Integer add |
|
||||||
|
| `ADDI` | Add immediate |
|
||||||
|
| `ADDIW` | Add immediate (32-bit) |
|
||||||
|
| `ADDUW` | |
|
||||||
|
| `ADDW` | Add (32-bit) |
|
||||||
|
| `AMOADDD` | Atomic add doubleword |
|
||||||
|
| `AMOADDW` | Atomic add word |
|
||||||
|
| `AMOANDD` | |
|
||||||
|
| `AMOANDW` | |
|
||||||
|
| `AMOMAXD` | |
|
||||||
|
| `AMOMAXUD` | |
|
||||||
|
| `AMOMAXUW` | |
|
||||||
|
| `AMOMAXW` | |
|
||||||
|
| `AMOMIND` | |
|
||||||
|
| `AMOMINUD` | |
|
||||||
|
| `AMOMINUW` | |
|
||||||
|
| `AMOMINW` | |
|
||||||
|
| `AMOORD` | |
|
||||||
|
| `AMOORW` | |
|
||||||
|
| `AMOSWAPD` | Atomic swap doubleword |
|
||||||
|
| `AMOSWAPW` | Atomic swap word |
|
||||||
|
| `AMOXORD` | |
|
||||||
|
| `AMOXORW` | |
|
||||||
|
| `AND` | Bitwise AND |
|
||||||
|
| `ANDI` | AND immediate |
|
||||||
|
| `ANDN` | |
|
||||||
|
| `AUIPC` | Add upper immediate to PC |
|
||||||
|
| `BCLR` | |
|
||||||
|
| `BCLRI` | |
|
||||||
|
| `BEQ` | Branch if equal |
|
||||||
|
| `BEQZ` | |
|
||||||
|
| `BEXT` | |
|
||||||
|
| `BEXTI` | |
|
||||||
|
| `BGE` | Branch if greater or equal |
|
||||||
|
| `BGEU` | Branch if greater or equal unsigned |
|
||||||
|
| `BGEZ` | |
|
||||||
|
| `BGT` | |
|
||||||
|
| `BGTU` | |
|
||||||
|
| `BGTZ` | |
|
||||||
|
| `BINV` | |
|
||||||
|
| `BINVI` | |
|
||||||
|
| `BLE` | |
|
||||||
|
| `BLEU` | |
|
||||||
|
| `BLEZ` | |
|
||||||
|
| `BLT` | Branch if less than |
|
||||||
|
| `BLTU` | Branch if less than unsigned |
|
||||||
|
| `BLTZ` | |
|
||||||
|
| `BNE` | Branch if not equal |
|
||||||
|
| `BNEZ` | |
|
||||||
|
| `BSET` | |
|
||||||
|
| `BSETI` | |
|
||||||
|
| `CADD` | |
|
||||||
|
| `CADDI` | |
|
||||||
|
| `CADDI16SP` | |
|
||||||
|
| `CADDI4SPN` | |
|
||||||
|
| `CADDIW` | |
|
||||||
|
| `CADDW` | |
|
||||||
|
| `CAND` | |
|
||||||
|
| `CANDI` | |
|
||||||
|
| `CBEQZ` | |
|
||||||
|
| `CBNEZ` | |
|
||||||
|
| `CEBREAK` | |
|
||||||
|
| `CFLD` | |
|
||||||
|
| `CFLDSP` | |
|
||||||
|
| `CFSD` | |
|
||||||
|
| `CFSDSP` | |
|
||||||
|
| `CJ` | |
|
||||||
|
| `CJALR` | |
|
||||||
|
| `CJR` | |
|
||||||
|
| `CLD` | |
|
||||||
|
| `CLDSP` | |
|
||||||
|
| `CLI` | |
|
||||||
|
| `CLUI` | |
|
||||||
|
| `CLW` | |
|
||||||
|
| `CLWSP` | |
|
||||||
|
| `CLZ` | |
|
||||||
|
| `CLZW` | |
|
||||||
|
| `CMV` | |
|
||||||
|
| `CNOP` | |
|
||||||
|
| `COR` | |
|
||||||
|
| `CPOP` | |
|
||||||
|
| `CPOPW` | |
|
||||||
|
| `CSD` | |
|
||||||
|
| `CSDSP` | |
|
||||||
|
| `CSLLI` | |
|
||||||
|
| `CSRAI` | |
|
||||||
|
| `CSRLI` | |
|
||||||
|
| `CSRRC` | |
|
||||||
|
| `CSRRCI` | |
|
||||||
|
| `CSRRS` | |
|
||||||
|
| `CSRRSI` | |
|
||||||
|
| `CSRRW` | |
|
||||||
|
| `CSRRWI` | |
|
||||||
|
| `CSUB` | |
|
||||||
|
| `CSUBW` | |
|
||||||
|
| `CSW` | |
|
||||||
|
| `CSWSP` | |
|
||||||
|
| `CTZ` | |
|
||||||
|
| `CTZW` | |
|
||||||
|
| `CXOR` | |
|
||||||
|
| `CZEROEQZ` | |
|
||||||
|
| `CZERONEZ` | |
|
||||||
|
| `DIV` | Divide |
|
||||||
|
| `DIVU` | Divide unsigned |
|
||||||
|
| `DIVUW` | |
|
||||||
|
| `DIVW` | Divide (32-bit) |
|
||||||
|
| `DRET` | |
|
||||||
|
| `EBREAK` | Breakpoint |
|
||||||
|
| `ECALL` | Environment call |
|
||||||
|
| `FABSD` | |
|
||||||
|
| `FABSS` | |
|
||||||
|
| `FADDD` | FP add (double) |
|
||||||
|
| `FADDQ` | |
|
||||||
|
| `FADDS` | FP add (single) |
|
||||||
|
| `FCLASSD` | |
|
||||||
|
| `FCLASSQ` | |
|
||||||
|
| `FCLASSS` | |
|
||||||
|
| `FCVTDL` | |
|
||||||
|
| `FCVTDLU` | |
|
||||||
|
| `FCVTDQ` | |
|
||||||
|
| `FCVTDS` | |
|
||||||
|
| `FCVTDW` | |
|
||||||
|
| `FCVTDWU` | |
|
||||||
|
| `FCVTLD` | |
|
||||||
|
| `FCVTLQ` | |
|
||||||
|
| `FCVTLS` | |
|
||||||
|
| `FCVTLUD` | |
|
||||||
|
| `FCVTLUQ` | |
|
||||||
|
| `FCVTLUS` | |
|
||||||
|
| `FCVTQD` | |
|
||||||
|
| `FCVTQL` | |
|
||||||
|
| `FCVTQLU` | |
|
||||||
|
| `FCVTQS` | |
|
||||||
|
| `FCVTQW` | |
|
||||||
|
| `FCVTQWU` | |
|
||||||
|
| `FCVTSD` | |
|
||||||
|
| `FCVTSL` | |
|
||||||
|
| `FCVTSLU` | |
|
||||||
|
| `FCVTSQ` | |
|
||||||
|
| `FCVTSW` | |
|
||||||
|
| `FCVTSWU` | |
|
||||||
|
| `FCVTWD` | |
|
||||||
|
| `FCVTWQ` | |
|
||||||
|
| `FCVTWS` | |
|
||||||
|
| `FCVTWUD` | |
|
||||||
|
| `FCVTWUQ` | |
|
||||||
|
| `FCVTWUS` | |
|
||||||
|
| `FDIVD` | FP divide (double) |
|
||||||
|
| `FDIVQ` | |
|
||||||
|
| `FDIVS` | FP divide (single) |
|
||||||
|
| `FENCE` | Memory barrier |
|
||||||
|
| `FEQD` | |
|
||||||
|
| `FEQQ` | |
|
||||||
|
| `FEQS` | |
|
||||||
|
| `FLD` | FP load doubleword |
|
||||||
|
| `FLED` | |
|
||||||
|
| `FLEQ` | |
|
||||||
|
| `FLES` | |
|
||||||
|
| `FLQ` | |
|
||||||
|
| `FLTD` | |
|
||||||
|
| `FLTQ` | |
|
||||||
|
| `FLTS` | |
|
||||||
|
| `FLW` | FP load word |
|
||||||
|
| `FMADDD` | |
|
||||||
|
| `FMADDQ` | |
|
||||||
|
| `FMADDS` | |
|
||||||
|
| `FMAXD` | |
|
||||||
|
| `FMAXQ` | |
|
||||||
|
| `FMAXS` | |
|
||||||
|
| `FMIND` | |
|
||||||
|
| `FMINQ` | |
|
||||||
|
| `FMINS` | |
|
||||||
|
| `FMSUBD` | |
|
||||||
|
| `FMSUBQ` | |
|
||||||
|
| `FMSUBS` | |
|
||||||
|
| `FMULD` | FP multiply (double) |
|
||||||
|
| `FMULQ` | |
|
||||||
|
| `FMULS` | FP multiply (single) |
|
||||||
|
| `FMVDX` | |
|
||||||
|
| `FMVSX` | |
|
||||||
|
| `FMVWX` | |
|
||||||
|
| `FMVXD` | |
|
||||||
|
| `FMVXS` | |
|
||||||
|
| `FMVXW` | |
|
||||||
|
| `FNED` | |
|
||||||
|
| `FNEGD` | |
|
||||||
|
| `FNEGS` | |
|
||||||
|
| `FNES` | |
|
||||||
|
| `FNMADDD` | |
|
||||||
|
| `FNMADDQ` | |
|
||||||
|
| `FNMADDS` | |
|
||||||
|
| `FNMSUBD` | |
|
||||||
|
| `FNMSUBQ` | |
|
||||||
|
| `FNMSUBS` | |
|
||||||
|
| `FSD` | FP store doubleword |
|
||||||
|
| `FSGNJD` | |
|
||||||
|
| `FSGNJND` | |
|
||||||
|
| `FSGNJNQ` | |
|
||||||
|
| `FSGNJNS` | |
|
||||||
|
| `FSGNJQ` | |
|
||||||
|
| `FSGNJS` | |
|
||||||
|
| `FSGNJXD` | |
|
||||||
|
| `FSGNJXQ` | |
|
||||||
|
| `FSGNJXS` | |
|
||||||
|
| `FSQ` | |
|
||||||
|
| `FSQRTD` | |
|
||||||
|
| `FSQRTQ` | |
|
||||||
|
| `FSQRTS` | |
|
||||||
|
| `FSUBD` | FP subtract (double) |
|
||||||
|
| `FSUBQ` | |
|
||||||
|
| `FSUBS` | FP subtract (single) |
|
||||||
|
| `FSW` | FP store word |
|
||||||
|
| `JAL` | Jump and link |
|
||||||
|
| `JALR` | Jump and link register |
|
||||||
|
| `LB` | Load byte |
|
||||||
|
| `LBU` | Load byte unsigned |
|
||||||
|
| `LD` | Load doubleword |
|
||||||
|
| `LH` | Load halfword |
|
||||||
|
| `LHU` | Load halfword unsigned |
|
||||||
|
| `LRD` | Load-reserved doubleword |
|
||||||
|
| `LRW` | Load-reserved word |
|
||||||
|
| `LUI` | Load upper immediate |
|
||||||
|
| `LW` | Load word |
|
||||||
|
| `LWU` | Load word unsigned |
|
||||||
|
| `MAX` | |
|
||||||
|
| `MAXU` | |
|
||||||
|
| `MIN` | |
|
||||||
|
| `MINU` | |
|
||||||
|
| `MOV` | |
|
||||||
|
| `MOVB` | |
|
||||||
|
| `MOVBU` | |
|
||||||
|
| `MOVD` | |
|
||||||
|
| `MOVF` | |
|
||||||
|
| `MOVH` | |
|
||||||
|
| `MOVHU` | |
|
||||||
|
| `MOVW` | |
|
||||||
|
| `MOVWU` | |
|
||||||
|
| `MRET` | |
|
||||||
|
| `MUL` | Multiply |
|
||||||
|
| `MULH` | Multiply high |
|
||||||
|
| `MULHSU` | Multiply high signed/unsigned |
|
||||||
|
| `MULHU` | Multiply high unsigned |
|
||||||
|
| `MULW` | Multiply (32-bit) |
|
||||||
|
| `NEG` | |
|
||||||
|
| `NEGW` | |
|
||||||
|
| `NOT` | |
|
||||||
|
| `OR` | Bitwise OR |
|
||||||
|
| `ORCB` | |
|
||||||
|
| `ORI` | OR immediate |
|
||||||
|
| `ORN` | |
|
||||||
|
| `RDCYCLE` | |
|
||||||
|
| `RDINSTRET` | |
|
||||||
|
| `RDTIME` | |
|
||||||
|
| `REM` | Remainder |
|
||||||
|
| `REMU` | Remainder unsigned |
|
||||||
|
| `REMUW` | |
|
||||||
|
| `REMW` | |
|
||||||
|
| `REV8` | |
|
||||||
|
| `ROL` | |
|
||||||
|
| `ROLW` | |
|
||||||
|
| `ROR` | |
|
||||||
|
| `RORI` | |
|
||||||
|
| `RORIW` | |
|
||||||
|
| `RORW` | |
|
||||||
|
| `SB` | Store byte |
|
||||||
|
| `SBREAK` | |
|
||||||
|
| `SCALL` | |
|
||||||
|
| `SCD` | Store-conditional doubleword |
|
||||||
|
| `SCW` | Store-conditional word |
|
||||||
|
| `SD` | Store doubleword |
|
||||||
|
| `SEQZ` | |
|
||||||
|
| `SEXTB` | |
|
||||||
|
| `SEXTH` | |
|
||||||
|
| `SFENCEVMA` | |
|
||||||
|
| `SH` | Store halfword |
|
||||||
|
| `SH1ADD` | |
|
||||||
|
| `SH1ADDUW` | |
|
||||||
|
| `SH2ADD` | |
|
||||||
|
| `SH2ADDUW` | |
|
||||||
|
| `SH3ADD` | |
|
||||||
|
| `SH3ADDUW` | |
|
||||||
|
| `SLL` | Shift left logical |
|
||||||
|
| `SLLI` | Shift left logical immediate |
|
||||||
|
| `SLLIUW` | |
|
||||||
|
| `SLLIW` | |
|
||||||
|
| `SLLW` | |
|
||||||
|
| `SLT` | Set if less than |
|
||||||
|
| `SLTI` | Set if less than immediate |
|
||||||
|
| `SLTIU` | Set if less than unsigned immediate |
|
||||||
|
| `SLTU` | Set if less than unsigned |
|
||||||
|
| `SNEZ` | |
|
||||||
|
| `SRA` | Shift right arithmetic |
|
||||||
|
| `SRAI` | Shift right arithmetic immediate |
|
||||||
|
| `SRAIW` | |
|
||||||
|
| `SRAW` | |
|
||||||
|
| `SRET` | |
|
||||||
|
| `SRL` | Shift right logical |
|
||||||
|
| `SRLI` | Shift right logical immediate |
|
||||||
|
| `SRLIW` | |
|
||||||
|
| `SRLW` | |
|
||||||
|
| `SUB` | Integer subtract |
|
||||||
|
| `SUBW` | Subtract (32-bit) |
|
||||||
|
| `SW` | Store word |
|
||||||
|
| `VAADDUVV` | |
|
||||||
|
| `VAADDUVX` | |
|
||||||
|
| `VAADDVV` | |
|
||||||
|
| `VAADDVX` | |
|
||||||
|
| `VADCVIM` | |
|
||||||
|
| `VADCVVM` | |
|
||||||
|
| `VADCVXM` | |
|
||||||
|
| `VADDVI` | |
|
||||||
|
| `VADDVV` | |
|
||||||
|
| `VADDVX` | |
|
||||||
|
| `VANDVI` | |
|
||||||
|
| `VANDVV` | |
|
||||||
|
| `VANDVX` | |
|
||||||
|
| `VASUBUVV` | |
|
||||||
|
| `VASUBUVX` | |
|
||||||
|
| `VASUBVV` | |
|
||||||
|
| `VASUBVX` | |
|
||||||
|
| `VCOMPRESSVM` | |
|
||||||
|
| `VCPOPM` | |
|
||||||
|
| `VDIVUVV` | |
|
||||||
|
| `VDIVUVX` | |
|
||||||
|
| `VDIVVV` | |
|
||||||
|
| `VDIVVX` | |
|
||||||
|
| `VFABSV` | |
|
||||||
|
| `VFADDVF` | |
|
||||||
|
| `VFADDVV` | |
|
||||||
|
| `VFCLASSV` | |
|
||||||
|
| `VFCVTFXUV` | |
|
||||||
|
| `VFCVTFXV` | |
|
||||||
|
| `VFCVTRTZXFV` | |
|
||||||
|
| `VFCVTRTZXUFV` | |
|
||||||
|
| `VFCVTXFV` | |
|
||||||
|
| `VFCVTXUFV` | |
|
||||||
|
| `VFDIVVF` | |
|
||||||
|
| `VFDIVVV` | |
|
||||||
|
| `VFIRSTM` | |
|
||||||
|
| `VFMACCVF` | |
|
||||||
|
| `VFMACCVV` | |
|
||||||
|
| `VFMADDVF` | |
|
||||||
|
| `VFMADDVV` | |
|
||||||
|
| `VFMAXVF` | |
|
||||||
|
| `VFMAXVV` | |
|
||||||
|
| `VFMERGEVFM` | |
|
||||||
|
| `VFMINVF` | |
|
||||||
|
| `VFMINVV` | |
|
||||||
|
| `VFMSACVF` | |
|
||||||
|
| `VFMSACVV` | |
|
||||||
|
| `VFMSUBVF` | |
|
||||||
|
| `VFMSUBVV` | |
|
||||||
|
| `VFMULVF` | |
|
||||||
|
| `VFMULVV` | |
|
||||||
|
| `VFMVFS` | |
|
||||||
|
| `VFMVSF` | |
|
||||||
|
| `VFMVVF` | |
|
||||||
|
| `VFNCVTFFW` | |
|
||||||
|
| `VFNCVTFXUW` | |
|
||||||
|
| `VFNCVTFXW` | |
|
||||||
|
| `VFNCVTRODFFW` | |
|
||||||
|
| `VFNCVTRTZXFW` | |
|
||||||
|
| `VFNCVTRTZXUFW` | |
|
||||||
|
| `VFNCVTXFW` | |
|
||||||
|
| `VFNCVTXUFW` | |
|
||||||
|
| `VFNEGV` | |
|
||||||
|
| `VFNMACCVF` | |
|
||||||
|
| `VFNMACCVV` | |
|
||||||
|
| `VFNMADDVF` | |
|
||||||
|
| `VFNMADDVV` | |
|
||||||
|
| `VFNMSACVF` | |
|
||||||
|
| `VFNMSACVV` | |
|
||||||
|
| `VFNMSUBVF` | |
|
||||||
|
| `VFNMSUBVV` | |
|
||||||
|
| `VFRDIVVF` | |
|
||||||
|
| `VFREC7V` | |
|
||||||
|
| `VFREDMAXVS` | |
|
||||||
|
| `VFREDMINVS` | |
|
||||||
|
| `VFREDOSUMVS` | |
|
||||||
|
| `VFREDUSUMVS` | |
|
||||||
|
| `VFRSQRT7V` | |
|
||||||
|
| `VFRSUBVF` | |
|
||||||
|
| `VFSGNJNVF` | |
|
||||||
|
| `VFSGNJNVV` | |
|
||||||
|
| `VFSGNJVF` | |
|
||||||
|
| `VFSGNJVV` | |
|
||||||
|
| `VFSGNJXVF` | |
|
||||||
|
| `VFSGNJXVV` | |
|
||||||
|
| `VFSLIDE1DOWNVF` | |
|
||||||
|
| `VFSLIDE1UPVF` | |
|
||||||
|
| `VFSQRTV` | |
|
||||||
|
| `VFSUBVF` | |
|
||||||
|
| `VFSUBVV` | |
|
||||||
|
| `VFWADDVF` | |
|
||||||
|
| `VFWADDVV` | |
|
||||||
|
| `VFWADDWF` | |
|
||||||
|
| `VFWADDWV` | |
|
||||||
|
| `VFWCVTFFV` | |
|
||||||
|
| `VFWCVTFXUV` | |
|
||||||
|
| `VFWCVTFXV` | |
|
||||||
|
| `VFWCVTRTZXFV` | |
|
||||||
|
| `VFWCVTRTZXUFV` | |
|
||||||
|
| `VFWCVTXFV` | |
|
||||||
|
| `VFWCVTXUFV` | |
|
||||||
|
| `VFWMACCVF` | |
|
||||||
|
| `VFWMACCVV` | |
|
||||||
|
| `VFWMSACVF` | |
|
||||||
|
| `VFWMSACVV` | |
|
||||||
|
| `VFWMULVF` | |
|
||||||
|
| `VFWMULVV` | |
|
||||||
|
| `VFWNMACCVF` | |
|
||||||
|
| `VFWNMACCVV` | |
|
||||||
|
| `VFWNMSACVF` | |
|
||||||
|
| `VFWNMSACVV` | |
|
||||||
|
| `VFWREDOSUMVS` | |
|
||||||
|
| `VFWREDUSUMVS` | |
|
||||||
|
| `VFWSUBVF` | |
|
||||||
|
| `VFWSUBVV` | |
|
||||||
|
| `VFWSUBWF` | |
|
||||||
|
| `VFWSUBWV` | |
|
||||||
|
| `VIDV` | |
|
||||||
|
| `VIOTAM` | |
|
||||||
|
| `VL1RE16V` | |
|
||||||
|
| `VL1RE32V` | |
|
||||||
|
| `VL1RE64V` | |
|
||||||
|
| `VL1RE8V` | |
|
||||||
|
| `VL1RV` | |
|
||||||
|
| `VL2RE16V` | |
|
||||||
|
| `VL2RE32V` | |
|
||||||
|
| `VL2RE64V` | |
|
||||||
|
| `VL2RE8V` | |
|
||||||
|
| `VL2RV` | |
|
||||||
|
| `VL4RE16V` | |
|
||||||
|
| `VL4RE32V` | |
|
||||||
|
| `VL4RE64V` | |
|
||||||
|
| `VL4RE8V` | |
|
||||||
|
| `VL4RV` | |
|
||||||
|
| `VL8RE16V` | |
|
||||||
|
| `VL8RE32V` | |
|
||||||
|
| `VL8RE64V` | |
|
||||||
|
| `VL8RE8V` | |
|
||||||
|
| `VL8RV` | |
|
||||||
|
| `VLE16FFV` | |
|
||||||
|
| `VLE16V` | |
|
||||||
|
| `VLE32FFV` | |
|
||||||
|
| `VLE32V` | |
|
||||||
|
| `VLE64FFV` | |
|
||||||
|
| `VLE64V` | |
|
||||||
|
| `VLE8FFV` | |
|
||||||
|
| `VLE8V` | |
|
||||||
|
| `VLMV` | |
|
||||||
|
| `VLOXEI16V` | |
|
||||||
|
| `VLOXEI32V` | |
|
||||||
|
| `VLOXEI64V` | |
|
||||||
|
| `VLOXEI8V` | |
|
||||||
|
| `VLOXSEG2EI16V` | |
|
||||||
|
| `VLOXSEG2EI32V` | |
|
||||||
|
| `VLOXSEG2EI64V` | |
|
||||||
|
| `VLOXSEG2EI8V` | |
|
||||||
|
| `VLOXSEG3EI16V` | |
|
||||||
|
| `VLOXSEG3EI32V` | |
|
||||||
|
| `VLOXSEG3EI64V` | |
|
||||||
|
| `VLOXSEG3EI8V` | |
|
||||||
|
| `VLOXSEG4EI16V` | |
|
||||||
|
| `VLOXSEG4EI32V` | |
|
||||||
|
| `VLOXSEG4EI64V` | |
|
||||||
|
| `VLOXSEG4EI8V` | |
|
||||||
|
| `VLOXSEG5EI16V` | |
|
||||||
|
| `VLOXSEG5EI32V` | |
|
||||||
|
| `VLOXSEG5EI64V` | |
|
||||||
|
| `VLOXSEG5EI8V` | |
|
||||||
|
| `VLOXSEG6EI16V` | |
|
||||||
|
| `VLOXSEG6EI32V` | |
|
||||||
|
| `VLOXSEG6EI64V` | |
|
||||||
|
| `VLOXSEG6EI8V` | |
|
||||||
|
| `VLOXSEG7EI16V` | |
|
||||||
|
| `VLOXSEG7EI32V` | |
|
||||||
|
| `VLOXSEG7EI64V` | |
|
||||||
|
| `VLOXSEG7EI8V` | |
|
||||||
|
| `VLOXSEG8EI16V` | |
|
||||||
|
| `VLOXSEG8EI32V` | |
|
||||||
|
| `VLOXSEG8EI64V` | |
|
||||||
|
| `VLOXSEG8EI8V` | |
|
||||||
|
| `VLSE16V` | |
|
||||||
|
| `VLSE32V` | |
|
||||||
|
| `VLSE64V` | |
|
||||||
|
| `VLSE8V` | |
|
||||||
|
| `VLSEG2E16FFV` | |
|
||||||
|
| `VLSEG2E16V` | |
|
||||||
|
| `VLSEG2E32FFV` | |
|
||||||
|
| `VLSEG2E32V` | |
|
||||||
|
| `VLSEG2E64FFV` | |
|
||||||
|
| `VLSEG2E64V` | |
|
||||||
|
| `VLSEG2E8FFV` | |
|
||||||
|
| `VLSEG2E8V` | |
|
||||||
|
| `VLSEG3E16FFV` | |
|
||||||
|
| `VLSEG3E16V` | |
|
||||||
|
| `VLSEG3E32FFV` | |
|
||||||
|
| `VLSEG3E32V` | |
|
||||||
|
| `VLSEG3E64FFV` | |
|
||||||
|
| `VLSEG3E64V` | |
|
||||||
|
| `VLSEG3E8FFV` | |
|
||||||
|
| `VLSEG3E8V` | |
|
||||||
|
| `VLSEG4E16FFV` | |
|
||||||
|
| `VLSEG4E16V` | |
|
||||||
|
| `VLSEG4E32FFV` | |
|
||||||
|
| `VLSEG4E32V` | |
|
||||||
|
| `VLSEG4E64FFV` | |
|
||||||
|
| `VLSEG4E64V` | |
|
||||||
|
| `VLSEG4E8FFV` | |
|
||||||
|
| `VLSEG4E8V` | |
|
||||||
|
| `VLSEG5E16FFV` | |
|
||||||
|
| `VLSEG5E16V` | |
|
||||||
|
| `VLSEG5E32FFV` | |
|
||||||
|
| `VLSEG5E32V` | |
|
||||||
|
| `VLSEG5E64FFV` | |
|
||||||
|
| `VLSEG5E64V` | |
|
||||||
|
| `VLSEG5E8FFV` | |
|
||||||
|
| `VLSEG5E8V` | |
|
||||||
|
| `VLSEG6E16FFV` | |
|
||||||
|
| `VLSEG6E16V` | |
|
||||||
|
| `VLSEG6E32FFV` | |
|
||||||
|
| `VLSEG6E32V` | |
|
||||||
|
| `VLSEG6E64FFV` | |
|
||||||
|
| `VLSEG6E64V` | |
|
||||||
|
| `VLSEG6E8FFV` | |
|
||||||
|
| `VLSEG6E8V` | |
|
||||||
|
| `VLSEG7E16FFV` | |
|
||||||
|
| `VLSEG7E16V` | |
|
||||||
|
| `VLSEG7E32FFV` | |
|
||||||
|
| `VLSEG7E32V` | |
|
||||||
|
| `VLSEG7E64FFV` | |
|
||||||
|
| `VLSEG7E64V` | |
|
||||||
|
| `VLSEG7E8FFV` | |
|
||||||
|
| `VLSEG7E8V` | |
|
||||||
|
| `VLSEG8E16FFV` | |
|
||||||
|
| `VLSEG8E16V` | |
|
||||||
|
| `VLSEG8E32FFV` | |
|
||||||
|
| `VLSEG8E32V` | |
|
||||||
|
| `VLSEG8E64FFV` | |
|
||||||
|
| `VLSEG8E64V` | |
|
||||||
|
| `VLSEG8E8FFV` | |
|
||||||
|
| `VLSEG8E8V` | |
|
||||||
|
| `VLSSEG2E16V` | |
|
||||||
|
| `VLSSEG2E32V` | |
|
||||||
|
| `VLSSEG2E64V` | |
|
||||||
|
| `VLSSEG2E8V` | |
|
||||||
|
| `VLSSEG3E16V` | |
|
||||||
|
| `VLSSEG3E32V` | |
|
||||||
|
| `VLSSEG3E64V` | |
|
||||||
|
| `VLSSEG3E8V` | |
|
||||||
|
| `VLSSEG4E16V` | |
|
||||||
|
| `VLSSEG4E32V` | |
|
||||||
|
| `VLSSEG4E64V` | |
|
||||||
|
| `VLSSEG4E8V` | |
|
||||||
|
| `VLSSEG5E16V` | |
|
||||||
|
| `VLSSEG5E32V` | |
|
||||||
|
| `VLSSEG5E64V` | |
|
||||||
|
| `VLSSEG5E8V` | |
|
||||||
|
| `VLSSEG6E16V` | |
|
||||||
|
| `VLSSEG6E32V` | |
|
||||||
|
| `VLSSEG6E64V` | |
|
||||||
|
| `VLSSEG6E8V` | |
|
||||||
|
| `VLSSEG7E16V` | |
|
||||||
|
| `VLSSEG7E32V` | |
|
||||||
|
| `VLSSEG7E64V` | |
|
||||||
|
| `VLSSEG7E8V` | |
|
||||||
|
| `VLSSEG8E16V` | |
|
||||||
|
| `VLSSEG8E32V` | |
|
||||||
|
| `VLSSEG8E64V` | |
|
||||||
|
| `VLSSEG8E8V` | |
|
||||||
|
| `VLUXEI16V` | |
|
||||||
|
| `VLUXEI32V` | |
|
||||||
|
| `VLUXEI64V` | |
|
||||||
|
| `VLUXEI8V` | |
|
||||||
|
| `VLUXSEG2EI16V` | |
|
||||||
|
| `VLUXSEG2EI32V` | |
|
||||||
|
| `VLUXSEG2EI64V` | |
|
||||||
|
| `VLUXSEG2EI8V` | |
|
||||||
|
| `VLUXSEG3EI16V` | |
|
||||||
|
| `VLUXSEG3EI32V` | |
|
||||||
|
| `VLUXSEG3EI64V` | |
|
||||||
|
| `VLUXSEG3EI8V` | |
|
||||||
|
| `VLUXSEG4EI16V` | |
|
||||||
|
| `VLUXSEG4EI32V` | |
|
||||||
|
| `VLUXSEG4EI64V` | |
|
||||||
|
| `VLUXSEG4EI8V` | |
|
||||||
|
| `VLUXSEG5EI16V` | |
|
||||||
|
| `VLUXSEG5EI32V` | |
|
||||||
|
| `VLUXSEG5EI64V` | |
|
||||||
|
| `VLUXSEG5EI8V` | |
|
||||||
|
| `VLUXSEG6EI16V` | |
|
||||||
|
| `VLUXSEG6EI32V` | |
|
||||||
|
| `VLUXSEG6EI64V` | |
|
||||||
|
| `VLUXSEG6EI8V` | |
|
||||||
|
| `VLUXSEG7EI16V` | |
|
||||||
|
| `VLUXSEG7EI32V` | |
|
||||||
|
| `VLUXSEG7EI64V` | |
|
||||||
|
| `VLUXSEG7EI8V` | |
|
||||||
|
| `VLUXSEG8EI16V` | |
|
||||||
|
| `VLUXSEG8EI32V` | |
|
||||||
|
| `VLUXSEG8EI64V` | |
|
||||||
|
| `VLUXSEG8EI8V` | |
|
||||||
|
| `VMACCVV` | |
|
||||||
|
| `VMACCVX` | |
|
||||||
|
| `VMADCVI` | |
|
||||||
|
| `VMADCVIM` | |
|
||||||
|
| `VMADCVV` | |
|
||||||
|
| `VMADCVVM` | |
|
||||||
|
| `VMADCVX` | |
|
||||||
|
| `VMADCVXM` | |
|
||||||
|
| `VMADDVV` | |
|
||||||
|
| `VMADDVX` | |
|
||||||
|
| `VMANDMM` | |
|
||||||
|
| `VMANDNMM` | |
|
||||||
|
| `VMAXUVV` | |
|
||||||
|
| `VMAXUVX` | |
|
||||||
|
| `VMAXVV` | |
|
||||||
|
| `VMAXVX` | |
|
||||||
|
| `VMCLRM` | |
|
||||||
|
| `VMERGEVIM` | |
|
||||||
|
| `VMERGEVVM` | |
|
||||||
|
| `VMERGEVXM` | |
|
||||||
|
| `VMFEQVF` | |
|
||||||
|
| `VMFEQVV` | |
|
||||||
|
| `VMFGEVF` | |
|
||||||
|
| `VMFGEVV` | |
|
||||||
|
| `VMFGTVF` | |
|
||||||
|
| `VMFGTVV` | |
|
||||||
|
| `VMFLEVF` | |
|
||||||
|
| `VMFLEVV` | |
|
||||||
|
| `VMFLTVF` | |
|
||||||
|
| `VMFLTVV` | |
|
||||||
|
| `VMFNEVF` | |
|
||||||
|
| `VMFNEVV` | |
|
||||||
|
| `VMINUVV` | |
|
||||||
|
| `VMINUVX` | |
|
||||||
|
| `VMINVV` | |
|
||||||
|
| `VMINVX` | |
|
||||||
|
| `VMMVM` | |
|
||||||
|
| `VMNANDMM` | |
|
||||||
|
| `VMNORMM` | |
|
||||||
|
| `VMNOTM` | |
|
||||||
|
| `VMORMM` | |
|
||||||
|
| `VMORNMM` | |
|
||||||
|
| `VMSBCVV` | |
|
||||||
|
| `VMSBCVVM` | |
|
||||||
|
| `VMSBCVX` | |
|
||||||
|
| `VMSBCVXM` | |
|
||||||
|
| `VMSBFM` | |
|
||||||
|
| `VMSEQVI` | |
|
||||||
|
| `VMSEQVV` | |
|
||||||
|
| `VMSEQVX` | |
|
||||||
|
| `VMSETM` | |
|
||||||
|
| `VMSGEUVI` | |
|
||||||
|
| `VMSGEUVV` | |
|
||||||
|
| `VMSGEVI` | |
|
||||||
|
| `VMSGEVV` | |
|
||||||
|
| `VMSGTUVI` | |
|
||||||
|
| `VMSGTUVV` | |
|
||||||
|
| `VMSGTUVX` | |
|
||||||
|
| `VMSGTVI` | |
|
||||||
|
| `VMSGTVV` | |
|
||||||
|
| `VMSGTVX` | |
|
||||||
|
| `VMSIFM` | |
|
||||||
|
| `VMSLEUVI` | |
|
||||||
|
| `VMSLEUVV` | |
|
||||||
|
| `VMSLEUVX` | |
|
||||||
|
| `VMSLEVI` | |
|
||||||
|
| `VMSLEVV` | |
|
||||||
|
| `VMSLEVX` | |
|
||||||
|
| `VMSLTUVI` | |
|
||||||
|
| `VMSLTUVV` | |
|
||||||
|
| `VMSLTUVX` | |
|
||||||
|
| `VMSLTVI` | |
|
||||||
|
| `VMSLTVV` | |
|
||||||
|
| `VMSLTVX` | |
|
||||||
|
| `VMSNEVI` | |
|
||||||
|
| `VMSNEVV` | |
|
||||||
|
| `VMSNEVX` | |
|
||||||
|
| `VMSOFM` | |
|
||||||
|
| `VMULHSUVV` | |
|
||||||
|
| `VMULHSUVX` | |
|
||||||
|
| `VMULHUVV` | |
|
||||||
|
| `VMULHUVX` | |
|
||||||
|
| `VMULHVV` | |
|
||||||
|
| `VMULHVX` | |
|
||||||
|
| `VMULVV` | |
|
||||||
|
| `VMULVX` | |
|
||||||
|
| `VMV1RV` | |
|
||||||
|
| `VMV2RV` | |
|
||||||
|
| `VMV4RV` | |
|
||||||
|
| `VMV8RV` | |
|
||||||
|
| `VMVSX` | |
|
||||||
|
| `VMVVI` | |
|
||||||
|
| `VMVVV` | |
|
||||||
|
| `VMVVX` | |
|
||||||
|
| `VMVXS` | |
|
||||||
|
| `VMXNORMM` | |
|
||||||
|
| `VMXORMM` | |
|
||||||
|
| `VNCLIPUWI` | |
|
||||||
|
| `VNCLIPUWV` | |
|
||||||
|
| `VNCLIPUWX` | |
|
||||||
|
| `VNCLIPWI` | |
|
||||||
|
| `VNCLIPWV` | |
|
||||||
|
| `VNCLIPWX` | |
|
||||||
|
| `VNCVTXXW` | |
|
||||||
|
| `VNEGV` | |
|
||||||
|
| `VNMSACVV` | |
|
||||||
|
| `VNMSACVX` | |
|
||||||
|
| `VNMSUBVV` | |
|
||||||
|
| `VNMSUBVX` | |
|
||||||
|
| `VNOTV` | |
|
||||||
|
| `VNSRAWI` | |
|
||||||
|
| `VNSRAWV` | |
|
||||||
|
| `VNSRAWX` | |
|
||||||
|
| `VNSRLWI` | |
|
||||||
|
| `VNSRLWV` | |
|
||||||
|
| `VNSRLWX` | |
|
||||||
|
| `VORVI` | |
|
||||||
|
| `VORVV` | |
|
||||||
|
| `VORVX` | |
|
||||||
|
| `VREDANDVS` | |
|
||||||
|
| `VREDMAXUVS` | |
|
||||||
|
| `VREDMAXVS` | |
|
||||||
|
| `VREDMINUVS` | |
|
||||||
|
| `VREDMINVS` | |
|
||||||
|
| `VREDORVS` | |
|
||||||
|
| `VREDSUMVS` | |
|
||||||
|
| `VREDXORVS` | |
|
||||||
|
| `VREMUVV` | |
|
||||||
|
| `VREMUVX` | |
|
||||||
|
| `VREMVV` | |
|
||||||
|
| `VREMVX` | |
|
||||||
|
| `VRGATHEREI16VV` | |
|
||||||
|
| `VRGATHERVI` | |
|
||||||
|
| `VRGATHERVV` | |
|
||||||
|
| `VRGATHERVX` | |
|
||||||
|
| `VRSUBVI` | |
|
||||||
|
| `VRSUBVX` | |
|
||||||
|
| `VS1RV` | |
|
||||||
|
| `VS2RV` | |
|
||||||
|
| `VS4RV` | |
|
||||||
|
| `VS8RV` | |
|
||||||
|
| `VSADDUVI` | |
|
||||||
|
| `VSADDUVV` | |
|
||||||
|
| `VSADDUVX` | |
|
||||||
|
| `VSADDVI` | |
|
||||||
|
| `VSADDVV` | |
|
||||||
|
| `VSADDVX` | |
|
||||||
|
| `VSBCVVM` | |
|
||||||
|
| `VSBCVXM` | |
|
||||||
|
| `VSE16V` | |
|
||||||
|
| `VSE32V` | |
|
||||||
|
| `VSE64V` | |
|
||||||
|
| `VSE8V` | |
|
||||||
|
| `VSETIVLI` | |
|
||||||
|
| `VSETVL` | |
|
||||||
|
| `VSETVLI` | |
|
||||||
|
| `VSEXTVF2` | |
|
||||||
|
| `VSEXTVF4` | |
|
||||||
|
| `VSEXTVF8` | |
|
||||||
|
| `VSLIDE1DOWNVX` | |
|
||||||
|
| `VSLIDE1UPVX` | |
|
||||||
|
| `VSLIDEDOWNVI` | |
|
||||||
|
| `VSLIDEDOWNVX` | |
|
||||||
|
| `VSLIDEUPVI` | |
|
||||||
|
| `VSLIDEUPVX` | |
|
||||||
|
| `VSLLVI` | |
|
||||||
|
| `VSLLVV` | |
|
||||||
|
| `VSLLVX` | |
|
||||||
|
| `VSMULVV` | |
|
||||||
|
| `VSMULVX` | |
|
||||||
|
| `VSMV` | |
|
||||||
|
| `VSOXEI16V` | |
|
||||||
|
| `VSOXEI32V` | |
|
||||||
|
| `VSOXEI64V` | |
|
||||||
|
| `VSOXEI8V` | |
|
||||||
|
| `VSOXSEG2EI16V` | |
|
||||||
|
| `VSOXSEG2EI32V` | |
|
||||||
|
| `VSOXSEG2EI64V` | |
|
||||||
|
| `VSOXSEG2EI8V` | |
|
||||||
|
| `VSOXSEG3EI16V` | |
|
||||||
|
| `VSOXSEG3EI32V` | |
|
||||||
|
| `VSOXSEG3EI64V` | |
|
||||||
|
| `VSOXSEG3EI8V` | |
|
||||||
|
| `VSOXSEG4EI16V` | |
|
||||||
|
| `VSOXSEG4EI32V` | |
|
||||||
|
| `VSOXSEG4EI64V` | |
|
||||||
|
| `VSOXSEG4EI8V` | |
|
||||||
|
| `VSOXSEG5EI16V` | |
|
||||||
|
| `VSOXSEG5EI32V` | |
|
||||||
|
| `VSOXSEG5EI64V` | |
|
||||||
|
| `VSOXSEG5EI8V` | |
|
||||||
|
| `VSOXSEG6EI16V` | |
|
||||||
|
| `VSOXSEG6EI32V` | |
|
||||||
|
| `VSOXSEG6EI64V` | |
|
||||||
|
| `VSOXSEG6EI8V` | |
|
||||||
|
| `VSOXSEG7EI16V` | |
|
||||||
|
| `VSOXSEG7EI32V` | |
|
||||||
|
| `VSOXSEG7EI64V` | |
|
||||||
|
| `VSOXSEG7EI8V` | |
|
||||||
|
| `VSOXSEG8EI16V` | |
|
||||||
|
| `VSOXSEG8EI32V` | |
|
||||||
|
| `VSOXSEG8EI64V` | |
|
||||||
|
| `VSOXSEG8EI8V` | |
|
||||||
|
| `VSRAVI` | |
|
||||||
|
| `VSRAVV` | |
|
||||||
|
| `VSRAVX` | |
|
||||||
|
| `VSRLVI` | |
|
||||||
|
| `VSRLVV` | |
|
||||||
|
| `VSRLVX` | |
|
||||||
|
| `VSSE16V` | |
|
||||||
|
| `VSSE32V` | |
|
||||||
|
| `VSSE64V` | |
|
||||||
|
| `VSSE8V` | |
|
||||||
|
| `VSSEG2E16V` | |
|
||||||
|
| `VSSEG2E32V` | |
|
||||||
|
| `VSSEG2E64V` | |
|
||||||
|
| `VSSEG2E8V` | |
|
||||||
|
| `VSSEG3E16V` | |
|
||||||
|
| `VSSEG3E32V` | |
|
||||||
|
| `VSSEG3E64V` | |
|
||||||
|
| `VSSEG3E8V` | |
|
||||||
|
| `VSSEG4E16V` | |
|
||||||
|
| `VSSEG4E32V` | |
|
||||||
|
| `VSSEG4E64V` | |
|
||||||
|
| `VSSEG4E8V` | |
|
||||||
|
| `VSSEG5E16V` | |
|
||||||
|
| `VSSEG5E32V` | |
|
||||||
|
| `VSSEG5E64V` | |
|
||||||
|
| `VSSEG5E8V` | |
|
||||||
|
| `VSSEG6E16V` | |
|
||||||
|
| `VSSEG6E32V` | |
|
||||||
|
| `VSSEG6E64V` | |
|
||||||
|
| `VSSEG6E8V` | |
|
||||||
|
| `VSSEG7E16V` | |
|
||||||
|
| `VSSEG7E32V` | |
|
||||||
|
| `VSSEG7E64V` | |
|
||||||
|
| `VSSEG7E8V` | |
|
||||||
|
| `VSSEG8E16V` | |
|
||||||
|
| `VSSEG8E32V` | |
|
||||||
|
| `VSSEG8E64V` | |
|
||||||
|
| `VSSEG8E8V` | |
|
||||||
|
| `VSSRAVI` | |
|
||||||
|
| `VSSRAVV` | |
|
||||||
|
| `VSSRAVX` | |
|
||||||
|
| `VSSRLVI` | |
|
||||||
|
| `VSSRLVV` | |
|
||||||
|
| `VSSRLVX` | |
|
||||||
|
| `VSSSEG2E16V` | |
|
||||||
|
| `VSSSEG2E32V` | |
|
||||||
|
| `VSSSEG2E64V` | |
|
||||||
|
| `VSSSEG2E8V` | |
|
||||||
|
| `VSSSEG3E16V` | |
|
||||||
|
| `VSSSEG3E32V` | |
|
||||||
|
| `VSSSEG3E64V` | |
|
||||||
|
| `VSSSEG3E8V` | |
|
||||||
|
| `VSSSEG4E16V` | |
|
||||||
|
| `VSSSEG4E32V` | |
|
||||||
|
| `VSSSEG4E64V` | |
|
||||||
|
| `VSSSEG4E8V` | |
|
||||||
|
| `VSSSEG5E16V` | |
|
||||||
|
| `VSSSEG5E32V` | |
|
||||||
|
| `VSSSEG5E64V` | |
|
||||||
|
| `VSSSEG5E8V` | |
|
||||||
|
| `VSSSEG6E16V` | |
|
||||||
|
| `VSSSEG6E32V` | |
|
||||||
|
| `VSSSEG6E64V` | |
|
||||||
|
| `VSSSEG6E8V` | |
|
||||||
|
| `VSSSEG7E16V` | |
|
||||||
|
| `VSSSEG7E32V` | |
|
||||||
|
| `VSSSEG7E64V` | |
|
||||||
|
| `VSSSEG7E8V` | |
|
||||||
|
| `VSSSEG8E16V` | |
|
||||||
|
| `VSSSEG8E32V` | |
|
||||||
|
| `VSSSEG8E64V` | |
|
||||||
|
| `VSSSEG8E8V` | |
|
||||||
|
| `VSSUBUVV` | |
|
||||||
|
| `VSSUBUVX` | |
|
||||||
|
| `VSSUBVV` | |
|
||||||
|
| `VSSUBVX` | |
|
||||||
|
| `VSUBVV` | |
|
||||||
|
| `VSUBVX` | |
|
||||||
|
| `VSUXEI16V` | |
|
||||||
|
| `VSUXEI32V` | |
|
||||||
|
| `VSUXEI64V` | |
|
||||||
|
| `VSUXEI8V` | |
|
||||||
|
| `VSUXSEG2EI16V` | |
|
||||||
|
| `VSUXSEG2EI32V` | |
|
||||||
|
| `VSUXSEG2EI64V` | |
|
||||||
|
| `VSUXSEG2EI8V` | |
|
||||||
|
| `VSUXSEG3EI16V` | |
|
||||||
|
| `VSUXSEG3EI32V` | |
|
||||||
|
| `VSUXSEG3EI64V` | |
|
||||||
|
| `VSUXSEG3EI8V` | |
|
||||||
|
| `VSUXSEG4EI16V` | |
|
||||||
|
| `VSUXSEG4EI32V` | |
|
||||||
|
| `VSUXSEG4EI64V` | |
|
||||||
|
| `VSUXSEG4EI8V` | |
|
||||||
|
| `VSUXSEG5EI16V` | |
|
||||||
|
| `VSUXSEG5EI32V` | |
|
||||||
|
| `VSUXSEG5EI64V` | |
|
||||||
|
| `VSUXSEG5EI8V` | |
|
||||||
|
| `VSUXSEG6EI16V` | |
|
||||||
|
| `VSUXSEG6EI32V` | |
|
||||||
|
| `VSUXSEG6EI64V` | |
|
||||||
|
| `VSUXSEG6EI8V` | |
|
||||||
|
| `VSUXSEG7EI16V` | |
|
||||||
|
| `VSUXSEG7EI32V` | |
|
||||||
|
| `VSUXSEG7EI64V` | |
|
||||||
|
| `VSUXSEG7EI8V` | |
|
||||||
|
| `VSUXSEG8EI16V` | |
|
||||||
|
| `VSUXSEG8EI32V` | |
|
||||||
|
| `VSUXSEG8EI64V` | |
|
||||||
|
| `VSUXSEG8EI8V` | |
|
||||||
|
| `VWADDUVV` | |
|
||||||
|
| `VWADDUVX` | |
|
||||||
|
| `VWADDUWV` | |
|
||||||
|
| `VWADDUWX` | |
|
||||||
|
| `VWADDVV` | |
|
||||||
|
| `VWADDVX` | |
|
||||||
|
| `VWADDWV` | |
|
||||||
|
| `VWADDWX` | |
|
||||||
|
| `VWCVTUXXV` | |
|
||||||
|
| `VWCVTXXV` | |
|
||||||
|
| `VWMACCSUVV` | |
|
||||||
|
| `VWMACCSUVX` | |
|
||||||
|
| `VWMACCUSVX` | |
|
||||||
|
| `VWMACCUVV` | |
|
||||||
|
| `VWMACCUVX` | |
|
||||||
|
| `VWMACCVV` | |
|
||||||
|
| `VWMACCVX` | |
|
||||||
|
| `VWMULSUVV` | |
|
||||||
|
| `VWMULSUVX` | |
|
||||||
|
| `VWMULUVV` | |
|
||||||
|
| `VWMULUVX` | |
|
||||||
|
| `VWMULVV` | |
|
||||||
|
| `VWMULVX` | |
|
||||||
|
| `VWREDSUMUVS` | |
|
||||||
|
| `VWREDSUMVS` | |
|
||||||
|
| `VWSUBUVV` | |
|
||||||
|
| `VWSUBUVX` | |
|
||||||
|
| `VWSUBUWV` | |
|
||||||
|
| `VWSUBUWX` | |
|
||||||
|
| `VWSUBVV` | |
|
||||||
|
| `VWSUBVX` | |
|
||||||
|
| `VWSUBWV` | |
|
||||||
|
| `VWSUBWX` | |
|
||||||
|
| `VXORVI` | |
|
||||||
|
| `VXORVV` | |
|
||||||
|
| `VXORVX` | |
|
||||||
|
| `VZEXTVF2` | |
|
||||||
|
| `VZEXTVF4` | |
|
||||||
|
| `VZEXTVF8` | |
|
||||||
|
| `WFI` | |
|
||||||
|
| `WORD` | |
|
||||||
|
| `XNOR` | |
|
||||||
|
| `XOR` | Bitwise XOR |
|
||||||
|
| `XORI` | XOR immediate |
|
||||||
|
| `ZEXTH` | |
|
||||||
|
|
||||||
|
Recognised: 975 mnemonics.
|
||||||
@@ -0,0 +1,140 @@
|
|||||||
|
# Language: lexicon, statements and expressions
|
||||||
|
|
||||||
|
Layer 1, the common language, the same on every target. Verified against
|
||||||
|
`go tool asm` of Go 1.27.1 and against gasm's parser, which is differentially
|
||||||
|
tested against the toolchain. The authoritative sources behind this page are
|
||||||
|
the assembler's lexer (`cmd/asm/internal/lex`), its parser
|
||||||
|
(`cmd/asm/internal/asm/parse.go`) and the toolchain's own test data.
|
||||||
|
|
||||||
|
## Source files and targets
|
||||||
|
|
||||||
|
An assembly source is a `.s` file. The Go build convention names a
|
||||||
|
target-specific file with the architecture suffix, `_amd64.s`, `_arm64.s`,
|
||||||
|
`_riscv64.s` or `_loong64.s`; files without a suffix are portable across
|
||||||
|
targets. The same assembler program assembles every target: `go tool asm`
|
||||||
|
picks the target from the `GOOS` and `GOARCH` environment variables, and gasm
|
||||||
|
from the file name suffix or the `--arch` flag.
|
||||||
|
|
||||||
|
## Character set and identifiers
|
||||||
|
|
||||||
|
Sources are ASCII text. An identifier is a sequence of ASCII letters, digits
|
||||||
|
and underscores, digits never first, with exactly two additions:
|
||||||
|
|
||||||
|
- U+00B7, the middle dot `·`, stands for the period in a symbol's
|
||||||
|
package-qualified name;
|
||||||
|
- U+2215, the division slash `∕`, stands for the slash in a package path.
|
||||||
|
|
||||||
|
The two substitutions exist because the parser treats a real period and a
|
||||||
|
real slash as punctuation. The syntax is otherwise uppercase throughout:
|
||||||
|
instructions, registers and directives are written in upper case. The one
|
||||||
|
inherited exception is the `g` register name on 32-bit ARM.
|
||||||
|
|
||||||
|
## Comments
|
||||||
|
|
||||||
|
Two comment forms, both Go's:
|
||||||
|
|
||||||
|
```text
|
||||||
|
// a line comment
|
||||||
|
/* a block comment */
|
||||||
|
```
|
||||||
|
|
||||||
|
A comment of the form `//go:build` or the legacy `+build` comment is not a
|
||||||
|
plain comment: the lexer reports it to the build system as a build
|
||||||
|
constraint.
|
||||||
|
|
||||||
|
## Statements
|
||||||
|
|
||||||
|
The grammar of one line, from the parser:
|
||||||
|
|
||||||
|
```text
|
||||||
|
{label:} WORD[.qualifier] [ arg {, arg} ] (';' | '\n')
|
||||||
|
```
|
||||||
|
|
||||||
|
- A **label** is an identifier followed by a colon. Labels are
|
||||||
|
function-local: two functions in one file may reuse the same name, and a
|
||||||
|
reference resolves within the function that contains it. A branch
|
||||||
|
instruction names its target with a bare label operand, and the assembler
|
||||||
|
resolves it PC-relative. The explicit forms `offset(PC)`, a constant
|
||||||
|
counting instructions from the branch, and `name(SB)`, a cross-function
|
||||||
|
static reference, appear as branch targets as well.
|
||||||
|
- **WORD** is the instruction or directive name, upper case. On the ARM
|
||||||
|
family the word may carry a dot qualifier selecting a condition or shift
|
||||||
|
mode, such as the condition suffixes on 32-bit ARM; the amd64, arm64,
|
||||||
|
riscv64 and loong64 assemblies carry no instruction qualifiers apart from
|
||||||
|
their own width suffixes, which are part of the mnemonic.
|
||||||
|
- **Arguments** are separated by commas, with no trailing comma.
|
||||||
|
- A statement ends at a newline or at a semicolon, so several statements fit
|
||||||
|
on one line separated by `;`. Blank lines are free.
|
||||||
|
|
||||||
|
The first word of a line is a directive if it is one of the directive names
|
||||||
|
(TEXT, DATA, GLOBL, FUNCDATA, PCDATA, PCALIGN) and an instruction otherwise.
|
||||||
|
Unknown instruction names are errors; the instruction set is the set the
|
||||||
|
toolchain itself defines per target, plus the common pseudo-instructions.
|
||||||
|
|
||||||
|
## Literals
|
||||||
|
|
||||||
|
| Form | Examples | Notes |
|
||||||
|
|---|---|---|
|
||||||
|
| Integer | `0`, `42`, `0x2a`, `0o52`, `0b101010`, `1_000` | decimal, hexadecimal, octal and binary forms with Go's digit separators |
|
||||||
|
| Character | `'a'`, `'\n'`, `'\x41'` | single quoted, Go escape rules |
|
||||||
|
| String | `"this program can only run\n"` | double quoted, Go escape rules; accepted where an operand takes raw bytes, in practice a DATA initialiser |
|
||||||
|
| Float | `1.5`, `1e9` | accepted by the lexer; only meaningful where the target's encoding takes a float operand |
|
||||||
|
|
||||||
|
## Expressions
|
||||||
|
|
||||||
|
Constant expressions may appear wherever a constant is expected: in
|
||||||
|
immediates after `$`, in memory offsets, in frame and data sizes. The
|
||||||
|
evaluator works on unsigned 64-bit values with Go's operator precedence, and
|
||||||
|
the parser states its grammar in exactly those terms:
|
||||||
|
|
||||||
|
```text
|
||||||
|
expr = term { '+' term | '-' term | '|' term | '^' term }
|
||||||
|
term = factor { '*' factor | '/' factor | '%' factor | '<<' factor | '>>' factor | '&' factor }
|
||||||
|
factor = const | '+' factor | '-' factor | '~' factor | '(' expr ')'
|
||||||
|
```
|
||||||
|
|
||||||
|
Two consequences are worth naming, because the arithmetic surprises people
|
||||||
|
who read it as C:
|
||||||
|
|
||||||
|
- Shifts bind at the multiplicative level, next to `*` and `&`, while `|`
|
||||||
|
and `^` bind at the additive level. `$x<<1|3` computes `(x<<1)|3`, which
|
||||||
|
differs from `x*2+3` whenever `x` is odd. Plan 9 arithmetic is Go
|
||||||
|
precedence applied to a byte-oriented language, not the C expression it
|
||||||
|
resembles.
|
||||||
|
- The evaluator is unsigned and guarded: division or modulo by zero is an
|
||||||
|
error, and so is dividing a value with the high bit set; shift counts must
|
||||||
|
be non-negative; and a right shift of a value with the high bit set is
|
||||||
|
rejected rather than sign-extended.
|
||||||
|
|
||||||
|
An address expression such as `(index*4)(base)` is evaluated at assembly
|
||||||
|
time only if every name in it is a constant; a name that resolves to a
|
||||||
|
symbol turns the expression into a relocation request, never into a folded
|
||||||
|
constant.
|
||||||
|
|
||||||
|
Named constants enter expressions through the preprocessor (`#define`,
|
||||||
|
`-D`) and, in Go-embedded packages, through the generated `go_asm.h`; see
|
||||||
|
PREPROCESSOR.md and RUNTIME.md.
|
||||||
|
|
||||||
|
## The common pseudo-instructions
|
||||||
|
|
||||||
|
A handful of instructions exist on every target, assembled by the assembler
|
||||||
|
itself rather than the encoder: `NOP`, which emits the target's no-operation
|
||||||
|
encoding, and the frame-management pseudo-instructions the compiler emits
|
||||||
|
(`FUNCDATA`, `PCDATA`) which DIRECTIVES.md specifies. Everything else is the
|
||||||
|
target's own instruction set, and the assembler knows only the instructions
|
||||||
|
the toolchain's compiler emits; a hand-written kernel wanting more lays the
|
||||||
|
encoding down with `BYTE` on amd64 or waits for the extended layer.
|
||||||
|
|
||||||
|
## Case study: three lines, decomposed
|
||||||
|
|
||||||
|
```text
|
||||||
|
B.EQ 1(PC) // arm64: condition qualifier on the mnemonic,
|
||||||
|
// target one instruction past the branch
|
||||||
|
JMP done // every target: bare label, function-local,
|
||||||
|
// resolved PC-relative
|
||||||
|
MOVQ $reader__size>>3, CX // amd64: expression over a go_asm.h constant
|
||||||
|
```
|
||||||
|
|
||||||
|
The first shows a qualifier and the explicit relative target form; the second
|
||||||
|
the ordinary label reference; the third an expression over a generated
|
||||||
|
constant. Labels are reusable between functions without conflict.
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
# LoongArch 64
|
||||||
|
|
||||||
|
Layer 1, target page. Verified against `go tool asm` of Go 1.27.1, against
|
||||||
|
the toolchain's own loong64 assembler manual (`cmd/internal/obj/loong64/doc.go`)
|
||||||
|
and against gasm's encoder, whose output is compared byte for byte with the
|
||||||
|
toolchain's. The complete mnemonic inventory lives in the generated appendix
|
||||||
|
[INSTRUCTIONS-LOONG64.md](INSTRUCTIONS-LOONG64.md).
|
||||||
|
|
||||||
|
## Registers
|
||||||
|
|
||||||
|
- General purpose `R0` to `R31`, floating point `F0` to `F31`, LSX vectors
|
||||||
|
`V0` to `V31` and LASX vectors `X0` to `X31`.
|
||||||
|
- Fixed roles from the toolchain's table: `R0` is the constant zero, `R1`
|
||||||
|
the return address, `R3` the stack pointer, `R22` the goroutine pointer,
|
||||||
|
`R29` the closure context and `R30` the assembler's temporary. `R12`,
|
||||||
|
`R13`, `R14`, `R15` and `R20` serve the PLT and trampoline sequences:
|
||||||
|
usable in assembly, but saved before any call.
|
||||||
|
|
||||||
|
## Widths ride the mnemonic
|
||||||
|
|
||||||
|
| Suffix | Width |
|
||||||
|
|---|---|
|
||||||
|
| `B`, `BU` | 8-bit, 8-bit unsigned |
|
||||||
|
| `H`, `HU` | 16-bit, 16-bit unsigned |
|
||||||
|
| `W`, `WU` | 32-bit, 32-bit unsigned |
|
||||||
|
| `V` | 64-bit |
|
||||||
|
| `F`, `D` | 32-bit and 64-bit float |
|
||||||
|
| `V` prefix (LSX) | 128-bit vector |
|
||||||
|
| `XV` prefix (LASX) | 256-bit vector |
|
||||||
|
|
||||||
|
The MOV series is the load and store interface: `MOVB (R2), R3` loads a
|
||||||
|
byte, `MOVV (R2), R3` a double word, `VMOVQ (R2), V1` a 128-bit vector and
|
||||||
|
`XVMOVQ (R2), X1` a 256-bit one.
|
||||||
|
|
||||||
|
## Operand order
|
||||||
|
|
||||||
|
Most instructions appear in left-to-right assignment order: `ADDV R11, R12,
|
||||||
|
R13` is `add.d R13, R12, R11`, and the two-operand form
|
||||||
|
`OR R5, R6` assigns into R6. Exceptions:
|
||||||
|
|
||||||
|
- Jump and branch instructions keep the GNU order: `BEQ R0, R4, label1`.
|
||||||
|
- The bitfield family is `BSTRINSW`, `BSTRINSV`, `BSTRPICKW`, `BSTRPICKV`
|
||||||
|
`$<msb>, <Rj>, $<lsb>, <Rd>`.
|
||||||
|
|
||||||
|
## Addressing
|
||||||
|
|
||||||
|
- Plain: `offset(Rbase)`.
|
||||||
|
- Base plus offset **register**, no scale: `(R4)(R5)`, as in
|
||||||
|
`MOVB (R4)(R5), R6`, the `ldx` family.
|
||||||
|
- The pointer loads and stores `MOVWP` and `MOVVP` take a source-level
|
||||||
|
16-bit offset that the encoder halves into the 14-bit field, writing
|
||||||
|
`MOVWP 8(R4), R5` as `ldptr.w r5, r4, $2`.
|
||||||
|
|
||||||
|
## Vector element syntax
|
||||||
|
|
||||||
|
The `VMOVQ` and `XVMOVQ` transfer family covers register-to-vector moves
|
||||||
|
with arrangement and index suffixes: `VMOVQ Rj, Vd.B[index]` inserts a
|
||||||
|
general register into one lane, `VMOVQ Vj.B[index], Rd` extracts one,
|
||||||
|
`VMOVQ Rj, Vd.B16` broadcasts across all sixteen, and `VMOVQ Vj.B[index],
|
||||||
|
Vd.B16` replicates one lane. The broadcast-from-memory form takes the true
|
||||||
|
byte offset at source level, which the encoder rescales per arrangement.
|
||||||
|
The permute and extract families take their 8-bit control word first:
|
||||||
|
`VPERMIW ui8, Vj, Vd`, `VEXTRINSB ui8, Vj, Vd`.
|
||||||
|
|
||||||
|
## Alignment
|
||||||
|
|
||||||
|
`PCALIGN $n` pads with NOOP to a power-of-two boundary between 8 and 2048,
|
||||||
|
and this target additionally auto-aligns loop heads to 16 bytes.
|
||||||
|
|
||||||
|
## Atomics, barriers and prefetch
|
||||||
|
|
||||||
|
- The `AM` atomic family comes in plain and `_DB` flavours; the `_DB`
|
||||||
|
forms, such as `AMSWAPDBW`, complete the atomic sequence and act as a
|
||||||
|
full data barrier. Within the AM family the destination and base
|
||||||
|
registers may not coincide and the destination may not equal the operand
|
||||||
|
register: one is an exception, the other silently unspecified.
|
||||||
|
- `DBAR` carries the graded hint encoding documented for LA664 and later,
|
||||||
|
with hint 0x700 as the read-after-read lightweight barrier; older cores
|
||||||
|
treat every hint as the full barrier.
|
||||||
|
- `PRELD offset(Rbase), $hint` prefetches with the documented hints (0
|
||||||
|
load to L1, 2 load to L3, 8 store to L1); `PRELDX` adds the encoded
|
||||||
|
block descriptor.
|
||||||
|
- `ALSL`-family shift-and-add writes the desired shift amount in source and
|
||||||
|
encodes one less: `ALSLV $4, R4, R5, R6` shifts by 4.
|
||||||
|
- `ADDV16 si16<<16, Rj, Rd` is the high-immediate add paired with the
|
||||||
|
pointer loads for GOT relative access.
|
||||||
|
|
||||||
|
## Relocations
|
||||||
|
|
||||||
|
`R_CALLLOONG64` for the 28-bit BL, `R_LOONG64_CALL36` for the
|
||||||
|
PCADDU18I-plus-JIRL pair, the `R_LOONG64_ADDR`, `ADDR64`, `TLS_LE`, `TLS_IE`,
|
||||||
|
`GOT` and `GOT64` high and low pairs, the aligned conditional jump forms
|
||||||
|
`R_JMP16LOONG64` and `R_JMP21LOONG64`, and `R_LOONG64_ADD64` and `SUB64`
|
||||||
|
for in-place arithmetic, all specified in [GOOBJ.md](../GOOBJ.md).
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
# Operands: grammar, pseudo-registers, addressing and symbols
|
||||||
|
|
||||||
|
Layer 1, the common language. Verified against `go tool asm` of Go 1.27.1 and
|
||||||
|
against gasm's parser. The operand grammar is the part of the language that
|
||||||
|
varies most between targets, so this page fixes the common grammar and the
|
||||||
|
pseudo-registers; the per architecture pages carry the register names and the
|
||||||
|
addressing quirks each target adds.
|
||||||
|
|
||||||
|
## The four operand kinds
|
||||||
|
|
||||||
|
Every operand is one of four kinds:
|
||||||
|
|
||||||
|
```text
|
||||||
|
R1 register
|
||||||
|
$4 immediate
|
||||||
|
label branch target or symbol
|
||||||
|
-8(BX)(DI*4) memory
|
||||||
|
```
|
||||||
|
|
||||||
|
**Operands go source first, destination last**: `MOVQ x+0(FP), AX` loads the
|
||||||
|
argument into AX. This is the opposite of Intel order and the same order as
|
||||||
|
AT&T, with the sigils removed: registers are bare names, immediates take
|
||||||
|
`$`, memory is `offset(base)`.
|
||||||
|
|
||||||
|
## Registers
|
||||||
|
|
||||||
|
A register operand is its bare name, with no prefix: `AX`, `X15`, `R14` on
|
||||||
|
amd64; `R0` to `R30`, `ZR`, `V0` to `V31` on arm64; `X0` to `X31`, `F0` to
|
||||||
|
`F31`, `V0` on riscv64; `R0` to `R31`, `F0` to `F31`, `V0` on loong64.
|
||||||
|
Sub-register and width selection rides the mnemonic, not the operand: the
|
||||||
|
amd64 family spells `MOVB`, `MOVW`, `MOVL`, `MOVQ`, and the arm64 family
|
||||||
|
suffices `B`, `H`, `S`, `D`, `Q` on the shared forms. Each architecture page
|
||||||
|
lists its registers and the reserved ones.
|
||||||
|
|
||||||
|
## Immediates
|
||||||
|
|
||||||
|
`$` introduces a constant: `$42`, `$-1`, `$0x2a`, `$'A'`, `$bufSize`. The
|
||||||
|
`$` applies to the whole constant expression that follows, so
|
||||||
|
`$(4*8+reader__size)` is one immediate. Without the `$`, a number in operand
|
||||||
|
position is an address, not a value; the classic error `ADDQ 1, AX` asks the
|
||||||
|
assembler for the byte at address 1.
|
||||||
|
|
||||||
|
The one place a `$` number is not an immediate is the frame and argument
|
||||||
|
size field of TEXT, `$16-24`, which is two separate constants and not a
|
||||||
|
subtraction; DIRECTIVES.md specifies it.
|
||||||
|
|
||||||
|
## Memory
|
||||||
|
|
||||||
|
```text
|
||||||
|
offset(base)
|
||||||
|
offset(base)(index*scale)
|
||||||
|
```
|
||||||
|
|
||||||
|
Both parts are optional where the target allows them: `(BX)` is the memory
|
||||||
|
at BX, `foo+16(SB)` is a global, and on amd64 `foo+32(SP)(R9*8)` adds a
|
||||||
|
scaled index. `offset` is a constant expression, optionally carrying a
|
||||||
|
symbol name. The extensions beyond `offset(base)` are where the targets
|
||||||
|
diverge, and each belongs to its architecture page: amd64 carries the
|
||||||
|
`index*scale` form with scale 1, 2, 4 or 8 and its own rules on which
|
||||||
|
registers may index; loong64 writes base plus index as `(R4)(R5)`; the ARM
|
||||||
|
family attaches shift amounts to the index register in its own spelling.
|
||||||
|
|
||||||
|
The address arithmetic is on **byte addresses**: the offset is added to the
|
||||||
|
base as it stands, whatever the operand width of the instruction. Loading
|
||||||
|
the third 8-byte word of an array at BX is `16(BX)`, not `2(BX)`.
|
||||||
|
|
||||||
|
## The four pseudo-registers
|
||||||
|
|
||||||
|
Four names denote locations no target register holds, and they mean the same
|
||||||
|
on every architecture:
|
||||||
|
|
||||||
|
- **FP**, the frame pointer: the arguments and results of the current
|
||||||
|
function, at positive offsets, in the order the Go prototype declares
|
||||||
|
them. Every FP reference must carry a name: `x+0(FP)`, and an unnamed
|
||||||
|
`0(FP)` is rejected. Results follow arguments; an unnamed result is called
|
||||||
|
`ret`.
|
||||||
|
- **SP**, the virtual stack pointer: the high end of the function's local
|
||||||
|
frame, so locals live at negative offsets, `x-8(SP)`. A reference without
|
||||||
|
a name and without a plus, `-8(SP)`, addresses the **hardware** stack
|
||||||
|
pointer instead: the two spellings are one character apart and mean
|
||||||
|
different registers. That is the sharpest edge in the language and the
|
||||||
|
source of the deepest bugs.
|
||||||
|
- **SB**, the static base: the origin of memory, used for globals and
|
||||||
|
cross-package symbols, always with a name: `foo(SB)`, `foo+4(SB)`.
|
||||||
|
- **PC**, the program counter: branch targets, and the explicit relative
|
||||||
|
form `1(PC)`.
|
||||||
|
|
||||||
|
## Symbol names
|
||||||
|
|
||||||
|
A symbol's full name is the package path, a period, and the base name. In
|
||||||
|
source, the period is written U+00B7 (`·`) and a slash in the path U+2215
|
||||||
|
(`∕`), because the parser treats the ASCII forms as punctuation. Inside the
|
||||||
|
package's own file, `·Name` is enough and is the preferred spelling, since
|
||||||
|
it survives a rename of the import path.
|
||||||
|
|
||||||
|
| Spelling | Meaning |
|
||||||
|
|---|---|
|
||||||
|
| `·Name(SB)` | this package's Name |
|
||||||
|
| `runtime·morestack(SB)` | another package's morestack |
|
||||||
|
| `sourcedock.dev∕petrbalvin∕pkg·Name(SB)` | fully qualified |
|
||||||
|
| `msg<>(SB)` | file-local, the static of this language; `<>` also makes the ABI field static in the object |
|
||||||
|
| `Name<ABIInternal>(SB)` | ABI-qualified reference, the ABI in angle brackets after the name |
|
||||||
|
|
||||||
|
The object file these symbols produce, with the index rules that decide what
|
||||||
|
is referenced by name and what by index, is specified in
|
||||||
|
[GOOBJ.md](../GOOBJ.md).
|
||||||
|
|
||||||
|
## What vet adds in Go
|
||||||
|
|
||||||
|
Inside a Go package, `go vet`'s asmdecl analyzer checks every FP offset and
|
||||||
|
name against the Go prototype, and checks the declared argument area against
|
||||||
|
the frame. That layer, the prototype requirement and `go_asm.h`, belongs to
|
||||||
|
RUNTIME.md; the grammar above is the whole of what the assembler itself
|
||||||
|
requires.
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
# Preprocessing: include, define and selection
|
||||||
|
|
||||||
|
Layer 1, the common language. Verified against the preprocessor inside
|
||||||
|
`go tool asm` of Go 1.27.1 (`cmd/asm/internal/lex`), whose directives are
|
||||||
|
`#define`, `#undef`, `#include`, `#ifdef`, `#ifndef`, `#else`, `#endif` and
|
||||||
|
`#line`, and against gasm's implementation, which is differentially tested
|
||||||
|
against the toolchain's.
|
||||||
|
|
||||||
|
Input runs through a simplified C preprocessor before the parser sees it.
|
||||||
|
The set is deliberately small: there is no `#if` with constant expressions
|
||||||
|
and no token pasting with `##`. `#line` is honoured, so it changes the
|
||||||
|
positions the assembler reports and records.
|
||||||
|
|
||||||
|
## #include
|
||||||
|
|
||||||
|
```text
|
||||||
|
#include "textflag.h"
|
||||||
|
#include "go_asm.h"
|
||||||
|
#include "defs_linux_amd64.h"
|
||||||
|
```
|
||||||
|
|
||||||
|
The search path, in order: the directory of the including file, then the
|
||||||
|
directories given by repeatable `-I` flags. The assembler seeds no default
|
||||||
|
of its own: a bare `go tool asm` invocation finds none of the standard
|
||||||
|
headers, and it is the `go` build system that passes `$GOROOT/pkg/include`
|
||||||
|
among the `-I` directories when it drives the build. That directory ships
|
||||||
|
`textflag.h`, `funcdata.h` and the per architecture register headers.
|
||||||
|
Includes nest; a file included twice through different paths is processed
|
||||||
|
twice, which is why headers guard their defines.
|
||||||
|
|
||||||
|
## #define and #undef
|
||||||
|
|
||||||
|
```text
|
||||||
|
#define bufSize 1024
|
||||||
|
#define MOVD(d, s) MOVQ s, d
|
||||||
|
#undef bufSize
|
||||||
|
```
|
||||||
|
|
||||||
|
- An object macro replaces its name with its token sequence at the point of
|
||||||
|
use.
|
||||||
|
- A parameterised macro takes its arguments in parentheses and substitutes
|
||||||
|
them into the body. Macro parameters compose with the rest of the
|
||||||
|
language: an argument used with an element suffix, as in `A.S4` on the
|
||||||
|
vector forms, substitutes correctly.
|
||||||
|
- Redefinition is an error; `#undef` first, or pick a new name.
|
||||||
|
- The `-D name[=value]` flag predefines an object macro from the command
|
||||||
|
line, repeatable, exactly as `#define` would; a `-D` without a value
|
||||||
|
defines the name as `1`.
|
||||||
|
- Expansion happens when the name is used, so a macro may expand to
|
||||||
|
instructions, operands or fragments of either, and a macro body may use
|
||||||
|
macros defined before it.
|
||||||
|
|
||||||
|
`textflag.h` and `funcdata.h` are themselves ordinary `#define` files: the
|
||||||
|
flag names and the runtime macros are preprocessor definitions, not language
|
||||||
|
keywords. That is why a missing include produces a parser error at the first
|
||||||
|
use of `NOSPLIT` rather than a complaint about the name.
|
||||||
|
|
||||||
|
## #ifdef, #ifndef, #else, #endif
|
||||||
|
|
||||||
|
```text
|
||||||
|
#ifdef GOOS_windows
|
||||||
|
#define SYSCALL_INT 0x2b
|
||||||
|
#endif
|
||||||
|
```
|
||||||
|
|
||||||
|
Selection is by defined-name only: `#ifdef`, `#ifndef`, `#else`, `#endif`,
|
||||||
|
nesting freely. There is no `#if defined(x) && y`, because the preprocessor
|
||||||
|
evaluates no expressions; reach that with a build-tag Go file generating a
|
||||||
|
header, which is exactly how the runtime's own `go_asm.h` and defs headers
|
||||||
|
are produced.
|
||||||
|
|
||||||
|
## What preprocessing does not cover
|
||||||
|
|
||||||
|
The preprocessor is textual and runs first, so it knows nothing of assembly
|
||||||
|
semantics: it does not check that a macro expansion is a legal instruction,
|
||||||
|
and it does not participate in the constant expression evaluator, which runs
|
||||||
|
later, in the parser. A constant folded with `#define` and a constant folded
|
||||||
|
in an operand expression end at the same value through different doors;
|
||||||
|
GOOBJ.md records both in the object identically.
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
# The Plan 9 assembly language
|
||||||
|
|
||||||
|
This directory is the reference for the Plan 9 assembly language as the Go
|
||||||
|
toolchain and gasm accept it, written to be complete enough to implement
|
||||||
|
against. It exists because no such reference exists upstream: Go documents
|
||||||
|
the language on a single page, and the rest of the knowledge lives in the
|
||||||
|
toolchain's source and in the practice of reading it.
|
||||||
|
|
||||||
|
Every page carries the same conformance statement: which layer of the system
|
||||||
|
it describes, which toolchain release it was verified against, and how the
|
||||||
|
claims were checked. Pages in this directory are verified against Go 1.27.1
|
||||||
|
and against gasm's own differential test suite, which compares gasm's
|
||||||
|
behaviour with `go tool asm` byte for byte and output for output.
|
||||||
|
|
||||||
|
## The three layers
|
||||||
|
|
||||||
|
The reference deliberately separates three layers, because their rules have
|
||||||
|
different owners and different lifetimes:
|
||||||
|
|
||||||
|
1. **The common language** (LANGUAGE, OPERANDS, DIRECTIVES,
|
||||||
|
PREPROCESSOR): the syntax, operands, directives and preprocessing, the
|
||||||
|
same on every target and meaningful without a Go runtime.
|
||||||
|
2. **The Go-embedded layer** (RUNTIME): everything that exists only because
|
||||||
|
the code runs inside a Go program: the ABI0 contract, generated wrappers,
|
||||||
|
`go_asm.h`, the garbage collector annotations and `go vet` checks.
|
||||||
|
3. **The standalone layer** (STANDALONE, planned with the standalone
|
||||||
|
compilation phase): using the language outside Go, through gasm's ELF
|
||||||
|
output and the extended instruction set, where the toolchain offers no
|
||||||
|
ground truth and execution testing is the only verification.
|
||||||
|
|
||||||
|
A rule stated in layer 1 holds on every target. A rule stated in layer 2
|
||||||
|
says which part of the Go machinery imposes it. Nothing in layer 3 changes
|
||||||
|
layers 1 or 2; it extends them.
|
||||||
|
|
||||||
|
## Pages
|
||||||
|
|
||||||
|
| Page | Layer | Contents |
|
||||||
|
|---|---|---|
|
||||||
|
| [LANGUAGE.md](LANGUAGE.md) | 1 | lexicon, statement structure, labels, literals, expressions |
|
||||||
|
| [OPERANDS.md](OPERANDS.md) | 1 | operand grammar, pseudo-registers, addressing modes, symbol naming |
|
||||||
|
| [DIRECTIVES.md](DIRECTIVES.md) | 1 | TEXT, DATA, GLOBL, FUNCDATA, PCDATA, PCALIGN and the function flags |
|
||||||
|
| [PREPROCESSOR.md](PREPROCESSOR.md) | 1 | `#include`, `#define`, `#ifdef` and friends, `-D`, `-I` |
|
||||||
|
| [RUNTIME.md](RUNTIME.md) | 2 | ABI0, prototypes, `go_asm.h`, `funcdata.h`, `go vet` |
|
||||||
|
| [AMD64.md](AMD64.md) | 1 | registers, addressing, the frame and split check, families, relocations |
|
||||||
|
| [ARM64.md](ARM64.md) | 1 | registers, the MOV load and store series, special operand orders, SIMD |
|
||||||
|
| [RISCV64.md](RISCV64.md) | 1 | registers and their constrained names, per class operand order, profiles, vector extension |
|
||||||
|
| [LOONG64.md](LOONG64.md) | 1 | registers, width suffixes, vector element syntax, atomics and barriers |
|
||||||
|
| INSTRUCTIONS-AMD64.md and the other three | 1 | generated per architecture inventory of every accepted mnemonic |
|
||||||
|
| STANDALONE.md | 3 | the language outside Go |
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
The common-language core, the Go-embedded layer, all four per-architecture
|
||||||
|
pages and the generated instruction appendices are written and verified.
|
||||||
|
STANDALONE.md lands with the standalone compilation phase. The object format
|
||||||
|
these pages feed is specified in [GOOBJ.md](../GOOBJ.md).
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
# RISC-V 64
|
||||||
|
|
||||||
|
Layer 1, target page. Verified against `go tool asm` of Go 1.27.1, against
|
||||||
|
the toolchain's own riscv64 assembler manual (`cmd/internal/obj/riscv/doc.go`)
|
||||||
|
and against gasm's encoder, whose output is compared byte for byte with the
|
||||||
|
toolchain's. The complete mnemonic inventory lives in the generated appendix
|
||||||
|
[INSTRUCTIONS-RISCV64.md](INSTRUCTIONS-RISCV64.md).
|
||||||
|
|
||||||
|
## Registers
|
||||||
|
|
||||||
|
- Integer: `X0` to `X31`. `X0` is hardwired zero. Three names the toolchain
|
||||||
|
constrains: `X4` must be written through its ABI name `TP`; `X27`, the
|
||||||
|
goroutine pointer, must be written `g` and may not be written `S11`; in
|
||||||
|
shared builds `X3` is off limits and must be written `GP`.
|
||||||
|
- The other integer registers may be written `Xn` or by their ABI names
|
||||||
|
(`A0`, `T0`, `S1`, and so on).
|
||||||
|
- Floating point: `F0` to `F31`. Vector: `V0` to `V31`.
|
||||||
|
- `X26` is the closure pointer and `X31` is the assembler's own scratch
|
||||||
|
register: its value may be clobbered by instruction sequences the
|
||||||
|
assembler inserts, so hand-written code must not rely on it.
|
||||||
|
- There is no reserved frame pointer register on this target.
|
||||||
|
|
||||||
|
## Operand order
|
||||||
|
|
||||||
|
The ordering differs from the ISA manual, and per instruction class:
|
||||||
|
|
||||||
|
- **R-type** is reversed: `ADD X10, X11, X12` is `add x12, x11, x10`.
|
||||||
|
- **I-type arithmetic** keeps that shape with the immediate first:
|
||||||
|
`ADDI $1, X11, X12`.
|
||||||
|
- **Loads and stores** are source first, like every Plan 9 dialect:
|
||||||
|
`MOV 16(X2), X10` loads and `MOV X10, (X2)` stores. The MOV series hides
|
||||||
|
the width; `MOVB` through `MOVD` spell it out.
|
||||||
|
- **Branches** keep the ISA order: `BLT X12, X23, loop1`, which jumps when
|
||||||
|
X12 < X23, the reverse of the SLT operand order.
|
||||||
|
- **FMA** is rotated one place left so the destination comes last:
|
||||||
|
`FMADDS F1, F2, F3, F4`.
|
||||||
|
- **AMO** is likewise rotated: `AMOSWAPW X5, (X6), X7`.
|
||||||
|
- **Ternary abbreviation** is supported and encouraged: `ADD X10, X12` means
|
||||||
|
`ADD X10, X12, X12`.
|
||||||
|
|
||||||
|
Where an R-type instruction has an I-type sibling, the assembler picks the
|
||||||
|
immediate form from the operand: `AND $3, X12, X13` assembles as `ANDI`.
|
||||||
|
|
||||||
|
## Names, suffixes and rounding
|
||||||
|
|
||||||
|
Dots are removed and suffixes are upper-cased: the ISA's `fmv.w.x` is
|
||||||
|
`FMVWX`. Floating-point rounding modes become suffixes, `FCVTLUS.RNE F0,
|
||||||
|
X5`, with RTZ assumed when the suffix is omitted; the toolchain never sets
|
||||||
|
the FCSR.
|
||||||
|
|
||||||
|
## Constants
|
||||||
|
|
||||||
|
- `MOV` materialises any 64-bit integer constant, synthesising it from a
|
||||||
|
few arithmetic instructions where possible and otherwise loading it from
|
||||||
|
a literal pool in the binary.
|
||||||
|
- A 32-bit constant is accepted by `ADDI`, `ANDI`, `ORI` and `XORI`, and
|
||||||
|
the assembler synthesises values that exceed the 12-bit encoding window.
|
||||||
|
- `MOVF` and `MOVD` materialise floating-point constants, encoding them as
|
||||||
|
`FLW` and `FLD` from a pool location unless the constant is exactly 0.0.
|
||||||
|
|
||||||
|
## Extensions and profiles
|
||||||
|
|
||||||
|
The default target profile is rva20u64, selected or raised with the
|
||||||
|
GORISCV64 environment variable. A short list of instructions outside the
|
||||||
|
default profile is synthesised by the assembler when the profile does not
|
||||||
|
provide them, so they are safe without guards: `ANDN`, `MAX`, `MAXU`, `MIN`,
|
||||||
|
`MINU`, `MOVB`, `MOVH`, `MOVHU`, `MOVWU`, `ORN`, `ROL`, `ROLW`, `ROR`,
|
||||||
|
`RORI`, `RORIW`, `RORW`, `XNOR`. The header `asm_riscv64.h` defines the
|
||||||
|
`hasZba`, `hasZbb`, `hasZbs` and `hasV` macros for guarding everything else.
|
||||||
|
|
||||||
|
## Fences and atomics
|
||||||
|
|
||||||
|
`FENCE` takes predecessor and successor sets in that order, uppercase
|
||||||
|
letters, `FENCE R, RW`; a bare `FENCE` is a full fence, as is
|
||||||
|
`FENCE IORW, IORW`. `FENCE.TSO` exists. The ordering bits of `LR`, `SC`
|
||||||
|
and the AMO instructions are not specifiable in source: the assembler sets
|
||||||
|
acquire and release on the AMO instructions, acquire on `LR` and release on
|
||||||
|
`SC`, always.
|
||||||
|
|
||||||
|
## Compressed instructions
|
||||||
|
|
||||||
|
The assembler converts 32-bit instructions to their compressed encodings
|
||||||
|
automatically; the conversion is a property of the emitted machine code, not
|
||||||
|
of the source, and register choice influences how much compresses.
|
||||||
|
Hand-writing compressed instructions in source is accepted but discouraged.
|
||||||
|
The debug flag `compressinstructions=0` turns the automatic conversion off.
|
||||||
|
|
||||||
|
## Vector extension
|
||||||
|
|
||||||
|
`VSETVLI` writes its vtype components in uppercase with the destination
|
||||||
|
last: `VSETVLI X10, E8, M1, TU, MU, X12`. Vector loads and stores are
|
||||||
|
source first like the scalar ones, with an optional stride or index register
|
||||||
|
second and the mask register, when present, always penultimate:
|
||||||
|
`VLE8V (X10), V3`, `VLE8V (X10), V0, V3` for the masked form. Vector
|
||||||
|
arithmetic reverses its operands, `VADDVV V1, V2, V3`, with the mask again
|
||||||
|
penultimate.
|
||||||
|
|
||||||
|
## Relocations
|
||||||
|
|
||||||
|
`R_RISCV_JAL`, `R_RISCV_CALL`, the `R_RISCV_PCREL_ITYPE` and `STYPE` pairs,
|
||||||
|
`R_RISCV_BRANCH`, the compressed branch and jump forms, the TLS and GOT
|
||||||
|
families and `R_RISCV_ADD32` and `SUB32`, all specified in
|
||||||
|
[GOOBJ.md](../GOOBJ.md). The assembler always emits the four-byte
|
||||||
|
`R_DWTXTADDR_U4` flavour inside its DWARF records.
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
# The Go-embedded layer: ABI0, prototypes and the runtime contract
|
||||||
|
|
||||||
|
Layer 2: everything that exists only because the assembly runs inside a Go
|
||||||
|
program. Without a Go runtime this page does not apply; the language of
|
||||||
|
OPERANDS.md and DIRECTIVES.md still does. Verified against Go 1.27.1, against
|
||||||
|
the shipped `funcdata.h` header, and against the object files the toolchain
|
||||||
|
produces, which were parsed and checked field by field while writing
|
||||||
|
[GOOBJ.md](../GOOBJ.md).
|
||||||
|
|
||||||
|
## Hand-written assembly is ABI0
|
||||||
|
|
||||||
|
Go functions compiled from source use ABIInternal, the register-based
|
||||||
|
calling convention, which the toolchain documents as unstable and free to
|
||||||
|
change between releases. A `.s` function is written against ABI0, the stack
|
||||||
|
based convention: arguments and results live in the caller's frame at
|
||||||
|
positive FP offsets, byte-addressed, in declaration order, with no registers
|
||||||
|
assigned at all. The toolchain generates the wrapper that translates between
|
||||||
|
the two; a caller in Go calling an assembly function goes through it, and it
|
||||||
|
is marked `ABIWRAPPER` in the object. Hand-writing a bridge is never needed
|
||||||
|
and never correct.
|
||||||
|
|
||||||
|
## Every assembly function carries a Go prototype
|
||||||
|
|
||||||
|
```go
|
||||||
|
package add
|
||||||
|
|
||||||
|
func Add(x, y int64) int64
|
||||||
|
```
|
||||||
|
|
||||||
|
The body-less declaration is not optional, and not only for the linker: it
|
||||||
|
is what tells the garbage collector which arguments and results hold
|
||||||
|
pointers, and what `go vet` checks the assembly against. Even a function
|
||||||
|
nothing in Go calls gets one. Consequences:
|
||||||
|
|
||||||
|
- The FP operand names and offsets are checked by vet's asmdecl analyzer
|
||||||
|
against the prototype: `x+0(FP)` must name an argument that exists, at the
|
||||||
|
offset the prototype says. A file that assembles and links can still fail
|
||||||
|
vet.
|
||||||
|
- The declared argument area in `$framesize-argsize` is checked against the
|
||||||
|
prototype's size. An omitted argsize marks the argument size unknown
|
||||||
|
(0x80000000 in the object, the value of `ArgsSizeUnknown` from
|
||||||
|
`funcdata.h`), which is the normal spelling for functions with no Go
|
||||||
|
callers.
|
||||||
|
- `//go:noescape` on the declaration tells the compiler that a pointer
|
||||||
|
argument does not escape, for assembly that keeps the pointer beyond the
|
||||||
|
call.
|
||||||
|
|
||||||
|
## The frame, the stack and the collector
|
||||||
|
|
||||||
|
The runtime owns the stack and the pointer map, and assembly must hold up
|
||||||
|
its end of four rules:
|
||||||
|
|
||||||
|
1. **Arguments are initialised on entry; results are not.** A function whose
|
||||||
|
results hold live pointers across a call must zero them and then execute
|
||||||
|
`GO_RESULTS_INITIALIZED`. Designing functions that return no pointers
|
||||||
|
avoids the problem.
|
||||||
|
2. **A frame with calls and no local pointers says so** with
|
||||||
|
`NO_LOCAL_POINTERS`. A frame with local pointers that the runtime cannot
|
||||||
|
see is not allowed at all: assembly cannot describe a pointer-containing
|
||||||
|
local, so it must not have one. Data symbols containing pointers are the
|
||||||
|
same: define them in Go.
|
||||||
|
3. **The stack may move.** Stack growth copies the frame, so no pointer into
|
||||||
|
the frame may be held across a call, and the raw hardware SP register may
|
||||||
|
not be cached across a call either.
|
||||||
|
4. **The split check is not optional by default.** Without NOSPLIT, the
|
||||||
|
assembler inserts the stack-growth preamble, including the morestack
|
||||||
|
block for framed functions; NOSPLIT is a contract that the frame and
|
||||||
|
everything below it fit in the remaining stack segment. On amd64 the
|
||||||
|
assembler also marks small leaf functions NoSplit itself and skips the
|
||||||
|
preamble, so silence is not a promise.
|
||||||
|
|
||||||
|
The simplest safe shape is a leaf function with no local frame and no calls:
|
||||||
|
it needs no annotation beyond the prototype.
|
||||||
|
|
||||||
|
## go_asm.h: Go constants and layout in assembly
|
||||||
|
|
||||||
|
A package with `.s` files gets a generated header. Include it and use the
|
||||||
|
generated names instead of hard-coding layouts, which lie silently when the
|
||||||
|
Go side changes:
|
||||||
|
|
||||||
|
| Go declaration | Assembly name |
|
||||||
|
|---|---|
|
||||||
|
| `const bufSize = 1024` | `const_bufSize` |
|
||||||
|
| field `r` of `type reader struct` | `reader_r` |
|
||||||
|
| size of `type reader struct` | `reader__size` |
|
||||||
|
|
||||||
|
The constants arrive as macros, usable as immediates and offsets, computed
|
||||||
|
from the Go declarations. An ambiguous name, such as a struct that really
|
||||||
|
has a `_size` field, fails the generation with a redefinition error.
|
||||||
|
|
||||||
|
## funcdata.h: the runtime macros
|
||||||
|
|
||||||
|
`$GOROOT/pkg/include/funcdata.h` defines the PCDATA and FUNCDATA ids and the
|
||||||
|
three macros assembly normally uses instead:
|
||||||
|
|
||||||
|
| Macro | Expands to | Meaning |
|
||||||
|
|---|---|---|
|
||||||
|
| `GO_ARGS` | `FUNCDATA $FUNCDATA_ArgsPointerMaps, go_args_stackmap(SB)` | the Go prototype defines the argument pointer map |
|
||||||
|
| `GO_RESULTS_INITIALIZED` | `PCDATA $PCDATA_StackMapIndex, $1` | results are initialised; treat them as live from here |
|
||||||
|
| `NO_LOCAL_POINTERS` | `FUNCDATA $FUNCDATA_LocalsPointerMaps, no_pointers_stackmap(SB)` | the frame holds no pointers |
|
||||||
|
|
||||||
|
`GO_ARGS` is inserted implicitly by the assembler for any function whose
|
||||||
|
package-qualified name belongs to the current package, which is why most
|
||||||
|
assembly never writes it. `NOSPLIT` leaf functions that call nothing need
|
||||||
|
none of the three.
|
||||||
|
|
||||||
|
The underlying ids, for reading toolchain output rather than for writing
|
||||||
|
source: FUNCDATA 0 to 7 are args pointer maps, locals pointer maps, stack
|
||||||
|
objects, inline tree, open-coded defer info, argument info, argument
|
||||||
|
liveness and wrap info; PCDATA 0 to 4 are unsafe point, stack map index,
|
||||||
|
inline tree index, argument liveness index and panic bounds.
|
||||||
|
|
||||||
|
## What the runtime does with all of this
|
||||||
|
|
||||||
|
The object file records the annotations as aux symbols and FuncInfo records;
|
||||||
|
GOOBJ.md specifies the encoding. The linker assembles them into the runtime's
|
||||||
|
pclntable, which traceback and the collector consume. An assembly function
|
||||||
|
that misdeclares its frame is not a compile error and usually not a link
|
||||||
|
error: it is a wrong collector decision or a wrong traceback at runtime,
|
||||||
|
which is why the annotations are a contract and not documentation.
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
.TH GASM-AUDIT-INSTRUCTIONS 1 "2026-09-19" "gasm" "User Commands"
|
.TH GASM-AUDIT-INSTRUCTIONS 1 "2026-09-21" "gasm" "User Commands"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
gasm-audit-instructions \- diff the encoder against the Go toolchain, or measure a corpus
|
gasm-audit-instructions \- diff the encoder against the Go toolchain, or measure a corpus
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
.B gasm audit\-instructions [\-\-corpus [\fIdir\fR]] [\-I dir] [amd64|arm64|riscv64|loong64]
|
.B gasm audit\-instructions [\-\-corpus [\fIdir\fR]] [\-\-list] [\-I dir] [amd64|arm64|riscv64|loong64]
|
||||||
.SH DESCRIPTION
|
.SH DESCRIPTION
|
||||||
Compare the gasm encoder for the given architecture (default amd64)
|
Compare the gasm encoder for the given architecture (default amd64)
|
||||||
against
|
against
|
||||||
@@ -39,6 +39,12 @@ second.
|
|||||||
Assemble a corpus of .s files and report pass rates and failure
|
Assemble a corpus of .s files and report pass rates and failure
|
||||||
reasons.
|
reasons.
|
||||||
.TP
|
.TP
|
||||||
|
.B \-\-list
|
||||||
|
With
|
||||||
|
.BR \-\-corpus ,
|
||||||
|
print every failing file with its failure reason, per architecture,
|
||||||
|
instead of one representative file per reason.
|
||||||
|
.TP
|
||||||
.B \-I \fIdir\fR
|
.B \-I \fIdir\fR
|
||||||
Directory to search for #include files; may be repeated, searched in
|
Directory to search for #include files; may be repeated, searched in
|
||||||
order after the source directory. A corpus run whose files include
|
order after the source directory. A corpus run whose files include
|
||||||
|
|||||||
@@ -489,6 +489,17 @@ func parseImmediate(g []token.Token) ast.Immediate {
|
|||||||
} else if g[i].Kind == token.Plus {
|
} else if g[i].Kind == token.Plus {
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
|
// A constant expression after the sign: $-(R - 8), $+(32-shift). The
|
||||||
|
// toolchain folds the negated value in place (the cgo ABI macros write
|
||||||
|
// ADJSP $-(REGS_HOST_TO_ABI0_STACK - 8)), so the sign applies to the
|
||||||
|
// folded value exactly as it does to a bare literal.
|
||||||
|
if i < len(g) && (g[i].Kind == token.LParen || g[i].Kind == token.Tilde) {
|
||||||
|
if v, rest, ok := foldExpr(g[i:]); ok && len(rest) == 0 {
|
||||||
|
imm.Val = v
|
||||||
|
imm.HasVal = true
|
||||||
|
return imm
|
||||||
|
}
|
||||||
|
}
|
||||||
if i < len(g) && g[i].Kind == token.Number {
|
if i < len(g) && g[i].Kind == token.Number {
|
||||||
text := g[i].Text
|
text := g[i].Text
|
||||||
if v, ok := tryInt(text); ok {
|
if v, ok := tryInt(text); ok {
|
||||||
@@ -673,6 +684,26 @@ func parseAddress(g []token.Token) ast.Address {
|
|||||||
if i > 0 && i < len(g) {
|
if i > 0 && i < len(g) {
|
||||||
addr.Shift = joinRaw(g[i:])
|
addr.Shift = joinRaw(g[i:])
|
||||||
}
|
}
|
||||||
|
// A lone (possibly signed) number is an absolute address: MOVL $0xf1,
|
||||||
|
// 0xf1 stores through the bare displacement with no base at all. In
|
||||||
|
// operand position a number without $ is an address, never a value.
|
||||||
|
if addr.Sym == nil && addr.Base == "" && addr.Index == "" && !addr.HasOff {
|
||||||
|
neg := false
|
||||||
|
j := 0
|
||||||
|
if j < len(g) && (g[j].Kind == token.Minus || g[j].Kind == token.Plus) {
|
||||||
|
neg = g[j].Kind == token.Minus
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
if j == len(g)-1 && g[j].Kind == token.Number {
|
||||||
|
v := parseInt(g[j].Text)
|
||||||
|
if neg {
|
||||||
|
v = -v
|
||||||
|
}
|
||||||
|
addr.Offset = v
|
||||||
|
addr.HasOff = true
|
||||||
|
return addr
|
||||||
|
}
|
||||||
|
}
|
||||||
return addr
|
return addr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -34,6 +34,12 @@ type Options struct {
|
|||||||
// Expand enables macro expansion, include splicing and the
|
// Expand enables macro expansion, include splicing and the
|
||||||
// statement-separator reading of ';' that the expanded bodies rely on.
|
// statement-separator reading of ';' that the expanded bodies rely on.
|
||||||
Expand bool
|
Expand bool
|
||||||
|
// Predefines names the macros defined before the file is read. The
|
||||||
|
// go command drives go tool asm with -D GOOS_<goos> -D GOARCH_<arch>,
|
||||||
|
// and GOROOT's own headers (go_tls.h, asm_riscv64.h) select their
|
||||||
|
// platform blocks with #ifdef on exactly those names, so an assembler
|
||||||
|
// without them cannot see the platform definitions at all.
|
||||||
|
Predefines map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseWithOptions parses src like Parse, optionally preprocessing it first.
|
// ParseWithOptions parses src like Parse, optionally preprocessing it first.
|
||||||
@@ -44,6 +50,9 @@ func ParseWithOptions(path, src string, opts Options) (*ast.File, []error) {
|
|||||||
var errs []error
|
var errs []error
|
||||||
if opts.Expand {
|
if opts.Expand {
|
||||||
pp := &preproc{opts: opts, macros: map[string]*macroDef{}}
|
pp := &preproc{opts: opts, macros: map[string]*macroDef{}}
|
||||||
|
for name, value := range opts.Predefines {
|
||||||
|
pp.macros[name] = ¯oDef{name: name, body: lexer.Tokenize(value)}
|
||||||
|
}
|
||||||
lines = pp.fileLines(path, tokens, token.Position{})
|
lines = pp.fileLines(path, tokens, token.Position{})
|
||||||
errs = pp.errs
|
errs = pp.errs
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Vendored
+52
@@ -0,0 +1,52 @@
|
|||||||
|
// Kernel: the operand forms the GOROOT campaign surfaced — numeric
|
||||||
|
// PC-relative jumps, symbol-immediate materialisation (the toolchain rewrites
|
||||||
|
// MOVQ $sym(SB) into a RIP-relative LEA) and the negated constant-expression
|
||||||
|
// ADJSP the cgo ABI macros write. Bytes are pinned against go tool asm by
|
||||||
|
// TestDifferentialKernels.
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
DATA sd<>(SB)/4, $7
|
||||||
|
GLOBL sd<>(SB), RODATA, $4
|
||||||
|
|
||||||
|
// func Jumps(flag int64) int64
|
||||||
|
TEXT ·Jumps(SB), NOSPLIT, $0-16
|
||||||
|
MOVQ flag+0(FP), AX
|
||||||
|
TESTQ AX, AX
|
||||||
|
JEQ 2(PC)
|
||||||
|
MOVQ $1, AX
|
||||||
|
JMP 3(PC)
|
||||||
|
MOVQ $2, AX
|
||||||
|
MOVQ AX, ret+0(FP)
|
||||||
|
RET
|
||||||
|
|
||||||
|
// func SymImm() int64
|
||||||
|
TEXT ·SymImm(SB), NOSPLIT, $0-16
|
||||||
|
MOVQ $sd<>(SB), AX
|
||||||
|
MOVQ $·SymImm(SB), CX
|
||||||
|
MOVQ AX, ret+0(FP)
|
||||||
|
RET
|
||||||
|
|
||||||
|
// func Frame()
|
||||||
|
TEXT ·Frame(SB), NOSPLIT, $0
|
||||||
|
PUSHFQ
|
||||||
|
CLD
|
||||||
|
ADJSP $(64 - 8)
|
||||||
|
ADJSP $-(64 - 8)
|
||||||
|
POPFQ
|
||||||
|
RET
|
||||||
|
|
||||||
|
// func Tls() int64
|
||||||
|
TEXT ·Tls(SB), NOSPLIT, $0-8
|
||||||
|
MOVQ TLS, BX
|
||||||
|
MOVQ 0(BX)(TLS*1), AX
|
||||||
|
MOVQ AX, ret+0(FP)
|
||||||
|
RET
|
||||||
|
|
||||||
|
// func Aligned() int64
|
||||||
|
TEXT ·Aligned(SB), NOSPLIT, $0-8
|
||||||
|
MOVQ $1, AX
|
||||||
|
PCALIGN $16
|
||||||
|
MOVQ $2, AX
|
||||||
|
PCALIGN $32
|
||||||
|
MOVQ AX, ret+0(FP)
|
||||||
|
RET
|
||||||
Reference in New Issue
Block a user