feat(lint): surface the amd64 extension layer's refusals
Assisted-by: GLM 5.3 Flash
This commit is contained in:
1 parent
5ee60c860b
commit
fa50521619
5 files changed
+281
-6
No files matched your search
@@ -71,6 +71,26 @@ func amd64ExtStatement(mnem string, ops []*ast.Operand) (extops []arch.ExtOperan
|
|||||||
return out, true, nil
|
return out, true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EncodeAmd64Statement runs one parsed amd64 statement through the layer
|
||||||
|
// exactly as the assembler does: the operands convert the amd64ExtOperand way
|
||||||
|
// and the mnemonic resolves and encodes through the registry. pinned reports
|
||||||
|
// that the statement belongs to the layer alone (the mnemonic is registered
|
||||||
|
// and the scalar paths cannot encode it); err is the layer's own refusal of
|
||||||
|
// the operands, the same text the assembler prints, so the linter surfaces
|
||||||
|
// one diagnostic where assembly would fail. A statement the scalar paths own
|
||||||
|
// returns pinned false, with nothing to report.
|
||||||
|
func EncodeAmd64Statement(mnem string, ops []*ast.Operand) (code []byte, pinned bool, err error) {
|
||||||
|
extops, pinned, err := amd64ExtStatement(mnem, ops)
|
||||||
|
if !pinned || err != nil {
|
||||||
|
return nil, pinned, err
|
||||||
|
}
|
||||||
|
code, err = EncodeExtension(arch.AMD64, mnem, extops...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, true, err
|
||||||
|
}
|
||||||
|
return code, true, nil
|
||||||
|
}
|
||||||
|
|
||||||
// amd64ExtOperand converts one parsed operand into the layer's form: a $ immediate,
|
// amd64ExtOperand converts one parsed operand into the layer's form: a $ immediate,
|
||||||
// a vector, general or opmask register, or a base-relative memory operand, each
|
// a vector, general or opmask register, or a base-relative memory operand, each
|
||||||
// with the brace decorations the spelling carries.
|
// with the brace decorations the spelling carries.
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
// The lint side of the extended-instruction layer. The generated tables and
|
||||||
|
// the extension registry are two homes for a mnemonic, so a registered
|
||||||
|
// mnemonic is known wherever the tables are consulted, and a statement pinned
|
||||||
|
// to the registry is validated by the registry's own encoding, whose refusal
|
||||||
|
// is the sharper diagnostic the layer carries.
|
||||||
|
|
||||||
|
package lint
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sourcedock.dev/petrbalvin/gasm-sdk/arch"
|
||||||
|
"sourcedock.dev/petrbalvin/gasm-sdk/asm"
|
||||||
|
"sourcedock.dev/petrbalvin/gasm-sdk/ast"
|
||||||
|
)
|
||||||
|
|
||||||
|
// amd64ExtPinned reports whether a statement belongs to the amd64 extension
|
||||||
|
// layer alone: the mnemonic is registered in the registry and the encoder
|
||||||
|
// mirror cannot encode it, the same routing the assembler applies before its
|
||||||
|
// scalar paths run. A statement the layer pins never passes through go tool
|
||||||
|
// asm, so the per-operand portability advice the scalar statements earn is
|
||||||
|
// noise on it.
|
||||||
|
func amd64ExtPinned(upper string) bool {
|
||||||
|
if _, ok := asm.LookupExtension(arch.AMD64, upper); !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return !asm.Encodable(upper)
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkAmd64Extension validates one amd64 statement pinned to the extension
|
||||||
|
// layer by encoding it through the registry: the layer's own validators carry
|
||||||
|
// the sharper diagnostic (a mask on a scalar form, rounding on a VL form, k0
|
||||||
|
// as a write mask), and their refusal is the diagnostic, the same text the
|
||||||
|
// assembler prints at build time. A statement the registry encodes is clean:
|
||||||
|
// the wired layer is the acceptance, not a gap of the generated table.
|
||||||
|
func checkAmd64Extension(st *ast.Instr, upper string) []Diagnostic {
|
||||||
|
_, _, err := asm.EncodeAmd64Statement(upper, st.Operands)
|
||||||
|
if err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return []Diagnostic{{
|
||||||
|
Pos: st.Mnemonic.Pos,
|
||||||
|
End: st.Mnemonic.End,
|
||||||
|
Severity: Error,
|
||||||
|
Code: CodeExtensionForm,
|
||||||
|
Message: err.Error(),
|
||||||
|
}}
|
||||||
|
}
|
||||||
@@ -4,10 +4,12 @@
|
|||||||
package lint
|
package lint
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"sourcedock.dev/petrbalvin/gasm-sdk/arch"
|
"sourcedock.dev/petrbalvin/gasm-sdk/arch"
|
||||||
"sourcedock.dev/petrbalvin/gasm-sdk/asm"
|
"sourcedock.dev/petrbalvin/gasm-sdk/asm"
|
||||||
|
"sourcedock.dev/petrbalvin/gasm-sdk/ast"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestExtensionInstructionsAreKnown pins the lint side of the
|
// TestExtensionInstructionsAreKnown pins the lint side of the
|
||||||
@@ -50,3 +52,189 @@ TEXT ·f(SB), NOSPLIT, $0
|
|||||||
t.Errorf("an unregistered mnemonic must fire unknown-instruction once: %+v", diags)
|
t.Errorf("an unregistered mnemonic must fire unknown-instruction once: %+v", diags)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// amd64ExtKernel spells one register-form statement per distinct mnemonic the
|
||||||
|
// amd64 extension layer registers, decorations beside them where the entries
|
||||||
|
// carry them, and the memory, broadcast and scaled-index spellings at the
|
||||||
|
// tail. TestAmd64ExtensionInstructionsAreKnown lints it; the completeness
|
||||||
|
// guard inside fails the moment a registered mnemonic lacks its row, so a
|
||||||
|
// family added to the registry lands here in the same change.
|
||||||
|
const amd64ExtKernel = `#include "textflag.h"
|
||||||
|
TEXT ·f(SB), NOSPLIT, $0
|
||||||
|
VCVTNE2PS2BF16 Z1, Z2, Z3{K5}
|
||||||
|
VCVTNEPS2BF16 Z1, Y2{K5}
|
||||||
|
VDPBF16PS Z1, Z2, Z3{K5}
|
||||||
|
VP2INTERSECTD Z1, Z2, K3
|
||||||
|
VP2INTERSECTQ Z1, Z2, K3
|
||||||
|
VMOVSH X1, X2, X3
|
||||||
|
VMOVW R12, X1
|
||||||
|
VADDSH X1, X2, X3{RZ-SAE}
|
||||||
|
VSUBSH X1, X2, X3{RZ-SAE}
|
||||||
|
VMULSH X1, X2, X3{RZ-SAE}
|
||||||
|
VDIVSH X1, X2, X3{RZ-SAE}
|
||||||
|
VMINSH X1, X2, X3{SAE}
|
||||||
|
VMAXSH X1, X2, X3{SAE}
|
||||||
|
VSQRTSH X1, X2, X3{RZ-SAE}
|
||||||
|
VSCALEFSH X1, X2, X3{RZ-SAE}
|
||||||
|
VGETEXPSH X1, X2, X3{SAE}
|
||||||
|
VCOMISH X1, X2{SAE}
|
||||||
|
VUCOMISH X1, X2{SAE}
|
||||||
|
VCVTSS2SH X1, X2, X3{RZ-SAE}
|
||||||
|
VCVTSH2SS X1, X2, X3
|
||||||
|
VCVTSH2SD X1, X2, X3
|
||||||
|
VCVTSD2SH X1, X2, X3{RZ-SAE}
|
||||||
|
VCVTSI2SH X1, R12D, X2
|
||||||
|
VCVTUSI2SH X1, R12D, X2
|
||||||
|
VCVTSH2SI X1, R12D
|
||||||
|
VCVTSH2USI X1, R12D
|
||||||
|
VADDPH Z1, Z2, Z3{K5}{RZ-SAE}
|
||||||
|
VSUBPH Z1, Z2, Z3{K5}{RZ-SAE}
|
||||||
|
VMULPH Z1, Z2, Z3{K5}{RZ-SAE}
|
||||||
|
VDIVPH Z1, Z2, Z3{K5}{RZ-SAE}
|
||||||
|
VMINPH Z1, Z2, Z3{K5}{SAE}
|
||||||
|
VMAXPH Z1, Z2, Z3{K5}{SAE}
|
||||||
|
VSQRTPH Z1, Z2{K5}{RZ-SAE}
|
||||||
|
VCMPSH $0x7b, X1, X2, K3
|
||||||
|
VGETMANTSH $0x0b, X1, X2, X4
|
||||||
|
VREDUCESH $0x7b, X1, X2, X4
|
||||||
|
VRNDSCALESH $0x7b, X1, X2, X4
|
||||||
|
VCVTPH2W Z1, Z2{K5}{RZ-SAE}
|
||||||
|
VCVTPH2UW Z1, Z2{K5}{RZ-SAE}
|
||||||
|
VCVTW2PH Z1, Z2{K5}{RZ-SAE}
|
||||||
|
VCVTUW2PH Z1, Z2{K5}{RZ-SAE}
|
||||||
|
VCVTPH2DQ Y1, Z2{K5}{RZ-SAE}
|
||||||
|
VCVTPH2UDQ Y1, Z2{K5}{RZ-SAE}
|
||||||
|
VCVTDQ2PH Z1, Y2{K5}{RZ-SAE}
|
||||||
|
VCVTUDQ2PH Z1, Y2{K5}{RZ-SAE}
|
||||||
|
VCVTPH2QQ X1, Z2{K5}{RZ-SAE}
|
||||||
|
VCVTPH2UQQ X1, Z2{K5}{RZ-SAE}
|
||||||
|
VCVTQQ2PH Z1, X2{K5}{RZ-SAE}
|
||||||
|
VCVTUQQ2PH Z1, X2{K5}{RZ-SAE}
|
||||||
|
VCVTPH2PD X1, Z2{K5}
|
||||||
|
VCVTPD2PH Z1, X2{K5}{RZ-SAE}
|
||||||
|
VRNDSCALEPH $0x7b, Z1, Z3{K5}
|
||||||
|
VREDUCEPH $0x7b, Z1, Z3{K5}
|
||||||
|
VGETMANTPH $0x0b, Z1, Z3{K5}
|
||||||
|
VFMADD132PH Z1, Z2, Z3{K5}{RZ-SAE}
|
||||||
|
VFMADD213PH Z1, Z2, Z3{K5}{RZ-SAE}
|
||||||
|
VFMADD231PH Z1, Z2, Z3{K5}{RZ-SAE}
|
||||||
|
VFMSUB132PH Z1, Z2, Z3{K5}{RZ-SAE}
|
||||||
|
VFMSUB213PH Z1, Z2, Z3{K5}{RZ-SAE}
|
||||||
|
VFMSUB231PH Z1, Z2, Z3{K5}{RZ-SAE}
|
||||||
|
VFMADDSUB132PH Z1, Z2, Z3{K5}{RZ-SAE}
|
||||||
|
VFMADDSUB213PH Z1, Z2, Z3{K5}{RZ-SAE}
|
||||||
|
VFMADDSUB231PH Z1, Z2, Z3{K5}{RZ-SAE}
|
||||||
|
VFMSUBADD132PH Z1, Z2, Z3{K5}{RZ-SAE}
|
||||||
|
VFMSUBADD213PH Z1, Z2, Z3{K5}{RZ-SAE}
|
||||||
|
VFMSUBADD231PH Z1, Z2, Z3{K5}{RZ-SAE}
|
||||||
|
VFMADD132SH X1, X2, X3{RZ-SAE}
|
||||||
|
VFMADD213SH X1, X2, X3{RZ-SAE}
|
||||||
|
VFMADD231SH X1, X2, X3{RZ-SAE}
|
||||||
|
VFMSUB132SH X1, X2, X3{RZ-SAE}
|
||||||
|
VFMSUB213SH X1, X2, X3{RZ-SAE}
|
||||||
|
VFMSUB231SH X1, X2, X3{RZ-SAE}
|
||||||
|
VFMULCPH Z1, Z2, Z3{K5}{RZ-SAE}
|
||||||
|
VFCMULCPH Z1, Z2, Z3{K5}{RZ-SAE}
|
||||||
|
VFMULCSH X1, X2, X3{RZ-SAE}
|
||||||
|
VFCMULCSH X1, X2, X3{RZ-SAE}
|
||||||
|
VFMADDCPH Z1, Z2, Z3{K5}{RZ-SAE}
|
||||||
|
VFCMADDCPH Z1, Z2, Z3{K5}{RZ-SAE}
|
||||||
|
VFMADDCSH X1, X2, X3{RZ-SAE}
|
||||||
|
VFCMADDCSH X1, X2, X3{RZ-SAE}
|
||||||
|
VMINMAXPH $0x7b, Z1, Z2, Z4{K5}
|
||||||
|
VMINMAXSH $0x7b, X1, X2, X4
|
||||||
|
VPDPWSUD X1, X2, X3
|
||||||
|
VPDPWSUDS X1, X2, X3
|
||||||
|
VPDPWUSD X1, X2, X3
|
||||||
|
VPDPWUSDS X1, X2, X3
|
||||||
|
VMOVSH (R9), X30
|
||||||
|
VMOVSH X30, (R9)
|
||||||
|
VADDPH Z28, (R9), Z30{K7}{Z}
|
||||||
|
VCVTDQ2PH (R9){1TO8}, Y30
|
||||||
|
VADDPH Z29, (R9)(R12*2), Z30
|
||||||
|
VPDPWSUD X2, 127(R9), X1
|
||||||
|
RET
|
||||||
|
`
|
||||||
|
|
||||||
|
// TestAmd64ExtensionInstructionsAreKnown pins the lint side of the amd64
|
||||||
|
// extension layer: every distinct mnemonic the registry carries lints without
|
||||||
|
// unknown-instruction, and the whole kernel, whose statements the encoder
|
||||||
|
// accepts one and all, lints clean, because the wired layer is the acceptance
|
||||||
|
// rather than a gap of the generated table.
|
||||||
|
func TestAmd64ExtensionInstructionsAreKnown(t *testing.T) {
|
||||||
|
// The completeness guard: a registered mnemonic without a row here is a
|
||||||
|
// gap in the kernel, not a pass.
|
||||||
|
for _, name := range asm.ExtensionNames(arch.AMD64) {
|
||||||
|
if !strings.Contains(amd64ExtKernel, "\t"+name+" ") {
|
||||||
|
t.Errorf("the amd64 extension kernel has no row for %q", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
diags := lintArchFile(t, "ext_amd64.s", amd64ExtKernel, arch.AMD64, func(f *ast.File) (*asm.Image, error) {
|
||||||
|
return asm.AssembleFile(f)
|
||||||
|
})
|
||||||
|
for _, code := range []string{CodeUnknownInstr, CodeUnencodable, CodeExtensionForm,
|
||||||
|
CodeNonportableRegister, CodeOperandCount, CodeRegisterWidthMismatch} {
|
||||||
|
if n := codes(diags)[code]; n != 0 {
|
||||||
|
t.Errorf("%s fired %d times on the wired extension: %+v", code, n, diags)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(diags) != 0 {
|
||||||
|
t.Errorf("a kernel the encoder accepts must lint clean, got %+v", diags)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The registry consult is the layer's own, not a loosening of the rule:
|
||||||
|
// a mnemonic neither table nor registry carries still fires.
|
||||||
|
diags = lintSrcArch(t, "ext_amd64.s", `#include "textflag.h"
|
||||||
|
TEXT ·f(SB), NOSPLIT, $0
|
||||||
|
FROBNICATE Z1, Z2, Z3
|
||||||
|
RET
|
||||||
|
`)
|
||||||
|
if codes(diags)[CodeUnknownInstr] != 1 {
|
||||||
|
t.Errorf("an unregistered mnemonic must fire unknown-instruction once: %+v", diags)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestAmd64ExtensionRefusals pins the sharper diagnostic the amd64 layer
|
||||||
|
// carries: a statement the registry refuses at assembly time is a lint error
|
||||||
|
// here, under the same text the assembler prints, and one diagnostic alone.
|
||||||
|
// The rows are the layer's own validator refusals: the decorations a form
|
||||||
|
// does not take, the registers outside a class, and the immediate layouts
|
||||||
|
// with their reserved bits.
|
||||||
|
func TestAmd64ExtensionRefusals(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
stmt string
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{"VADDSH X5, X4, X6{K3}", "the entry's destination takes none"},
|
||||||
|
{"VADDPH Y5, Y4, Y6{RZ-SAE}", "wants a ZMM register"},
|
||||||
|
{"VADDPH Z5, Z4, Z6{K0}", "outside the masking registers k1-k7"},
|
||||||
|
{"VADDPH Z5, Z4, Z6{Z}", "zeroing without a write mask"},
|
||||||
|
{"VADDPH Z5, Z4, Z6{SAE}", "spells {sae} without a mode"},
|
||||||
|
{"VADDPH Z29, (R9){RZ-SAE}, Z30", "the memory operand takes none"},
|
||||||
|
{"VFMULCPH Z5, (R9){1TO8}, Z6", "the entry's memory operand takes none"},
|
||||||
|
{"VADDPH Z29, Z28, Z30{BOGUS}", "is not a decoration the layer reads"},
|
||||||
|
{"VADDPH Z29, Z28, Z30{K7}{K3}", "carries two write masks"},
|
||||||
|
{"VCMPSH $300, X29, X28, K5", "outside the unsigned byte range"},
|
||||||
|
{"VGETMANTSH $0x20, X29, X28, X30", "the upper nibble of the mantissa control is reserved"},
|
||||||
|
{"VCVTSI2SH X29, X12, X30", "general register"},
|
||||||
|
{"VADDPH Z5, Z4", "got 2 operands"},
|
||||||
|
{"VMOVSH (Z4), X30", "not an extended-layer operand"},
|
||||||
|
{"VMOVSH foo+4(SB), X30", "not an extended-layer operand"},
|
||||||
|
{"VMOVSH 8(R9)(DX*3), X30", "outside the byte multipliers"},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
src := "#include \"textflag.h\"\nTEXT ·f(SB), NOSPLIT, $0\n\t" + tt.stmt + "\n\tRET\n"
|
||||||
|
diags := lintSrcArch(t, "ext_amd64.s", src)
|
||||||
|
if len(diags) != 1 {
|
||||||
|
t.Errorf("%s: got %d diagnostics, want the one extension-form error: %+v", tt.stmt, len(diags), diags)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
d := diags[0]
|
||||||
|
if d.Code != CodeExtensionForm || d.Severity != Error {
|
||||||
|
t.Errorf("%s: got %s %s, want an error-severity %s: %+v", tt.stmt, d.Severity, d.Code, CodeExtensionForm, d)
|
||||||
|
}
|
||||||
|
if !strings.Contains(d.Message, tt.want) {
|
||||||
|
t.Errorf("%s: message %q does not name %q", tt.stmt, d.Message, tt.want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+17
-6
@@ -96,6 +96,7 @@ const (
|
|||||||
CodeDataStringWidth = "data-string-width"
|
CodeDataStringWidth = "data-string-width"
|
||||||
CodeDataNoGlobl = "data-without-globl"
|
CodeDataNoGlobl = "data-without-globl"
|
||||||
CodeDataExceedsGlobl = "data-exceeds-globl"
|
CodeDataExceedsGlobl = "data-exceeds-globl"
|
||||||
|
CodeExtensionForm = "extension-form"
|
||||||
)
|
)
|
||||||
|
|
||||||
// knownTextFlags are the flags recognised by runtime/textflag.h, plus the
|
// knownTextFlags are the flags recognised by runtime/textflag.h, plus the
|
||||||
@@ -327,13 +328,14 @@ func lintText(t *ast.Text, tab *arch.Table, archKnown bool, cfg Config, macros m
|
|||||||
hasRet = true
|
hasRet = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The extended-instruction layer is a mnemonic's second home:
|
||||||
|
// one registered there (the arm64 SVE family and the amd64
|
||||||
|
// BF16, VP2INTERSECT, FP16 and VNNI-INT16 families) is known,
|
||||||
|
// and it encodes through the extension registry rather than
|
||||||
|
// the encoder mirror the unencodable rule consults, so the
|
||||||
|
// wired extensions are never flagged as a gap of the table.
|
||||||
|
_, extended := asm.LookupExtension(cfg.Arch, mnem)
|
||||||
if archKnown && !cfg.Disable[CodeUnknownInstr] && !pseudoOps[upper] && !isMacroInvocation(mnem, macros) {
|
if archKnown && !cfg.Disable[CodeUnknownInstr] && !pseudoOps[upper] && !isMacroInvocation(mnem, macros) {
|
||||||
// The extended-instruction layer is a mnemonic's second home:
|
|
||||||
// one registered there (arm64's SVE family today) is known,
|
|
||||||
// and it encodes through the extension registry rather than
|
|
||||||
// the encoder mirror the unencodable rule consults, so the
|
|
||||||
// wired extensions are never flagged as a gap of the table.
|
|
||||||
_, extended := asm.LookupExtension(cfg.Arch, mnem)
|
|
||||||
if _, ok := tab.Lookup(mnem); !ok {
|
if _, ok := tab.Lookup(mnem); !ok {
|
||||||
if !extended {
|
if !extended {
|
||||||
out = append(out, Diagnostic{
|
out = append(out, Diagnostic{
|
||||||
@@ -359,6 +361,15 @@ func lintText(t *ast.Text, tab *arch.Table, archKnown bool, cfg Config, macros m
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The amd64 extension layer carries the sharper diagnostic: a
|
||||||
|
// statement pinned to the registry is validated by encoding it,
|
||||||
|
// and the layer's own refusal (a mask on a scalar form, rounding
|
||||||
|
// on a VL form, k0 as a write mask) surfaces as the finding, the
|
||||||
|
// same text the assembler prints at build time.
|
||||||
|
if cfg.Arch == arch.AMD64 && amd64ExtPinned(upper) && !cfg.Disable[CodeExtensionForm] {
|
||||||
|
out = append(out, checkAmd64Extension(st, upper)...)
|
||||||
|
}
|
||||||
|
|
||||||
if archKnown && !cfg.Disable[CodeOperandCount] && !isMacroInvocation(mnem, macros) && !maskedEvex(mnem, st.Operands) {
|
if archKnown && !cfg.Disable[CodeOperandCount] && !isMacroInvocation(mnem, macros) && !maskedEvex(mnem, st.Operands) {
|
||||||
if in, ok := tab.Lookup(mnem); ok && in.MinOps >= 0 {
|
if in, ok := tab.Lookup(mnem); ok && in.MinOps >= 0 {
|
||||||
n := len(st.Operands)
|
n := len(st.Operands)
|
||||||
|
|||||||
@@ -71,6 +71,13 @@ func scanNonportableRegisters(t *ast.Text) []Diagnostic {
|
|||||||
if !ok {
|
if !ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
// A statement pinned to the extension layer sits outside the
|
||||||
|
// portability premise already: go tool asm knows none of its
|
||||||
|
// mnemonics, so advising the canonical word spelling inside one is
|
||||||
|
// noise on a statement the toolchain would refuse whole.
|
||||||
|
if amd64ExtPinned(strings.ToUpper(in.Mnemonic.Text)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
for _, op := range in.Operands {
|
for _, op := range in.Operands {
|
||||||
if op.Addr.Sym != nil && op.Addr.Sym.Pseudo == "" {
|
if op.Addr.Sym != nil && op.Addr.Sym.Pseudo == "" {
|
||||||
report(op.Addr.Sym.Name, op.Pos)
|
report(op.Addr.Sym.Name, op.Pos)
|
||||||
|
|||||||
Reference in new issue
Block a user