...

Source file src/go.starlark.net/starlark/int_posix64.go

Documentation: go.starlark.net/starlark

     1  //go:build (linux || darwin || dragonfly || freebsd || netbsd || solaris) && (amd64 || arm64 || mips64x || ppc64x || loong64)
     2  // +build linux darwin dragonfly freebsd netbsd solaris
     3  // +build amd64 arm64 mips64x ppc64x loong64
     4  
     5  package 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  
    14  // Although iOS (which, like macOS, appears as darwin/arm64) is
    15  // POSIX-compliant, it limits each process to about 700MB of virtual
    16  // address space, which defeats the optimization.  Similarly,
    17  // OpenBSD's default ulimit for virtual memory is a measly GB or so.
    18  // On both those platforms the attempted optimization will fail and
    19  // fall back to the slow implementation.
    20  
    21  // An alternative approach to this optimization would be to embed the
    22  // int32 values in pointers using odd values, which can be distinguished
    23  // from (even) *big.Int pointers. However, the Go runtime does not allow
    24  // user programs to manufacture pointers to arbitrary locations such as
    25  // within the zero page, or non-span, non-mmap, non-stack locations,
    26  // and it may panic if it encounters them; see Issue #382.
    27  
    28  import (
    29  	"log"
    30  	"math"
    31  	"math/big"
    32  	"unsafe"
    33  
    34  	"golang.org/x/sys/unix"
    35  )
    36  
    37  // intImpl represents a union of (int32, *big.Int) in a single pointer,
    38  // so that Int-to-Value conversions need not allocate.
    39  //
    40  // The pointer is either a *big.Int, if the value is big, or a pointer into a
    41  // reserved portion of the address space (smallints), if the value is small
    42  // and the address space allocation succeeded.
    43  //
    44  // See int_generic.go for the basic representation concepts.
    45  type intImpl unsafe.Pointer
    46  
    47  // get returns the (small, big) arms of the union.
    48  func (i Int) get() (int64, *big.Int) {
    49  	if smallints == 0 {
    50  		// optimization disabled
    51  		if x := (*big.Int)(i.impl); isSmall(x) {
    52  			return x.Int64(), nil
    53  		} else {
    54  			return 0, x
    55  		}
    56  	}
    57  
    58  	if ptr := uintptr(i.impl); ptr >= smallints && ptr < smallints+1<<32 {
    59  		return math.MinInt32 + int64(ptr-smallints), nil
    60  	}
    61  	return 0, (*big.Int)(i.impl)
    62  }
    63  
    64  // Precondition: math.MinInt32 <= x && x <= math.MaxInt32
    65  func makeSmallInt(x int64) Int {
    66  	if smallints == 0 {
    67  		// optimization disabled
    68  		return Int{intImpl(big.NewInt(x))}
    69  	}
    70  
    71  	return Int{intImpl(uintptr(x-math.MinInt32) + smallints)}
    72  }
    73  
    74  // Precondition: x cannot be represented as int32.
    75  func makeBigInt(x *big.Int) Int { return Int{intImpl(x)} }
    76  
    77  // smallints is the base address of a 2^32 byte memory region.
    78  // Pointers to addresses in this region represent int32 values.
    79  // We assume smallints is not at the very top of the address space.
    80  //
    81  // Zero means the optimization is disabled and all Ints allocate a big.Int.
    82  var smallints = reserveAddresses(1 << 32)
    83  
    84  func reserveAddresses(len int) uintptr {
    85  	b, err := unix.Mmap(-1, 0, len, unix.PROT_READ, unix.MAP_PRIVATE|unix.MAP_ANON)
    86  	if err != nil {
    87  		log.Printf("Starlark failed to allocate 4GB address space: %v. Integer performance may suffer.", err)
    88  		return 0 // optimization disabled
    89  	}
    90  	return uintptr(unsafe.Pointer(&b[0]))
    91  }
    92  

View as plain text