mirror of https://github.com/tinygo-org/tinygo.git
wasmstm32webassemblymicrocontrollerarmavrspiwasiadafruitarduinocircuitplayground-expressgpioi2cllvmmicrobitnrf51nrf52nrf52840samd21tinygo
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
168 lines
4.8 KiB
168 lines
4.8 KiB
package builder
|
|
|
|
import (
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
"path/filepath"
|
|
"runtime"
|
|
"testing"
|
|
|
|
"github.com/tinygo-org/tinygo/compileopts"
|
|
"github.com/tinygo-org/tinygo/goenv"
|
|
"tinygo.org/x/go-llvm"
|
|
)
|
|
|
|
// Test whether the Clang generated "target-cpu" and "target-features"
|
|
// attributes match the CPU and Features property in TinyGo target files.
|
|
func TestClangAttributes(t *testing.T) {
|
|
var targetNames = []string{
|
|
// Please keep this list sorted!
|
|
"atmega328p",
|
|
"atmega1280",
|
|
"atmega1284p",
|
|
"atmega2560",
|
|
"attiny85",
|
|
"cortex-m0",
|
|
"cortex-m0plus",
|
|
"cortex-m3",
|
|
//"cortex-m33", // TODO: broken in LLVM 11, fixed in https://reviews.llvm.org/D90305
|
|
"cortex-m4",
|
|
"cortex-m7",
|
|
"esp32c3",
|
|
"fe310",
|
|
"gameboy-advance",
|
|
"k210",
|
|
"nintendoswitch",
|
|
"riscv-qemu",
|
|
"wasi",
|
|
"wasm",
|
|
}
|
|
if hasBuiltinTools {
|
|
// hasBuiltinTools is set when TinyGo is statically linked with LLVM,
|
|
// which also implies it was built with Xtensa support.
|
|
targetNames = append(targetNames, "esp32", "esp8266")
|
|
}
|
|
for _, targetName := range targetNames {
|
|
targetName := targetName
|
|
t.Run(targetName, func(t *testing.T) {
|
|
testClangAttributes(t, &compileopts.Options{Target: targetName})
|
|
})
|
|
}
|
|
|
|
for _, options := range []*compileopts.Options{
|
|
{GOOS: "linux", GOARCH: "386"},
|
|
{GOOS: "linux", GOARCH: "amd64"},
|
|
{GOOS: "linux", GOARCH: "arm", GOARM: "5"},
|
|
{GOOS: "linux", GOARCH: "arm", GOARM: "6"},
|
|
{GOOS: "linux", GOARCH: "arm", GOARM: "7"},
|
|
{GOOS: "linux", GOARCH: "arm64"},
|
|
{GOOS: "darwin", GOARCH: "amd64"},
|
|
{GOOS: "darwin", GOARCH: "arm64"},
|
|
{GOOS: "windows", GOARCH: "amd64"},
|
|
} {
|
|
name := "GOOS=" + options.GOOS + ",GOARCH=" + options.GOARCH
|
|
if options.GOARCH == "arm" {
|
|
name += ",GOARM=" + options.GOARM
|
|
}
|
|
t.Run(name, func(t *testing.T) {
|
|
testClangAttributes(t, options)
|
|
})
|
|
}
|
|
}
|
|
|
|
func testClangAttributes(t *testing.T, options *compileopts.Options) {
|
|
testDir := t.TempDir()
|
|
clangHeaderPath := getClangHeaderPath(goenv.Get("TINYGOROOT"))
|
|
|
|
ctx := llvm.NewContext()
|
|
defer ctx.Dispose()
|
|
|
|
target, err := compileopts.LoadTarget(options)
|
|
if err != nil {
|
|
t.Fatalf("could not load target: %s", err)
|
|
}
|
|
config := compileopts.Config{
|
|
Options: options,
|
|
Target: target,
|
|
ClangHeaders: clangHeaderPath,
|
|
}
|
|
|
|
// Create a very simple C input file.
|
|
srcpath := filepath.Join(testDir, "test.c")
|
|
err = ioutil.WriteFile(srcpath, []byte("int add(int a, int b) { return a + b; }"), 0o666)
|
|
if err != nil {
|
|
t.Fatalf("could not write target file %s: %s", srcpath, err)
|
|
}
|
|
|
|
// Compile this file using Clang.
|
|
outpath := filepath.Join(testDir, "test.bc")
|
|
flags := append([]string{"-c", "-emit-llvm", "-o", outpath, srcpath}, config.CFlags()...)
|
|
if config.GOOS() == "darwin" {
|
|
// Silence some warnings that happen when testing GOOS=darwin on
|
|
// something other than MacOS.
|
|
flags = append(flags, "-Wno-missing-sysroot", "-Wno-incompatible-sysroot")
|
|
}
|
|
err = runCCompiler(flags...)
|
|
if err != nil {
|
|
t.Fatalf("failed to compile %s: %s", srcpath, err)
|
|
}
|
|
|
|
// Read the resulting LLVM bitcode.
|
|
mod, err := ctx.ParseBitcodeFile(outpath)
|
|
if err != nil {
|
|
t.Fatalf("could not parse bitcode file %s: %s", outpath, err)
|
|
}
|
|
defer mod.Dispose()
|
|
|
|
// Check whether the LLVM target matches.
|
|
if mod.Target() != config.Triple() {
|
|
t.Errorf("target has LLVM triple %#v but Clang makes it LLVM triple %#v", config.Triple(), mod.Target())
|
|
}
|
|
|
|
// Check the "target-cpu" and "target-features" string attribute of the add
|
|
// function.
|
|
add := mod.NamedFunction("add")
|
|
var cpu, features string
|
|
cpuAttr := add.GetStringAttributeAtIndex(-1, "target-cpu")
|
|
featuresAttr := add.GetStringAttributeAtIndex(-1, "target-features")
|
|
if !cpuAttr.IsNil() {
|
|
cpu = cpuAttr.GetStringValue()
|
|
}
|
|
if !featuresAttr.IsNil() {
|
|
features = featuresAttr.GetStringValue()
|
|
}
|
|
if cpu != config.CPU() {
|
|
t.Errorf("target has CPU %#v but Clang makes it CPU %#v", config.CPU(), cpu)
|
|
}
|
|
if features != config.Features() {
|
|
if hasBuiltinTools || runtime.GOOS != "linux" {
|
|
// Skip this step when using an external Clang invocation on Linux.
|
|
// The reason is that Debian has patched Clang in a way that
|
|
// modifies the LLVM features string, changing lots of FPU/float
|
|
// related flags. We want to test vanilla Clang, not Debian Clang.
|
|
t.Errorf("target has LLVM features\n\t%#v\nbut Clang makes it\n\t%#v", config.Features(), features)
|
|
}
|
|
}
|
|
}
|
|
|
|
// This TestMain is necessary because TinyGo may also be invoked to run certain
|
|
// LLVM tools in a separate process. Not capturing these invocations would lead
|
|
// to recursive tests.
|
|
func TestMain(m *testing.M) {
|
|
if len(os.Args) >= 2 {
|
|
switch os.Args[1] {
|
|
case "clang", "ld.lld", "wasm-ld":
|
|
// Invoke a specific tool.
|
|
err := RunTool(os.Args[1], os.Args[2:]...)
|
|
if err != nil {
|
|
fmt.Fprintln(os.Stderr, err)
|
|
os.Exit(1)
|
|
}
|
|
os.Exit(0)
|
|
}
|
|
}
|
|
|
|
// Run normal tests.
|
|
os.Exit(m.Run())
|
|
}
|
|
|