From 5127b9d65b132547d5b4c1a60a8be381871d8ac8 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Sun, 28 Mar 2021 19:56:03 -0400 Subject: [PATCH] all: add LLVM 12 support Originally based on a PR by @QuLogic, but extended a lot to get all tests to pass. --- .circleci/config.yml | 18 +++++++++--------- .github/workflows/linux.yml | 8 ++++---- .github/workflows/windows.yml | 4 ++-- Makefile | 14 ++++++-------- cgo/libclang_config.go | 1 + cgo/libclang_config_llvm12.go | 14 ++++++++++++++ compileopts/target.go | 14 ++++++++++++++ compiler/compiler_test.go | 27 ++++++++++++++++++++++----- compiler/testdata/channel.ll | 6 +++--- compiler/testdata/intrinsics-wasm.ll | 6 +++--- go.mod | 4 ++-- go.sum | 10 ++++------ interp/interp_test.go | 1 + transform/maps_test.go | 1 + transform/testdata/coroutines.out.ll | 6 +++--- transform/transform_test.go | 3 ++- 16 files changed, 91 insertions(+), 46 deletions(-) create mode 100644 cgo/libclang_config_llvm12.go diff --git a/.circleci/config.yml b/.circleci/config.yml index 52f4f33b..f709e4a5 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -22,12 +22,12 @@ commands: steps: - restore_cache: keys: - - llvm-source-11-v2 + - llvm-source-12-v1 - run: name: "Fetch LLVM source" command: make llvm-source - save_cache: - key: llvm-source-11-v2 + key: llvm-source-12-v1 paths: - llvm-project/clang/lib/Headers - llvm-project/clang/include @@ -118,12 +118,12 @@ commands: - go-cache-macos-v3-{{ checksum "go.mod" }} - restore_cache: keys: - - llvm-source-11-macos-v3 + - llvm-source-12-macos-v1 - run: name: "Fetch LLVM source" command: make llvm-source - save_cache: - key: llvm-source-11-macos-v3 + key: llvm-source-12-macos-v1 paths: - llvm-project/clang/lib/Headers - llvm-project/clang/include @@ -131,7 +131,7 @@ commands: - llvm-project/llvm/include - restore_cache: keys: - - llvm-build-11-macos-v5 + - llvm-build-12-macos-v1 - run: name: "Build LLVM" command: | @@ -145,7 +145,7 @@ commands: find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \; fi - save_cache: - key: llvm-build-11-macos-v5 + key: llvm-build-12-macos-v1 paths: llvm-build - restore_cache: @@ -205,12 +205,12 @@ jobs: steps: - test-linux: llvm: "11" - test-llvm11-go116: + test-llvm12-go116: docker: - image: circleci/golang:1.16-buster steps: - test-linux: - llvm: "11" + llvm: "12" build-macos: macos: xcode: "11.1.0" # macOS 10.14 @@ -223,5 +223,5 @@ workflows: test-all: jobs: - test-llvm11-go115 - - test-llvm11-go116 + - test-llvm12-go116 - build-macos diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 4e4b21b8..02f89803 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -38,7 +38,7 @@ jobs: uses: actions/cache@v2 id: cache-llvm-source with: - key: llvm-source-11-linux-v1 + key: llvm-source-12-linux-v1 path: | llvm-project/clang/lib/Headers llvm-project/clang/include @@ -51,7 +51,7 @@ jobs: uses: actions/cache@v2 id: cache-llvm-build with: - key: llvm-build-11-linux-v1 + key: llvm-build-12-linux-v1 path: llvm-build - name: Build LLVM if: steps.cache-llvm-build.outputs.cache-hit != 'true' @@ -170,7 +170,7 @@ jobs: uses: actions/cache@v2 id: cache-llvm-source with: - key: llvm-source-11-linux-asserts-v1 + key: llvm-source-12-linux-asserts-v1 path: | llvm-project/clang/lib/Headers llvm-project/clang/include @@ -183,7 +183,7 @@ jobs: uses: actions/cache@v2 id: cache-llvm-build with: - key: llvm-build-11-linux-asserts-v2 + key: llvm-build-12-linux-asserts-v1 path: llvm-build - name: Build LLVM if: steps.cache-llvm-build.outputs.cache-hit != 'true' diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 7442795f..a14ca207 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -34,7 +34,7 @@ jobs: uses: actions/cache@v2 id: cache-llvm-source with: - key: llvm-source-11-windows-v1 + key: llvm-source-12-windows-v1 path: | llvm-project/clang/lib/Headers llvm-project/clang/include @@ -47,7 +47,7 @@ jobs: uses: actions/cache@v2 id: cache-llvm-build with: - key: llvm-build-11-windows-v2 + key: llvm-build-12-windows-v1 path: llvm-build - name: Build LLVM if: steps.cache-llvm-build.outputs.cache-hit != 'true' diff --git a/Makefile b/Makefile index 5d400fff..569defc0 100644 --- a/Makefile +++ b/Makefile @@ -59,18 +59,13 @@ ifeq ($(OS),Windows_NT) BINARYEN_OPTION += -DCMAKE_EXE_LINKER_FLAGS='-static-libgcc -static-libstdc++' - LIBCLANG_NAME = libclang - else ifeq ($(shell uname -s),Darwin) MD5SUM = md5 - LIBCLANG_NAME = clang else ifeq ($(shell uname -s),FreeBSD) MD5SUM = md5 - LIBCLANG_NAME = clang START_GROUP = -Wl,--start-group END_GROUP = -Wl,--end-group else - LIBCLANG_NAME = clang START_GROUP = -Wl,--start-group END_GROUP = -Wl,--end-group endif @@ -86,19 +81,22 @@ LLD_LIBS = $(START_GROUP) $(addprefix -l,$(LLD_LIB_NAMES)) $(END_GROUP) # Other libraries that are needed to link TinyGo. EXTRA_LIB_NAMES = LLVMInterpreter +# All libraries to be built and linked with the tinygo binary (lib/lib*.a). +LIB_NAMES = clang $(CLANG_LIB_NAMES) $(LLD_LIB_NAMES) $(EXTRA_LIB_NAMES) + # These build targets appear to be the only ones necessary to build all TinyGo # dependencies. Only building a subset significantly speeds up rebuilding LLVM. # The Makefile rules convert a name like lldELF to lib/liblldELF.a to match the # library path (for ninja). # This list also includes a few tools that are necessary as part of the full # TinyGo build. -NINJA_BUILD_TARGETS = clang llvm-config llvm-ar llvm-nm $(addprefix lib/lib,$(addsuffix .a,$(LIBCLANG_NAME) $(CLANG_LIB_NAMES) $(LLD_LIB_NAMES) $(EXTRA_LIB_NAMES))) +NINJA_BUILD_TARGETS = clang llvm-config llvm-ar llvm-nm $(addprefix lib/lib,$(addsuffix .a,$(LIB_NAMES))) # For static linking. ifneq ("$(wildcard $(LLVM_BUILDDIR)/bin/llvm-config*)","") CGO_CPPFLAGS+=$(shell $(LLVM_BUILDDIR)/bin/llvm-config --cppflags) -I$(abspath $(LLVM_BUILDDIR))/tools/clang/include -I$(abspath $(CLANG_SRC))/include -I$(abspath $(LLD_SRC))/include CGO_CXXFLAGS=-std=c++14 - CGO_LDFLAGS+=$(abspath $(LLVM_BUILDDIR))/lib/lib$(LIBCLANG_NAME).a -L$(abspath $(LLVM_BUILDDIR)/lib) $(CLANG_LIBS) $(LLD_LIBS) $(shell $(LLVM_BUILDDIR)/bin/llvm-config --ldflags --libs --system-libs $(LLVM_COMPONENTS)) -lstdc++ $(CGO_LDFLAGS_EXTRA) + CGO_LDFLAGS+=-L$(abspath $(LLVM_BUILDDIR)/lib) -lclang $(CLANG_LIBS) $(LLD_LIBS) $(shell $(LLVM_BUILDDIR)/bin/llvm-config --ldflags --libs --system-libs $(LLVM_COMPONENTS)) -lstdc++ $(CGO_LDFLAGS_EXTRA) endif @@ -162,7 +160,7 @@ gen-device-rp: build/gen-device-svd # Get LLVM sources. $(LLVM_PROJECTDIR)/llvm: - git clone -b xtensa_release_11.0.0 --depth=1 https://github.com/tinygo-org/llvm-project $(LLVM_PROJECTDIR) + git clone -b xtensa_release_12.0.1 --depth=1 https://github.com/tinygo-org/llvm-project $(LLVM_PROJECTDIR) llvm-source: $(LLVM_PROJECTDIR)/llvm # Configure LLVM. diff --git a/cgo/libclang_config.go b/cgo/libclang_config.go index 4b4ce2db..9f7cdc1c 100644 --- a/cgo/libclang_config.go +++ b/cgo/libclang_config.go @@ -1,4 +1,5 @@ // +build !byollvm +// +build !llvm12 package cgo diff --git a/cgo/libclang_config_llvm12.go b/cgo/libclang_config_llvm12.go new file mode 100644 index 00000000..1837cc15 --- /dev/null +++ b/cgo/libclang_config_llvm12.go @@ -0,0 +1,14 @@ +// +build !byollvm +// +build llvm12 + +package cgo + +/* +#cgo linux CFLAGS: -I/usr/lib/llvm-12/include +#cgo darwin CFLAGS: -I/usr/local/opt/llvm@12/include +#cgo freebsd CFLAGS: -I/usr/local/llvm12/include +#cgo linux LDFLAGS: -L/usr/lib/llvm-12/lib -lclang +#cgo darwin LDFLAGS: -L/usr/local/opt/llvm@12/lib -lclang -lffi +#cgo freebsd LDFLAGS: -L/usr/local/llvm12/lib -lclang +*/ +import "C" diff --git a/compileopts/target.go b/compileopts/target.go index ba19cae0..c49759da 100644 --- a/compileopts/target.go +++ b/compileopts/target.go @@ -12,9 +12,11 @@ import ( "path/filepath" "reflect" "runtime" + "strconv" "strings" "github.com/tinygo-org/tinygo/goenv" + "tinygo.org/x/go-llvm" ) // Target specification for a given target. Used for bare metal targets. @@ -279,6 +281,12 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) { } else if goos == "windows" { spec.Linker = "ld.lld" spec.Libc = "mingw-w64" + // Note: using a medium code model, low image base and no ASLR + // because Go doesn't really need those features. ASLR patches + // around issues for unsafe languages like C/C++ that are not + // normally present in Go (without explicitly opting in). + // For more discussion: + // https://groups.google.com/g/Golang-nuts/c/Jd9tlNc6jUE/m/Zo-7zIP_m3MJ?pli=1 spec.LDFlags = append(spec.LDFlags, "-m", "i386pep", "-Bdynamic", @@ -286,6 +294,12 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) { "--gc-sections", "--no-insert-timestamp", ) + llvmMajor, _ := strconv.Atoi(strings.Split(llvm.Version, ".")[0]) + if llvmMajor >= 12 { + // This flag was added in LLVM 12. At the same time, LLVM 12 + // switched the default from --dynamicbase to --no-dynamicbase. + spec.LDFlags = append(spec.LDFlags, "--no-dynamicbase") + } } else { spec.LDFlags = append(spec.LDFlags, "-no-pie", "-Wl,--gc-sections") // WARNING: clang < 5.0 requires -nopie } diff --git a/compiler/compiler_test.go b/compiler/compiler_test.go index cbf89fe2..3c978274 100644 --- a/compiler/compiler_test.go +++ b/compiler/compiler_test.go @@ -41,6 +41,13 @@ func TestCompiler(t *testing.T) { t.Skip("compiler tests require LLVM 11 or above, got LLVM ", llvm.Version) } + // Determine Go minor version (e.g. 16 in go1.16.3). + _, goMinor, err := goenv.GetGorootVersion(goenv.Get("GOROOT")) + if err != nil { + t.Fatal("could not read Go version:", err) + } + + // Determine which tests to run, depending on the Go and LLVM versions. tests := []testCase{ {"basic.go", "", ""}, {"pointer.go", "", ""}, @@ -58,12 +65,11 @@ func TestCompiler(t *testing.T) { {"intrinsics.go", "wasm", ""}, {"gc.go", "", ""}, } - - _, minor, err := goenv.GetGorootVersion(goenv.Get("GOROOT")) - if err != nil { - t.Fatal("could not read Go version:", err) + if llvmMajor >= 12 { + tests = append(tests, testCase{"intrinsics.go", "cortex-m-qemu", ""}) + tests = append(tests, testCase{"intrinsics.go", "wasm", ""}) } - if minor >= 17 { + if goMinor >= 17 { tests = append(tests, testCase{"go1.17.go", "", ""}) } @@ -201,6 +207,12 @@ func fuzzyEqualIR(s1, s2 string) bool { // stripped out. func filterIrrelevantIRLines(lines []string) []string { var out []string + llvmVersion, err := strconv.Atoi(strings.Split(llvm.Version, ".")[0]) + if err != nil { + // Note: this should never happen and if it does, it will always happen + // for a particular build because llvm.Version is a constant. + panic(err) + } for _, line := range lines { line = strings.Split(line, ";")[0] // strip out comments/info line = strings.TrimRight(line, "\r ") // drop '\r' on Windows and remove trailing spaces from comments @@ -210,6 +222,11 @@ func filterIrrelevantIRLines(lines []string) []string { if strings.HasPrefix(line, "source_filename = ") { continue } + if llvmVersion < 12 && strings.HasPrefix(line, "attributes ") { + // Ignore attribute groups. These may change between LLVM versions. + // Right now test outputs are for LLVM 12. + continue + } out = append(out, line) } return out diff --git a/compiler/testdata/channel.ll b/compiler/testdata/channel.ll index 04bfa4af..36d7fd96 100644 --- a/compiler/testdata/channel.ll +++ b/compiler/testdata/channel.ll @@ -34,12 +34,12 @@ entry: ret void } -; Function Attrs: argmemonly nounwind willreturn +; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #1 declare void @runtime.chanSend(%runtime.channel* dereferenceable_or_null(32), i8*, %runtime.channelBlockedList* dereferenceable_or_null(24), i8*, i8*) -; Function Attrs: argmemonly nounwind willreturn +; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #1 ; Function Attrs: nounwind @@ -119,4 +119,4 @@ select.body: ; preds = %select.next declare { i32, i1 } @runtime.tryChanSelect(i8*, %runtime.chanSelectState*, i32, i32, i8*, i8*) attributes #0 = { nounwind } -attributes #1 = { argmemonly nounwind willreturn } +attributes #1 = { argmemonly nofree nosync nounwind willreturn } diff --git a/compiler/testdata/intrinsics-wasm.ll b/compiler/testdata/intrinsics-wasm.ll index bebadb08..9e6687d5 100644 --- a/compiler/testdata/intrinsics-wasm.ll +++ b/compiler/testdata/intrinsics-wasm.ll @@ -18,7 +18,7 @@ entry: ret double %0 } -; Function Attrs: nounwind readnone speculatable willreturn +; Function Attrs: nofree nosync nounwind readnone speculatable willreturn declare double @llvm.sqrt.f64(double) #1 ; Function Attrs: nounwind @@ -28,8 +28,8 @@ entry: ret double %0 } -; Function Attrs: nounwind readnone speculatable willreturn +; Function Attrs: nofree nosync nounwind readnone speculatable willreturn declare double @llvm.trunc.f64(double) #1 attributes #0 = { nounwind } -attributes #1 = { nounwind readnone speculatable willreturn } +attributes #1 = { nofree nosync nounwind readnone speculatable willreturn } diff --git a/go.mod b/go.mod index 26a4b027..52dd71c3 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/tinygo-org/tinygo go 1.15 require ( - github.com/aykevl/go-wasm v0.0.2-0.20211030161413-11881cb9032d // indirect + github.com/aykevl/go-wasm v0.0.2-0.20211119014117-0761b1ddcd1a // indirect github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2 github.com/chromedp/cdproto v0.0.0-20210113043257-dabd2f2e7693 github.com/chromedp/chromedp v0.6.4 @@ -13,5 +13,5 @@ require ( go.bug.st/serial v1.1.3 golang.org/x/sys v0.0.0-20210510120138-977fb7262007 golang.org/x/tools v0.1.6-0.20210813165731-45389f592fe9 - tinygo.org/x/go-llvm v0.0.0-20210325115028-e7b85195e81c + tinygo.org/x/go-llvm v0.0.0-20210907125547-fd2d62ea06be ) diff --git a/go.sum b/go.sum index 1daa9388..73697653 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/aykevl/go-wasm v0.0.2-0.20211030161413-11881cb9032d h1:JeuI5/546naK5hpOIX+Lq5xE8rvt7uwiTp6iL+pLQgk= -github.com/aykevl/go-wasm v0.0.2-0.20211030161413-11881cb9032d/go.mod h1:7sXyiaA0WtSogCu67R2252fQpVmJMh9JWJ9ddtGkpWw= +github.com/aykevl/go-wasm v0.0.2-0.20211119014117-0761b1ddcd1a h1:QPU7APo6y/6VkCDq6HU3WWIUzER8iywSac23+1UQv60= +github.com/aykevl/go-wasm v0.0.2-0.20211119014117-0761b1ddcd1a/go.mod h1:7sXyiaA0WtSogCu67R2252fQpVmJMh9JWJ9ddtGkpWw= github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2 h1:oMCHnXa6CCCafdPDbMh/lWRhRByN0VFLvv+g+ayx1SI= github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2/go.mod h1:PkYb9DJNAwrSvRx5DYA+gUcOIgTGVMNkfSCbZM8cWpI= github.com/chromedp/cdproto v0.0.0-20210113043257-dabd2f2e7693 h1:11eq/RkpaotwdF6b1TRMcdgQUPNmyFEJOB7zLvh0O/Y= @@ -68,7 +68,5 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/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-20210308112806-9ef958b6bed4 h1:CMUHxVTb+UuUePuMf8vkWjZ3gTp9BBK91KrgOCwoNHs= -tinygo.org/x/go-llvm v0.0.0-20210308112806-9ef958b6bed4/go.mod h1:fv1F0BSNpxMfCL0zF3M4OPFbgYHnhtB6ST0HvUtu/LE= -tinygo.org/x/go-llvm v0.0.0-20210325115028-e7b85195e81c h1:vn9IPshzYmzZis10UEVrsIBRv9FpykADw6M3/tHHROg= -tinygo.org/x/go-llvm v0.0.0-20210325115028-e7b85195e81c/go.mod h1:fv1F0BSNpxMfCL0zF3M4OPFbgYHnhtB6ST0HvUtu/LE= +tinygo.org/x/go-llvm v0.0.0-20210907125547-fd2d62ea06be h1:syIpWbi/yESuoyijF2nhRdgX4422sNfmij+o73B3+vU= +tinygo.org/x/go-llvm v0.0.0-20210907125547-fd2d62ea06be/go.mod h1:fv1F0BSNpxMfCL0zF3M4OPFbgYHnhtB6ST0HvUtu/LE= diff --git a/interp/interp_test.go b/interp/interp_test.go index 516fdd11..ae2635af 100644 --- a/interp/interp_test.go +++ b/interp/interp_test.go @@ -71,6 +71,7 @@ func runTest(t *testing.T, pathPrefix string) { defer pm.Dispose() pm.AddGlobalOptimizerPass() pm.AddDeadStoreEliminationPass() + pm.AddAggressiveDCEPass() pm.Run(mod) // Read the expected output IR. diff --git a/transform/maps_test.go b/transform/maps_test.go index 1f821d4b..e8b11133 100644 --- a/transform/maps_test.go +++ b/transform/maps_test.go @@ -18,6 +18,7 @@ func TestOptimizeMaps(t *testing.T) { pm := llvm.NewPassManager() defer pm.Dispose() pm.AddDeadStoreEliminationPass() + pm.AddAggressiveDCEPass() pm.Run(mod) }) } diff --git a/transform/testdata/coroutines.out.ll b/transform/testdata/coroutines.out.ll index d4a49a5e..de902884 100644 --- a/transform/testdata/coroutines.out.ll +++ b/transform/testdata/coroutines.out.ll @@ -301,13 +301,13 @@ declare i8* @llvm.coro.free(token, i8* nocapture readonly) #0 ; Function Attrs: nounwind declare token @llvm.coro.save(i8*) #2 -; Function Attrs: argmemonly nounwind willreturn +; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #3 -; Function Attrs: argmemonly nounwind willreturn +; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #3 attributes #0 = { argmemonly nounwind readonly } attributes #1 = { nounwind readnone } attributes #2 = { nounwind } -attributes #3 = { argmemonly nounwind willreturn } +attributes #3 = { argmemonly nofree nosync nounwind willreturn } diff --git a/transform/transform_test.go b/transform/transform_test.go index ae531eeb..935d660b 100644 --- a/transform/transform_test.go +++ b/transform/transform_test.go @@ -116,8 +116,9 @@ func filterIrrelevantIRLines(lines []string) []string { if strings.HasPrefix(line, "source_filename = ") { continue } - if llvmVersion < 11 && strings.HasPrefix(line, "attributes ") { + if llvmVersion < 12 && strings.HasPrefix(line, "attributes ") { // Ignore attribute groups. These may change between LLVM versions. + // Right now test outputs are for LLVM 12. continue } out = append(out, line)