fix(parser): substitute macro parameters behind element selectors
Assisted-by: GLM 5.3 Flash
This commit is contained in:
@@ -536,6 +536,127 @@ func TestConstantExpressionFoldsWithoutExpand(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestParameterWithSelectorSubstitutes(t *testing.T) {
|
||||
// The lexer folds A.S4 into one identifier token, so a parameter used
|
||||
// with an element or lane selector never matched the whole-token
|
||||
// substitution; the toolchain's lexer splits on the period and its
|
||||
// substitution sees the name alone. Several parameters carry selectors
|
||||
// in one body here, which is the chacha8_arm64.s QR shape in miniature.
|
||||
_, got := expand(t, `
|
||||
#define QR(A, B, C, D) VADD A.S4, B.S4, C.S4; VEOR D.B16, A.B16, D.B16
|
||||
TEXT ·f(SB), NOSPLIT, $0
|
||||
QR(V0, V1, V2, V3)
|
||||
RET
|
||||
`)
|
||||
wantLines(t, got,
|
||||
"VADD V0.S4, V1.S4, V2.S4",
|
||||
"VEOR V3.B16, V0.B16, V3.B16",
|
||||
"RET",
|
||||
)
|
||||
}
|
||||
|
||||
func TestSelectorWithCompoundArgumentPastesTextually(t *testing.T) {
|
||||
// An argument that is itself one compound identifier pastes verbatim:
|
||||
// A.S4 over V0.B16 spells V0.B16.S4, the operand the toolchain's
|
||||
// split-then-substitute leaves behind.
|
||||
_, got := expand(t, `
|
||||
#define M(A) VADD A.S4, A.S4, A.S4
|
||||
TEXT ·f(SB), NOSPLIT, $0
|
||||
M(V0.B16)
|
||||
RET
|
||||
`)
|
||||
wantLines(t, got, "VADD V0.B16.S4, V0.B16.S4, V0.B16.S4", "RET")
|
||||
}
|
||||
|
||||
func TestSelectorAlongsideBareParameter(t *testing.T) {
|
||||
// A body may use the parameter bare and suffixed, and the argument may
|
||||
// itself end in a selector; neither disturbs the other.
|
||||
_, got := expand(t, `
|
||||
#define M(A) VADD A, A.S4, A
|
||||
TEXT ·f(SB), NOSPLIT, $0
|
||||
M(V0)
|
||||
M(V1.B16)
|
||||
RET
|
||||
`)
|
||||
wantLines(t, got,
|
||||
"VADD V0, V0.S4, V0",
|
||||
"VADD V1.B16, V1.B16.S4, V1.B16",
|
||||
"RET",
|
||||
)
|
||||
}
|
||||
|
||||
func TestSelectorKeepsNonParameterPrefixes(t *testing.T) {
|
||||
// The prefix before the period must be the whole parameter name:
|
||||
// AB.S4 never reaches a parameter A.
|
||||
_, got := expand(t, `
|
||||
#define M(A) VADD AB.S4, A.S4, AB.S4
|
||||
TEXT ·f(SB), NOSPLIT, $0
|
||||
M(V0)
|
||||
RET
|
||||
`)
|
||||
wantLines(t, got, "VADD AB.S4, V0.S4, AB.S4", "RET")
|
||||
}
|
||||
|
||||
func TestSelectorExpandsMacroValuedArgument(t *testing.T) {
|
||||
// gcm_arm64.s invokes mulRound(B1) where B1 is itself an object macro:
|
||||
// the paste stays rescannable, so B1.D1 still expands to V1.D1 the way
|
||||
// the toolchain's rescan of substituted tokens does.
|
||||
_, got := expand(t, `
|
||||
#define B1 V1
|
||||
#define mulRound(X) VPMULL X.D1, T1.D1, T3.Q1
|
||||
TEXT ·f(SB), NOSPLIT, $0
|
||||
mulRound(B1)
|
||||
RET
|
||||
`)
|
||||
wantLines(t, got, "VPMULL V1.D1, T1.D1, T3.Q1", "RET")
|
||||
}
|
||||
|
||||
func TestObjectMacroBehindSelectorExpands(t *testing.T) {
|
||||
// Ordinary code writes ACC0.B16 where ACC0 is an object macro; the
|
||||
// toolchain expands the alias because its lexer reads the selector as
|
||||
// its own token, and the lookup here must reach the macro through the
|
||||
// compound spelling the same way.
|
||||
_, got := expand(t, `
|
||||
#define ACC0 V8
|
||||
TEXT ·f(SB), NOSPLIT, $0
|
||||
VEOR ACC0.B16, ACC0.B16, ACC0.B16
|
||||
RET
|
||||
`)
|
||||
wantLines(t, got, "VEOR V8.B16, V8.B16, V8.B16", "RET")
|
||||
}
|
||||
|
||||
func TestChacha8QRMacroExpands(t *testing.T) {
|
||||
// The real QR round of chacha8_arm64.s end to end: every parameter
|
||||
// carries a selector somewhere, and the round is sixteen instructions.
|
||||
_, got := expand(t, `
|
||||
#define QR(A, B, C, D) \
|
||||
VADD A.S4, B.S4, A.S4; VEOR D.B16, A.B16, D.B16; VREV32 D.H8, D.H8; \
|
||||
VADD C.S4, D.S4, C.S4; VEOR B.B16, C.B16, V30.B16; VSHL $12, V30.S4, B.S4; VSRI $20, V30.S4, B.S4; \
|
||||
VADD A.S4, B.S4, A.S4; VEOR D.B16, A.B16, D.B16; VTBL V31.B16, [D.B16], D.B16; \
|
||||
VADD C.S4, D.S4, C.S4; VEOR B.B16, C.B16, V30.B16; VSHL $7, V30.S4, B.S4; VSRI $25, V30.S4, B.S4
|
||||
TEXT ·f(SB), NOSPLIT, $0
|
||||
QR(V0, V1, V2, V3)
|
||||
RET
|
||||
`)
|
||||
wantLines(t, got,
|
||||
"VADD V0.S4, V1.S4, V0.S4",
|
||||
"VEOR V3.B16, V0.B16, V3.B16",
|
||||
"VREV32 V3.H8, V3.H8",
|
||||
"VADD V2.S4, V3.S4, V2.S4",
|
||||
"VEOR V1.B16, V2.B16, V30.B16",
|
||||
"VSHL $12, V30.S4, V1.S4",
|
||||
"VSRI $20, V30.S4, V1.S4",
|
||||
"VADD V0.S4, V1.S4, V0.S4",
|
||||
"VEOR V3.B16, V0.B16, V3.B16",
|
||||
"VTBL V31.B16, [V3.B16], V3.B16",
|
||||
"VADD V2.S4, V3.S4, V2.S4",
|
||||
"VEOR V1.B16, V2.B16, V30.B16",
|
||||
"VSHL $7, V30.S4, V1.S4",
|
||||
"VSRI $25, V30.S4, V1.S4",
|
||||
"RET",
|
||||
)
|
||||
}
|
||||
|
||||
func TestNotAnExpressionFallsBack(t *testing.T) {
|
||||
// Symbol immediates and floats must keep their ordinary parse.
|
||||
f, errs := ParseWithOptions("t_amd64.s", "TEXT ·f(SB), NOSPLIT, $0\n\tMOVQ $1.5, AX\n\tMOVQ $·sym(SB), AX\n\tRET\n", Options{})
|
||||
|
||||
Reference in New Issue
Block a user