1// Copyright 2014 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// +build amd64 arm64 mips64 mips64le ppc64 ppc64le riscv64 s390x wasm arm64be alpha sparc64 ia64
6
7package runtime
8
9import "unsafe"
10
11const (
12	// addrBits is the number of bits needed to represent a virtual address.
13	//
14	// See heapAddrBits for a table of address space sizes on
15	// various architectures. 48 bits is enough for all
16	// architectures except s390x.
17	//
18	// On AMD64, virtual addresses are 48-bit (or 57-bit) numbers sign extended to 64.
19	// We shift the address left 16 to eliminate the sign extended part and make
20	// room in the bottom for the count.
21	//
22	// On s390x, virtual addresses are 64-bit. There's not much we
23	// can do about this, so we just hope that the kernel doesn't
24	// get to really high addresses and panic if it does.
25	addrBits = 48
26
27	// In addition to the 16 bits taken from the top, we can take 3 from the
28	// bottom, because node must be pointer-aligned, giving a total of 19 bits
29	// of count.
30	cntBits = 64 - addrBits + 3
31
32	// On sparc64-linux, user addresses are 52-bit numbers sign extended to 64.
33	// We shift the address left 12 to eliminate the sign extended part and make
34	// room in the bottom for the count.
35	sparcLinuxAddrBits = 52
36	sparcLinuxCntBits  = 64 - sparcLinuxAddrBits + 3
37
38	// On IA64, the virtual address space is devided into 8 regions, with
39	// 52 address bits each (with 64k page size).
40	ia64AddrBits = 55
41	ia64CntBits  = 64 - ia64AddrBits + 3
42
43	// On AIX, 64-bit addresses are split into 36-bit segment number and 28-bit
44	// offset in segment.  Segment numbers in the range 0x0A0000000-0x0AFFFFFFF(LSA)
45	// are available for mmap.
46	// We assume all lfnode addresses are from memory allocated with mmap.
47	// We use one bit to distinguish between the two ranges.
48	aixAddrBits = 57
49	aixCntBits  = 64 - aixAddrBits + 3
50)
51
52func lfstackPack(node *lfnode, cnt uintptr) uint64 {
53	if GOARCH == "sparc64" && GOOS == "linux" {
54		return uint64(uintptr(unsafe.Pointer(node)))<<(64-sparcLinuxAddrBits) | uint64(cnt&(1<<sparcLinuxCntBits-1))
55	}
56	if GOARCH == "ia64" {
57		// Top three bits are the region number
58		val := uint64(uintptr(unsafe.Pointer(node)))
59		return (val<<(64-ia64AddrBits))&(1<<(64-3)-1) | val&^(1<<(64-3)-1) | uint64(cnt&(1<<ia64CntBits-1))
60	}
61	if GOARCH == "ppc64" && GOOS == "aix" {
62		return uint64(uintptr(unsafe.Pointer(node)))<<(64-aixAddrBits) | uint64(cnt&(1<<aixCntBits-1))
63	}
64	return uint64(uintptr(unsafe.Pointer(node)))<<(64-addrBits) | uint64(cnt&(1<<cntBits-1))
65}
66
67func lfstackUnpack(val uint64) *lfnode {
68	if GOARCH == "amd64" || GOOS == "solaris" {
69		// amd64 or Solaris systems can place the stack above the VA hole, so we need to sign extend
70		// val before unpacking.
71		return (*lfnode)(unsafe.Pointer(uintptr(int64(val) >> cntBits << 3)))
72	}
73	if GOARCH == "sparc64" && GOOS == "linux" {
74		return (*lfnode)(unsafe.Pointer(uintptr(int64(val) >> sparcLinuxCntBits << 3)))
75	}
76	if GOARCH == "ia64" {
77		return (*lfnode)(unsafe.Pointer(uintptr(((val & (1<<(64-3) - 1)) >> ia64CntBits << 3) | val&^(1<<(64-3)-1))))
78	}
79	if GOARCH == "ppc64" && GOOS == "aix" {
80		return (*lfnode)(unsafe.Pointer(uintptr((val >> aixCntBits << 3) | 0xa<<56)))
81	}
82	return (*lfnode)(unsafe.Pointer(uintptr(val >> cntBits << 3)))
83}
84