diff --git a/asm/arm64_assemble.go b/asm/arm64_assemble.go index ce42aa5..c9e7144 100644 --- a/asm/arm64_assemble.go +++ b/asm/arm64_assemble.go @@ -4978,11 +4978,22 @@ func encodeARM64VLDST(mnem string, post uint32, ops []*ast.Operand) ([]byte, err if off != 0 && post == 0 { return nil, fmt.Errorf("%s: offset %d not supported, plain and list accesses take a plain (Rn) operand", mnem, off) } + // The toolchain's addressing contract for the structure forms (asm7.go's + // class ladder): an index register exists only as the post-index + // increment, so it is illegal without .P and takes neither an offset nor + // an extend nor a shift beside it. + idx := ops[memIdx].Addr.Index + if idx != "" && post == 0 { + return nil, fmt.Errorf("%s: illegal combination: the register index is a post-index, it needs the .P spelling", mnem) + } + if idx != "" && (ops[memIdx].Addr.Shift != "" || ops[memIdx].Addr.HasOff) { + return nil, fmt.Errorf("%s: invalid extended register op: the post-index register takes no offset, extend or shift", mnem) + } // The post-index increment: 11111 for an immediate offset, else the // spelled (Rn)(Rm) register. rm := 31 if post != 0 { - if idx := ops[memIdx].Addr.Index; idx != "" { + if idx != "" { if rm = arm64RegNum(idx); rm < 0 { return nil, fmt.Errorf("%s: invalid post-index register %q", mnem, idx) } @@ -5000,6 +5011,14 @@ func encodeARM64VLDST(mnem string, post uint32, ops []*ast.Operand) ([]byte, err if !ok { return nil, fmt.Errorf("%s: invalid arrangement %q", mnem, vs[0].arr) } + // The immediate post-increment transfers one element per register, + // not a whole register (VLD3R.P 6(R15), [V15.H4,V16.H4,V17.H4]). + // An unspelled offset (or a spelled zero, the same class) is the + // implicit by-size increment the Rm=11111 encoding carries; any + // other spelled value must match. + if post != 0 && idx == "" && off != 0 && off != int64(n)*(1< 0 { + continue // a parse rejection is a rejection + } + if _, err := AssembleFileARM64(f); err == nil { + t.Errorf("expected rejection for %q, got nil", strings.TrimSpace(src)) + } + } +} + // TestArm64FPImmediate pins the FP immediate moves against `go tool asm` // words: the FMOV (immediate) instruction for the 8-bit encodable values and // the FMOV-from-ZR move for zero, plus the rejections the toolchain raises diff --git a/asm/arm64_errorparity_test.go b/asm/arm64_errorparity_test.go index 125f9f9..f8c128f 100644 --- a/asm/arm64_errorparity_test.go +++ b/asm/arm64_errorparity_test.go @@ -17,18 +17,7 @@ import ( // only shrinks: every tightening of the encoder moves spellings out of it, // and a spelling reappearing here means a regression. var arm64AcceptedErrorShapes = []string{ - // VLD1/VST1 post-index shapes: the register post-index without the .P // spelling, the .P immediate against the aggregate register size, the - // scaled register increment and the replicating register count. - "VLD1 (R8)(R13), [V2.B16]", - "VST1 [V1.B16], (R8)(R13)", - "VST1.P [V4.S4,V5.S4], 48(R1)", - "VST1.P [V4.S4], 8(R1)", - "VLD1.P 32(R1), [V8.S4, V9.S4, V10.S4]", - "VLD1.P 48(R1), [V7.S4, V8.S4, V9.S4, V10.S4]", - "VLD1.P (R8)(R9<<2), [V2.B16]", - "VST1.P [V1.B16], (R8)(R9<<1)", - "VLD3R.P 24(R15), [V15.H4,V16.H4,V17.H4]", // REGTMP (R27) as an explicit operand the toolchain refuses, the pair // split paths included. "ADD $0x1234567, R27, R3",