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 · 10 comments
Labels
compiler/runtime Issues related to the Go compiler and/or runtime. NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.
Milestone

Comments

@mcdee
Copy link

mcdee 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 the NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. label Aug 2, 2019
@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
Copy link
Contributor

bcmills 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
Copy link
Author

mcdee commented Aug 2, 2019

Yes, same issue with 1.12.7

@ALTree
Copy link
Member

ALTree 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
Copy link
Member

ALTree 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
Copy link
Author

mcdee 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
Copy link
Member

ALTree 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
Copy link
Author

mcdee 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
Copy link
Contributor

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

@odeke-em
Copy link
Member

odeke-em commented Oct 7, 2019

@randall77, @mcdee posted a full example which you can access at https://raw.githubusercontent.com/wealdtech/compilebug/master/signatures.go, it is 12.5MB though :) so I've derived for you a repro that generates the offending code, and then invokes
go run main.go on it as per https://play.golang.org/p/yVvr7zK5W7c or inlined below (this smaller file can then be checked into source code)

package main

import (
	"context"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
)

var code = fmt.Sprintf(`
package main

type function struct {
	name   string
	params []string
}

var functions map[uint32]function

func main() {
     functions = make(map[uint32]function)
     %s
}`, makeFunctions(140000))

func makeFunctions(n int) string {
	sb := new(strings.Builder)
	for i := 0; i < n; i++ {
		name := fmt.Sprintf("fn-%d", i)
		fmt.Fprintf(sb, "\tfunctions[%d] = function{name: %q, params: []string{%q}}\n", i, name, name)
	}
	return sb.String()
}

func main() {
	tmpDir, err := ioutil.TempDir(os.TempDir(), "issue-33437")
	if err != nil {
		log.Fatalf("Failed to create tempdir: %v", err)
	}
	defer os.Remove(tmpDir)

	generatedMainGoFile := filepath.Join(tmpDir, "main.go")
	if err := ioutil.WriteFile(generatedMainGoFile, []byte(code), 0655); err != nil {
		log.Printf("Failed to write generated main.go file: %v", err)
		return
	}

	ctx := context.Background() // Perhaps set it to a timer.
	cmd := exec.CommandContext(ctx, "go", "run", generatedMainGoFile)
	output, err := cmd.CombinedOutput()
	if err != nil {
		log.Printf("Failed to run the command: %v\n%s", err, output)
	}
}

@odeke-em
Copy link
Member

odeke-em commented Oct 7, 2019

On my Darwin machine, it took about 50 minutes to run but it did so successfully, and here is my uname -a output

Darwin Emmanuels-MacBook-Pro-2.local 18.7.0 Darwin Kernel Version 18.7.0: Tue Aug 20 16:57:14 PDT 2019; root:xnu-4903.271.2~2/RELEASE_X86_64 x86_64

and go version

$ go version
go version devel +30da79d958 Mon Oct 7 17:19:13 2019 +0000 darwin/amd64

@rsc rsc modified the milestones: Go1.14, Backlog Oct 9, 2019
steiler added a commit to steiler/ygot that referenced this issue Apr 8, 2022
As described in golang/go#33437 (comment) maps of too large size cannot be defined declaratively.
So I split the map in a Key (ΛEnumTypesKeys) and a Value (ΛEnumTypesValues) Slice and compose the ΛEnumTypes map via init().
steiler added a commit to steiler/ygot that referenced this issue Apr 13, 2022
As described in golang/go#33437 (comment) maps of too large size cannot be defined declaratively.
So I split the map in a Key (ΛEnumTypesKeys) and a Value (ΛEnumTypesValues) Slice and compose the ΛEnumTypes map via init().
wedaly added a commit to aretext/aretext that referenced this issue Jul 1, 2022
Dlv was crashing trying to debug a large number of code-generated
test cases. I believe it's caused by
golang/go#33437
Workaround by grouping the test cases into slices of at most
256 items.
@gopherbot gopherbot added the compiler/runtime Issues related to the Go compiler and/or runtime. label Jul 13, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
compiler/runtime Issues related to the Go compiler and/or runtime. NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.
Projects
Status: Triage Backlog
Development

No branches or pull requests

7 participants