Source file src/internal/cpu/datacache_x86.go

     1  // Copyright 2025 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  //go:build 386 || amd64
     6  
     7  package cpu
     8  
     9  // DataCacheSizes returns the size of each data cache from lowest
    10  // level in the hierarchy to highest.
    11  //
    12  // Unlike other parts of this package's public API, it is not safe
    13  // to reference early in runtime initialization because it allocates.
    14  // It's intended for testing only.
    15  func DataCacheSizes() []uintptr {
    16  	maxFunctionInformation, ebx0, ecx0, edx0 := cpuid(0, 0)
    17  	if maxFunctionInformation < 1 {
    18  		return nil
    19  	}
    20  
    21  	switch {
    22  	// Check for "GenuineIntel"
    23  	case ebx0 == 0x756E6547 && ecx0 == 0x6C65746E && edx0 == 0x49656E69:
    24  		return getDataCacheSizesIntel(maxFunctionInformation)
    25  	// Check for "AuthenticAMD"
    26  	case ebx0 == 0x68747541 && ecx0 == 0x444D4163 && edx0 == 0x69746E65:
    27  		return getDataCacheSizesAMD()
    28  	}
    29  	return nil
    30  }
    31  
    32  func extractBits(arg uint32, l int, r int) uint32 {
    33  	if l > r {
    34  		panic("bad bit range")
    35  	}
    36  	return (arg >> l) & ((1 << (r - l + 1)) - 1)
    37  }
    38  
    39  func getDataCacheSizesIntel(maxID uint32) []uintptr {
    40  	// Constants for cache types
    41  	const (
    42  		noCache          = 0
    43  		dataCache        = 1
    44  		instructionCache = 2
    45  		unifiedCache     = 3
    46  	)
    47  	if maxID < 4 {
    48  		return nil
    49  	}
    50  
    51  	// Iterate through CPUID leaf 4 (deterministic cache parameters)
    52  	var caches []uintptr
    53  	for i := uint32(0); i < 0xFFFF; i++ {
    54  		eax, ebx, ecx, _ := cpuid(4, i)
    55  
    56  		cacheType := eax & 0xF // EAX bits 4-0: Cache Type
    57  		if cacheType == 0 {
    58  			break
    59  		}
    60  
    61  		// Report only data caches.
    62  		if !(cacheType == dataCache || cacheType == unifiedCache) {
    63  			continue
    64  		}
    65  
    66  		// Guaranteed to always start counting from 1.
    67  		level := (eax >> 5) & 0x7
    68  
    69  		lineSize := extractBits(ebx, 0, 11) + 1         // Bits 11-0: Line size in bytes - 1
    70  		partitions := extractBits(ebx, 12, 21) + 1      // Bits 21-12: Physical line partitions - 1
    71  		ways := extractBits(ebx, 22, 31) + 1            // Bits 31-22: Ways of associativity - 1
    72  		sets := uint64(ecx) + 1                         // Number of sets - 1
    73  		size := uint64(ways*partitions*lineSize) * sets // Calculate cache size in bytes
    74  
    75  		caches = append(caches, uintptr(size))
    76  
    77  		// If we see more than one cache described per level, or they appear
    78  		// out of order, crash.
    79  		//
    80  		// Going by the SDM, it's not clear whether this is actually possible,
    81  		// so this code is purely defensive.
    82  		if level != uint32(len(caches)) {
    83  			panic("expected levels to be in order and for there to be one data/unified cache per level")
    84  		}
    85  	}
    86  	return caches
    87  }
    88  
    89  func getDataCacheSizesAMD() []uintptr {
    90  	maxExtendedFunctionInformation, _, _, _ := cpuid(0x80000000, 0)
    91  	if maxExtendedFunctionInformation < 0x80000006 {
    92  		return nil
    93  	}
    94  
    95  	var caches []uintptr
    96  
    97  	_, _, ecx5, _ := cpuid(0x80000005, 0)
    98  	_, _, ecx6, edx6 := cpuid(0x80000006, 0)
    99  
   100  	// The size is return in kb, turning into bytes.
   101  	l1dSize := uintptr(extractBits(ecx5, 24, 31) << 10)
   102  	caches = append(caches, l1dSize)
   103  
   104  	// Check that L2 cache is present.
   105  	if l2Assoc := extractBits(ecx6, 12, 15); l2Assoc == 0 {
   106  		return caches
   107  	}
   108  	l2Size := uintptr(extractBits(ecx6, 16, 31) << 10)
   109  	caches = append(caches, l2Size)
   110  
   111  	// Check that L3 cache is present.
   112  	if l3Assoc := extractBits(edx6, 12, 15); l3Assoc == 0 {
   113  		return caches
   114  	}
   115  	// Specifies the L3 cache size is within the following range:
   116  	// (L3Size[31:18] * 512KB) <= L3 cache size < ((L3Size[31:18]+1) * 512KB).
   117  	l3Size := uintptr(extractBits(edx6, 18, 31) * (512 << 10))
   118  	caches = append(caches, l3Size)
   119  
   120  	return caches
   121  }
   122  

View as plain text