// Copyright 2017 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 cpu implements processor feature detection
// used by the Go standard library.
package cpu

// debugOptions is set to true by the runtime if go was compiled with GOEXPERIMENT=debugcpu
// and GOOS is Linux or Darwin. This variable is linknamed in runtime/proc.go.
var debugOptions bool

var X86 x86

// "Loads data or instructions from memory to the second-level cache.
// To use the streamer, organize the data or instructions in blocks of 128 bytes,
// aligned on 128 bytes."
// From <Intel® 64 and IA-32 architectures optimization reference manual>,
// in section 3.7.3 "Hardware Prefetching for Second-Level Cache"
//
// In practice, I have found use 128bytes can gain better performance than 64bytes (one cache line).
const X86FalseSharingRange = 128

// The booleans in x86 contain the correspondingly named cpuid feature bit.
// HasAVX and HasAVX2 are only set if the OS does support XMM and YMM registers
// in addition to the cpuid feature bit being set.
// The struct is padded to avoid false sharing.
type x86 struct {
	_            [X86FalseSharingRange]byte
	HasCMPXCHG16B bool
	HasAES       bool
	HasADX       bool
	HasAVX       bool
	HasAVX2      bool
	HasAVX512F   bool
	HasAVX512DQ  bool
	HasAVX512BW  bool
	HasAVX512VL  bool
	HasBMI1      bool
	HasBMI2      bool
	HasERMS      bool
	HasFMA       bool
	HasOSXSAVE   bool
	HasPCLMULQDQ bool
	HasPOPCNT    bool
	HasSSE2      bool
	HasSSE3      bool
	HasSSSE3     bool
	HasSSE41     bool
	HasSSE42     bool
	// The invariant TSC will run at a constant rate in all ACPI P-, C-, and T-states.
	// This is the architectural behavior moving forward. On processors with
	// invariant TSC support, the OS may use the TSC for wall clock timer services (instead of ACPI or HPET timers).
	HasInvariantTSC bool

	Cache Cache

	// TSCFrequency only meaningful when HasInvariantTSC == true.
	// Unit: Hz.
	//
	// Warn:
	// 1. If it's 0, means failed to get it from frequency table provided by Intel manual.
	TSCFrequency uint64

	Name       string
	Signature  string // DisplayFamily_DisplayModel.
	Family     uint32 // CPU family number.
	Model      uint32 // CPU model number.
	SteppingID uint32

	_ [X86FalseSharingRange]byte
}

// CPU Cache Size.
// -1 if undetected.
type Cache struct {
	L1I int
	L1D int
	L2  int
	L3  int
}

var PPC64 ppc64

// For ppc64x, it is safe to check only for ISA level starting on ISA v3.00,
// since there are no optional categories. There are some exceptions that also
// require kernel support to work (darn, scv), so there are feature bits for
// those as well. The minimum processor requirement is POWER8 (ISA 2.07), so we
// maintain some of the old feature checks for optional categories for
// safety.
// The struct is padded to avoid false sharing.
type ppc64 struct {
	_          [CacheLineSize]byte
	HasVMX     bool // Vector unit (Altivec)
	HasDFP     bool // Decimal Floating Point unit
	HasVSX     bool // Vector-scalar unit
	HasHTM     bool // Hardware Transactional Memory
	HasISEL    bool // Integer select
	HasVCRYPTO bool // Vector cryptography
	HasHTMNOSC bool // HTM: kernel-aborted transaction in syscalls
	HasDARN    bool // Hardware random number generator (requires kernel enablement)
	HasSCV     bool // Syscall vectored (requires kernel enablement)
	IsPOWER8   bool // ISA v2.07 (POWER8)
	IsPOWER9   bool // ISA v3.00 (POWER9)
	_          [CacheLineSize]byte
}

var ARM64 arm64

// The booleans in arm64 contain the correspondingly named cpu feature bit.
// The struct is padded to avoid false sharing.
type arm64 struct {
	_           [CacheLineSize]byte
	HasFP       bool
	HasASIMD    bool
	HasEVTSTRM  bool
	HasAES      bool
	HasPMULL    bool
	HasSHA1     bool
	HasSHA2     bool
	HasCRC32    bool
	HasATOMICS  bool
	HasFPHP     bool
	HasASIMDHP  bool
	HasCPUID    bool
	HasASIMDRDM bool
	HasJSCVT    bool
	HasFCMA     bool
	HasLRCPC    bool
	HasDCPOP    bool
	HasSHA3     bool
	HasSM3      bool
	HasSM4      bool
	HasASIMDDP  bool
	HasSHA512   bool
	HasSVE      bool
	HasASIMDFHM bool
	_           [CacheLineSize]byte
}

var S390X s390x

type s390x struct {
	_               [CacheLineSize]byte
	HasZArch        bool // z architecture mode is active [mandatory]
	HasSTFLE        bool // store facility list extended [mandatory]
	HasLDisp        bool // long (20-bit) displacements [mandatory]
	HasEImm         bool // 32-bit immediates [mandatory]
	HasDFP          bool // decimal floating point
	HasETF3Enhanced bool // ETF-3 enhanced
	HasMSA          bool // message security assist (CPACF)
	HasAES          bool // KM-AES{128,192,256} functions
	HasAESCBC       bool // KMC-AES{128,192,256} functions
	HasAESCTR       bool // KMCTR-AES{128,192,256} functions
	HasAESGCM       bool // KMA-GCM-AES{128,192,256} functions
	HasGHASH        bool // KIMD-GHASH function
	HasSHA1         bool // K{I,L}MD-SHA-1 functions
	HasSHA256       bool // K{I,L}MD-SHA-256 functions
	HasSHA512       bool // K{I,L}MD-SHA-512 functions
	HasVX           bool // vector facility. Note: the runtime sets this when it processes auxv records.
	_               [CacheLineSize]byte
}

// initialize examines the processor and sets the relevant variables above.
// This is called by the runtime package early in program initialization,
// before normal init functions are run. env is set by runtime on Linux and Darwin
// if go was compiled with GOEXPERIMENT=debugcpu.
func init() {
	doinit()
	processOptions("")
}

// options contains the cpu debug options that can be used in GODEBUGCPU.
// Options are arch dependent and are added by the arch specific doinit functions.
// Features that are mandatory for the specific GOARCH should not be added to options
// (e.g. SSE2 on amd64).
var options []option

// Option names should be lower case. e.g. avx instead of AVX.
type option struct {
	Name    string
	Feature *bool
}

// processOptions disables CPU feature values based on the parsed env string.
// The env string is expected to be of the form feature1=0,feature2=0...
// where feature names is one of the architecture specifc list stored in the
// cpu packages options variable. If env contains all=0 then all capabilities
// referenced through the options variable are disabled. Other feature
// names and values other than 0 are silently ignored.
func processOptions(env string) {
field:
	for env != "" {
		field := ""
		i := indexByte(env, ',')
		if i < 0 {
			field, env = env, ""
		} else {
			field, env = env[:i], env[i+1:]
		}
		i = indexByte(field, '=')
		if i < 0 {
			continue
		}
		key, value := field[:i], field[i+1:]

		// Only allow turning off CPU features by specifying '0'.
		if value == "0" {
			if key == "all" {
				for _, v := range options {
					*v.Feature = false
				}
				return
			} else {
				for _, v := range options {
					if v.Name == key {
						*v.Feature = false
						continue field
					}
				}
			}
		}
	}
}

// indexByte returns the index of the first instance of c in s,
// or -1 if c is not present in s.
func indexByte(s string, c byte) int {
	for i := 0; i < len(s); i++ {
		if s[i] == c {
			return i
		}
	}
	return -1
}
