all: refactor compile options
Move most of the logic of determining which compiler configuration to use (such as GOOS/GOARCH, build tags, whether to include debug symbols, panic strategy, etc.) into the compileopts package. This makes it a single source of truth for anything related to compiler configuration. It has a few advantages: * The compile configuration is independent of the compiler package. This makes it possible to move optimization passes out of the compiler, as they don't rely on compiler.Config anymore. * There is only one place to look if an incorrect compile option is used. * The compileopts provides some resistance against unintentionally picking the wrong option, such as with c.selectGC() vs c.GC() in the compiler. * It is now a lot easier to change compile options, as most options are getters now.
Этот коммит содержится в:
родитель
59cc901340
коммит
3b0ed63c29
10 изменённых файлов: 181 добавлений и 146 удалений
|
@ -2,27 +2,124 @@
|
||||||
// binary.
|
// binary.
|
||||||
package compileopts
|
package compileopts
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/tinygo-org/tinygo/goenv"
|
||||||
|
)
|
||||||
|
|
||||||
// Config keeps all configuration affecting the build in a single struct.
|
// Config keeps all configuration affecting the build in a single struct.
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Triple string // LLVM target triple, e.g. x86_64-unknown-linux-gnu (empty string means default)
|
Options *Options
|
||||||
CPU string // LLVM CPU name, e.g. atmega328p (empty string means default)
|
Target *TargetSpec
|
||||||
Features []string // LLVM CPU features
|
GoMinorVersion int
|
||||||
GOOS string //
|
ClangHeaders string // Clang built-in header include path
|
||||||
GOARCH string //
|
TestConfig TestConfig
|
||||||
GC string // garbage collection strategy
|
}
|
||||||
Scheduler string // scheduler implementation ("coroutines" or "tasks")
|
|
||||||
PanicStrategy string // panic strategy ("print" or "trap")
|
// Triple returns the LLVM target triple, like armv6m-none-eabi.
|
||||||
CFlags []string // cflags to pass to cgo
|
func (c *Config) Triple() string {
|
||||||
LDFlags []string // ldflags to pass to cgo
|
return c.Target.Triple
|
||||||
ClangHeaders string // Clang built-in header include path
|
}
|
||||||
DumpSSA bool // dump Go SSA, for compiler debugging
|
|
||||||
VerifyIR bool // run extra checks on the IR
|
// CPU returns the LLVM CPU name, like atmega328p or arm7tdmi. It may return an
|
||||||
Debug bool // add debug symbols for gdb
|
// empty string if the CPU name is not known.
|
||||||
GOROOT string // GOROOT
|
func (c *Config) CPU() string {
|
||||||
TINYGOROOT string // GOROOT for TinyGo
|
return c.Target.CPU
|
||||||
GOPATH string // GOPATH, like `go env GOPATH`
|
}
|
||||||
BuildTags []string // build tags for TinyGo (empty means {Config.GOOS/Config.GOARCH})
|
|
||||||
TestConfig TestConfig
|
// Features returns a list of features this CPU supports. For example, for a
|
||||||
|
// RISC-V processor, that could be ["+a", "+c", "+m"]. For many targets, an
|
||||||
|
// empty list will be returned.
|
||||||
|
func (c *Config) Features() []string {
|
||||||
|
return c.Target.Features
|
||||||
|
}
|
||||||
|
|
||||||
|
// GOOS returns the GOOS of the target. This might not always be the actual OS:
|
||||||
|
// for example, bare-metal targets will usually pretend to be linux to get the
|
||||||
|
// standard library to compile.
|
||||||
|
func (c *Config) GOOS() string {
|
||||||
|
return c.Target.GOOS
|
||||||
|
}
|
||||||
|
|
||||||
|
// GOARCH returns the GOARCH of the target. This might not always be the actual
|
||||||
|
// archtecture: for example, the AVR target is not supported by the Go standard
|
||||||
|
// library so such targets will usually pretend to be linux/arm.
|
||||||
|
func (c *Config) GOARCH() string {
|
||||||
|
return c.Target.GOARCH
|
||||||
|
}
|
||||||
|
|
||||||
|
// BuildTags returns the complete list of build tags used during this build.
|
||||||
|
func (c *Config) BuildTags() []string {
|
||||||
|
tags := append(c.Target.BuildTags, []string{"tinygo", "gc." + c.GC(), "scheduler." + c.Scheduler()}...)
|
||||||
|
for i := 1; i <= c.GoMinorVersion; i++ {
|
||||||
|
tags = append(tags, fmt.Sprintf("go1.%d", i))
|
||||||
|
}
|
||||||
|
if extraTags := strings.Fields(c.Options.Tags); len(extraTags) != 0 {
|
||||||
|
tags = append(tags, extraTags...)
|
||||||
|
}
|
||||||
|
return tags
|
||||||
|
}
|
||||||
|
|
||||||
|
// GC returns the garbage collection strategy in use on this platform. Valid
|
||||||
|
// values are "none", "leaking", and "conservative".
|
||||||
|
func (c *Config) GC() string {
|
||||||
|
if c.Options.GC != "" {
|
||||||
|
return c.Options.GC
|
||||||
|
}
|
||||||
|
if c.Target.GC != "" {
|
||||||
|
return c.Target.GC
|
||||||
|
}
|
||||||
|
return "conservative"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scheduler returns the scheduler implementation. Valid values are "coroutines"
|
||||||
|
// and "tasks".
|
||||||
|
func (c *Config) Scheduler() string {
|
||||||
|
if c.Options.Scheduler != "" {
|
||||||
|
return c.Options.Scheduler
|
||||||
|
}
|
||||||
|
if c.Target.Scheduler != "" {
|
||||||
|
return c.Target.Scheduler
|
||||||
|
}
|
||||||
|
// Fall back to coroutines, which are supported everywhere.
|
||||||
|
return "coroutines"
|
||||||
|
}
|
||||||
|
|
||||||
|
// PanicStrategy returns the panic strategy selected for this target. Valid
|
||||||
|
// values are "print" (print the panic value, then exit) or "trap" (issue a trap
|
||||||
|
// instruction).
|
||||||
|
func (c *Config) PanicStrategy() string {
|
||||||
|
return c.Options.PanicStrategy
|
||||||
|
}
|
||||||
|
|
||||||
|
// CFlags returns the flags to pass to the C compiler. This is necessary for CGo
|
||||||
|
// preprocessing.
|
||||||
|
func (c *Config) CFlags() []string {
|
||||||
|
cflags := append([]string{}, c.Options.CFlags...)
|
||||||
|
for _, flag := range c.Target.CFlags {
|
||||||
|
cflags = append(cflags, strings.Replace(flag, "{root}", goenv.Get("TINYGOROOT"), -1))
|
||||||
|
}
|
||||||
|
return cflags
|
||||||
|
}
|
||||||
|
|
||||||
|
// DumpSSA returns whether to dump Go SSA while compiling (-dumpssa flag). Only
|
||||||
|
// enable this for debugging.
|
||||||
|
func (c *Config) DumpSSA() bool {
|
||||||
|
return c.Options.DumpSSA
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerifyIR returns whether to run extra checks on the IR. This is normally
|
||||||
|
// disabled but enabled during testing.
|
||||||
|
func (c *Config) VerifyIR() bool {
|
||||||
|
return c.Options.VerifyIR
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debug returns whether to add debug symbols to the IR, for debugging with GDB
|
||||||
|
// and similar.
|
||||||
|
func (c *Config) Debug() bool {
|
||||||
|
return c.Options.Debug
|
||||||
}
|
}
|
||||||
|
|
||||||
type TestConfig struct {
|
type TestConfig struct {
|
||||||
|
|
|
@ -15,6 +15,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/tinygo-org/tinygo/compileopts"
|
"github.com/tinygo-org/tinygo/compileopts"
|
||||||
|
"github.com/tinygo-org/tinygo/goenv"
|
||||||
"github.com/tinygo-org/tinygo/ir"
|
"github.com/tinygo-org/tinygo/ir"
|
||||||
"github.com/tinygo-org/tinygo/loader"
|
"github.com/tinygo-org/tinygo/loader"
|
||||||
"golang.org/x/tools/go/ssa"
|
"golang.org/x/tools/go/ssa"
|
||||||
|
@ -103,35 +104,26 @@ type Phi struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCompiler(pkgName string, config *compileopts.Config) (*Compiler, error) {
|
func NewCompiler(pkgName string, config *compileopts.Config) (*Compiler, error) {
|
||||||
if config.Triple == "" {
|
|
||||||
config.Triple = llvm.DefaultTargetTriple()
|
|
||||||
}
|
|
||||||
if len(config.BuildTags) == 0 {
|
|
||||||
config.BuildTags = []string{config.GOOS, config.GOARCH}
|
|
||||||
}
|
|
||||||
c := &Compiler{
|
c := &Compiler{
|
||||||
Config: config,
|
Config: config,
|
||||||
difiles: make(map[string]llvm.Metadata),
|
difiles: make(map[string]llvm.Metadata),
|
||||||
ditypes: make(map[types.Type]llvm.Metadata),
|
ditypes: make(map[types.Type]llvm.Metadata),
|
||||||
}
|
}
|
||||||
|
|
||||||
target, err := llvm.GetTargetFromTriple(config.Triple)
|
target, err := llvm.GetTargetFromTriple(config.Triple())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
features := ""
|
features := strings.Join(config.Features(), ",")
|
||||||
if len(config.Features) > 0 {
|
c.machine = target.CreateTargetMachine(config.Triple(), config.CPU(), features, llvm.CodeGenLevelDefault, llvm.RelocStatic, llvm.CodeModelDefault)
|
||||||
features = strings.Join(config.Features, `,`)
|
|
||||||
}
|
|
||||||
c.machine = target.CreateTargetMachine(config.Triple, config.CPU, features, llvm.CodeGenLevelDefault, llvm.RelocStatic, llvm.CodeModelDefault)
|
|
||||||
c.targetData = c.machine.CreateTargetData()
|
c.targetData = c.machine.CreateTargetData()
|
||||||
|
|
||||||
c.ctx = llvm.NewContext()
|
c.ctx = llvm.NewContext()
|
||||||
c.mod = c.ctx.NewModule(pkgName)
|
c.mod = c.ctx.NewModule(pkgName)
|
||||||
c.mod.SetTarget(config.Triple)
|
c.mod.SetTarget(config.Triple())
|
||||||
c.mod.SetDataLayout(c.targetData.String())
|
c.mod.SetDataLayout(c.targetData.String())
|
||||||
c.builder = c.ctx.NewBuilder()
|
c.builder = c.ctx.NewBuilder()
|
||||||
if c.Debug {
|
if c.Debug() {
|
||||||
c.dibuilder = llvm.NewDIBuilder(c.mod)
|
c.dibuilder = llvm.NewDIBuilder(c.mod)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,35 +156,16 @@ func (c *Compiler) Module() llvm.Module {
|
||||||
return c.mod
|
return c.mod
|
||||||
}
|
}
|
||||||
|
|
||||||
// selectGC picks an appropriate GC strategy if none was provided.
|
|
||||||
func (c *Compiler) selectGC() string {
|
|
||||||
if c.GC != "" {
|
|
||||||
return c.GC
|
|
||||||
}
|
|
||||||
return "conservative"
|
|
||||||
}
|
|
||||||
|
|
||||||
// selectScheduler picks an appropriate scheduler for the target if none was
|
|
||||||
// given.
|
|
||||||
func (c *Compiler) selectScheduler() string {
|
|
||||||
if c.Scheduler != "" {
|
|
||||||
// A scheduler was specified in the target description.
|
|
||||||
return c.Scheduler
|
|
||||||
}
|
|
||||||
// Fall back to coroutines, which are supported everywhere.
|
|
||||||
return "coroutines"
|
|
||||||
}
|
|
||||||
|
|
||||||
// getFunctionsUsedInTransforms gets a list of all special functions that should be preserved during transforms and optimization.
|
// getFunctionsUsedInTransforms gets a list of all special functions that should be preserved during transforms and optimization.
|
||||||
func (c *Compiler) getFunctionsUsedInTransforms() []string {
|
func (c *Compiler) getFunctionsUsedInTransforms() []string {
|
||||||
fnused := functionsUsedInTransforms
|
fnused := functionsUsedInTransforms
|
||||||
switch c.selectScheduler() {
|
switch c.Scheduler() {
|
||||||
case "coroutines":
|
case "coroutines":
|
||||||
fnused = append(append([]string{}, fnused...), coroFunctionsUsedInTransforms...)
|
fnused = append(append([]string{}, fnused...), coroFunctionsUsedInTransforms...)
|
||||||
case "tasks":
|
case "tasks":
|
||||||
fnused = append(append([]string{}, fnused...), taskFunctionsUsedInTransforms...)
|
fnused = append(append([]string{}, fnused...), taskFunctionsUsedInTransforms...)
|
||||||
default:
|
default:
|
||||||
panic(fmt.Errorf("invalid scheduler %q", c.selectScheduler()))
|
panic(fmt.Errorf("invalid scheduler %q", c.Scheduler()))
|
||||||
}
|
}
|
||||||
return fnused
|
return fnused
|
||||||
}
|
}
|
||||||
|
@ -202,38 +175,37 @@ func (c *Compiler) getFunctionsUsedInTransforms() []string {
|
||||||
func (c *Compiler) Compile(mainPath string) []error {
|
func (c *Compiler) Compile(mainPath string) []error {
|
||||||
// Prefix the GOPATH with the system GOROOT, as GOROOT is already set to
|
// Prefix the GOPATH with the system GOROOT, as GOROOT is already set to
|
||||||
// the TinyGo root.
|
// the TinyGo root.
|
||||||
overlayGopath := c.GOPATH
|
overlayGopath := goenv.Get("GOPATH")
|
||||||
if overlayGopath == "" {
|
if overlayGopath == "" {
|
||||||
overlayGopath = c.GOROOT
|
overlayGopath = goenv.Get("GOROOT")
|
||||||
} else {
|
} else {
|
||||||
overlayGopath = c.GOROOT + string(filepath.ListSeparator) + overlayGopath
|
overlayGopath = goenv.Get("GOROOT") + string(filepath.ListSeparator) + overlayGopath
|
||||||
}
|
}
|
||||||
|
|
||||||
wd, err := os.Getwd()
|
wd, err := os.Getwd()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []error{err}
|
return []error{err}
|
||||||
}
|
}
|
||||||
buildTags := append([]string{"tinygo", "gc." + c.selectGC(), "scheduler." + c.selectScheduler()}, c.BuildTags...)
|
|
||||||
lprogram := &loader.Program{
|
lprogram := &loader.Program{
|
||||||
Build: &build.Context{
|
Build: &build.Context{
|
||||||
GOARCH: c.GOARCH,
|
GOARCH: c.GOARCH(),
|
||||||
GOOS: c.GOOS,
|
GOOS: c.GOOS(),
|
||||||
GOROOT: c.GOROOT,
|
GOROOT: goenv.Get("GOROOT"),
|
||||||
GOPATH: c.GOPATH,
|
GOPATH: goenv.Get("GOPATH"),
|
||||||
CgoEnabled: true,
|
CgoEnabled: true,
|
||||||
UseAllFiles: false,
|
UseAllFiles: false,
|
||||||
Compiler: "gc", // must be one of the recognized compilers
|
Compiler: "gc", // must be one of the recognized compilers
|
||||||
BuildTags: buildTags,
|
BuildTags: c.BuildTags(),
|
||||||
},
|
},
|
||||||
OverlayBuild: &build.Context{
|
OverlayBuild: &build.Context{
|
||||||
GOARCH: c.GOARCH,
|
GOARCH: c.GOARCH(),
|
||||||
GOOS: c.GOOS,
|
GOOS: c.GOOS(),
|
||||||
GOROOT: c.TINYGOROOT,
|
GOROOT: goenv.Get("TINYGOROOT"),
|
||||||
GOPATH: overlayGopath,
|
GOPATH: overlayGopath,
|
||||||
CgoEnabled: true,
|
CgoEnabled: true,
|
||||||
UseAllFiles: false,
|
UseAllFiles: false,
|
||||||
Compiler: "gc", // must be one of the recognized compilers
|
Compiler: "gc", // must be one of the recognized compilers
|
||||||
BuildTags: buildTags,
|
BuildTags: c.BuildTags(),
|
||||||
},
|
},
|
||||||
OverlayPath: func(path string) string {
|
OverlayPath: func(path string) string {
|
||||||
// Return the (overlay) import path when it should be overlaid, and
|
// Return the (overlay) import path when it should be overlaid, and
|
||||||
|
@ -250,7 +222,7 @@ func (c *Compiler) Compile(mainPath string) []error {
|
||||||
if strings.HasPrefix(path, "device/") || strings.HasPrefix(path, "examples/") {
|
if strings.HasPrefix(path, "device/") || strings.HasPrefix(path, "examples/") {
|
||||||
return path
|
return path
|
||||||
} else if path == "syscall" {
|
} else if path == "syscall" {
|
||||||
for _, tag := range c.BuildTags {
|
for _, tag := range c.BuildTags() {
|
||||||
if tag == "baremetal" || tag == "darwin" {
|
if tag == "baremetal" || tag == "darwin" {
|
||||||
return path
|
return path
|
||||||
}
|
}
|
||||||
|
@ -267,8 +239,8 @@ func (c *Compiler) Compile(mainPath string) []error {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Dir: wd,
|
Dir: wd,
|
||||||
TINYGOROOT: c.TINYGOROOT,
|
TINYGOROOT: goenv.Get("TINYGOROOT"),
|
||||||
CFlags: c.CFlags,
|
CFlags: c.CFlags(),
|
||||||
ClangHeaders: c.ClangHeaders,
|
ClangHeaders: c.ClangHeaders,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -300,7 +272,7 @@ func (c *Compiler) Compile(mainPath string) []error {
|
||||||
c.ir.SimpleDCE()
|
c.ir.SimpleDCE()
|
||||||
|
|
||||||
// Initialize debug information.
|
// Initialize debug information.
|
||||||
if c.Debug {
|
if c.Debug() {
|
||||||
c.cu = c.dibuilder.CreateCompileUnit(llvm.DICompileUnit{
|
c.cu = c.dibuilder.CreateCompileUnit(llvm.DICompileUnit{
|
||||||
Language: 0xb, // DW_LANG_C99 (0xc, off-by-one?)
|
Language: 0xb, // DW_LANG_C99 (0xc, off-by-one?)
|
||||||
File: mainPath,
|
File: mainPath,
|
||||||
|
@ -344,7 +316,7 @@ func (c *Compiler) Compile(mainPath string) []error {
|
||||||
initFn := c.ir.GetFunction(c.ir.Program.ImportedPackage("runtime").Members["initAll"].(*ssa.Function))
|
initFn := c.ir.GetFunction(c.ir.Program.ImportedPackage("runtime").Members["initAll"].(*ssa.Function))
|
||||||
initFn.LLVMFn.SetLinkage(llvm.InternalLinkage)
|
initFn.LLVMFn.SetLinkage(llvm.InternalLinkage)
|
||||||
initFn.LLVMFn.SetUnnamedAddr(true)
|
initFn.LLVMFn.SetUnnamedAddr(true)
|
||||||
if c.Debug {
|
if c.Debug() {
|
||||||
difunc := c.attachDebugInfo(initFn)
|
difunc := c.attachDebugInfo(initFn)
|
||||||
pos := c.ir.Program.Fset.Position(initFn.Pos())
|
pos := c.ir.Program.Fset.Position(initFn.Pos())
|
||||||
c.builder.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), difunc, llvm.Metadata{})
|
c.builder.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), difunc, llvm.Metadata{})
|
||||||
|
@ -408,7 +380,7 @@ func (c *Compiler) Compile(mainPath string) []error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// see: https://reviews.llvm.org/D18355
|
// see: https://reviews.llvm.org/D18355
|
||||||
if c.Debug {
|
if c.Debug() {
|
||||||
c.mod.AddNamedMetadataOperand("llvm.module.flags",
|
c.mod.AddNamedMetadataOperand("llvm.module.flags",
|
||||||
c.ctx.MDNode([]llvm.Metadata{
|
c.ctx.MDNode([]llvm.Metadata{
|
||||||
llvm.ConstInt(c.ctx.Int32Type(), 1, false).ConstantAsMetadata(), // Error on mismatch
|
llvm.ConstInt(c.ctx.Int32Type(), 1, false).ConstantAsMetadata(), // Error on mismatch
|
||||||
|
@ -856,7 +828,7 @@ func (c *Compiler) attachDebugInfoRaw(f *ir.Function, llvmFn llvm.Value, suffix,
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Compiler) parseFunc(frame *Frame) {
|
func (c *Compiler) parseFunc(frame *Frame) {
|
||||||
if c.DumpSSA {
|
if c.DumpSSA() {
|
||||||
fmt.Printf("\nfunc %s:\n", frame.fn.Function)
|
fmt.Printf("\nfunc %s:\n", frame.fn.Function)
|
||||||
}
|
}
|
||||||
if !frame.fn.LLVMFn.IsDeclaration() {
|
if !frame.fn.LLVMFn.IsDeclaration() {
|
||||||
|
@ -867,7 +839,7 @@ func (c *Compiler) parseFunc(frame *Frame) {
|
||||||
frame.fn.LLVMFn.SetLinkage(llvm.InternalLinkage)
|
frame.fn.LLVMFn.SetLinkage(llvm.InternalLinkage)
|
||||||
frame.fn.LLVMFn.SetUnnamedAddr(true)
|
frame.fn.LLVMFn.SetUnnamedAddr(true)
|
||||||
}
|
}
|
||||||
if frame.fn.IsInterrupt() && strings.HasPrefix(c.Triple, "avr") {
|
if frame.fn.IsInterrupt() && strings.HasPrefix(c.Triple(), "avr") {
|
||||||
frame.fn.LLVMFn.SetFunctionCallConv(85) // CallingConv::AVR_SIGNAL
|
frame.fn.LLVMFn.SetFunctionCallConv(85) // CallingConv::AVR_SIGNAL
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -884,7 +856,7 @@ func (c *Compiler) parseFunc(frame *Frame) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add debug info, if needed.
|
// Add debug info, if needed.
|
||||||
if c.Debug {
|
if c.Debug() {
|
||||||
if frame.fn.Synthetic == "package initializer" {
|
if frame.fn.Synthetic == "package initializer" {
|
||||||
// Package initializers have no debug info. Create some fake debug
|
// Package initializers have no debug info. Create some fake debug
|
||||||
// info to at least have *something*.
|
// info to at least have *something*.
|
||||||
|
@ -918,7 +890,7 @@ func (c *Compiler) parseFunc(frame *Frame) {
|
||||||
frame.locals[param] = c.collapseFormalParam(llvmType, fields)
|
frame.locals[param] = c.collapseFormalParam(llvmType, fields)
|
||||||
|
|
||||||
// Add debug information to this parameter (if available)
|
// Add debug information to this parameter (if available)
|
||||||
if c.Debug && frame.fn.Syntax() != nil {
|
if c.Debug() && frame.fn.Syntax() != nil {
|
||||||
pos := c.ir.Program.Fset.Position(frame.fn.Syntax().Pos())
|
pos := c.ir.Program.Fset.Position(frame.fn.Syntax().Pos())
|
||||||
diType := c.getDIType(param.Type())
|
diType := c.getDIType(param.Type())
|
||||||
dbgParam := c.dibuilder.CreateParameterVariable(frame.difunc, llvm.DIParameterVariable{
|
dbgParam := c.dibuilder.CreateParameterVariable(frame.difunc, llvm.DIParameterVariable{
|
||||||
|
@ -980,7 +952,7 @@ func (c *Compiler) parseFunc(frame *Frame) {
|
||||||
|
|
||||||
// Fill blocks with instructions.
|
// Fill blocks with instructions.
|
||||||
for _, block := range frame.fn.DomPreorder() {
|
for _, block := range frame.fn.DomPreorder() {
|
||||||
if c.DumpSSA {
|
if c.DumpSSA() {
|
||||||
fmt.Printf("%d: %s:\n", block.Index, block.Comment)
|
fmt.Printf("%d: %s:\n", block.Index, block.Comment)
|
||||||
}
|
}
|
||||||
c.builder.SetInsertPointAtEnd(frame.blockEntries[block])
|
c.builder.SetInsertPointAtEnd(frame.blockEntries[block])
|
||||||
|
@ -989,7 +961,7 @@ func (c *Compiler) parseFunc(frame *Frame) {
|
||||||
if _, ok := instr.(*ssa.DebugRef); ok {
|
if _, ok := instr.(*ssa.DebugRef); ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if c.DumpSSA {
|
if c.DumpSSA() {
|
||||||
if val, ok := instr.(ssa.Value); ok && val.Name() != "" {
|
if val, ok := instr.(ssa.Value); ok && val.Name() != "" {
|
||||||
fmt.Printf("\t%s = %s\n", val.Name(), val.String())
|
fmt.Printf("\t%s = %s\n", val.Name(), val.String())
|
||||||
} else {
|
} else {
|
||||||
|
@ -1015,7 +987,7 @@ func (c *Compiler) parseFunc(frame *Frame) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Compiler) parseInstr(frame *Frame, instr ssa.Instruction) {
|
func (c *Compiler) parseInstr(frame *Frame, instr ssa.Instruction) {
|
||||||
if c.Debug {
|
if c.Debug() {
|
||||||
pos := c.ir.Program.Fset.Position(instr.Pos())
|
pos := c.ir.Program.Fset.Position(instr.Pos())
|
||||||
c.builder.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), frame.difunc, llvm.Metadata{})
|
c.builder.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), frame.difunc, llvm.Metadata{})
|
||||||
}
|
}
|
||||||
|
@ -1076,7 +1048,7 @@ func (c *Compiler) parseInstr(frame *Frame, instr ssa.Instruction) {
|
||||||
// * The function pointer (for tasks).
|
// * The function pointer (for tasks).
|
||||||
funcPtr, context := c.decodeFuncValue(c.getValue(frame, instr.Call.Value), instr.Call.Value.Type().(*types.Signature))
|
funcPtr, context := c.decodeFuncValue(c.getValue(frame, instr.Call.Value), instr.Call.Value.Type().(*types.Signature))
|
||||||
params = append(params, context) // context parameter
|
params = append(params, context) // context parameter
|
||||||
switch c.selectScheduler() {
|
switch c.Scheduler() {
|
||||||
case "coroutines":
|
case "coroutines":
|
||||||
// There are no additional parameters needed for the goroutine start operation.
|
// There are no additional parameters needed for the goroutine start operation.
|
||||||
case "tasks":
|
case "tasks":
|
||||||
|
|
|
@ -34,7 +34,7 @@ const (
|
||||||
func (c *Compiler) funcImplementation() funcValueImplementation {
|
func (c *Compiler) funcImplementation() funcValueImplementation {
|
||||||
// Always pick the switch implementation, as it allows the use of blocking
|
// Always pick the switch implementation, as it allows the use of blocking
|
||||||
// inside a function that is used as a func value.
|
// inside a function that is used as a func value.
|
||||||
switch c.selectScheduler() {
|
switch c.Scheduler() {
|
||||||
case "coroutines":
|
case "coroutines":
|
||||||
return funcValueSwitch
|
return funcValueSwitch
|
||||||
case "tasks":
|
case "tasks":
|
||||||
|
|
|
@ -14,10 +14,10 @@ import (
|
||||||
// needsStackObjects returns true if the compiler should insert stack objects
|
// needsStackObjects returns true if the compiler should insert stack objects
|
||||||
// that can be traced by the garbage collector.
|
// that can be traced by the garbage collector.
|
||||||
func (c *Compiler) needsStackObjects() bool {
|
func (c *Compiler) needsStackObjects() bool {
|
||||||
if c.selectGC() != "conservative" {
|
if c.GC() != "conservative" {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
for _, tag := range c.BuildTags {
|
for _, tag := range c.BuildTags() {
|
||||||
if tag == "baremetal" {
|
if tag == "baremetal" {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
|
@ -126,7 +126,7 @@ type asyncFunc struct {
|
||||||
// coroutine or the tasks implementation of goroutines, and whether goroutines
|
// coroutine or the tasks implementation of goroutines, and whether goroutines
|
||||||
// are necessary at all.
|
// are necessary at all.
|
||||||
func (c *Compiler) LowerGoroutines() error {
|
func (c *Compiler) LowerGoroutines() error {
|
||||||
switch c.selectScheduler() {
|
switch c.Scheduler() {
|
||||||
case "coroutines":
|
case "coroutines":
|
||||||
return c.lowerCoroutines()
|
return c.lowerCoroutines()
|
||||||
case "tasks":
|
case "tasks":
|
||||||
|
@ -312,7 +312,7 @@ func (c *Compiler) markAsyncFunctions() (needsScheduler bool, err error) {
|
||||||
|
|
||||||
// Check whether a scheduler is needed.
|
// Check whether a scheduler is needed.
|
||||||
makeGoroutine := c.mod.NamedFunction("runtime.makeGoroutine")
|
makeGoroutine := c.mod.NamedFunction("runtime.makeGoroutine")
|
||||||
if strings.HasPrefix(c.Triple, "avr") {
|
if strings.HasPrefix(c.Triple(), "avr") {
|
||||||
needsScheduler = false
|
needsScheduler = false
|
||||||
getCoroutine := c.mod.NamedFunction("runtime.getCoroutine")
|
getCoroutine := c.mod.NamedFunction("runtime.getCoroutine")
|
||||||
for _, inst := range getUses(getCoroutine) {
|
for _, inst := range getUses(getCoroutine) {
|
||||||
|
|
|
@ -13,7 +13,7 @@ import "tinygo.org/x/go-llvm"
|
||||||
//
|
//
|
||||||
// Because a go statement doesn't return anything, return undef.
|
// Because a go statement doesn't return anything, return undef.
|
||||||
func (c *Compiler) emitStartGoroutine(funcPtr llvm.Value, params []llvm.Value) llvm.Value {
|
func (c *Compiler) emitStartGoroutine(funcPtr llvm.Value, params []llvm.Value) llvm.Value {
|
||||||
switch c.selectScheduler() {
|
switch c.Scheduler() {
|
||||||
case "tasks":
|
case "tasks":
|
||||||
paramBundle := c.emitPointerPack(params)
|
paramBundle := c.emitPointerPack(params)
|
||||||
paramBundle = c.builder.CreatePtrToInt(paramBundle, c.uintptrType, "")
|
paramBundle = c.builder.CreatePtrToInt(paramBundle, c.uintptrType, "")
|
||||||
|
|
|
@ -483,7 +483,7 @@ func (c *Compiler) createInterfaceInvokeWrapper(state interfaceInvokeWrapper) {
|
||||||
wrapper.SetUnnamedAddr(true)
|
wrapper.SetUnnamedAddr(true)
|
||||||
|
|
||||||
// add debug info if needed
|
// add debug info if needed
|
||||||
if c.Debug {
|
if c.Debug() {
|
||||||
pos := c.ir.Program.Fset.Position(fn.Pos())
|
pos := c.ir.Program.Fset.Position(fn.Pos())
|
||||||
difunc := c.attachDebugInfoRaw(fn, wrapper, "$invoke", pos.Filename, pos.Line)
|
difunc := c.attachDebugInfoRaw(fn, wrapper, "$invoke", pos.Filename, pos.Line)
|
||||||
c.builder.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), difunc, llvm.Metadata{})
|
c.builder.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), difunc, llvm.Metadata{})
|
||||||
|
|
|
@ -19,12 +19,12 @@ func (c *Compiler) Optimize(optLevel, sizeLevel int, inlinerThreshold uint) erro
|
||||||
}
|
}
|
||||||
builder.AddCoroutinePassesToExtensionPoints()
|
builder.AddCoroutinePassesToExtensionPoints()
|
||||||
|
|
||||||
if c.PanicStrategy == "trap" {
|
if c.PanicStrategy() == "trap" {
|
||||||
c.replacePanicsWithTrap() // -panic=trap
|
c.replacePanicsWithTrap() // -panic=trap
|
||||||
}
|
}
|
||||||
|
|
||||||
// run a check of all of our code
|
// run a check of all of our code
|
||||||
if c.VerifyIR {
|
if c.VerifyIR() {
|
||||||
err := c.checkModule()
|
err := c.checkModule()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -96,7 +96,7 @@ func (c *Compiler) Optimize(optLevel, sizeLevel int, inlinerThreshold uint) erro
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if c.VerifyIR {
|
if c.VerifyIR() {
|
||||||
if err := c.checkModule(); err != nil {
|
if err := c.checkModule(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,8 +16,8 @@ func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value,
|
||||||
num := c.getValue(frame, call.Args[0])
|
num := c.getValue(frame, call.Args[0])
|
||||||
var syscallResult llvm.Value
|
var syscallResult llvm.Value
|
||||||
switch {
|
switch {
|
||||||
case c.GOARCH == "amd64":
|
case c.GOARCH() == "amd64":
|
||||||
if c.GOOS == "darwin" {
|
if c.GOOS() == "darwin" {
|
||||||
// Darwin adds this magic number to system call numbers:
|
// Darwin adds this magic number to system call numbers:
|
||||||
//
|
//
|
||||||
// > Syscall classes for 64-bit system call entry.
|
// > Syscall classes for 64-bit system call entry.
|
||||||
|
@ -58,7 +58,7 @@ func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value,
|
||||||
fnType := llvm.FunctionType(c.uintptrType, argTypes, false)
|
fnType := llvm.FunctionType(c.uintptrType, argTypes, false)
|
||||||
target := llvm.InlineAsm(fnType, "syscall", constraints, true, false, llvm.InlineAsmDialectIntel)
|
target := llvm.InlineAsm(fnType, "syscall", constraints, true, false, llvm.InlineAsmDialectIntel)
|
||||||
syscallResult = c.builder.CreateCall(target, args, "")
|
syscallResult = c.builder.CreateCall(target, args, "")
|
||||||
case c.GOARCH == "386" && c.GOOS == "linux":
|
case c.GOARCH() == "386" && c.GOOS() == "linux":
|
||||||
// Sources:
|
// Sources:
|
||||||
// syscall(2) man page
|
// syscall(2) man page
|
||||||
// https://stackoverflow.com/a/2538212
|
// https://stackoverflow.com/a/2538212
|
||||||
|
@ -84,7 +84,7 @@ func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value,
|
||||||
fnType := llvm.FunctionType(c.uintptrType, argTypes, false)
|
fnType := llvm.FunctionType(c.uintptrType, argTypes, false)
|
||||||
target := llvm.InlineAsm(fnType, "int 0x80", constraints, true, false, llvm.InlineAsmDialectIntel)
|
target := llvm.InlineAsm(fnType, "int 0x80", constraints, true, false, llvm.InlineAsmDialectIntel)
|
||||||
syscallResult = c.builder.CreateCall(target, args, "")
|
syscallResult = c.builder.CreateCall(target, args, "")
|
||||||
case c.GOARCH == "arm" && c.GOOS == "linux":
|
case c.GOARCH() == "arm" && c.GOOS() == "linux":
|
||||||
// Implement the EABI system call convention for Linux.
|
// Implement the EABI system call convention for Linux.
|
||||||
// Source: syscall(2) man page.
|
// Source: syscall(2) man page.
|
||||||
args := []llvm.Value{}
|
args := []llvm.Value{}
|
||||||
|
@ -116,7 +116,7 @@ func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value,
|
||||||
fnType := llvm.FunctionType(c.uintptrType, argTypes, false)
|
fnType := llvm.FunctionType(c.uintptrType, argTypes, false)
|
||||||
target := llvm.InlineAsm(fnType, "svc #0", constraints, true, false, 0)
|
target := llvm.InlineAsm(fnType, "svc #0", constraints, true, false, 0)
|
||||||
syscallResult = c.builder.CreateCall(target, args, "")
|
syscallResult = c.builder.CreateCall(target, args, "")
|
||||||
case c.GOARCH == "arm64" && c.GOOS == "linux":
|
case c.GOARCH() == "arm64" && c.GOOS() == "linux":
|
||||||
// Source: syscall(2) man page.
|
// Source: syscall(2) man page.
|
||||||
args := []llvm.Value{}
|
args := []llvm.Value{}
|
||||||
argTypes := []llvm.Type{}
|
argTypes := []llvm.Type{}
|
||||||
|
@ -149,9 +149,9 @@ func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value,
|
||||||
target := llvm.InlineAsm(fnType, "svc #0", constraints, true, false, 0)
|
target := llvm.InlineAsm(fnType, "svc #0", constraints, true, false, 0)
|
||||||
syscallResult = c.builder.CreateCall(target, args, "")
|
syscallResult = c.builder.CreateCall(target, args, "")
|
||||||
default:
|
default:
|
||||||
return llvm.Value{}, c.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+c.GOOS+"/"+c.GOARCH)
|
return llvm.Value{}, c.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+c.GOOS()+"/"+c.GOARCH())
|
||||||
}
|
}
|
||||||
switch c.GOOS {
|
switch c.GOOS() {
|
||||||
case "linux":
|
case "linux":
|
||||||
// Return values: r0, r1 uintptr, err Errno
|
// Return values: r0, r1 uintptr, err Errno
|
||||||
// Pseudocode:
|
// Pseudocode:
|
||||||
|
@ -187,6 +187,6 @@ func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value,
|
||||||
retval = c.builder.CreateInsertValue(retval, errResult, 2, "")
|
retval = c.builder.CreateInsertValue(retval, errResult, 2, "")
|
||||||
return retval, nil
|
return retval, nil
|
||||||
default:
|
default:
|
||||||
return llvm.Value{}, c.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+c.GOOS+"/"+c.GOARCH)
|
return llvm.Value{}, c.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+c.GOOS()+"/"+c.GOARCH())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
60
main.go
60
main.go
|
@ -50,29 +50,13 @@ func (e *multiError) Error() string {
|
||||||
|
|
||||||
// Helper function for Compiler object.
|
// Helper function for Compiler object.
|
||||||
func Compile(pkgName, outpath string, spec *compileopts.TargetSpec, options *compileopts.Options, action func(string) error) error {
|
func Compile(pkgName, outpath string, spec *compileopts.TargetSpec, options *compileopts.Options, action func(string) error) error {
|
||||||
if options.GC == "" && spec.GC != "" {
|
|
||||||
options.GC = spec.GC
|
|
||||||
}
|
|
||||||
|
|
||||||
root := goenv.Get("TINYGOROOT")
|
root := goenv.Get("TINYGOROOT")
|
||||||
|
|
||||||
// Merge and adjust CFlags.
|
|
||||||
cflags := append([]string{}, options.CFlags...)
|
|
||||||
for _, flag := range spec.CFlags {
|
|
||||||
cflags = append(cflags, strings.Replace(flag, "{root}", root, -1))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Merge and adjust LDFlags.
|
|
||||||
ldflags := append([]string{}, options.LDFlags...)
|
|
||||||
for _, flag := range spec.LDFlags {
|
|
||||||
ldflags = append(ldflags, strings.Replace(flag, "{root}", root, -1))
|
|
||||||
}
|
|
||||||
|
|
||||||
goroot := goenv.Get("GOROOT")
|
goroot := goenv.Get("GOROOT")
|
||||||
if goroot == "" {
|
if goroot == "" {
|
||||||
return errors.New("cannot locate $GOROOT, please set it manually")
|
return errors.New("cannot locate $GOROOT, please set it manually")
|
||||||
}
|
}
|
||||||
tags := spec.BuildTags
|
|
||||||
major, minor, err := getGorootVersion(goroot)
|
major, minor, err := getGorootVersion(goroot)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("could not read version from GOROOT (%v): %v", goroot, err)
|
return fmt.Errorf("could not read version from GOROOT (%v): %v", goroot, err)
|
||||||
|
@ -80,36 +64,12 @@ func Compile(pkgName, outpath string, spec *compileopts.TargetSpec, options *com
|
||||||
if major != 1 || (minor != 11 && minor != 12 && minor != 13) {
|
if major != 1 || (minor != 11 && minor != 12 && minor != 13) {
|
||||||
return fmt.Errorf("requires go version 1.11, 1.12, or 1.13, got go%d.%d", major, minor)
|
return fmt.Errorf("requires go version 1.11, 1.12, or 1.13, got go%d.%d", major, minor)
|
||||||
}
|
}
|
||||||
for i := 1; i <= minor; i++ {
|
|
||||||
tags = append(tags, fmt.Sprintf("go1.%d", i))
|
|
||||||
}
|
|
||||||
if extraTags := strings.Fields(options.Tags); len(extraTags) != 0 {
|
|
||||||
tags = append(tags, extraTags...)
|
|
||||||
}
|
|
||||||
scheduler := spec.Scheduler
|
|
||||||
if options.Scheduler != "" {
|
|
||||||
scheduler = options.Scheduler
|
|
||||||
}
|
|
||||||
compilerConfig := &compileopts.Config{
|
compilerConfig := &compileopts.Config{
|
||||||
Triple: spec.Triple,
|
Options: options,
|
||||||
CPU: spec.CPU,
|
Target: spec,
|
||||||
Features: spec.Features,
|
GoMinorVersion: minor,
|
||||||
GOOS: spec.GOOS,
|
ClangHeaders: getClangHeaderPath(root),
|
||||||
GOARCH: spec.GOARCH,
|
TestConfig: options.TestConfig,
|
||||||
GC: options.GC,
|
|
||||||
PanicStrategy: options.PanicStrategy,
|
|
||||||
Scheduler: scheduler,
|
|
||||||
CFlags: cflags,
|
|
||||||
LDFlags: ldflags,
|
|
||||||
ClangHeaders: getClangHeaderPath(root),
|
|
||||||
Debug: options.Debug,
|
|
||||||
DumpSSA: options.DumpSSA,
|
|
||||||
VerifyIR: options.VerifyIR,
|
|
||||||
TINYGOROOT: root,
|
|
||||||
GOROOT: goroot,
|
|
||||||
GOPATH: goenv.Get("GOPATH"),
|
|
||||||
BuildTags: tags,
|
|
||||||
TestConfig: options.TestConfig,
|
|
||||||
}
|
}
|
||||||
c, err := compiler.NewCompiler(pkgName, compilerConfig)
|
c, err := compiler.NewCompiler(pkgName, compilerConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -227,6 +187,12 @@ func Compile(pkgName, outpath string, spec *compileopts.TargetSpec, options *com
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Merge and adjust LDFlags.
|
||||||
|
ldflags := append([]string{}, options.LDFlags...)
|
||||||
|
for _, flag := range spec.LDFlags {
|
||||||
|
ldflags = append(ldflags, strings.Replace(flag, "{root}", root, -1))
|
||||||
|
}
|
||||||
|
|
||||||
// Prepare link command.
|
// Prepare link command.
|
||||||
executable := filepath.Join(dir, "main")
|
executable := filepath.Join(dir, "main")
|
||||||
tmppath := executable // final file
|
tmppath := executable // final file
|
||||||
|
@ -249,7 +215,7 @@ func Compile(pkgName, outpath string, spec *compileopts.TargetSpec, options *com
|
||||||
if names, ok := commands[spec.Compiler]; ok {
|
if names, ok := commands[spec.Compiler]; ok {
|
||||||
cmdNames = names
|
cmdNames = names
|
||||||
}
|
}
|
||||||
err := execCommand(cmdNames, append(cflags, "-c", "-o", outpath, abspath)...)
|
err := execCommand(cmdNames, append(compilerConfig.CFlags(), "-c", "-o", outpath, abspath)...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &commandError{"failed to build", path, err}
|
return &commandError{"failed to build", path, err}
|
||||||
}
|
}
|
||||||
|
@ -265,7 +231,7 @@ func Compile(pkgName, outpath string, spec *compileopts.TargetSpec, options *com
|
||||||
if names, ok := commands[spec.Compiler]; ok {
|
if names, ok := commands[spec.Compiler]; ok {
|
||||||
cmdNames = names
|
cmdNames = names
|
||||||
}
|
}
|
||||||
err := execCommand(cmdNames, append(cflags, "-c", "-o", outpath, path)...)
|
err := execCommand(cmdNames, append(compilerConfig.CFlags(), "-c", "-o", outpath, path)...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &commandError{"failed to build", path, err}
|
return &commandError{"failed to build", path, err}
|
||||||
}
|
}
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче