Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cmd/compile: 'internal compiler error: bvbulkalloc too big' when compiling a file containing a large map #33437

Open
mcdee opened this issue Aug 2, 2019 · 8 comments

Comments

@mcdee
Copy link

commented Aug 2, 2019

$ go version
go version devel +2d6ee6e89a Thu Aug 1 20:37:08 2019 +0000 linux/amd64

Does this issue reproduce with the latest release?

Yes.

What operating system and processor architecture are you using (go env)?

$ go env
GO111MODULE=""
GOARCH="amd64"
GOBIN=""
GOCACHE="/home/jgm/.cache/go-build"
GOENV="/home/jgm/.config/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GONOPROXY=""
GONOSUMDB=""
GOOS="linux"
GOPATH="/home/jgm/.go"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/home/jgm/snippets/goroot"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/home/jgm/snippets/goroot/pkg/tool/linux_amd64"
GCCGO="gccgo"
AR="ar"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD=""
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build261965408=/tmp/go-build -gno-record-gcc-switches"

What did you do?

Attempting to go build on a file that creates a large map fails:

$ go build
# _/home/jgm/src/go/buildfail
./signatures.go:146811:24: internal compiler error: bvbulkalloc too big: nbit=5752 count=3948889 nword=180 size=710800020

goroutine 22 [running]:
runtime/debug.Stack(0xfdd400, 0xc00000e018, 0x0)
        /home/jgm/snippets/goroot/src/runtime/debug/stack.go:24 +0x9d
cmd/compile/internal/gc.Fatalf(0xe54748, 0x36, 0xc3c222d7f8, 0x4, 0x4)
        /home/jgm/snippets/goroot/src/cmd/compile/internal/gc/subr.go:188 +0x291
cmd/compile/internal/gc.bvbulkalloc(0x3c415900001678, 0x89ba0, 0x89ba0, 0xc8e9cd8000, 0xc3c222d8b8)
        /home/jgm/snippets/goroot/src/cmd/compile/internal/gc/bv.go:34 +0x1c4
cmd/compile/internal/gc.newliveness(0xc00029e160, 0xc00029e420, 0xc0cb6c8000, 0x1678, 0x1c00, 0xc07a7bf560, 0xb3e0, 0xe3e6d1)
        /home/jgm/snippets/goroot/src/cmd/compile/internal/gc/plive.go:503 +0x199
cmd/compile/internal/gc.liveness(0xc02f747e60, 0xc00029e420, 0xc0a752f0a0, 0x0, 0xe3e6de, 0xd)
        /home/jgm/snippets/goroot/src/cmd/compile/internal/gc/plive.go:1394 +0x95
cmd/compile/internal/gc.genssa(0xc00029e420, 0xc0a752f0a0)
        /home/jgm/snippets/goroot/src/cmd/compile/internal/gc/ssa.go:5286 +0x92
cmd/compile/internal/gc.compileSSA(0xc00029e160, 0x3)
        /home/jgm/snippets/goroot/src/cmd/compile/internal/gc/pgen.go:308 +0x3c2
cmd/compile/internal/gc.compileFunctions.func2(0xc030fb5ec0, 0xc0072d7e10, 0x3)
        /home/jgm/snippets/goroot/src/cmd/compile/internal/gc/pgen.go:363 +0x49
created by cmd/compile/internal/gc.compileFunctions
        /home/jgm/snippets/goroot/src/cmd/compile/internal/gc/pgen.go:361 +0x128

This contains approximately 140K additions, carried out as individual calls (rather than initialising the map during declaration). Relevant parts of code are:

  type function struct {
      name   string
      params []string
  }
  
  var functions map[uint32]function
  
  func InitFunctionMap() {
      functions = make(map[uint32]function)
      functions[305651098] = function{name: "decimalMul", params: []string{"uint256", "uint256"}}
      functions[3393457315] = function{name: "decimalDiv", params: []string{"uint256", "uint256"}}
      ...

A full copy of the file is at https://github.com/wealdtech/compilebug

What did you expect to see?

Would expect the build to complete.

What did you see instead?

Error output as above after approximately 50 minutes of building.

@bcmills bcmills added this to the Go1.14 milestone Aug 2, 2019

@bcmills bcmills changed the title go build fails with 'bvbulkalloc too big' cmd/compile: 'internal compiler error: bvbulkalloc too big' when compiling a file containing a large map Aug 2, 2019

@bcmills

This comment has been minimized.

Copy link
Member

commented Aug 2, 2019

Does this also reproduce using Go 1.12.7, or is it a regression in 1.13?

CC @randall77 @griesemer

@mcdee

This comment has been minimized.

Copy link
Author

commented Aug 2, 2019

Yes, same issue with 1.12.7

@ALTree

This comment has been minimized.

Copy link
Member

commented Aug 2, 2019

AFAIK this is not new. See #26560 (comment). It can be triggered by huge, auto-generated literals or functions (I've triggered this a few times while fuzzing the compiler with really big autogenerated functions).

The standard workaround is to copy the data in the map in init().

@ALTree

This comment has been minimized.

Copy link
Member

commented Aug 2, 2019

Clearly it still would be interesting to know if there was a regression on this, i.e. if a map of the same size compiled fine in Go1.10 and it doesn't now.

@mcdee

This comment has been minimized.

Copy link
Author

commented Aug 2, 2019

@ALTree I'm not sure what you mean by "copy the data in the map in init()" here. As per the original report, the map is created and individual items added on an entry-by-entry basis rather than initialising the map with all entries in one go.

@ALTree

This comment has been minimized.

Copy link
Member

commented Aug 2, 2019

@mcdee

I used a smaller version of your reproducer with ~5000 map entries. Your code does this:

var functions map[uint32]function

func main() {
	functions = make(map[uint32]function)
	functions[305651098] = function{name: "decimalMul", params: []string{"uint256", "uint256"}}
	functions[3393457315] = function{name: "decimalDiv", params: []string{"uint256", "uint256"}}
        // other 5000 entries
}

Compare with this:

var functions map[uint32]function

var functionsKeys = []uint32{
	305651098,
	3393457315,
        // other 5000 keys
}

var functionsValues = []function{
	function{name: "decimalMul", params: []string{"uint256", "uint256"}},
	function{name: "decimalDiv", params: []string{"uint256", "uint256"}},
        // other 5000 values
}

func init() {
	functions = make(map[uint32]function)
	for i := 0; i < len(functionsKeys); i++ {
		functions[functionsKeys[i]] = functionsValues[i]
	}
}

Note how the second version initializes the map in init().

Compiling the first version:

$ time go tool compile signatures.go 

real	0m5.687s
user	0m12.209s
sys	0m0.167s

Compiling the second version:

$ time go tool compile signatures2.go 

real	0m0.157s
user	0m0.247s
sys	0m0.020s
@mcdee

This comment has been minimized.

Copy link
Author

commented Aug 4, 2019

@ALTree thanks. This does work, but increases the size of my binary by about 18MB compared to the prior runtime initialisation method I used e.g. rather than functions[305651098] = function{name: "decimalMul", params: []string{"uint256", "uint256"}} I used addFunction("decimalMul(uint256,uint256)") where addFunction() parsed the string to break out the relevant values and add the map entry.

Looks like I'll take the runtime parsing hit and stick with my existing method. Should this be left open for someone to address the issue of the compiler failure?

@randall77

This comment has been minimized.

Copy link
Contributor

commented Aug 4, 2019

Yes, you can leave this open. The complete example will help.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
4 participants
You can’t perform that action at this time.