| // Copyright 2026 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| package arm64 | |
| import ( | |
| "math/bits" | |
| "testing" | |
| "cmd/internal/obj" | |
| ) | |
| // decodeLogicalImmArrEncoding is translated from DecodeBitMasks in ARM64 | |
| // ASL. It implements the decoding logic of imm13 defined by ARM64 specification. | |
| func decodeLogicalImmArrEncoding(imm13 uint32, arr uint32) (uint64, bool) { | |
| n := (imm13 >> 12) & 1 | |
| immr := (imm13 >> 6) & 0x3F | |
| imms := imm13 & 0x3F | |
| notImms := (^imms) & 0x3F | |
| combined := (n << 6) | notImms | |
| // if immN::NOT(imms) == '000000x' then Undefined(); end; | |
| if combined <= 1 { | |
| return 0, false | |
| } | |
| lenVal := bits.Len32(uint32(combined)) - 1 | |
| esize := uint64(1 << lenVal) | |
| levels := uint32((1 << lenVal) - 1) | |
| // if immediate && (imms AND levels) == levels then Undefined(); end; | |
| if (imms & levels) == levels { | |
| return 0, false | |
| } | |
| s := uint64(imms & levels) | |
| r := uint64(immr & levels) | |
| // welem has s + 1 ones | |
| welem := (uint64(1) << (s + 1)) - 1 | |
| // Rotate right welem by r bits within esize bits window | |
| rotated := welem | |
| mask := (uint64(1) << esize) - 1 | |
| if r > 0 { | |
| r %= esize | |
| rotated = ((welem >> r) | (welem << (esize - r))) & mask | |
| } | |
| // Replicate rotated to fill 64 bits (as encode replicates to 64 bits) | |
| wmask := uint64(0) | |
| for i := uint64(0); i < 64; i += esize { | |
| wmask |= rotated << i | |
| } | |
| // Now mask back to the arrangement's lane size M | |
| var M uint64 | |
| switch arr { | |
| case ARNG_B: | |
| M = 8 | |
| case ARNG_H: | |
| M = 16 | |
| case ARNG_S: | |
| M = 32 | |
| case ARNG_D: | |
| M = 64 | |
| default: | |
| return 0, false | |
| } | |
| mask = (uint64(1) << M) - 1 | |
| return wmask & mask, true | |
| } | |
| func FuzzLogicalImmArrEncoding(f *testing.F) { | |
| f.Add(uint64(0x55), uint32(ARNG_B)) | |
| f.Add(uint64(0xAA), uint32(ARNG_B)) | |
| f.Add(uint64(0x0F), uint32(ARNG_B)) | |
| f.Add(uint64(0x0101), uint32(ARNG_H)) | |
| f.Add(uint64(0x00FF), uint32(ARNG_H)) | |
| f.Add(uint64(0x0000FFFF), uint32(ARNG_S)) | |
| f.Add(uint64(0x55555555), uint32(ARNG_S)) | |
| f.Add(uint64(0x0000FFFF), uint32(ARNG_D)) | |
| f.Add(uint64(0x0101010101010101), uint32(ARNG_D)) | |
| f.Fuzz(func(t *testing.T, v uint64, arr uint32) { | |
| if arr != ARNG_B && arr != ARNG_H && arr != ARNG_S && arr != ARNG_D { | |
| return | |
| } | |
| adjacentAddr := &obj.Addr{ | |
| Type: obj.TYPE_REG, | |
| Reg: int16(REG_ZARNG) + 0 + (int16(arr) << 5), | |
| } | |
| encoded, ok := encodeLogicalImmArrEncoding(v, adjacentAddr) | |
| if !ok { | |
| return | |
| } | |
| imm13 := encoded >> 5 | |
| decoded, ok := decodeLogicalImmArrEncoding(imm13, arr) | |
| if !ok { | |
| t.Errorf("Failed to decode imm13=0x%x arr=%d", imm13, arr) | |
| return | |
| } | |
| var mask uint64 | |
| switch arr { | |
| case ARNG_B: | |
| mask = 0xFF | |
| case ARNG_H: | |
| mask = 0xFFFF | |
| case ARNG_S: | |
| mask = 0xFFFFFFFF | |
| case ARNG_D: | |
| mask = 0xFFFFFFFFFFFFFFFF | |
| } | |
| expected := v & mask | |
| if decoded != expected { | |
| t.Errorf("Fuzz roundtrip failed for v=0x%x arr=%d. Expected 0x%x, got 0x%x (imm13=0x%x)", v, arr, expected, decoded, imm13) | |
| } | |
| }) | |
| } |