alandonovan | 474f21a | 2020-07-06 23:27:45 -0400 | [diff] [blame] | 1 | //+build linux darwin dragonfly freebsd netbsd openbsd solaris |
alandonovan | 5d2ea04 | 2020-07-06 17:12:36 -0400 | [diff] [blame] | 2 | //+build amd64 arm64,!darwin mips64x ppc64x |
alandonovan | c6daab6 | 2020-06-17 14:27:56 -0400 | [diff] [blame] | 3 | |
| 4 | package 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 | |
alandonovan | 5d2ea04 | 2020-07-06 17:12:36 -0400 | [diff] [blame] | 13 | // 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 | |
alandonovan | c6daab6 | 2020-06-17 14:27:56 -0400 | [diff] [blame] | 21 | import ( |
| 22 | "log" |
| 23 | "math" |
| 24 | "math/big" |
| 25 | "unsafe" |
alandonovan | e55f603 | 2020-11-18 13:34:35 -0500 | [diff] [blame] | 26 | |
| 27 | "golang.org/x/sys/unix" |
alandonovan | c6daab6 | 2020-06-17 14:27:56 -0400 | [diff] [blame] | 28 | ) |
| 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. |
| 37 | type intImpl unsafe.Pointer |
| 38 | |
| 39 | // get returns the (small, big) arms of the union. |
| 40 | func (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 |
| 49 | func makeSmallInt(x int64) Int { |
| 50 | return Int{intImpl(uintptr(x-math.MinInt32) + smallints)} |
| 51 | } |
| 52 | |
| 53 | // Precondition: x cannot be represented as int32. |
| 54 | func 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. |
| 59 | var smallints = reserveAddresses(1 << 32) |
| 60 | |
| 61 | func reserveAddresses(len int) uintptr { |
alandonovan | e55f603 | 2020-11-18 13:34:35 -0500 | [diff] [blame] | 62 | b, err := unix.Mmap(-1, 0, len, unix.PROT_READ, unix.MAP_PRIVATE|unix.MAP_ANON) |
alandonovan | c6daab6 | 2020-06-17 14:27:56 -0400 | [diff] [blame] | 63 | if err != nil { |
| 64 | log.Fatalf("mmap: %v", err) |
| 65 | } |
| 66 | return uintptr(unsafe.Pointer(&b[0])) |
| 67 | } |