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