// Copyright 2025 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.

//go:build 386 || amd64

package cpu

// DataCacheSizes returns the size of each data cache from lowest
// level in the hierarchy to highest.
//
// Unlike other parts of this package's public API, it is not safe
// to reference early in runtime initialization because it allocates.
// It's intended for testing only.
func () []uintptr {
	, , ,  := cpuid(0, 0)
	if  < 1 {
		return nil
	}

	switch {
	// Check for "GenuineIntel"
	case  == 0x756E6547 &&  == 0x6C65746E &&  == 0x49656E69:
		return getDataCacheSizesIntel()
	// Check for "AuthenticAMD"
	case  == 0x68747541 &&  == 0x444D4163 &&  == 0x69746E65:
		return getDataCacheSizesAMD()
	}
	return nil
}

func extractBits( uint32,  int,  int) uint32 {
	if  >  {
		panic("bad bit range")
	}
	return ( >> ) & ((1 << ( -  + 1)) - 1)
}

func getDataCacheSizesIntel( uint32) []uintptr {
	// Constants for cache types
	const (
		          = 0
		        = 1
		 = 2
		     = 3
	)
	if  < 4 {
		return nil
	}

	// Iterate through CPUID leaf 4 (deterministic cache parameters)
	var  []uintptr
	for  := uint32(0);  < 0xFFFF; ++ {
		, , ,  := cpuid(4, )

		 :=  & 0xF // EAX bits 4-0: Cache Type
		if  == 0 {
			break
		}

		// Report only data caches.
		if !( ==  ||  == ) {
			continue
		}

		// Guaranteed to always start counting from 1.
		 := ( >> 5) & 0x7

		 := extractBits(, 0, 11) + 1         // Bits 11-0: Line size in bytes - 1
		 := extractBits(, 12, 21) + 1      // Bits 21-12: Physical line partitions - 1
		 := extractBits(, 22, 31) + 1            // Bits 31-22: Ways of associativity - 1
		 := uint64() + 1                         // Number of sets - 1
		 := uint64(**) *  // Calculate cache size in bytes

		 = append(, uintptr())

		// If we see more than one cache described per level, or they appear
		// out of order, crash.
		//
		// Going by the SDM, it's not clear whether this is actually possible,
		// so this code is purely defensive.
		if  != uint32(len()) {
			panic("expected levels to be in order and for there to be one data/unified cache per level")
		}
	}
	return 
}

func getDataCacheSizesAMD() []uintptr {
	, , ,  := cpuid(0x80000000, 0)
	if  < 0x80000006 {
		return nil
	}

	var  []uintptr

	, , ,  := cpuid(0x80000005, 0)
	, , ,  := cpuid(0x80000006, 0)

	// The size is return in kb, turning into bytes.
	 := uintptr(extractBits(, 24, 31) << 10)
	 = append(, )

	// Check that L2 cache is present.
	if  := extractBits(, 12, 15);  == 0 {
		return 
	}
	 := uintptr(extractBits(, 16, 31) << 10)
	 = append(, )

	// Check that L3 cache is present.
	if  := extractBits(, 12, 15);  == 0 {
		return 
	}
	// Specifies the L3 cache size is within the following range:
	// (L3Size[31:18] * 512KB) <= L3 cache size < ((L3Size[31:18]+1) * 512KB).
	 := uintptr(extractBits(, 18, 31) * (512 << 10))
	 = append(, )

	return 
}