diff --git a/.circleci/config.yml b/.circleci/config.yml index eedf08d27d..029789a42d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -413,12 +413,12 @@ jobs: steps: - test-linux: llvm: "10" - test-llvm10-go115: + test-llvm11-go115: docker: - image: circleci/golang:1.15-buster steps: - test-linux: - llvm: "10" + llvm: "11" assert-test-linux: docker: - image: circleci/golang:1.14-stretch @@ -450,7 +450,7 @@ workflows: - test-llvm10-go112 - test-llvm10-go113 - test-llvm10-go114 - - test-llvm10-go115 + - test-llvm11-go115 - build-linux - build-macos - assert-test-linux diff --git a/builder/build.go b/builder/build.go index 67c09df0ab..e59fec485c 100644 --- a/builder/build.go +++ b/builder/build.go @@ -129,6 +129,17 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil } } + // LLVM 11 by default tries to emit tail calls (even with the target feature + // disabled) unless it is explicitly disabled with a function attribute. + // This is a problem, as it tries to emit them and prints an error when it + // can't with this feature disabled. + // Because as of september 2020 tail calls are not yet widely supported, + // they need to be disabled until they are widely supported (at which point + // the +tail-call target feautre can be set). + if strings.HasPrefix(config.Triple(), "wasm") { + transform.DisableTailCalls(mod) + } + // Make sure stack sizes are loaded from a separate section so they can be // modified after linking. var stackSizeLoads []string diff --git a/builder/objcopy.go b/builder/objcopy.go index c2fefa0477..425e609872 100644 --- a/builder/objcopy.go +++ b/builder/objcopy.go @@ -57,7 +57,7 @@ func extractROM(path string) (uint64, []byte, error) { progs := make(progSlice, 0, 2) for _, prog := range f.Progs { - if prog.Type != elf.PT_LOAD || prog.Filesz == 0 { + if prog.Type != elf.PT_LOAD || prog.Filesz == 0 || prog.Off == 0 { continue } progs = append(progs, prog) @@ -69,6 +69,12 @@ func extractROM(path string) (uint64, []byte, error) { var rom []byte for _, prog := range progs { + romEnd := progs[0].Paddr + uint64(len(rom)) + if prog.Paddr > romEnd && prog.Paddr < romEnd+16 { + // Sometimes, the linker seems to insert a bit of padding between + // segments. Simply zero-fill these parts. + rom = append(rom, make([]byte, prog.Paddr-romEnd)...) + } if prog.Paddr != progs[0].Paddr+uint64(len(rom)) { return 0, nil, objcopyError{"ROM segments are non-contiguous: " + path, nil} } diff --git a/cgo/libclang_config.go b/cgo/libclang_config.go index ec65d8a89d..24b9501436 100644 --- a/cgo/libclang_config.go +++ b/cgo/libclang_config.go @@ -1,5 +1,5 @@ // +build !byollvm -// +build !llvm9 +// +build !llvm9,!llvm11 package cgo diff --git a/cgo/libclang_config_llvm11.go b/cgo/libclang_config_llvm11.go new file mode 100644 index 0000000000..6fab82beff --- /dev/null +++ b/cgo/libclang_config_llvm11.go @@ -0,0 +1,14 @@ +// +build !byollvm +// +build llvm11 + +package cgo + +/* +#cgo linux CFLAGS: -I/usr/lib/llvm-11/include +#cgo darwin CFLAGS: -I/usr/local/opt/llvm@11/include +#cgo freebsd CFLAGS: -I/usr/local/llvm11/include +#cgo linux LDFLAGS: -L/usr/lib/llvm-11/lib -lclang +#cgo darwin LDFLAGS: -L/usr/local/opt/llvm@11/lib -lclang -lffi +#cgo freebsd LDFLAGS: -L/usr/local/llvm11/lib -lclang +*/ +import "C" diff --git a/go.mod b/go.mod index 688793696d..98a2ab93d4 100644 --- a/go.mod +++ b/go.mod @@ -10,5 +10,5 @@ require ( github.com/marcinbor85/gohex v0.0.0-20200531091804-343a4b548892 go.bug.st/serial v1.0.0 golang.org/x/tools v0.0.0-20200216192241-b320d3a0f5a2 - tinygo.org/x/go-llvm v0.0.0-20200503225853-345b2947b59d + tinygo.org/x/go-llvm v0.0.0-20200503224449-70c558526021 ) diff --git a/go.sum b/go.sum index c24491ef21..9ceebac1aa 100644 --- a/go.sum +++ b/go.sum @@ -46,5 +46,5 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbO golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -tinygo.org/x/go-llvm v0.0.0-20200503225853-345b2947b59d h1:hcX7vpB067GWM/EH4sGGOti0PMgIx+0bbZwUXctOIvE= -tinygo.org/x/go-llvm v0.0.0-20200503225853-345b2947b59d/go.mod h1:fv1F0BSNpxMfCL0zF3M4OPFbgYHnhtB6ST0HvUtu/LE= +tinygo.org/x/go-llvm v0.0.0-20200503224449-70c558526021 h1:d8T98WXGjrTgDmMXgxa6nb9EAYXGXwnzXygnJl6d+ac= +tinygo.org/x/go-llvm v0.0.0-20200503224449-70c558526021/go.mod h1:fv1F0BSNpxMfCL0zF3M4OPFbgYHnhtB6ST0HvUtu/LE= diff --git a/interp/interp_test.go b/interp/interp_test.go index 6b0cc38f2c..dba7387187 100644 --- a/interp/interp_test.go +++ b/interp/interp_test.go @@ -3,6 +3,7 @@ package interp import ( "io/ioutil" "os" + "regexp" "strings" "testing" @@ -66,6 +67,8 @@ func runTest(t *testing.T, pathPrefix string) { } } +var alignRegexp = regexp.MustCompile(", align [0-9]+$") + // fuzzyEqualIR returns true if the two LLVM IR strings passed in are roughly // equal. That means, only relevant lines are compared (excluding comments // etc.). @@ -75,8 +78,18 @@ func fuzzyEqualIR(s1, s2 string) bool { if len(lines1) != len(lines2) { return false } - for i, line := range lines1 { - if line != lines2[i] { + for i, line1 := range lines1 { + line2 := lines2[i] + match1 := alignRegexp.MatchString(line1) + match2 := alignRegexp.MatchString(line2) + if match1 != match2 { + // Only one of the lines has the align keyword. Remove it. + // This is a change to make the test work in both LLVM 10 and LLVM + // 11 (LLVM 11 appears to automatically add alignment everywhere). + line1 = alignRegexp.ReplaceAllString(line1, "") + line2 = alignRegexp.ReplaceAllString(line2, "") + } + if line1 != line2 { return false } } diff --git a/targets/gameboy-advance.ld b/targets/gameboy-advance.ld index 8b35042999..566f78ea25 100644 --- a/targets/gameboy-advance.ld +++ b/targets/gameboy-advance.ld @@ -19,6 +19,7 @@ SECTIONS { KEEP (*(.init)) *(.text) + *(.text.*) . = ALIGN(4); } >rom @@ -26,7 +27,7 @@ SECTIONS { . = ALIGN(4); *(.rodata) - *(.rodata*) + *(.rodata.*) . = ALIGN(4); } >rom @@ -51,7 +52,7 @@ SECTIONS . = ALIGN(4); _sdata = .; /* used by startup code */ *(.data) - *(.data*) + *(.data.*) *(.iwram .iwram.*) . = ALIGN(4); _edata = .; /* used by startup code */ @@ -63,7 +64,7 @@ SECTIONS . = ALIGN(4); _sbss = .; /* used by startup code */ *(.bss) - *(.bss*) + *(.bss.*) *(COMMON) . = ALIGN(4); _ebss = .; /* used by startup code */ diff --git a/transform/globals.go b/transform/globals.go index 89386fd84b..7a2968356f 100644 --- a/transform/globals.go +++ b/transform/globals.go @@ -31,3 +31,17 @@ func NonConstGlobals(mod llvm.Module) { global = llvm.NextGlobal(global) } } + +// DisableTailCalls adds the "disable-tail-calls"="true" function attribute to +// all functions. This may be necessary, in particular to avoid an error with +// WebAssembly in LLVM 11. +func DisableTailCalls(mod llvm.Module) { + attribute := mod.Context().CreateStringAttribute("disable-tail-calls", "true") + llvmFn := mod.FirstFunction() + for !llvmFn.IsNil() { + if !llvmFn.IsDeclaration() { + llvmFn.AddFunctionAttr(attribute) + } + llvmFn = llvm.NextFunction(llvmFn) + } +} diff --git a/transform/transform_test.go b/transform/transform_test.go index 7a607f13b5..28faf86815 100644 --- a/transform/transform_test.go +++ b/transform/transform_test.go @@ -61,6 +61,8 @@ func testTransform(t *testing.T, pathPrefix string, transform func(mod llvm.Modu } } +var alignRegexp = regexp.MustCompile(", align [0-9]+$") + // fuzzyEqualIR returns true if the two LLVM IR strings passed in are roughly // equal. That means, only relevant lines are compared (excluding comments // etc.). @@ -70,8 +72,18 @@ func fuzzyEqualIR(s1, s2 string) bool { if len(lines1) != len(lines2) { return false } - for i, line := range lines1 { - if line != lines2[i] { + for i, line1 := range lines1 { + line2 := lines2[i] + match1 := alignRegexp.MatchString(line1) + match2 := alignRegexp.MatchString(line2) + if match1 != match2 { + // Only one of the lines has the align keyword. Remove it. + // This is a change to make the test work in both LLVM 10 and LLVM + // 11 (LLVM 11 appears to automatically add alignment everywhere). + line1 = alignRegexp.ReplaceAllString(line1, "") + line2 = alignRegexp.ReplaceAllString(line2, "") + } + if line1 != line2 { return false } }