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