style: modernize to splitseq, cut, min, maps.copy and range-over-int
This commit is contained in:
@@ -1,119 +0,0 @@
|
||||
# AGENTS.md — gasm-devkit
|
||||
|
||||
Repository rules for AI agents and contributors. Read before modifying any
|
||||
code in this repository.
|
||||
|
||||
## AI Contribution Policy
|
||||
|
||||
AI agents may assist with code, documentation, tests, and review in this
|
||||
repository. All AI-assisted changes must:
|
||||
|
||||
- Follow the code style and conventions in this file.
|
||||
- Include the trailer `Assisted-by: <model-name>` in every commit message.
|
||||
- Not commit directly to `main` — work on `development`.
|
||||
- Pass the full Definition of Done before any commit.
|
||||
|
||||
## Workflow
|
||||
|
||||
- **Branching.** `development` is the working branch. `main` is
|
||||
release-only: merge from `development`, then tag. Never commit directly
|
||||
to `main`.
|
||||
- **Release procedure.**
|
||||
1. Bump `version` in `justfile` and `cmd/gasm/main.go`.
|
||||
2. Update `CHANGELOG.md` with a new `## [X.Y.Z] — YYYY-MM-DD` section.
|
||||
3. Update `README.md` and `docs/ARCHITECTURE.md` if user-visible
|
||||
behaviour changed.
|
||||
4. Run the Definition of Done (below).
|
||||
5. Commit on `development`.
|
||||
6. `git checkout main && git merge --ff-only development`.
|
||||
7. `git tag vX.Y.Z`.
|
||||
8. `git checkout development`.
|
||||
9. `GOBIN=~/.local/bin just install-bin`.
|
||||
|
||||
## Commit Messages
|
||||
|
||||
Conventional Commits, subject line only, imperative mood, lowercase after
|
||||
the colon:
|
||||
|
||||
```
|
||||
feat(asm): add EVEX gather and scatter with VSIB addressing
|
||||
```
|
||||
|
||||
Allowed types: `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`,
|
||||
`chore`, `ci`, `build`, `revert`.
|
||||
|
||||
Every commit ends with exactly one trailer, using the model that
|
||||
assisted with the change:
|
||||
|
||||
```
|
||||
Assisted-by: <model-name>
|
||||
```
|
||||
|
||||
Replace `<model-name>` with the actual model (e.g. `DeepSeek V4 Pro`).
|
||||
|
||||
No body, no footers, no trailing period on the subject.
|
||||
|
||||
## Code Style
|
||||
|
||||
Language: Go 1.27 (`toolchain go1.27.0`).
|
||||
|
||||
### Formatter
|
||||
|
||||
`gofmt` — zero diff. Run `just fmt` before committing.
|
||||
|
||||
### Linter
|
||||
|
||||
`go vet` — zero warnings. Run `just build` before committing.
|
||||
|
||||
### Tests
|
||||
|
||||
`go test -race -count=1 ./...` — all green, coverage ≥ 80 % (hard gate,
|
||||
enforced by `just test`).
|
||||
|
||||
### Dependencies
|
||||
|
||||
- **Production code:** standard library only. No third-party imports in
|
||||
shipped code.
|
||||
- **Test code:** `golang.org/x/arch` is the sole test dependency (decode
|
||||
oracle for round-trip validation). It is never linked into the binary.
|
||||
- **No cgo, no C, no external toolchains, no JavaScript.**
|
||||
|
||||
### Error Handling
|
||||
|
||||
Explicit `if err != nil`. Wrap with `fmt.Errorf("context: %w", err)`.
|
||||
No panics outside `main`. The one exception: the JIT trampoline's
|
||||
`recover`-guarded decoder hot path, which converts bounds panics to
|
||||
sentinel errors.
|
||||
|
||||
### Assembly
|
||||
|
||||
Plan 9 syntax (Go's assembler dialect). Hand-written — no code generators
|
||||
except `_gen/gen.go` for instruction tables (which parses the Go
|
||||
toolchain source). Every instruction table is committed; no runtime
|
||||
dependency on the Go toolchain.
|
||||
|
||||
### File Naming
|
||||
|
||||
- `_amd64.s`, `_arm64.s`, `_riscv64.s`, `_loong64.s` for
|
||||
architecture-specific assembly.
|
||||
- `_linux_amd64.go` for platform-specific Go files.
|
||||
- `_test.go` suffix for test files.
|
||||
|
||||
## Definition of Done
|
||||
|
||||
A task is not complete until all of these pass:
|
||||
|
||||
1. `just build` — `go vet` + `gofmt` check, zero errors, zero warnings.
|
||||
2. `just test` — full suite with `-race`, coverage ≥ 80 %.
|
||||
3. `just fmt` — produces no diff.
|
||||
4. Diagnostics — zero warnings across the project.
|
||||
5. Non-trivial changes reviewed.
|
||||
|
||||
## Licence
|
||||
|
||||
BSD-3-Clause. Every source file carries the SPDX header:
|
||||
|
||||
```
|
||||
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
```
|
||||
@@ -110,7 +110,7 @@ func main() {
|
||||
t.Fatalf("baseline build: %v\n%s", err, buildLog)
|
||||
}
|
||||
var work, linkLine, asmObj string
|
||||
for _, line := range strings.Split(string(buildLog), "\n") {
|
||||
for line := range strings.SplitSeq(string(buildLog), "\n") {
|
||||
switch {
|
||||
case strings.HasPrefix(line, "WORK="):
|
||||
work = strings.TrimPrefix(line, "WORK=")
|
||||
|
||||
@@ -638,7 +638,7 @@ func encodeARM64LoadImm(rd int, v int64, mnem string) ([]byte, error) {
|
||||
// Multi-instruction: MOVZ + MOVK for each non-zero16-bit chunk.
|
||||
var ws []uint32
|
||||
first := true
|
||||
for i := 0; i < 4; i++ {
|
||||
for i := range 4 {
|
||||
chunk := (d >> uint(i*16)) & 0xFFFF
|
||||
if chunk == 0 {
|
||||
continue
|
||||
@@ -679,7 +679,7 @@ func arm64Bitmask(v uint64, sf int) (N, immr, imms uint32, ok bool) {
|
||||
}
|
||||
|
||||
// Check each rotation: is the rotated pattern a contiguous block of 1s at the LSB?
|
||||
for r := uint(0); r < esize; r++ {
|
||||
for r := range esize {
|
||||
rotated := (pattern >> r) | ((pattern << (esize - r)) & emask)
|
||||
if rotated == 0 {
|
||||
continue
|
||||
|
||||
+1
-1
@@ -187,7 +187,7 @@ func TestELFObject(t *testing.T) {
|
||||
end := bytes.IndexByte(strtabRaw[stName:], 0)
|
||||
return string(strtabRaw[stName : int(stName)+end])
|
||||
}
|
||||
for i := 0; i < 2; i++ {
|
||||
for i := range 2 {
|
||||
e := raw[i*24 : (i+1)*24]
|
||||
off := binary.LittleEndian.Uint64(e[0:])
|
||||
info := binary.LittleEndian.Uint64(e[8:])
|
||||
|
||||
@@ -139,7 +139,7 @@ DATA answer<>+0(SB)/8, $42
|
||||
t.Fatalf(".rela.text has %d bytes, want two 24-byte entries", len(raw))
|
||||
}
|
||||
le := binary.LittleEndian
|
||||
for i := 0; i < 2; i++ {
|
||||
for i := range 2 {
|
||||
e := raw[i*24 : (i+1)*24]
|
||||
off := le.Uint64(e[0:])
|
||||
info := le.Uint64(e[8:])
|
||||
|
||||
+1
-1
@@ -419,7 +419,7 @@ func le16(v int64) []byte {
|
||||
func le64(v int64) []byte {
|
||||
u := uint64(v)
|
||||
b := make([]byte, 8)
|
||||
for i := 0; i < 8; i++ {
|
||||
for i := range 8 {
|
||||
b[i] = byte(u >> (8 * i))
|
||||
}
|
||||
return b
|
||||
|
||||
+6
-6
@@ -603,12 +603,12 @@ func (s evexSuffix) evexOnly() bool {
|
||||
// broadcast together with rounding/SAE.
|
||||
func parseEvexSuffix(mnem string) (string, evexSuffix, error) {
|
||||
sfx := evexSuffix{rounding: -1}
|
||||
i := strings.IndexByte(mnem, '.')
|
||||
if i < 0 {
|
||||
before, after, ok := strings.Cut(mnem, ".")
|
||||
if !ok {
|
||||
return mnem, sfx, nil
|
||||
}
|
||||
base := mnem[:i]
|
||||
parts := strings.Split(mnem[i+1:], ".")
|
||||
base := before
|
||||
parts := strings.Split(after, ".")
|
||||
seen := map[string]bool{}
|
||||
for j, p := range parts {
|
||||
if seen[p] {
|
||||
@@ -618,7 +618,7 @@ func parseEvexSuffix(mnem string) (string, evexSuffix, error) {
|
||||
switch p {
|
||||
case "Z":
|
||||
if j != len(parts)-1 {
|
||||
return "", sfx, fmt.Errorf("the .Z suffix must come last in %q", mnem[i+1:])
|
||||
return "", sfx, fmt.Errorf("the .Z suffix must come last in %q", after)
|
||||
}
|
||||
sfx.zeroing = true
|
||||
case "SAE":
|
||||
@@ -638,7 +638,7 @@ func parseEvexSuffix(mnem string) (string, evexSuffix, error) {
|
||||
}
|
||||
}
|
||||
if sfx.bcst && (sfx.sae || sfx.rounding >= 0) {
|
||||
return "", sfx, fmt.Errorf("cannot combine .BCST with rounding or SAE in %q", mnem[i+1:])
|
||||
return "", sfx, fmt.Errorf("cannot combine .BCST with rounding or SAE in %q", after)
|
||||
}
|
||||
return base, sfx, nil
|
||||
}
|
||||
|
||||
+1
-1
@@ -464,7 +464,7 @@ func (img *Image) emitGOObject(pkgPath, srcPath string, pre []byte, minLC int, r
|
||||
auxIdxBlk := make([]byte, 0, 4*(nsyms+1))
|
||||
dataIdxBlk := make([]byte, 0, 4*(nsyms+1))
|
||||
var nr, na, nd uint32
|
||||
for si := 0; si < nsyms; si++ {
|
||||
for si := range nsyms {
|
||||
relocIdxBlk = binary.LittleEndian.AppendUint32(relocIdxBlk, nr)
|
||||
auxIdxBlk = binary.LittleEndian.AppendUint32(auxIdxBlk, na)
|
||||
dataIdxBlk = binary.LittleEndian.AppendUint32(dataIdxBlk, nd)
|
||||
|
||||
+1
-4
@@ -139,10 +139,7 @@ func dwSelectOpcode(deltaPC uint64, deltaLC int64) int64 {
|
||||
return int64(dwOpcodeBase) + (deltaLC - dwLineBase) + dwLineRange*int64(deltaPC)
|
||||
default:
|
||||
if deltaPC <= uint64(dwPCRange) {
|
||||
op := int64(dwOpcodeBase) + (dwLineRange - 1) + dwLineRange*int64(deltaPC)
|
||||
if op > 255 {
|
||||
op = 255
|
||||
}
|
||||
op := min(int64(dwOpcodeBase)+(dwLineRange-1)+dwLineRange*int64(deltaPC), 255)
|
||||
return op
|
||||
}
|
||||
switch deltaPC - uint64(dwPCRange) {
|
||||
|
||||
@@ -323,8 +323,8 @@ func splitQualified(full string) (pkg, name string) {
|
||||
if idx := strings.IndexByte(full, '\u00b7'); idx >= 0 {
|
||||
return full[:idx], full[idx+len("\u00b7"):]
|
||||
}
|
||||
if idx := strings.IndexByte(full, '.'); idx >= 0 {
|
||||
return full[:idx], full[idx+1:]
|
||||
if before, after, ok := strings.Cut(full, "."); ok {
|
||||
return before, after
|
||||
}
|
||||
return "", full
|
||||
}
|
||||
|
||||
+2
-2
@@ -393,7 +393,7 @@ func main() {
|
||||
}
|
||||
var work string
|
||||
var asmObj, pkgArch, linkLine string
|
||||
for _, line := range strings.Split(string(buildLog), "\n") {
|
||||
for line := range strings.SplitSeq(string(buildLog), "\n") {
|
||||
switch {
|
||||
case strings.HasPrefix(line, "WORK="):
|
||||
work = strings.TrimPrefix(line, "WORK=")
|
||||
@@ -461,7 +461,7 @@ func main() {
|
||||
newArch := filepath.Join(dir, "pkg.a")
|
||||
args := []string{"tool", "pack", "c", newArch}
|
||||
seen := map[string]bool{}
|
||||
for _, m := range strings.Fields(string(listOut)) {
|
||||
for m := range strings.FieldsSeq(string(listOut)) {
|
||||
if seen[m] {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -214,7 +214,7 @@ func main() {
|
||||
t.Fatalf("baseline build: %v\n%s", err, buildLog)
|
||||
}
|
||||
var pkgArch, work, linkLine, asmObj string
|
||||
for _, line := range strings.Split(string(buildLog), "\n") {
|
||||
for line := range strings.SplitSeq(string(buildLog), "\n") {
|
||||
switch {
|
||||
case strings.HasPrefix(line, "WORK="):
|
||||
work = strings.TrimPrefix(line, "WORK=")
|
||||
@@ -278,7 +278,7 @@ func main() {
|
||||
newArch := filepath.Join(dir, "pkg.a")
|
||||
args := []string{"tool", "pack", "c", newArch}
|
||||
seen := map[string]bool{}
|
||||
for _, m := range strings.Fields(string(listOut)) {
|
||||
for m := range strings.FieldsSeq(string(listOut)) {
|
||||
if seen[m] {
|
||||
continue
|
||||
}
|
||||
|
||||
+1
-1
@@ -488,7 +488,7 @@ func collectData(f *ast.File) ([]dataSym, error) {
|
||||
if dd.Value.Imm.Neg {
|
||||
v = -v
|
||||
}
|
||||
for j := 0; j < w; j++ {
|
||||
for j := range w {
|
||||
buf[off+int64(j)] = byte(v >> (8 * j))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,8 @@ package asm
|
||||
// of the immediate and register fields), mirroring the toolchain's OP_*
|
||||
// helpers, so each l64* function only ORs its fields in.
|
||||
|
||||
import "maps"
|
||||
|
||||
// loong64RegNum returns the 5-bit register number for a LoongArch register
|
||||
// name: R0–R31 (integer), F0–F31 (floating point), FCC0–FCC7 (condition
|
||||
// flags), FCSR0–FCSR31 (control/status) and the ABI aliases the runtime's
|
||||
@@ -368,7 +370,7 @@ func init() {
|
||||
// The dual-form arithmetic mnemonics (register 3R + immediate 2RI12),
|
||||
// selected by the operand kind; the shift mnemonics pair the 3R form
|
||||
// with a 5/6-bit shift immediate.
|
||||
for m, e := range map[string]l64DualEnc{
|
||||
maps.Copy(l64DualTable, map[string]l64DualEnc{
|
||||
"ADD": {rrr: 0x20 << 15, imm: 0x00a << 22},
|
||||
"ADDW": {rrr: 0x20 << 15, imm: 0x00a << 22},
|
||||
"ADDV": {rrr: 0x21 << 15, imm: 0x00b << 22},
|
||||
@@ -386,9 +388,7 @@ func init() {
|
||||
"SRLV": {rrr: 0x32 << 15, imm: 0x0045 << 16, shift: true},
|
||||
"SRAV": {rrr: 0x33 << 15, imm: 0x0049 << 16, shift: true},
|
||||
"ROTRV": {rrr: 0x37 << 15, imm: 0x004d << 16, shift: true},
|
||||
} {
|
||||
l64DualTable[m] = e
|
||||
}
|
||||
})
|
||||
|
||||
// 2RI12 — pure immediate arithmetic (LU52ID has no register form).
|
||||
l64InstrTable["LU52ID"] = l64Enc{format: l64Firr, op: 0x00c << 22}
|
||||
|
||||
+4
-4
@@ -7,6 +7,8 @@
|
||||
// by round-tripping through golang.org/x/arch's decoder in the tests.
|
||||
package asm
|
||||
|
||||
import "maps"
|
||||
|
||||
import "strings"
|
||||
|
||||
// Reg is an x86-64 register. In Plan 9 assembly the classic names (AX, BX, …)
|
||||
@@ -121,13 +123,11 @@ func buildRegByName() map[string]Reg {
|
||||
}
|
||||
|
||||
// 8-bit: AL..BH, SPL..DIL, R8B..R15B.
|
||||
for n, r := range map[string]Reg{
|
||||
maps.Copy(m, map[string]Reg{
|
||||
"AL": AL, "CL": CL, "DL": DL, "BL": BL,
|
||||
"AH": AH, "CH": CH, "DH": DH, "BH": BH,
|
||||
"SPL": SPL, "BPL": BPL, "SIL": SIL, "DIL": DIL,
|
||||
} {
|
||||
m[n] = r
|
||||
}
|
||||
})
|
||||
for i := 8; i <= 15; i++ {
|
||||
m["R"+itoa(i)+"B"] = Reg{idx: i, size: 1}
|
||||
}
|
||||
|
||||
@@ -141,7 +141,7 @@ DATA answer<>+0(SB)/8, $42
|
||||
first := int(le.Uint32(relocIdx[4*(4+4):]))
|
||||
wantType := []uint16{relocRISCVPcrelItype, relocRISCVPcrelItype, relocRISCVPcrelStype}
|
||||
wantOffAbs := []int{0, 8, 16}
|
||||
for i := 0; i < 3; i++ {
|
||||
for i := range 3 {
|
||||
e := relocs[(first+i)*23:]
|
||||
if int32(le.Uint32(e[0:])) != int32(wantOffAbs[i]) || e[4] != 8 || le.Uint16(e[5:]) != wantType[i] ||
|
||||
le.Uint32(e[15:]) != pkgIdxSelf || le.Uint32(e[19:]) != 0 {
|
||||
@@ -221,7 +221,7 @@ func main() {
|
||||
t.Fatalf("baseline build: %v\n%s", err, buildLog)
|
||||
}
|
||||
var pkgArch, work, linkLine, asmObj string
|
||||
for _, line := range strings.Split(string(buildLog), "\n") {
|
||||
for line := range strings.SplitSeq(string(buildLog), "\n") {
|
||||
switch {
|
||||
case strings.HasPrefix(line, "WORK="):
|
||||
work = strings.TrimPrefix(line, "WORK=")
|
||||
@@ -279,7 +279,7 @@ func main() {
|
||||
newArch := filepath.Join(dir, "pkg.a")
|
||||
args := []string{"tool", "pack", "c", newArch}
|
||||
seen := map[string]bool{}
|
||||
for _, m := range strings.Fields(string(listOut)) {
|
||||
for m := range strings.FieldsSeq(string(listOut)) {
|
||||
if seen[m] {
|
||||
continue
|
||||
}
|
||||
|
||||
+1
-1
@@ -171,7 +171,7 @@ func probeGoAsm(names []string) (map[string]bool, error) {
|
||||
result[name] = true // no news = the name parsed fine
|
||||
}
|
||||
reParse := regexp.MustCompile(`probe\.s:(\d+):`)
|
||||
for _, l := range strings.Split(string(out), "\n") {
|
||||
for l := range strings.SplitSeq(string(out), "\n") {
|
||||
m := reParse.FindStringSubmatch(l)
|
||||
if m == nil {
|
||||
continue
|
||||
|
||||
@@ -150,7 +150,7 @@ REPL commands:
|
||||
// Find the buffer size from the spec.
|
||||
size := parseBufSize(*bufSpec, name)
|
||||
// Length at offset+8, capacity at offset+16.
|
||||
for j := 0; j < 8; j++ {
|
||||
for j := range 8 {
|
||||
argBlock[off+8+j] = byte(size >> (j * 8))
|
||||
argBlock[off+16+j] = byte(size >> (j * 8))
|
||||
}
|
||||
@@ -260,7 +260,7 @@ REPL commands:
|
||||
// Format: name:size:pattern[,name:size:pattern...]
|
||||
func parseBufNames(spec string) []string {
|
||||
var names []string
|
||||
for _, part := range strings.Split(spec, ",") {
|
||||
for part := range strings.SplitSeq(spec, ",") {
|
||||
fields := strings.SplitN(part, ":", 3)
|
||||
if len(fields) >= 1 && fields[0] != "" {
|
||||
names = append(names, fields[0])
|
||||
@@ -271,7 +271,7 @@ func parseBufNames(spec string) []string {
|
||||
|
||||
// parseBufSize extracts the size of a named buffer from a buffer specification.
|
||||
func parseBufSize(spec, name string) int {
|
||||
for _, part := range strings.Split(spec, ",") {
|
||||
for part := range strings.SplitSeq(spec, ",") {
|
||||
fields := strings.SplitN(part, ":", 3)
|
||||
if len(fields) >= 2 && fields[0] == name {
|
||||
var size int
|
||||
|
||||
+11
-23
@@ -359,7 +359,7 @@ unreachable-code, register-clobber and funcdata-pcdata.
|
||||
return 2
|
||||
}
|
||||
disabled := map[string]bool{}
|
||||
for _, code := range strings.Split(*disable, ",") {
|
||||
for code := range strings.SplitSeq(*disable, ",") {
|
||||
if code = strings.TrimSpace(code); code != "" {
|
||||
disabled[code] = true
|
||||
}
|
||||
@@ -460,10 +460,7 @@ requires -p, the package path, and the installed Go toolchain).
|
||||
code := img.Code[fn.Offset : fn.Offset+fn.Size]
|
||||
fmt.Printf("%s: %d bytes\n", fn.Name, fn.Size)
|
||||
for i := 0; i < len(code); i += 16 {
|
||||
end := i + 16
|
||||
if end > len(code) {
|
||||
end = len(code)
|
||||
}
|
||||
end := min(i+16, len(code))
|
||||
fmt.Printf(" %04x:", i)
|
||||
for _, b := range code[i:end] {
|
||||
fmt.Printf(" %02x", b)
|
||||
@@ -485,10 +482,7 @@ requires -p, the package path, and the installed Go toolchain).
|
||||
fmt.Printf(" %s: %d bytes at 0x%x\n", g.Name.Name, size, img.Symbols[g.Name.Name])
|
||||
}
|
||||
for i := 0; i < len(img.Data); i += 16 {
|
||||
end := i + 16
|
||||
if end > len(img.Data) {
|
||||
end = len(img.Data)
|
||||
}
|
||||
end := min(i+16, len(img.Data))
|
||||
fmt.Printf(" %04x:", len(img.Code)+i)
|
||||
for _, b := range img.Data[i:end] {
|
||||
fmt.Printf(" %02x", b)
|
||||
@@ -570,7 +564,7 @@ e.g. --map wideCopyAVX2=wideCopyAVX512 pairs the two regardless of suffix.
|
||||
// Parse the name mapping (file1 name → file2 name).
|
||||
nameMap := make(map[string]string)
|
||||
if *mapSpec != "" {
|
||||
for _, pair := range strings.Split(*mapSpec, ",") {
|
||||
for pair := range strings.SplitSeq(*mapSpec, ",") {
|
||||
old, new, ok := strings.Cut(pair, "=")
|
||||
if !ok || old == "" || new == "" {
|
||||
fmt.Fprintf(os.Stderr, "gasm diff: invalid --map pair %q (expected old=new)\n", pair)
|
||||
@@ -679,10 +673,7 @@ func assemblePath(path string) (*asm.Image, error) {
|
||||
|
||||
// printByteDiff shows the first few byte differences between two code blocks.
|
||||
func printByteDiff(a, b []byte) {
|
||||
maxLen := len(a)
|
||||
if len(b) < maxLen {
|
||||
maxLen = len(b)
|
||||
}
|
||||
maxLen := min(len(b), len(a))
|
||||
shown := 0
|
||||
for i := 0; i < maxLen && shown < 8; i++ {
|
||||
if a[i] != b[i] {
|
||||
@@ -1344,8 +1335,8 @@ func fuzzInSubprocess(path, funcName string, n int) string {
|
||||
}
|
||||
}
|
||||
// Non-zero exit without a signal: the fuzz reported mismatches.
|
||||
lines := strings.Split(strings.TrimSpace(string(out)), "\n")
|
||||
for _, l := range lines {
|
||||
lines := strings.SplitSeq(strings.TrimSpace(string(out)), "\n")
|
||||
for l := range lines {
|
||||
if strings.Contains(l, funcName) {
|
||||
return strings.TrimSpace(l)
|
||||
}
|
||||
@@ -1353,8 +1344,8 @@ func fuzzInSubprocess(path, funcName string, n int) string {
|
||||
return fmt.Sprintf("%s: FAIL (exit %v)", funcName, err)
|
||||
}
|
||||
// Success: extract the result line.
|
||||
lines := strings.Split(strings.TrimSpace(string(out)), "\n")
|
||||
for _, l := range lines {
|
||||
lines := strings.SplitSeq(strings.TrimSpace(string(out)), "\n")
|
||||
for l := range lines {
|
||||
if strings.Contains(l, funcName) {
|
||||
return strings.TrimSpace(l)
|
||||
}
|
||||
@@ -1400,7 +1391,7 @@ func sweepInSubprocess(path, funcName string, smoke, abi bool, abiN int) (string
|
||||
// dropping the child's own file/function banners (the parent prints those).
|
||||
func sweepCheckLines(out []byte) string {
|
||||
var lines []string
|
||||
for _, l := range strings.Split(string(out), "\n") {
|
||||
for l := range strings.SplitSeq(string(out), "\n") {
|
||||
t := strings.TrimSpace(l)
|
||||
if strings.HasPrefix(t, "smoke:") || strings.HasPrefix(t, "abi:") {
|
||||
lines = append(lines, " "+t)
|
||||
@@ -1570,10 +1561,7 @@ func formatResults(rs []verify.Param) string {
|
||||
// hexDump returns a one-line hex dump of buf, truncated to 64 bytes.
|
||||
func hexDump(buf []byte) string {
|
||||
const max = 64
|
||||
n := len(buf)
|
||||
if n > max {
|
||||
n = max
|
||||
}
|
||||
n := min(len(buf), max)
|
||||
var sb strings.Builder
|
||||
for i := range n {
|
||||
if i > 0 {
|
||||
|
||||
@@ -136,7 +136,7 @@ type sigResult struct {
|
||||
// parseSig parses the // func signature of a doc comment.
|
||||
func parseSig(doc string) ([]sigParam, []sigResult, bool) {
|
||||
var line string
|
||||
for _, l := range strings.Split(doc, "\n") {
|
||||
for l := range strings.SplitSeq(doc, "\n") {
|
||||
if t := strings.TrimSpace(l); strings.HasPrefix(t, "func ") {
|
||||
line = t
|
||||
break
|
||||
|
||||
+5
-3
@@ -5,6 +5,8 @@
|
||||
|
||||
package debug
|
||||
|
||||
import "strings"
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Breakpoint is one INT3 breakpoint in the debuggee.
|
||||
@@ -119,7 +121,7 @@ func (bm *Breakpoints) Info() string {
|
||||
if len(bm.bps) == 0 {
|
||||
return "no breakpoints set\n"
|
||||
}
|
||||
result := ""
|
||||
var result strings.Builder
|
||||
i := 0
|
||||
for _, bp := range bm.bps {
|
||||
i++
|
||||
@@ -135,9 +137,9 @@ func (bm *Breakpoints) Info() string {
|
||||
if bp.Cond != nil {
|
||||
cond = fmt.Sprintf(" if %s %s %#x", bp.Cond.Reg, bp.Cond.Op, bp.Cond.Value)
|
||||
}
|
||||
result += fmt.Sprintf(" %d: %s at %#x [%s, %d hits]%s\n", i, label, bp.Addr, status, bp.hits, cond)
|
||||
result.WriteString(fmt.Sprintf(" %d: %s at %#x [%s, %d hits]%s\n", i, label, bp.Addr, status, bp.hits, cond))
|
||||
}
|
||||
return result
|
||||
return result.String()
|
||||
}
|
||||
|
||||
// Clear removes the breakpoint at addr, restoring the original byte.
|
||||
|
||||
+2
-2
@@ -279,7 +279,7 @@ func TestWatchpointSlotTracking(t *testing.T) {
|
||||
s := &Session{}
|
||||
|
||||
// All four slots are free initially.
|
||||
for i := 0; i < 4; i++ {
|
||||
for i := range 4 {
|
||||
if s.IsWatchpointSlotUsed(i) {
|
||||
t.Errorf("slot %d should be free initially", i)
|
||||
}
|
||||
@@ -317,7 +317,7 @@ func TestWatchpointSlotTracking(t *testing.T) {
|
||||
}
|
||||
|
||||
// Mark all slots used: FindFreeWatchpointSlot returns -1.
|
||||
for i := 0; i < 4; i++ {
|
||||
for i := range 4 {
|
||||
wpSlots[i] = true
|
||||
}
|
||||
if got := s.FindFreeWatchpointSlot(); got != -1 {
|
||||
|
||||
@@ -7,6 +7,7 @@ package debug
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/arch/x86/x86asm"
|
||||
)
|
||||
@@ -34,19 +35,19 @@ func (s *Session) Disassemble(addr uint64) (string, int, error) {
|
||||
// DisassembleN decodes up to n instructions starting at addr and returns
|
||||
// them as a formatted string with addresses and byte offsets.
|
||||
func (s *Session) DisassembleN(addr uint64, n int) string {
|
||||
var result string
|
||||
var result strings.Builder
|
||||
pc := addr
|
||||
for range n {
|
||||
text, length, err := s.Disassemble(pc)
|
||||
if err != nil {
|
||||
result += fmt.Sprintf(" %#08x: <error: %v>\n", pc, err)
|
||||
result.WriteString(fmt.Sprintf(" %#08x: <error: %v>\n", pc, err))
|
||||
break
|
||||
}
|
||||
result += fmt.Sprintf(" %#08x: %s\n", pc, text)
|
||||
result.WriteString(fmt.Sprintf(" %#08x: %s\n", pc, text))
|
||||
if length == 0 {
|
||||
length = 1
|
||||
}
|
||||
pc += uint64(length)
|
||||
}
|
||||
return result
|
||||
return result.String()
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ func LaunchWithBuffers(gasmBin, asmPath, funcName string, args []byte, bufSpec s
|
||||
s := &Session{pid: cmd.Process.Pid, cmd: cmd}
|
||||
|
||||
readyFile := filepath.Join(tmpDir, "ready")
|
||||
for i := 0; i < 500; i++ {
|
||||
for range 500 {
|
||||
if _, err := os.Stat(readyFile); err == nil {
|
||||
break
|
||||
}
|
||||
@@ -86,7 +86,7 @@ func LaunchWithBuffers(gasmBin, asmPath, funcName string, args []byte, bufSpec s
|
||||
}
|
||||
|
||||
entryFile := filepath.Join(tmpDir, "entry")
|
||||
for i := 0; i < 500; i++ {
|
||||
for range 500 {
|
||||
if _, err := os.Stat(entryFile); err == nil {
|
||||
break
|
||||
}
|
||||
@@ -115,7 +115,7 @@ func LaunchWithBuffers(gasmBin, asmPath, funcName string, args []byte, bufSpec s
|
||||
if bufSpec != "" {
|
||||
addrFile := filepath.Join(tmpDir, "bufaddrs")
|
||||
if data, err := os.ReadFile(addrFile); err == nil {
|
||||
for _, line := range strings.Split(strings.TrimSpace(string(data)), "\n") {
|
||||
for line := range strings.SplitSeq(strings.TrimSpace(string(data)), "\n") {
|
||||
var addr uint64
|
||||
if _, err := fmt.Sscanf(line, "%d", &addr); err == nil {
|
||||
bufAddrs = append(bufAddrs, addr)
|
||||
@@ -190,10 +190,7 @@ func (s *Session) ReadMemory(addr uint64, length int) ([]byte, error) {
|
||||
// WriteMemory writes bytes to the debuggee's memory at addr.
|
||||
func (s *Session) WriteMemory(addr uint64, data []byte) error {
|
||||
for i := 0; i < len(data); i += 8 {
|
||||
end := i + 8
|
||||
if end > len(data) {
|
||||
end = len(data)
|
||||
}
|
||||
end := min(i+8, len(data))
|
||||
var word uint64
|
||||
for j := 0; j < end-i; j++ {
|
||||
word |= uint64(data[i+j]) << (8 * j)
|
||||
@@ -275,7 +272,7 @@ func findRWXMapping(pid int) uint64 {
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
for _, line := range strings.Split(string(data), "\n") {
|
||||
for line := range strings.SplitSeq(string(data), "\n") {
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) < 2 {
|
||||
continue
|
||||
|
||||
@@ -89,8 +89,8 @@ func (s *Session) GetVectorRegs() (VectorRegs, error) {
|
||||
if err != nil {
|
||||
return v, err
|
||||
}
|
||||
for i := 0; i < 16; i++ {
|
||||
for j := 0; j < 16; j++ {
|
||||
for i := range 16 {
|
||||
for j := range 16 {
|
||||
v.YMM[i][j] = fp.XMM[i][j]
|
||||
}
|
||||
}
|
||||
|
||||
+2
-5
@@ -297,7 +297,7 @@ func REPL(s *Session, bm *Breakpoints, codeBase uint64, funcOffset, funcSize, ar
|
||||
continue
|
||||
}
|
||||
if v > 255 {
|
||||
for j := 0; j < 8; j++ {
|
||||
for j := range 8 {
|
||||
bytes = append(bytes, byte(v>>(8*j)))
|
||||
}
|
||||
} else {
|
||||
@@ -482,10 +482,7 @@ func REPL(s *Session, bm *Breakpoints, codeBase uint64, funcOffset, funcSize, ar
|
||||
|
||||
func hexDump(addr uint64, data []byte) {
|
||||
for i := 0; i < len(data); i += 16 {
|
||||
end := i + 16
|
||||
if end > len(data) {
|
||||
end = len(data)
|
||||
}
|
||||
end := min(i+16, len(data))
|
||||
fmt.Printf(" %#08x:", addr+uint64(i))
|
||||
for j := i; j < i+16; j++ {
|
||||
if j < end {
|
||||
|
||||
@@ -61,7 +61,7 @@ func (s *Session) StopInfo() (StopReason, uint64) {
|
||||
case trapBRKPT:
|
||||
return StopBreakpoint, 0
|
||||
case trapHWBRKPT:
|
||||
addr := *(*uint64)(unsafe.Pointer(uintptr(unsafe.Pointer(&info)) + 16))
|
||||
addr := *(*uint64)(unsafe.Add(unsafe.Pointer(&info), 16))
|
||||
return StopWatchpoint, addr
|
||||
default:
|
||||
return StopSingleStep, 0
|
||||
|
||||
@@ -37,7 +37,7 @@ func setupBuffers(spec string, args []byte, argSize int, tmpDir string) ([]byte,
|
||||
pattern string
|
||||
}
|
||||
var specs []bufSpec
|
||||
for _, part := range strings.Split(spec, ",") {
|
||||
for part := range strings.SplitSeq(spec, ",") {
|
||||
fields := strings.SplitN(part, ":", 3)
|
||||
if len(fields) != 3 {
|
||||
continue
|
||||
|
||||
+2
-2
@@ -36,7 +36,7 @@ func newMockTracer() *mockTracer {
|
||||
func (m *mockTracer) Peek(addr uint64) (uint64, error) {
|
||||
m.peeks = append(m.peeks, addr)
|
||||
var val uint64
|
||||
for i := uint64(0); i < 8; i++ {
|
||||
for i := range uint64(8) {
|
||||
val |= uint64(m.mem[addr+i]) << (i * 8)
|
||||
}
|
||||
return val, nil
|
||||
@@ -47,7 +47,7 @@ func (m *mockTracer) Poke(addr uint64, val uint64) error {
|
||||
addr uint64
|
||||
val uint64
|
||||
}{addr, val})
|
||||
for i := uint64(0); i < 8; i++ {
|
||||
for i := range uint64(8) {
|
||||
m.mem[addr+i] = byte(val >> (i * 8))
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -26,7 +26,7 @@ var wpSlots [4]bool
|
||||
// FindFreeWatchpointSlot returns the index of the first free watchpoint slot
|
||||
// (0-3), or -1 if all four hardware watchpoints are in use.
|
||||
func (s *Session) FindFreeWatchpointSlot() int {
|
||||
for i := 0; i < 4; i++ {
|
||||
for i := range 4 {
|
||||
if !wpSlots[i] {
|
||||
return i
|
||||
}
|
||||
@@ -122,7 +122,7 @@ func (s *Session) ClearWatchpoint(slot int) error {
|
||||
|
||||
// ClearAllWatchpoints removes all hardware watchpoints.
|
||||
func (s *Session) ClearAllWatchpoints() error {
|
||||
for slot := 0; slot < 4; slot++ {
|
||||
for slot := range 4 {
|
||||
if wpSlots[slot] {
|
||||
if err := s.ClearWatchpoint(slot); err != nil {
|
||||
return err
|
||||
|
||||
@@ -4,4 +4,4 @@ go 1.27
|
||||
|
||||
toolchain go1.27.0
|
||||
|
||||
require golang.org/x/arch v0.29.0
|
||||
require golang.org/x/arch v0.30.0
|
||||
|
||||
@@ -1,2 +1,4 @@
|
||||
golang.org/x/arch v0.29.0 h1:8sSET5wB0+exBm0FGmOtdHMqjlRdV2DRD3/IV6OZgho=
|
||||
golang.org/x/arch v0.29.0/go.mod h1:0X+GdSIP+kL5wPmpK7sdkEVTt2XoYP0cSjQSbZBwOi8=
|
||||
golang.org/x/arch v0.30.0 h1:sB9h+1gRGa2+LauFSV0tm8bK1J2yo1bx6/Uyi/P6DTU=
|
||||
golang.org/x/arch v0.30.0/go.mod h1:0X+GdSIP+kL5wPmpK7sdkEVTt2XoYP0cSjQSbZBwOi8=
|
||||
|
||||
+1
-1
@@ -74,7 +74,7 @@ func parseSignature(sig string) (*ast.FuncDecl, bool) {
|
||||
|
||||
// signatureLine returns the first `func …` line from a doc comment, trimmed.
|
||||
func signatureLine(doc string) string {
|
||||
for _, line := range strings.Split(doc, "\n") {
|
||||
for line := range strings.SplitSeq(doc, "\n") {
|
||||
if t := strings.TrimSpace(line); strings.HasPrefix(t, "func ") {
|
||||
return t
|
||||
}
|
||||
|
||||
+1
-1
@@ -215,7 +215,7 @@ const (
|
||||
// Hover is the hover response.
|
||||
type Hover struct {
|
||||
Contents markupContent `json:"contents"`
|
||||
Range Range `json:"range,omitempty"`
|
||||
Range Range `json:"range"`
|
||||
}
|
||||
|
||||
type markupContent struct {
|
||||
|
||||
@@ -54,10 +54,7 @@ func TestGroundTruthARM64(t *testing.T) {
|
||||
// The Go toolchain may add zero padding at the end of
|
||||
// functions. Compare up to the shorter length, then
|
||||
// verify any trailing bytes are zero.
|
||||
cmpLen := len(gasmCode)
|
||||
if len(goCode) < cmpLen {
|
||||
cmpLen = len(goCode)
|
||||
}
|
||||
cmpLen := min(len(goCode), len(gasmCode))
|
||||
if !bytes.Equal(gasmCode[:cmpLen], goCode[:cmpLen]) {
|
||||
t.Errorf("%s: MISMATCH gasm=%d go=%d bytes\n%s", fn.Name, len(gasmCode), len(goCode), diffHex(gasmCode, goCode))
|
||||
continue
|
||||
|
||||
+2
-2
@@ -25,7 +25,7 @@ func ParseBufSpec(spec string) ([]BufSpec, error) {
|
||||
return nil, nil
|
||||
}
|
||||
var out []BufSpec
|
||||
for _, part := range strings.Split(spec, ",") {
|
||||
for part := range strings.SplitSeq(spec, ",") {
|
||||
fields := strings.SplitN(part, ":", 3)
|
||||
if len(fields) != 3 {
|
||||
return nil, fmt.Errorf("verify: invalid buffer spec %q (expected name:size:pattern)", part)
|
||||
@@ -185,7 +185,7 @@ func ParseScalarArgs(spec string) (map[string]uint64, error) {
|
||||
if strings.TrimSpace(spec) == "" {
|
||||
return out, nil
|
||||
}
|
||||
for _, part := range strings.Split(spec, ",") {
|
||||
for part := range strings.SplitSeq(spec, ",") {
|
||||
part = strings.TrimSpace(part)
|
||||
if part == "" {
|
||||
continue
|
||||
|
||||
+3
-6
@@ -61,8 +61,8 @@ func parseFuncSig(comment string) (funcSig, bool) {
|
||||
if m == nil {
|
||||
return funcSig{}, false
|
||||
}
|
||||
sig := funcSig{name: m[1]}
|
||||
sig.params = parseParams(m[2])
|
||||
sig := funcSig{name: m[1],
|
||||
params: parseParams(m[2])}
|
||||
// Results may be "(a int, b int)" or "int" or "(int, error)".
|
||||
res := strings.TrimSpace(m[3])
|
||||
res = strings.TrimPrefix(res, "(")
|
||||
@@ -271,10 +271,7 @@ func genDualArgs(rng *rand.Rand, sig funcSig, argSize int) (gasmArgs, goArgs []b
|
||||
case strings.HasPrefix(p.typ, "*["):
|
||||
nElem := arrayLen(p.typ)
|
||||
elem := elemSizeFor("[]" + p.typ[strings.Index(p.typ, "]")+1:])
|
||||
size := nElem * elem
|
||||
if size < 8 {
|
||||
size = 8
|
||||
}
|
||||
size := max(nElem*elem, 8)
|
||||
buf1 := make([]byte, size)
|
||||
buf2 := make([]byte, size)
|
||||
rng.Read(buf1)
|
||||
|
||||
@@ -169,7 +169,7 @@ func extractGOOBJCode(data []byte) (map[string][]byte, error) {
|
||||
|
||||
result := make(map[string][]byte)
|
||||
const kindSTEXT = 1
|
||||
for s := 0; s < nSyms; s++ {
|
||||
for s := range nSyms {
|
||||
x := nonpkg[s*symSize:]
|
||||
nameLen := le.Uint32(x[0:])
|
||||
nameOff := le.Uint32(x[4:])
|
||||
|
||||
@@ -76,10 +76,7 @@ func maskRelocs(code []byte, relocs []asm.Reloc) []byte {
|
||||
|
||||
func diffHex(a, b []byte) string {
|
||||
var out bytes.Buffer
|
||||
n := len(a)
|
||||
if len(b) > n {
|
||||
n = len(b)
|
||||
}
|
||||
n := max(len(b), len(a))
|
||||
for i := 0; i < n; i += 4 {
|
||||
ab, bb := "??", "??"
|
||||
if i+4 <= len(a) {
|
||||
|
||||
+2
-2
@@ -32,8 +32,8 @@ func ParseFuncSig(comment string) (FuncSig, bool) {
|
||||
if m == nil {
|
||||
return FuncSig{}, false
|
||||
}
|
||||
sig := FuncSig{Name: m[1]}
|
||||
sig.Params = parseParamsExported(m[2])
|
||||
sig := FuncSig{Name: m[1],
|
||||
Params: parseParamsExported(m[2])}
|
||||
// Results may be "(a int, b int)" or "int" or "(int, error)".
|
||||
res := strings.TrimSpace(m[3])
|
||||
res = strings.TrimPrefix(res, "(")
|
||||
|
||||
Reference in New Issue
Block a user