
I mistakenly believed the difference was in LLVM version 11.0.0 vs LLVM 11.1.0. However, the difference is in whether we use the Debian version of Clang. The Debian version has had lots of patches. I'm not sure which is to blame, but it could be this one: https://salsa.debian.org/pkg-llvm-team/llvm-toolchain/-/blob/snapshot/debian/patches/clang-arm-default-vfp3-on-armv7a.patch
168 строки
4,8 КиБ
Go
168 строки
4,8 КиБ
Go
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())
|
|
}
|