diff --git a/.circleci/config.yml b/.circleci/config.yml index 31d6051f5e..67ae73422d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -14,14 +14,14 @@ commands: - run: name: "Install apt dependencies" command: | - echo 'deb https://apt.llvm.org/buster/ llvm-toolchain-buster<> main' | sudo tee /etc/apt/sources.list.d/llvm.list + echo 'deb https://apt.llvm.org/buster/ llvm-toolchain-buster-<> main' | sudo tee /etc/apt/sources.list.d/llvm.list wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key|sudo apt-key add - sudo apt-get update sudo apt-get install \ - llvm<>-dev \ - clang<> \ - libclang<>-dev \ - lld<> \ + llvm-<>-dev \ + clang-<> \ + libclang-<>-dev \ + lld-<> \ gcc-arm-linux-gnueabihf \ gcc-aarch64-linux-gnu \ qemu-system-arm \ @@ -41,48 +41,51 @@ commands: steps: - restore_cache: keys: - - llvm-source-9-v0 + - llvm-source-10-v0 - run: name: "Fetch LLVM source" command: make llvm-source - save_cache: - key: llvm-source-9-v0 + key: llvm-source-10-v0 paths: - llvm-project build-wasi-libc: steps: - restore_cache: keys: - - wasi-libc-sysroot-v1 + - wasi-libc-sysroot-v2 - run: name: "Build wasi-libc" command: make wasi-libc - save_cache: - key: wasi-libc-sysroot-v1 + key: wasi-libc-sysroot-v2 paths: - lib/wasi-libc/sysroot test-linux: + parameters: + llvm: + type: string steps: - checkout - submodules - apt-dependencies: - llvm: "-9" + llvm: "<>" - install-node - restore_cache: keys: - go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }} - go-cache-v2-{{ checksum "go.mod" }} - llvm-source-linux - - run: go install . + - run: go install -tags=llvm<> . - restore_cache: keys: - - wasi-libc-sysroot-systemclang-v0 + - wasi-libc-sysroot-systemclang-v1 - run: make wasi-libc - save_cache: - key: wasi-libc-sysroot-systemclang-v0 + key: wasi-libc-sysroot-systemclang-v1 paths: - lib/wasi-libc/sysroot - - run: go test -v ./cgo ./compileopts ./interp ./transform . + - run: go test -v -tags=llvm<> ./cgo ./compileopts ./interp ./transform . - run: make gen-device -j4 - run: make smoketest - save_cache: @@ -117,7 +120,7 @@ commands: - llvm-source-linux - restore_cache: keys: - - llvm-build-9-linux-v0-assert + - llvm-build-10-linux-v0-assert - run: name: "Build LLVM" command: | @@ -135,7 +138,7 @@ commands: make ASSERT=1 llvm-build fi - save_cache: - key: llvm-build-9-linux-v0-assert + key: llvm-build-10-linux-v0-assert paths: llvm-build - run: make ASSERT=1 @@ -176,7 +179,7 @@ commands: - llvm-source-linux - restore_cache: keys: - - llvm-build-9-linux-v0 + - llvm-build-10-linux-v0 - run: name: "Build LLVM" command: | @@ -194,7 +197,7 @@ commands: make llvm-build fi - save_cache: - key: llvm-build-9-linux-v0 + key: llvm-build-10-linux-v0 paths: llvm-build - build-wasi-libc @@ -239,17 +242,17 @@ commands: - go-cache-macos-v2-{{ checksum "go.mod" }} - restore_cache: keys: - - llvm-source-9-macos-v0 + - llvm-source-10-macos-v0 - run: name: "Fetch LLVM source" command: make llvm-source - save_cache: - key: llvm-source-9-macos-v0 + key: llvm-source-10-macos-v0 paths: - llvm-project - restore_cache: keys: - - llvm-build-9-macos-v0 + - llvm-build-10-macos-v0 - run: name: "Build LLVM" command: | @@ -261,7 +264,7 @@ commands: make llvm-build fi - save_cache: - key: llvm-build-9-macos-v0 + key: llvm-build-10-macos-v0 paths: llvm-build - restore_cache: @@ -310,17 +313,20 @@ jobs: docker: - image: circleci/golang:1.11-buster steps: - - test-linux - test-llvm9-go112: + - test-linux: + llvm: "9" + test-llvm10-go112: docker: - image: circleci/golang:1.12-buster steps: - - test-linux - test-llvm9-go113: + - test-linux: + llvm: "10" + test-llvm10-go113: docker: - image: circleci/golang:1.13-buster steps: - - test-linux + - test-linux: + llvm: "10" assert-test-linux: docker: - image: circleci/golang:1.13-stretch @@ -344,8 +350,8 @@ workflows: test-all: jobs: - test-llvm9-go111 - - test-llvm9-go112 - - test-llvm9-go113 + - test-llvm10-go112 + - test-llvm10-go113 - build-linux - build-macos - assert-test-linux diff --git a/Dockerfile b/Dockerfile index 80ae1046ce..4632c4fb2a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,10 +1,10 @@ -# TinyGo base stage installs Go 1.13, LLVM 9 and the TinyGo compiler itself. +# TinyGo base stage installs Go 1.13, LLVM 10 and the TinyGo compiler itself. FROM golang:1.13 AS tinygo-base RUN wget -O- https://apt.llvm.org/llvm-snapshot.gpg.key| apt-key add - && \ - echo "deb http://apt.llvm.org/buster/ llvm-toolchain-buster-9 main" >> /etc/apt/sources.list && \ + echo "deb http://apt.llvm.org/buster/ llvm-toolchain-buster-10 main" >> /etc/apt/sources.list && \ apt-get update && \ - apt-get install -y llvm-9-dev libclang-9-dev git + apt-get install -y llvm-10-dev libclang-10-dev git COPY . /tinygo @@ -28,9 +28,9 @@ COPY --from=tinygo-base /tinygo/src /tinygo/src COPY --from=tinygo-base /tinygo/targets /tinygo/targets RUN wget -O- https://apt.llvm.org/llvm-snapshot.gpg.key| apt-key add - && \ - echo "deb http://apt.llvm.org/buster/ llvm-toolchain-buster-9 main" >> /etc/apt/sources.list && \ + echo "deb http://apt.llvm.org/buster/ llvm-toolchain-buster-10 main" >> /etc/apt/sources.list && \ apt-get update && \ - apt-get install -y libllvm9 lld-9 + apt-get install -y libllvm10 lld-10 # tinygo-avr stage installs the needed dependencies to compile TinyGo programs for AVR microcontrollers. FROM tinygo-base AS tinygo-avr @@ -61,7 +61,7 @@ COPY --from=tinygo-base /tinygo/lib /tinygo/lib RUN cd /tinygo/ && \ apt-get update && \ - apt-get install -y apt-utils make clang-9 && \ + apt-get install -y apt-utils make clang-10 && \ make gen-device-nrf && make gen-device-stm32 # tinygo-all stage installs the needed dependencies to compile TinyGo programs for all platforms. @@ -73,7 +73,7 @@ COPY --from=tinygo-base /tinygo/lib /tinygo/lib RUN cd /tinygo/ && \ apt-get update && \ - apt-get install -y apt-utils make clang-9 binutils-avr gcc-avr avr-libc && \ + apt-get install -y apt-utils make clang-10 binutils-avr gcc-avr avr-libc && \ make gen-device CMD ["tinygo"] diff --git a/Makefile b/Makefile index e16f225a8c..c10633d5f4 100644 --- a/Makefile +++ b/Makefile @@ -12,19 +12,19 @@ LLD_SRC ?= $(LLVM_PROJECTDIR)/lld ifneq (, $(shell command -v llvm-build/bin/clang 2> /dev/null)) CLANG ?= $(abspath llvm-build/bin/clang) else - CLANG ?= clang-9 + CLANG ?= clang-10 endif ifneq (, $(shell command -v llvm-build/bin/llvm-ar 2> /dev/null)) LLVM_AR ?= $(abspath llvm-build/bin/llvm-ar) -else ifneq (, $(shell command -v llvm-ar-9 2> /dev/null)) - LLVM_AR ?= llvm-ar-9 +else ifneq (, $(shell command -v llvm-ar-10 2> /dev/null)) + LLVM_AR ?= llvm-ar-10 else LLVM_AR ?= llvm-ar endif ifneq (, $(shell command -v llvm-build/bin/llvm-nm 2> /dev/null)) LLVM_NM ?= $(abspath llvm-build/bin/llvm-nm) -else ifneq (, $(shell command -v llvm-nm-9 2> /dev/null)) - LLVM_NM ?= llvm-nm-9 +else ifneq (, $(shell command -v llvm-nm-10 2> /dev/null)) + LLVM_NM ?= llvm-nm-10 else LLVM_NM ?= llvm-nm endif @@ -53,7 +53,7 @@ endif .PHONY: all tinygo test $(LLVM_BUILDDIR) llvm-source clean fmt gen-device gen-device-nrf gen-device-avr -LLVM_COMPONENTS = all-targets analysis asmparser asmprinter bitreader bitwriter codegen core coroutines coverage debuginfodwarf executionengine instrumentation interpreter ipo irreader linker lto mc mcjit objcarcopts option profiledata scalaropts support target +LLVM_COMPONENTS = all-targets analysis asmparser asmprinter bitreader bitwriter codegen core coroutines coverage debuginfodwarf executionengine frontendopenmp instrumentation interpreter ipo irreader linker lto mc mcjit objcarcopts option profiledata scalaropts support target ifeq ($(OS),Windows_NT) EXE = .exe @@ -103,8 +103,8 @@ LLD_LIBS = $(START_GROUP) -llldCOFF -llldCommon -llldCore -llldDriver -llldELF - # 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++11 - CGO_LDFLAGS+=$(LIBCLANG_PATH) -std=c++11 -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_CXXFLAGS=-std=c++14 + CGO_LDFLAGS+=$(LIBCLANG_PATH) -std=c++14 -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) endif @@ -148,7 +148,7 @@ gen-device-stm32: build/gen-device-svd # Get LLVM sources. $(LLVM_PROJECTDIR)/README.md: - git clone -b release/9.x https://github.com/llvm/llvm-project $(LLVM_PROJECTDIR) + git clone -b release/10.x https://github.com/llvm/llvm-project $(LLVM_PROJECTDIR) llvm-source: $(LLVM_PROJECTDIR)/README.md # Configure LLVM. diff --git a/README.md b/README.md index b1c55822d0..83f9a9eed3 100644 --- a/README.md +++ b/README.md @@ -135,4 +135,4 @@ The original reasoning was: if [Python](https://micropython.org/) can run on mic This project is licensed under the BSD 3-clause license, just like the [Go project](https://golang.org/LICENSE) itself. -Some code has been copied from the LLVM project and is therefore licensed under [a variant of the Apache 2.0 license](http://releases.llvm.org/9.0.0/LICENSE.TXT). This has been clearly indicated in the header of these files. +Some code has been copied from the LLVM project and is therefore licensed under [a variant of the Apache 2.0 license](http://releases.llvm.org/10.0.0/LICENSE.TXT). This has been clearly indicated in the header of these files. diff --git a/azure-pipelines.yml b/azure-pipelines.yml index d8c4b8a473..135dcf4142 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -12,12 +12,12 @@ jobs: steps: - task: GoTool@0 inputs: - version: '1.13.8' + version: '1.13.8' - checkout: self - task: CacheBeta@0 displayName: Cache LLVM source inputs: - key: llvm-source-9-windows-v0 + key: llvm-source-10-windows-v0 path: llvm-project - task: Bash@3 displayName: Download LLVM source @@ -27,7 +27,7 @@ jobs: - task: CacheBeta@0 displayName: Cache LLVM build inputs: - key: llvm-build-9-windows-v0 + key: llvm-build-10-windows-v0 path: llvm-build - task: Bash@3 displayName: Build LLVM @@ -37,10 +37,6 @@ jobs: if [ ! -f llvm-build/lib/liblldELF.a ] then choco install ninja - # LLVM 9 cannot be built with MinGW 8. - # For details: https://reviews.llvm.org/D70266 - choco uninstall mingw - choco install mingw --version=7.3.0 make llvm-build fi - task: Bash@3 @@ -51,7 +47,7 @@ jobs: - task: CacheBeta@0 displayName: Cache wasi-libc sysroot inputs: - key: wasi-libc-sysroot-v1 + key: wasi-libc-sysroot-v2 path: lib/wasi-libc/sysroot - task: Bash@3 displayName: Build wasi-libc diff --git a/builder/cc1as.cpp b/builder/cc1as.cpp index 95b0a0a442..d8e4028898 100644 --- a/builder/cc1as.cpp +++ b/builder/cc1as.cpp @@ -48,6 +48,7 @@ #include "llvm/Support/Host.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" +#include "llvm/Support/Process.h" #include "llvm/Support/Signals.h" #include "llvm/Support/SourceMgr.h" #include "llvm/Support/TargetRegistry.h" @@ -70,12 +71,12 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts, bool Success = true; // Parse the arguments. - std::unique_ptr OptTbl(createDriverOptTable()); + const OptTable &OptTbl = getDriverOptTable(); const unsigned IncludedFlagsBitmask = options::CC1AsOption; unsigned MissingArgIndex, MissingArgCount; - InputArgList Args = OptTbl->ParseArgs(Argv, MissingArgIndex, MissingArgCount, - IncludedFlagsBitmask); + InputArgList Args = OptTbl.ParseArgs(Argv, MissingArgIndex, MissingArgCount, + IncludedFlagsBitmask); // Check for missing argument error. if (MissingArgCount) { @@ -88,7 +89,7 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts, for (const Arg *A : Args.filtered(OPT_UNKNOWN)) { auto ArgString = A->getAsString(Args); std::string Nearest; - if (OptTbl->findNearest(ArgString, Nearest, IncludedFlagsBitmask) > 1) + if (OptTbl.findNearest(ArgString, Nearest, IncludedFlagsBitmask) > 1) Diags.Report(diag::err_drv_unknown_argument) << ArgString; else Diags.Report(diag::err_drv_unknown_argument_with_suggestion) @@ -181,6 +182,7 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts, Opts.RelaxAll = Args.hasArg(OPT_mrelax_all); Opts.NoExecStack = Args.hasArg(OPT_mno_exec_stack); Opts.FatalWarnings = Args.hasArg(OPT_massembler_fatal_warnings); + Opts.NoWarn = Args.hasArg(OPT_massembler_no_warn); Opts.RelocationModel = Args.getLastArgValue(OPT_mrelocation_model, "pic"); Opts.TargetABI = Args.getLastArgValue(OPT_target_abi); Opts.IncrementalLinkerCompatible = @@ -208,8 +210,8 @@ getOutputStream(StringRef Path, DiagnosticsEngine &Diags, bool Binary) { sys::RemoveFileOnSignal(Path); std::error_code EC; - auto Out = llvm::make_unique( - Path, EC, (Binary ? sys::fs::F_None : sys::fs::F_Text)); + auto Out = std::make_unique( + Path, EC, (Binary ? sys::fs::OF_None : sys::fs::OF_Text)); if (EC) { Diags.Report(diag::err_fe_unable_to_open_output) << Path << EC.message(); return nullptr; @@ -245,7 +247,9 @@ bool ExecuteAssembler(AssemblerInvocation &Opts, DiagnosticsEngine &Diags) { std::unique_ptr MRI(TheTarget->createMCRegInfo(Opts.Triple)); assert(MRI && "Unable to create target register info!"); - std::unique_ptr MAI(TheTarget->createMCAsmInfo(*MRI, Opts.Triple)); + MCTargetOptions MCOptions; + std::unique_ptr MAI( + TheTarget->createMCAsmInfo(*MRI, Opts.Triple, MCOptions)); assert(MAI && "Unable to create target asm info!"); // Ensure MCAsmInfo initialization occurs before any use, otherwise sections @@ -269,7 +273,7 @@ bool ExecuteAssembler(AssemblerInvocation &Opts, DiagnosticsEngine &Diags) { // MCObjectFileInfo needs a MCContext reference in order to initialize itself. std::unique_ptr MOFI(new MCObjectFileInfo()); - MCContext Ctx(MAI.get(), MRI.get(), MOFI.get(), &SrcMgr); + MCContext Ctx(MAI.get(), MRI.get(), MOFI.get(), &SrcMgr, &MCOptions); bool PIC = false; if (Opts.RelocationModel == "static") { @@ -326,7 +330,8 @@ bool ExecuteAssembler(AssemblerInvocation &Opts, DiagnosticsEngine &Diags) { raw_pwrite_stream *Out = FDOS.get(); std::unique_ptr BOS; - MCTargetOptions MCOptions; + MCOptions.MCNoWarn = Opts.NoWarn; + MCOptions.MCFatalWarnings = Opts.FatalWarnings; MCOptions.ABIName = Opts.TargetABI; // FIXME: There is a bit of code duplication with addPassesToEmitFile. @@ -340,7 +345,7 @@ bool ExecuteAssembler(AssemblerInvocation &Opts, DiagnosticsEngine &Diags) { std::unique_ptr MAB( TheTarget->createMCAsmBackend(*STI, *MRI, MCOptions)); - auto FOut = llvm::make_unique(*Out); + auto FOut = std::make_unique(*Out); Str.reset(TheTarget->createAsmStreamer( Ctx, std::move(FOut), /*asmverbose*/ true, /*useDwarfDirectory*/ true, IP, std::move(CE), std::move(MAB), @@ -351,7 +356,7 @@ bool ExecuteAssembler(AssemblerInvocation &Opts, DiagnosticsEngine &Diags) { assert(Opts.OutputType == AssemblerInvocation::FT_Obj && "Invalid file type!"); if (!FDOS->supportsSeeking()) { - BOS = make_unique(*FDOS); + BOS = std::make_unique(*FDOS); Out = BOS.get(); } @@ -436,7 +441,7 @@ static void LLVMErrorHandler(void *UserData, const std::string &Message, Diags.Report(diag::err_fe_error_backend) << Message; // We cannot recover from llvm errors. - exit(1); + sys::Process::Exit(1); } int cc1as_main(ArrayRef Argv, const char *Argv0, void *MainAddr) { @@ -464,11 +469,11 @@ int cc1as_main(ArrayRef Argv, const char *Argv0, void *MainAddr) { return 1; if (Asm.ShowHelp) { - std::unique_ptr Opts(driver::createDriverOptTable()); - Opts->PrintHelp(llvm::outs(), "clang -cc1as [options] file...", - "Clang Integrated Assembler", - /*Include=*/driver::options::CC1AsOption, /*Exclude=*/0, - /*ShowAllAliases=*/false); + getDriverOptTable().PrintHelp( + llvm::outs(), "clang -cc1as [options] file...", + "Clang Integrated Assembler", + /*Include=*/driver::options::CC1AsOption, /*Exclude=*/0, + /*ShowAllAliases=*/false); return 0; } @@ -485,7 +490,7 @@ int cc1as_main(ArrayRef Argv, const char *Argv0, void *MainAddr) { // FIXME: Remove this, one day. if (!Asm.LLVMArgs.empty()) { unsigned NumArgs = Asm.LLVMArgs.size(); - auto Args = llvm::make_unique(NumArgs + 2); + auto Args = std::make_unique(NumArgs + 2); Args[0] = "clang (LLVM option parsing)"; for (unsigned i = 0; i != NumArgs; ++i) Args[i + 1] = Asm.LLVMArgs[i].c_str(); @@ -499,6 +504,7 @@ int cc1as_main(ArrayRef Argv, const char *Argv0, void *MainAddr) { // If any timers were active but haven't been destroyed yet, print their // results now. TimerGroup::printAll(errs()); + TimerGroup::clearAll(); return !!Failed; } diff --git a/builder/cc1as.h b/builder/cc1as.h index 1475adc1c1..ce9a1781cc 100644 --- a/builder/cc1as.h +++ b/builder/cc1as.h @@ -11,8 +11,6 @@ // //===----------------------------------------------------------------------===// -#include "llvm/ADT/ArrayRef.h" - /// Helper class for representing a single invocation of the assembler. struct AssemblerInvocation { /// @name Target Options @@ -82,6 +80,7 @@ struct AssemblerInvocation { unsigned RelaxAll : 1; unsigned NoExecStack : 1; unsigned FatalWarnings : 1; + unsigned NoWarn : 1; unsigned IncrementalLinkerCompatible : 1; unsigned EmbedBitcode : 1; @@ -107,6 +106,7 @@ struct AssemblerInvocation { RelaxAll = 0; NoExecStack = 0; FatalWarnings = 0; + NoWarn = 0; IncrementalLinkerCompatible = 0; DwarfVersion = 0; EmbedBitcode = 0; diff --git a/builder/clang.cpp b/builder/clang.cpp index e0993bf6e7..a511772ad6 100644 --- a/builder/clang.cpp +++ b/builder/clang.cpp @@ -52,8 +52,7 @@ bool tinygo_clang_driver(int argc, char **argv) { std::unique_ptr Clang(new clang::CompilerInstance()); bool success = clang::CompilerInvocation::CreateFromArgs( Clang->getInvocation(), - const_cast(CCArgs.data()), - const_cast(CCArgs.data()) + CCArgs.size(), + CCArgs, Diags); if (!success) { return false; @@ -74,7 +73,8 @@ bool tinygo_clang_driver(int argc, char **argv) { } else if (strcmp(*CCArgs.data(), "-cc1as") == 0) { // This is the assembler frontend. Parse the arguments. AssemblerInvocation Asm; - if (!AssemblerInvocation::CreateFromArgs(Asm, llvm::ArrayRef(CCArgs).slice(1), Diags)) + ArrayRef Argv = llvm::ArrayRef(CCArgs); + if (!AssemblerInvocation::CreateFromArgs(Asm, Argv.slice(1), Diags)) return false; // Execute the invocation, unless there were parsing errors. diff --git a/builder/commands.go b/builder/commands.go index ab196337d2..e96aca67d4 100644 --- a/builder/commands.go +++ b/builder/commands.go @@ -6,25 +6,28 @@ import ( "os/exec" "runtime" "strings" + + "tinygo.org/x/go-llvm" ) // Commands lists command alternatives for various operating systems. These // commands may have a slightly different name across operating systems and // distributions or may not even exist in $PATH, in which case absolute paths // may be used. -var commands = map[string][]string{ - "clang": {"clang-9"}, - "ld.lld": {"ld.lld-9", "ld.lld"}, - "wasm-ld": {"wasm-ld-9", "wasm-ld"}, -} +var commands = map[string][]string{} func init() { - // Add the path to a Homebrew-installed LLVM 9 for ease of use (no need to + llvmMajor := strings.Split(llvm.Version, ".")[0] + commands["clang"] = []string{"clang-" + llvmMajor} + commands["ld.lld"] = []string{"ld.lld-" + llvmMajor, "ld.lld"} + commands["wasm-ld"] = []string{"wasm-ld-" + llvmMajor, "wasm-ld"} + // Add the path to a Homebrew-installed LLVM for ease of use (no need to // manually set $PATH). if runtime.GOOS == "darwin" { - commands["clang"] = append(commands["clang"], "/usr/local/opt/llvm@9/bin/clang-9") - commands["ld.lld"] = append(commands["ld.lld"], "/usr/local/opt/llvm@9/bin/ld.lld") - commands["wasm-ld"] = append(commands["wasm-ld"], "/usr/local/opt/llvm@9/bin/wasm-ld") + prefix := "/usr/local/opt/llvm@" + llvmMajor + "/bin/" + commands["clang"] = append(commands["clang"], prefix+"clang-"+llvmMajor) + commands["ld.lld"] = append(commands["ld.lld"], prefix+"ld.lld") + commands["wasm-ld"] = append(commands["wasm-ld"], prefix+"wasm-ld") } // Add the path for when LLVM was installed with the installer from // llvm.org, which by default doesn't add LLVM to the $PATH environment @@ -34,11 +37,12 @@ func init() { commands["ld.lld"] = append(commands["ld.lld"], "lld", "C:\\Program Files\\LLVM\\bin\\lld.exe") commands["wasm-ld"] = append(commands["wasm-ld"], "C:\\Program Files\\LLVM\\bin\\wasm-ld.exe") } - // Add the path to the llvm90 installed from ports + // Add the path to LLVM installed from ports. if runtime.GOOS == "freebsd" { - commands["clang"] = append(commands["clang"], "/usr/local/llvm90/bin/clang-9") - commands["ld.lld"] = append(commands["ld.lld"], "/usr/local/llvm90/bin/ld.lld") - commands["wasm-ld"] = append(commands["wasm-ld"], "/usr/local/llvm90/bin/wasm-ld") + prefix := "/usr/local/llvm" + llvmMajor + "/bin/" + commands["clang"] = append(commands["clang"], prefix+"clang-"+llvmMajor) + commands["ld.lld"] = append(commands["ld.lld"], prefix+"ld.lld") + commands["wasm-ld"] = append(commands["wasm-ld"], prefix+"wasm-ld") } } diff --git a/builder/lld.cpp b/builder/lld.cpp index 2373edeec3..9231df9cf6 100644 --- a/builder/lld.cpp +++ b/builder/lld.cpp @@ -8,12 +8,12 @@ extern "C" { bool tinygo_link_elf(int argc, char **argv) { std::vector args(argv, argv + argc); - return lld::elf::link(args, false); + return lld::elf::link(args, false, llvm::outs(), llvm::errs()); } bool tinygo_link_wasm(int argc, char **argv) { std::vector args(argv, argv + argc); - return lld::wasm::link(args, false); + return lld::wasm::link(args, false, llvm::outs(), llvm::errs()); } } // external "C" diff --git a/builder/sizes.go b/builder/sizes.go index c88c9b7ba1..750c4bd94b 100644 --- a/builder/sizes.go +++ b/builder/sizes.go @@ -84,7 +84,18 @@ func loadProgramSize(path string) (*programSize, error) { if section.Type != elf.SHT_PROGBITS && section.Type != elf.SHT_NOBITS { continue } - if section.Type == elf.SHT_NOBITS { + if section.Name == ".stack" { + // HACK: this works around a bug in ld.lld from LLVM 10. The linker + // marks sections with no input symbols (such as is the case for the + // .stack section) as SHT_PROGBITS instead of SHT_NOBITS. While it + // doesn't affect the generated binaries (.hex and .bin), it does + // affect the reported size. + // https://bugs.llvm.org/show_bug.cgi?id=45336 + // https://reviews.llvm.org/D76981 + // It has been merged in master, but it has not (yet) been + // backported to the LLVM 10 release branch. + sumBSS += section.Size + } else if section.Type == elf.SHT_NOBITS { sumBSS += section.Size } else if section.Flags&elf.SHF_EXECINSTR != 0 { sumCode += section.Size diff --git a/cgo/libclang.go b/cgo/libclang.go index 3aaded21e6..3d7695b5e6 100644 --- a/cgo/libclang.go +++ b/cgo/libclang.go @@ -15,7 +15,7 @@ import ( ) /* -#include // if this fails, install libclang-9-dev +#include // if this fails, install libclang-10-dev #include #include diff --git a/cgo/libclang_config.go b/cgo/libclang_config.go index 908d7d5ba0..ec65d8a89d 100644 --- a/cgo/libclang_config.go +++ b/cgo/libclang_config.go @@ -1,13 +1,14 @@ // +build !byollvm +// +build !llvm9 package cgo /* -#cgo linux CFLAGS: -I/usr/lib/llvm-9/include -#cgo darwin CFLAGS: -I/usr/local/opt/llvm@9/include -#cgo freebsd CFLAGS: -I/usr/local/llvm90/include -#cgo linux LDFLAGS: -L/usr/lib/llvm-9/lib -lclang -#cgo darwin LDFLAGS: -L/usr/local/opt/llvm@9/lib -lclang -lffi -#cgo freebsd LDFLAGS: -L/usr/local/llvm90/lib -lclang +#cgo linux CFLAGS: -I/usr/lib/llvm-10/include +#cgo darwin CFLAGS: -I/usr/local/opt/llvm@10/include +#cgo freebsd CFLAGS: -I/usr/local/llvm10/include +#cgo linux LDFLAGS: -L/usr/lib/llvm-10/lib -lclang +#cgo darwin LDFLAGS: -L/usr/local/opt/llvm@10/lib -lclang -lffi +#cgo freebsd LDFLAGS: -L/usr/local/llvm10/lib -lclang */ import "C" diff --git a/cgo/libclang_config_llvm9.go b/cgo/libclang_config_llvm9.go new file mode 100644 index 0000000000..6e09b01ad2 --- /dev/null +++ b/cgo/libclang_config_llvm9.go @@ -0,0 +1,14 @@ +// +build !byollvm +// +build llvm9 + +package cgo + +/* +#cgo linux CFLAGS: -I/usr/lib/llvm-9/include +#cgo darwin CFLAGS: -I/usr/local/opt/llvm@9/include +#cgo freebsd CFLAGS: -I/usr/local/llvm9/include +#cgo linux LDFLAGS: -L/usr/lib/llvm-9/lib -lclang +#cgo darwin LDFLAGS: -L/usr/local/opt/llvm@9/lib -lclang -lffi +#cgo freebsd LDFLAGS: -L/usr/local/llvm9/lib -lclang +*/ +import "C" diff --git a/cgo/libclang_stubs.c b/cgo/libclang_stubs.c index 9c193076bf..293ca6026c 100644 --- a/cgo/libclang_stubs.c +++ b/cgo/libclang_stubs.c @@ -3,7 +3,7 @@ // are slightly different from the ones defined in libclang.go, but they // should be ABI compatible. -#include // if this fails, install libclang-9-dev +#include // if this fails, install libclang-10-dev CXCursor tinygo_clang_getTranslationUnitCursor(CXTranslationUnit tu) { return clang_getTranslationUnitCursor(tu); diff --git a/go.mod b/go.mod index f03ef00c87..b668cf69e1 100644 --- a/go.mod +++ b/go.mod @@ -9,5 +9,5 @@ require ( go.bug.st/serial v1.0.0 golang.org/x/tools v0.0.0-20200216192241-b320d3a0f5a2 google.golang.org/appengine v1.4.0 // indirect - tinygo.org/x/go-llvm v0.0.0-20200226165415-53522ab6713d + tinygo.org/x/go-llvm v0.0.0-20200401165421-8d120882fc7a ) diff --git a/go.sum b/go.sum index 96ed941624..3133c874f8 100644 --- a/go.sum +++ b/go.sum @@ -15,6 +15,7 @@ go.bug.st/serial v1.0.0 h1:ogEPzrllCsnG00EqKRjeYvPRsO7NJW6DqykzkdD6E/k= go.bug.st/serial v1.0.0/go.mod h1:rpXPISGjuNjPTRTcMlxi9lN6LoIPxd1ixVjBd8aSk/Q= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee h1:WG0RUwxtNT4qqaXX3DPA8zHFNm/D9xaBpxzHt1WcA/E= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -31,6 +32,7 @@ golang.org/x/tools v0.0.0-20190227180812-8dcc6e70cdef h1:ymc9FeDom3RIEA3coKokSll golang.org/x/tools v0.0.0-20190227180812-8dcc6e70cdef/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20200216192241-b320d3a0f5a2 h1:0sfSpGSa544Fwnbot3Oxq/U6SXqjty6Jy/3wRhVS7ig= golang.org/x/tools v0.0.0-20200216192241-b320d3a0f5a2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbOeHJjicWYPqR9bpxqxYG2pA= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -53,3 +55,5 @@ tinygo.org/x/go-llvm v0.0.0-20200104190746-1ff21df33566 h1:a4y30bTf7U0zDA75v2PTL tinygo.org/x/go-llvm v0.0.0-20200104190746-1ff21df33566/go.mod h1:fv1F0BSNpxMfCL0zF3M4OPFbgYHnhtB6ST0HvUtu/LE= tinygo.org/x/go-llvm v0.0.0-20200226165415-53522ab6713d h1:mtgZh/e8a3wxneQFuLXoQYO//1mvlki02yZ1JCwMKp4= tinygo.org/x/go-llvm v0.0.0-20200226165415-53522ab6713d/go.mod h1:fv1F0BSNpxMfCL0zF3M4OPFbgYHnhtB6ST0HvUtu/LE= +tinygo.org/x/go-llvm v0.0.0-20200401165421-8d120882fc7a h1:Ugje2Lxuv8CFncHzs5W+hWfJvPsM+W4K0zRvzFbLvoE= +tinygo.org/x/go-llvm v0.0.0-20200401165421-8d120882fc7a/go.mod h1:fv1F0BSNpxMfCL0zF3M4OPFbgYHnhtB6ST0HvUtu/LE= diff --git a/loader/loader.go b/loader/loader.go index 8c13cb8130..5682bfa4a8 100644 --- a/loader/loader.go +++ b/loader/loader.go @@ -423,7 +423,7 @@ func (p *Package) parseFiles(includeTests bool) ([]*ast.File, error) { if len(p.CgoFiles) != 0 { cflags := append(p.CFlags, "-I"+p.Package.Dir) if p.ClangHeaders != "" { - cflags = append(cflags, "-I"+p.ClangHeaders) + cflags = append(cflags, "-Xclang", "-internal-isystem", "-Xclang", p.ClangHeaders) } generated, errs := cgo.Process(files, p.Program.Dir, p.fset, cflags) if errs != nil { diff --git a/main.go b/main.go index bf1717580b..af9a770550 100644 --- a/main.go +++ b/main.go @@ -23,6 +23,7 @@ import ( "github.com/tinygo-org/tinygo/goenv" "github.com/tinygo-org/tinygo/interp" "github.com/tinygo-org/tinygo/loader" + "tinygo.org/x/go-llvm" "go.bug.st/serial" ) @@ -902,7 +903,7 @@ func main() { if s, err := builder.GorootVersionString(goenv.Get("GOROOT")); err == nil { goversion = s } - fmt.Printf("tinygo version %s %s/%s (using go version %s)\n", version, runtime.GOOS, runtime.GOARCH, goversion) + fmt.Printf("tinygo version %s %s/%s (using go version %s and LLVM version %s)\n", version, runtime.GOOS, runtime.GOARCH, goversion, llvm.Version) case "env": if flag.NArg() == 0 { // Show all environment variables. diff --git a/transform/testdata/allocs.out.ll b/transform/testdata/allocs.out.ll index e788beeba1..eacf62b24d 100644 --- a/transform/testdata/allocs.out.ll +++ b/transform/testdata/allocs.out.ll @@ -54,13 +54,15 @@ define void @testNonEscapingLoop() { entry: %stackalloc.alloca = alloca [1 x i32] br label %loop -loop: + +loop: ; preds = %loop, %entry store [1 x i32] zeroinitializer, [1 x i32]* %stackalloc.alloca %stackalloc = bitcast [1 x i32]* %stackalloc.alloca to i32* %0 = call i32* @noescapeIntPtr(i32* %stackalloc) %1 = icmp eq i32* null, %0 br i1 %1, label %loop, label %end -end: + +end: ; preds = %loop ret void } diff --git a/transform/testdata/coroutines.out.ll b/transform/testdata/coroutines.out.ll index ec03650bc2..c7850a5b15 100644 --- a/transform/testdata/coroutines.out.ll +++ b/transform/testdata/coroutines.out.ll @@ -5,11 +5,13 @@ target triple = "armv7m-none-eabi" %"internal/task.state" = type { i8* } declare void @"internal/task.start"(i32, i8*, i8*, i8*) + declare void @"internal/task.Pause"(i8*, i8*) declare void @runtime.scheduler(i8*, i8*) declare i8* @runtime.alloc(i32, i8*, i8*) + declare void @runtime.free(i8*, i8*, i8*) declare %"internal/task.Task"* @"internal/task.Current"(i8*, i8*) @@ -17,9 +19,11 @@ declare %"internal/task.Task"* @"internal/task.Current"(i8*, i8*) declare i8* @"(*internal/task.Task).setState"(%"internal/task.Task"*, i8*, i8*, i8*) declare void @"(*internal/task.Task).setReturnPtr"(%"internal/task.Task"*, i8*, i8*, i8*) + declare i8* @"(*internal/task.Task).getReturnPtr"(%"internal/task.Task"*, i8*, i8*) declare void @"(*internal/task.Task).returnTo"(%"internal/task.Task"*, i8*, i8*, i8*) + declare void @"(*internal/task.Task).returnCurrent"(%"internal/task.Task"*, i8*, i8*) declare %"internal/task.Task"* @"internal/task.createTask"(i8*, i8*) @@ -27,7 +31,8 @@ declare %"internal/task.Task"* @"internal/task.createTask"(i8*, i8*) declare void @callMain(i8*, i8*) declare void @enqueueTimer(%"internal/task.Task"*, i64, i8*, i8*) -define void @sleep(i64, i8*, i8* %parentHandle) { + +define void @sleep(i64 %0, i8* %1, i8* %parentHandle) { entry: %task.current = bitcast i8* %parentHandle to %"internal/task.Task"* %task.current1 = bitcast i8* %parentHandle to %"internal/task.Task"* @@ -35,7 +40,7 @@ entry: ret void } -define i32 @delayedValue(i32, i64, i8*, i8* %parentHandle) { +define i32 @delayedValue(i32 %0, i64 %1, i8* %2, i8* %parentHandle) { entry: %task.current = bitcast i8* %parentHandle to %"internal/task.Task"* %ret.ptr = call i8* @"(*internal/task.Task).getReturnPtr"(%"internal/task.Task"* %task.current, i8* undef, i8* undef) @@ -45,20 +50,20 @@ entry: ret i32 undef } -define void @deadlock(i8*, i8* %parentHandle) { +define void @deadlock(i8* %0, i8* %parentHandle) { entry: %task.current = bitcast i8* %parentHandle to %"internal/task.Task"* ret void } -define i32 @tail(i32, i64, i8*, i8* %parentHandle) { +define i32 @tail(i32 %0, i64 %1, i8* %2, i8* %parentHandle) { entry: %task.current = bitcast i8* %parentHandle to %"internal/task.Task"* %3 = call i32 @delayedValue(i32 %0, i64 %1, i8* undef, i8* %parentHandle) ret i32 undef } -define void @ditchTail(i32, i64, i8*, i8* %parentHandle) { +define void @ditchTail(i32 %0, i64 %1, i8* %2, i8* %parentHandle) { entry: %task.current = bitcast i8* %parentHandle to %"internal/task.Task"* %ret.ditch = call i8* @runtime.alloc(i32 4, i8* undef, i8* undef) @@ -67,14 +72,14 @@ entry: ret void } -define void @voidTail(i32, i64, i8*, i8* %parentHandle) { +define void @voidTail(i32 %0, i64 %1, i8* %2, i8* %parentHandle) { entry: %task.current = bitcast i8* %parentHandle to %"internal/task.Task"* call void @ditchTail(i32 %0, i64 %1, i8* undef, i8* %parentHandle) ret void } -define i32 @alternateTail(i32, i32, i64, i8*, i8* %parentHandle) { +define i32 @alternateTail(i32 %0, i32 %1, i64 %2, i8* %3, i8* %parentHandle) { entry: %task.current = bitcast i8* %parentHandle to %"internal/task.Task"* %ret.ptr = call i8* @"(*internal/task.Task).getReturnPtr"(%"internal/task.Task"* %task.current, i8* undef, i8* undef) @@ -86,7 +91,7 @@ entry: ret i32 undef } -define i1 @coroutine(i32, i64, i8*, i8* %parentHandle) { +define i1 @coroutine(i32 %0, i64 %1, i8* %2, i8* %parentHandle) { entry: %call.return = alloca i32 %coro.id = call token @llvm.coro.id(i32 0, i8* null, i8* null, i8* null) @@ -128,18 +133,18 @@ cleanup: ; preds = %entry, %wakeup br label %suspend } -define void @doNothing(i8*, i8*) { +define void @doNothing(i8* %0, i8* %1) { entry: ret void } -define void @sleepGoroutine(i8*, i8* %parentHandle) { +define void @sleepGoroutine(i8* %0, i8* %parentHandle) { %task.current = bitcast i8* %parentHandle to %"internal/task.Task"* call void @sleep(i64 1000000, i8* undef, i8* %parentHandle) ret void } -define void @progMain(i8*, i8* %parentHandle) { +define void @progMain(i8* %0, i8* %parentHandle) { entry: %task.current = bitcast i8* %parentHandle to %"internal/task.Task"* call void @doNothing(i8* undef, i8* undef) @@ -159,18 +164,34 @@ entry: ret void } +; Function Attrs: argmemonly nounwind readonly declare token @llvm.coro.id(i32, i8* readnone, i8* nocapture readonly, i8*) #0 + +; Function Attrs: nounwind readnone declare i32 @llvm.coro.size.i32() #1 + +; Function Attrs: nounwind declare i8* @llvm.coro.begin(token, i8* writeonly) #2 + +; Function Attrs: nounwind declare i8 @llvm.coro.suspend(token, i1) #2 + +; Function Attrs: nounwind declare i1 @llvm.coro.end(i8*, i1) #2 + +; Function Attrs: argmemonly nounwind readonly 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 declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #3 + +; Function Attrs: argmemonly 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 } +attributes #3 = { argmemonly nounwind willreturn } diff --git a/transform/testdata/func-lowering.out.ll b/transform/testdata/func-lowering.out.ll index 97621730bd..50558fc8fa 100644 --- a/transform/testdata/func-lowering.out.ll +++ b/transform/testdata/func-lowering.out.ll @@ -29,64 +29,57 @@ declare void @func1Uint8(i8, i8*, i8*) declare void @func2Uint8(i8, i8*, i8*) -; Call a function of which only one function with this signature is used as a -; function value. This means that lowering it to IR is trivial: simply check -; whether the func value is nil, and if not, call that one function directly. -define void @runFunc1(i8*, i32, i8, i8* %context, i8* %parentHandle) { +define void @runFunc1(i8* %0, i32 %1, i8 %2, i8* %context, i8* %parentHandle) { entry: %3 = icmp eq i32 %1, 0 %4 = select i1 %3, void (i8, i8*, i8*)* null, void (i8, i8*, i8*)* @funcInt8 %5 = icmp eq void (i8, i8*, i8*)* %4, null br i1 %5, label %fpcall.nil, label %fpcall.next -fpcall.nil: +fpcall.nil: ; preds = %entry call void @runtime.nilPanic(i8* undef, i8* null) unreachable -fpcall.next: +fpcall.next: ; preds = %entry call void %4(i8 %2, i8* %0, i8* undef) ret void } -; There are two functions with this signature used in a func value. That means -; that we'll have to check at runtime which of the two it is (or whether the -; func value is nil). This call will thus be lowered to a switch statement. -define void @runFunc2(i8*, i32, i8, i8* %context, i8* %parentHandle) { +define void @runFunc2(i8* %0, i32 %1, i8 %2, i8* %context, i8* %parentHandle) { entry: br i1 false, label %fpcall.nil, label %fpcall.next -fpcall.nil: +fpcall.nil: ; preds = %entry call void @runtime.nilPanic(i8* undef, i8* null) unreachable -fpcall.next: +fpcall.next: ; preds = %entry switch i32 %1, label %func.default [ i32 0, label %func.nil i32 1, label %func.call1 i32 2, label %func.call2 ] -func.nil: +func.nil: ; preds = %fpcall.next call void @runtime.nilPanic(i8* undef, i8* null) unreachable -func.call1: +func.call1: ; preds = %fpcall.next call void @func1Uint8(i8 %2, i8* %0, i8* undef) br label %func.next -func.call2: +func.call2: ; preds = %fpcall.next call void @func2Uint8(i8 %2, i8* %0, i8* undef) br label %func.next -func.next: +func.next: ; preds = %func.call2, %func.call1 ret void -func.default: +func.default: ; preds = %fpcall.next unreachable } -; Special case for runtime.makeGoroutine. -define void @sleepFuncValue(i8*, i32, i8* nocapture readnone %context, i8* nocapture readnone %parentHandle) { +define void @sleepFuncValue(i8* %0, i32 %1, i8* nocapture readnone %context, i8* nocapture readnone %parentHandle) { entry: switch i32 %1, label %func.default [ i32 0, label %func.nil @@ -94,21 +87,21 @@ entry: i32 2, label %func.call2 ] -func.nil: +func.nil: ; preds = %entry call void @runtime.nilPanic(i8* undef, i8* null) unreachable -func.call1: +func.call1: ; preds = %entry call void @"internal/task.start"(i32 ptrtoint (void (i32, i8*, i8*)* @"main$1" to i32), i8* null, i8* undef, i8* null) br label %func.next -func.call2: +func.call2: ; preds = %entry call void @"internal/task.start"(i32 ptrtoint (void (i32, i8*, i8*)* @"main$2" to i32), i8* null, i8* undef, i8* null) br label %func.next -func.next: +func.next: ; preds = %func.call2, %func.call1 ret void -func.default: +func.default: ; preds = %entry unreachable } diff --git a/transform/testdata/gc-stackslots.out.ll b/transform/testdata/gc-stackslots.out.ll index c24793ea97..88ee8c02a3 100644 --- a/transform/testdata/gc-stackslots.out.ll +++ b/transform/testdata/gc-stackslots.out.ll @@ -94,7 +94,7 @@ entry: store i8 1, i8* %entry.y br label %loop -loop: +loop: ; preds = %loop, %entry %prev.y = phi i8* [ %entry.y, %entry ], [ %prev.x, %loop ] %prev.x = phi i8* [ %entry.x, %entry ], [ %next.x, %loop ] %5 = getelementptr { %runtime.stackChainObject*, i32, i8*, i8*, i8*, i8*, i8* }, { %runtime.stackChainObject*, i32, i8*, i8*, i8*, i8*, i8* }* %gc.stackobject, i32 0, i32 5 @@ -108,7 +108,7 @@ loop: %loop.done = icmp ult i8 40, %next.x.val br i1 %loop.done, label %end, label %loop -end: +end: ; preds = %loop store %runtime.stackChainObject* %0, %runtime.stackChainObject** @runtime.stackChainStart ret i8* %next.x } diff --git a/transform/testdata/interface.out.ll b/transform/testdata/interface.out.ll index 25f47d0c85..9ae8b48867 100644 --- a/transform/testdata/interface.out.ll +++ b/transform/testdata/interface.out.ll @@ -36,32 +36,32 @@ define void @printInterface(i32 %typecode, i8* %value) { %typeassert.ok1 = call i1 @"Unmatched$typeassert"(i32 %typecode) br i1 %typeassert.ok1, label %typeswitch.Unmatched, label %typeswitch.notUnmatched -typeswitch.Unmatched: +typeswitch.Unmatched: ; preds = %0 %unmatched = ptrtoint i8* %value to i32 call void @runtime.printptr(i32 %unmatched) call void @runtime.printnl() ret void -typeswitch.notUnmatched: +typeswitch.notUnmatched: ; preds = %0 %typeassert.ok = call i1 @"Doubler$typeassert"(i32 %typecode) br i1 %typeassert.ok, label %typeswitch.Doubler, label %typeswitch.notDoubler -typeswitch.Doubler: +typeswitch.Doubler: ; preds = %typeswitch.notUnmatched %doubler.result = call i32 @"(Number).Double$invoke"(i8* %value, i8* null) call void @runtime.printint32(i32 %doubler.result) ret void -typeswitch.notDoubler: +typeswitch.notDoubler: ; preds = %typeswitch.notUnmatched %typeassert.ok2 = icmp eq i32 16, %typecode br i1 %typeassert.ok2, label %typeswitch.byte, label %typeswitch.notByte -typeswitch.byte: +typeswitch.byte: ; preds = %typeswitch.notDoubler %byte = ptrtoint i8* %value to i8 call void @runtime.printuint8(i8 %byte) call void @runtime.printnl() ret void -typeswitch.notByte: +typeswitch.notByte: ; preds = %typeswitch.notDoubler ret void } @@ -82,10 +82,10 @@ entry: i32 68, label %then ] -then: +then: ; preds = %entry ret i1 true -else: +else: ; preds = %entry ret i1 false } @@ -94,9 +94,9 @@ entry: switch i32 %actualType, label %else [ ] -then: +then: ; No predecessors! ret i1 true -else: +else: ; preds = %entry ret i1 false } diff --git a/transform/testdata/interrupt-avr.out.ll b/transform/testdata/interrupt-avr.out.ll index b71c439988..322d6516a3 100644 --- a/transform/testdata/interrupt-avr.out.ll +++ b/transform/testdata/interrupt-avr.out.ll @@ -19,7 +19,7 @@ declare i32 @"runtime/interrupt.Register"(i32, i8*, i16, i8*, i8*) addrspace(1) declare void @"runtime/interrupt.use"(%"runtime/interrupt.Interrupt") addrspace(1) -define void @"(machine.UART).Configure"(%machine.RingBuffer*, i32, i8, i8, i8* %context, i8* %parentHandle) unnamed_addr addrspace(1) { +define void @"(machine.UART).Configure"(%machine.RingBuffer* %0, i32 %1, i8 %2, i8 %3, i8* %context, i8* %parentHandle) unnamed_addr addrspace(1) { ret void } diff --git a/transform/testdata/interrupt-cortexm.out.ll b/transform/testdata/interrupt-cortexm.out.ll index 41fa9a776c..2e35f26336 100644 --- a/transform/testdata/interrupt-cortexm.out.ll +++ b/transform/testdata/interrupt-cortexm.out.ll @@ -16,14 +16,14 @@ declare void @"device/arm.EnableIRQ"(i32, i8* nocapture readnone, i8* nocapture declare void @"device/arm.SetPriority"(i32, i32, i8* nocapture readnone, i8* nocapture readnone) -define void @runtime.initAll(i8* nocapture readnone, i8* nocapture readnone) unnamed_addr { +define void @runtime.initAll(i8* nocapture readnone %0, i8* nocapture readnone %1) unnamed_addr { entry: call void @"device/arm.SetPriority"(i32 2, i32 192, i8* undef, i8* undef) call void @"device/arm.EnableIRQ"(i32 2, i8* undef, i8* undef) ret void } -define internal void @"(*machine.UART).handleInterrupt$bound"(i32, i8* nocapture %context, i8* nocapture readnone %parentHandle) { +define internal void @"(*machine.UART).handleInterrupt$bound"(i32 %0, i8* nocapture %context, i8* nocapture readnone %parentHandle) { entry: %unpack.ptr = bitcast i8* %context to %machine.UART* call void @"(*machine.UART).handleInterrupt"(%machine.UART* %unpack.ptr, i32 %0, i8* undef, i8* undef) diff --git a/transform/testdata/wasm-abi.out.ll b/transform/testdata/wasm-abi.out.ll index 6fb9a94824..68bed05e86 100644 --- a/transform/testdata/wasm-abi.out.ll +++ b/transform/testdata/wasm-abi.out.ll @@ -37,7 +37,7 @@ define internal void @callExportedFunction(i64 %foo) { declare void @externalCall(i64*, i8*, i32, i64*) -define void @exportedFunction(i64*) { +define void @exportedFunction(i64* %0) { entry: %i64 = load i64, i64* %0 call void @"exportedFunction$i64wrap"(i64 %i64) diff --git a/transform/transform_test.go b/transform/transform_test.go index d0f125692d..7a607f13b5 100644 --- a/transform/transform_test.go +++ b/transform/transform_test.go @@ -3,14 +3,19 @@ package transform // This file defines some helper functions for testing transforms. import ( + "flag" "io/ioutil" "os" + "regexp" + "strconv" "strings" "testing" "tinygo.org/x/go-llvm" ) +var update = flag.Bool("update", false, "update transform package tests") + // testTransform runs a transformation pass on an input file (pathPrefix+".ll") // and checks whether it matches the expected output (pathPrefix+".out.ll"). The // output is compared with a fuzzy match that ignores some irrelevant lines such @@ -31,18 +36,28 @@ func testTransform(t *testing.T, pathPrefix string, transform func(mod llvm.Modu // Perform the transform. transform(mod) - // Read the expected output IR. - out, err := ioutil.ReadFile(pathPrefix + ".out.ll") - if err != nil { - t.Fatalf("could not read output file %s: %v", pathPrefix+".out.ll", err) - } - - // See whether the transform output matches with the expected output IR. - expected := string(out) + // Get the output from the test and filter some irrelevant lines. actual := mod.String() - if !fuzzyEqualIR(expected, actual) { - t.Logf("output does not match expected output:\n%s", actual) - t.Fail() + actual = actual[strings.Index(actual, "\ntarget datalayout = ")+1:] + + if *update { + err := ioutil.WriteFile(pathPrefix+".out.ll", []byte(actual), 0666) + if err != nil { + t.Error("failed to write out new output:", err) + } + } else { + // Read the expected output IR. + out, err := ioutil.ReadFile(pathPrefix + ".out.ll") + if err != nil { + t.Fatalf("could not read output file %s: %v", pathPrefix+".out.ll", err) + } + + // See whether the transform output matches with the expected output IR. + expected := string(out) + if !fuzzyEqualIR(expected, actual) { + t.Logf("output does not match expected output:\n%s", actual) + t.Fail() + } } } @@ -69,6 +84,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 @@ -78,6 +99,19 @@ func filterIrrelevantIRLines(lines []string) []string { if strings.HasPrefix(line, "source_filename = ") { continue } + if llvmVersion < 10 && strings.HasPrefix(line, "attributes ") { + // Ignore attribute groups. These may change between LLVM versions. + // Right now test outputs are for LLVM 10. + continue + } + if llvmVersion < 10 && strings.HasPrefix(line, "define ") { + // Remove parameter values such as %0 in function definitions. These + // were added in LLVM 10 so to get the tests to pass on older + // versions, ignore them there (there are other tests that verify + // correct behavior). + re := regexp.MustCompile(` %[0-9]+(\)|,)`) + line = re.ReplaceAllString(line, "$1") + } out = append(out, line) } return out