blob: 1f13d662d28d4308809cfbd3cda48a27abed4d13 [file] [log] [blame]
alandonovan474f21a2020-07-06 23:27:45 -04001//+build linux darwin dragonfly freebsd netbsd openbsd solaris
alandonovan5d2ea042020-07-06 17:12:36 -04002//+build amd64 arm64,!darwin mips64x ppc64x
alandonovanc6daab62020-06-17 14:27:56 -04003
4package starlark
5
6// This file defines an optimized Int implementation for 64-bit machines
7// running POSIX. It reserves a 4GB portion of the address space using
8// mmap and represents int32 values as addresses within that range. This
9// disambiguates int32 values from *big.Int pointers, letting all Int
10// values be represented as an unsafe.Pointer, so that Int-to-Value
11// interface conversion need not allocate.
12
alandonovan5d2ea042020-07-06 17:12:36 -040013// Although iOS (arm64,darwin) claims to be a POSIX-compliant,
14// it limits each process to about 700MB of virtual address space,
15// which defeats the optimization.
16//
17// TODO(golang.org/issue/38485): darwin,arm64 may refer to macOS in the future.
18// Update this when there are distinct GOOS values for macOS, iOS, and other Apple
19// operating systems on arm64.
20
alandonovanc6daab62020-06-17 14:27:56 -040021import (
22 "log"
23 "math"
24 "math/big"
25 "unsafe"
alandonovane55f6032020-11-18 13:34:35 -050026
27 "golang.org/x/sys/unix"
alandonovanc6daab62020-06-17 14:27:56 -040028)
29
30// intImpl represents a union of (int32, *big.Int) in a single pointer,
31// so that Int-to-Value conversions need not allocate.
32//
33// The pointer is either a *big.Int, if the value is big, or a pointer into a
34// reserved portion of the address space (smallints), if the value is small.
35//
36// See int_generic.go for the basic representation concepts.
37type intImpl unsafe.Pointer
38
39// get returns the (small, big) arms of the union.
40func (i Int) get() (int64, *big.Int) {
41 ptr := uintptr(i.impl)
42 if ptr >= smallints && ptr < smallints+1<<32 {
43 return math.MinInt32 + int64(ptr-smallints), nil
44 }
45 return 0, (*big.Int)(i.impl)
46}
47
48// Precondition: math.MinInt32 <= x && x <= math.MaxInt32
49func makeSmallInt(x int64) Int {
50 return Int{intImpl(uintptr(x-math.MinInt32) + smallints)}
51}
52
53// Precondition: x cannot be represented as int32.
54func makeBigInt(x *big.Int) Int { return Int{intImpl(x)} }
55
56// smallints is the base address of a 2^32 byte memory region.
57// Pointers to addresses in this region represent int32 values.
58// We assume smallints is not at the very top of the address space.
59var smallints = reserveAddresses(1 << 32)
60
61func reserveAddresses(len int) uintptr {
alandonovane55f6032020-11-18 13:34:35 -050062 b, err := unix.Mmap(-1, 0, len, unix.PROT_READ, unix.MAP_PRIVATE|unix.MAP_ANON)
alandonovanc6daab62020-06-17 14:27:56 -040063 if err != nil {
64 log.Fatalf("mmap: %v", err)
65 }
66 return uintptr(unsafe.Pointer(&b[0]))
67}