compiler: move settings to a separate Config struct
Moving settings to a separate config struct has two benefits: - It decouples the compiler a bit from other packages, most importantly the compileopts package. Decoupling is generally a good thing. - Perhaps more importantly, it precisely specifies which settings are used while compiling and affect the resulting LLVM module. This will be necessary for caching the LLVM module. While it would have been possible to cache without this refactor, it would have been very easy to miss a setting and thus let the compiler work with invalid/stale data.
Этот коммит содержится в:
родитель
868933e67c
коммит
9612af466b
14 изменённых файлов: 136 добавлений и 98 удалений
|
@ -45,10 +45,27 @@ type BuildResult struct {
|
||||||
// The error value may be of type *MultiError. Callers will likely want to check
|
// The error value may be of type *MultiError. Callers will likely want to check
|
||||||
// for this case and print such errors individually.
|
// for this case and print such errors individually.
|
||||||
func Build(pkgName, outpath string, config *compileopts.Config, action func(BuildResult) error) error {
|
func Build(pkgName, outpath string, config *compileopts.Config, action func(BuildResult) error) error {
|
||||||
|
compilerConfig := &compiler.Config{
|
||||||
|
Triple: config.Triple(),
|
||||||
|
CPU: config.CPU(),
|
||||||
|
Features: config.Features(),
|
||||||
|
GOOS: config.GOOS(),
|
||||||
|
GOARCH: config.GOARCH(),
|
||||||
|
CodeModel: config.CodeModel(),
|
||||||
|
RelocationModel: config.RelocationModel(),
|
||||||
|
|
||||||
|
Scheduler: config.Scheduler(),
|
||||||
|
FuncImplementation: config.FuncImplementation(),
|
||||||
|
AutomaticStackSize: config.AutomaticStackSize(),
|
||||||
|
DefaultStackSize: config.Target.DefaultStackSize,
|
||||||
|
NeedsStackObjects: config.NeedsStackObjects(),
|
||||||
|
Debug: config.Debug(),
|
||||||
|
}
|
||||||
|
|
||||||
// Load the target machine, which is the LLVM object that contains all
|
// Load the target machine, which is the LLVM object that contains all
|
||||||
// details of a target (alignment restrictions, pointer size, default
|
// details of a target (alignment restrictions, pointer size, default
|
||||||
// address spaces, etc).
|
// address spaces, etc).
|
||||||
machine, err := compiler.NewTargetMachine(config)
|
machine, err := compiler.NewTargetMachine(compilerConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -77,7 +94,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||||
programJob := &compileJob{
|
programJob := &compileJob{
|
||||||
description: "compile Go files",
|
description: "compile Go files",
|
||||||
run: func() (err error) {
|
run: func() (err error) {
|
||||||
mod, err = compileWholeProgram(pkgName, config, lprogram, machine)
|
mod, err = compileWholeProgram(pkgName, config, compilerConfig, lprogram, machine)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -340,9 +357,9 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||||
|
|
||||||
// compileWholeProgram compiles the entire *loader.Program to a LLVM module and
|
// compileWholeProgram compiles the entire *loader.Program to a LLVM module and
|
||||||
// applies most necessary optimizations and transformations.
|
// applies most necessary optimizations and transformations.
|
||||||
func compileWholeProgram(pkgName string, config *compileopts.Config, lprogram *loader.Program, machine llvm.TargetMachine) (llvm.Module, error) {
|
func compileWholeProgram(pkgName string, config *compileopts.Config, compilerConfig *compiler.Config, lprogram *loader.Program, machine llvm.TargetMachine) (llvm.Module, error) {
|
||||||
// Compile AST to IR.
|
// Compile AST to IR.
|
||||||
mod, errs := compiler.CompileProgram(pkgName, lprogram, machine, config)
|
mod, errs := compiler.CompileProgram(lprogram, machine, compilerConfig, config.DumpSSA())
|
||||||
if errs != nil {
|
if errs != nil {
|
||||||
return mod, newMultiError(errs)
|
return mod, newMultiError(errs)
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,29 +21,6 @@ type Config struct {
|
||||||
TestConfig TestConfig
|
TestConfig TestConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
// FuncValueImplementation is an enum for the particular implementations of Go
|
|
||||||
// func values.
|
|
||||||
type FuncValueImplementation int
|
|
||||||
|
|
||||||
// These constants describe the various possible implementations of Go func
|
|
||||||
// values.
|
|
||||||
const (
|
|
||||||
FuncValueNone FuncValueImplementation = iota
|
|
||||||
|
|
||||||
// A func value is implemented as a pair of pointers:
|
|
||||||
// {context, function pointer}
|
|
||||||
// where the context may be a pointer to a heap-allocated struct containing
|
|
||||||
// the free variables, or it may be undef if the function being pointed to
|
|
||||||
// doesn't need a context. The function pointer is a regular function
|
|
||||||
// pointer.
|
|
||||||
FuncValueDoubleword
|
|
||||||
|
|
||||||
// As funcValueDoubleword, but with the function pointer replaced by a
|
|
||||||
// unique ID per function signature. Function values are called by using a
|
|
||||||
// switch statement and choosing which function to call.
|
|
||||||
FuncValueSwitch
|
|
||||||
)
|
|
||||||
|
|
||||||
// Triple returns the LLVM target triple, like armv6m-none-eabi.
|
// Triple returns the LLVM target triple, like armv6m-none-eabi.
|
||||||
func (c *Config) Triple() string {
|
func (c *Config) Triple() string {
|
||||||
return c.Target.Triple
|
return c.Target.Triple
|
||||||
|
@ -143,14 +120,24 @@ func (c *Config) Scheduler() string {
|
||||||
|
|
||||||
// FuncImplementation picks an appropriate func value implementation for the
|
// FuncImplementation picks an appropriate func value implementation for the
|
||||||
// target.
|
// target.
|
||||||
func (c *Config) FuncImplementation() FuncValueImplementation {
|
func (c *Config) FuncImplementation() string {
|
||||||
// Always pick the switch implementation, as it allows the use of blocking
|
|
||||||
// inside a function that is used as a func value.
|
|
||||||
switch c.Scheduler() {
|
switch c.Scheduler() {
|
||||||
case "none", "coroutines":
|
|
||||||
return FuncValueSwitch
|
|
||||||
case "tasks":
|
case "tasks":
|
||||||
return FuncValueDoubleword
|
// A func value is implemented as a pair of pointers:
|
||||||
|
// {context, function pointer}
|
||||||
|
// where the context may be a pointer to a heap-allocated struct
|
||||||
|
// containing the free variables, or it may be undef if the function
|
||||||
|
// being pointed to doesn't need a context. The function pointer is a
|
||||||
|
// regular function pointer.
|
||||||
|
return "doubleword"
|
||||||
|
case "none", "coroutines":
|
||||||
|
// As "doubleword", but with the function pointer replaced by a unique
|
||||||
|
// ID per function signature. Function values are called by using a
|
||||||
|
// switch statement and choosing which function to call.
|
||||||
|
// Pick the switch implementation with the coroutines scheduler, as it
|
||||||
|
// allows the use of blocking inside a function that is used as a func
|
||||||
|
// value.
|
||||||
|
return "switch"
|
||||||
default:
|
default:
|
||||||
panic("unknown scheduler type")
|
panic("unknown scheduler type")
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,6 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/tinygo-org/tinygo/compileopts"
|
|
||||||
"github.com/tinygo-org/tinygo/compiler/llvmutil"
|
"github.com/tinygo-org/tinygo/compiler/llvmutil"
|
||||||
"github.com/tinygo-org/tinygo/loader"
|
"github.com/tinygo-org/tinygo/loader"
|
||||||
"golang.org/x/tools/go/ssa"
|
"golang.org/x/tools/go/ssa"
|
||||||
|
@ -32,11 +31,37 @@ func init() {
|
||||||
// The TinyGo import path.
|
// The TinyGo import path.
|
||||||
const tinygoPath = "github.com/tinygo-org/tinygo"
|
const tinygoPath = "github.com/tinygo-org/tinygo"
|
||||||
|
|
||||||
|
// Config is the configuration for the compiler. Most settings should be copied
|
||||||
|
// directly from compileopts.Config, it recreated here to decouple the compiler
|
||||||
|
// package a bit and because it makes caching easier.
|
||||||
|
//
|
||||||
|
// This struct can be used for caching: if one of the flags here changes the
|
||||||
|
// code must be recompiled.
|
||||||
|
type Config struct {
|
||||||
|
// Target and output information.
|
||||||
|
Triple string
|
||||||
|
CPU string
|
||||||
|
Features []string
|
||||||
|
GOOS string
|
||||||
|
GOARCH string
|
||||||
|
CodeModel string
|
||||||
|
RelocationModel string
|
||||||
|
|
||||||
|
// Various compiler options that determine how code is generated.
|
||||||
|
Scheduler string
|
||||||
|
FuncImplementation string
|
||||||
|
AutomaticStackSize bool
|
||||||
|
DefaultStackSize uint64
|
||||||
|
NeedsStackObjects bool
|
||||||
|
Debug bool // Whether to emit debug information in the LLVM module.
|
||||||
|
}
|
||||||
|
|
||||||
// compilerContext contains function-independent data that should still be
|
// compilerContext contains function-independent data that should still be
|
||||||
// available while compiling every function. It is not strictly read-only, but
|
// available while compiling every function. It is not strictly read-only, but
|
||||||
// must not contain function-dependent data such as an IR builder.
|
// must not contain function-dependent data such as an IR builder.
|
||||||
type compilerContext struct {
|
type compilerContext struct {
|
||||||
*compileopts.Config
|
*Config
|
||||||
|
DumpSSA bool
|
||||||
mod llvm.Module
|
mod llvm.Module
|
||||||
ctx llvm.Context
|
ctx llvm.Context
|
||||||
dibuilder *llvm.DIBuilder
|
dibuilder *llvm.DIBuilder
|
||||||
|
@ -57,9 +82,10 @@ type compilerContext struct {
|
||||||
|
|
||||||
// newCompilerContext returns a new compiler context ready for use, most
|
// newCompilerContext returns a new compiler context ready for use, most
|
||||||
// importantly with a newly created LLVM context and module.
|
// importantly with a newly created LLVM context and module.
|
||||||
func newCompilerContext(moduleName string, machine llvm.TargetMachine, config *compileopts.Config) *compilerContext {
|
func newCompilerContext(moduleName string, machine llvm.TargetMachine, config *Config, dumpSSA bool) *compilerContext {
|
||||||
c := &compilerContext{
|
c := &compilerContext{
|
||||||
Config: config,
|
Config: config,
|
||||||
|
DumpSSA: dumpSSA,
|
||||||
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),
|
||||||
machine: machine,
|
machine: machine,
|
||||||
|
@ -68,9 +94,9 @@ func newCompilerContext(moduleName string, machine llvm.TargetMachine, config *c
|
||||||
|
|
||||||
c.ctx = llvm.NewContext()
|
c.ctx = llvm.NewContext()
|
||||||
c.mod = c.ctx.NewModule(moduleName)
|
c.mod = c.ctx.NewModule(moduleName)
|
||||||
c.mod.SetTarget(config.Triple())
|
c.mod.SetTarget(config.Triple)
|
||||||
c.mod.SetDataLayout(c.targetData.String())
|
c.mod.SetDataLayout(c.targetData.String())
|
||||||
if c.Debug() {
|
if c.Debug {
|
||||||
c.dibuilder = llvm.NewDIBuilder(c.mod)
|
c.dibuilder = llvm.NewDIBuilder(c.mod)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,17 +174,17 @@ type phiNode struct {
|
||||||
// NewTargetMachine returns a new llvm.TargetMachine based on the passed-in
|
// NewTargetMachine returns a new llvm.TargetMachine based on the passed-in
|
||||||
// configuration. It is used by the compiler and is needed for machine code
|
// configuration. It is used by the compiler and is needed for machine code
|
||||||
// emission.
|
// emission.
|
||||||
func NewTargetMachine(config *compileopts.Config) (llvm.TargetMachine, error) {
|
func NewTargetMachine(config *Config) (llvm.TargetMachine, error) {
|
||||||
target, err := llvm.GetTargetFromTriple(config.Triple())
|
target, err := llvm.GetTargetFromTriple(config.Triple)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return llvm.TargetMachine{}, err
|
return llvm.TargetMachine{}, err
|
||||||
}
|
}
|
||||||
features := strings.Join(config.Features(), ",")
|
features := strings.Join(config.Features, ",")
|
||||||
|
|
||||||
var codeModel llvm.CodeModel
|
var codeModel llvm.CodeModel
|
||||||
var relocationModel llvm.RelocMode
|
var relocationModel llvm.RelocMode
|
||||||
|
|
||||||
switch config.CodeModel() {
|
switch config.CodeModel {
|
||||||
case "default":
|
case "default":
|
||||||
codeModel = llvm.CodeModelDefault
|
codeModel = llvm.CodeModelDefault
|
||||||
case "tiny":
|
case "tiny":
|
||||||
|
@ -173,7 +199,7 @@ func NewTargetMachine(config *compileopts.Config) (llvm.TargetMachine, error) {
|
||||||
codeModel = llvm.CodeModelLarge
|
codeModel = llvm.CodeModelLarge
|
||||||
}
|
}
|
||||||
|
|
||||||
switch config.RelocationModel() {
|
switch config.RelocationModel {
|
||||||
case "static":
|
case "static":
|
||||||
relocationModel = llvm.RelocStatic
|
relocationModel = llvm.RelocStatic
|
||||||
case "pic":
|
case "pic":
|
||||||
|
@ -182,7 +208,7 @@ func NewTargetMachine(config *compileopts.Config) (llvm.TargetMachine, error) {
|
||||||
relocationModel = llvm.RelocDynamicNoPic
|
relocationModel = llvm.RelocDynamicNoPic
|
||||||
}
|
}
|
||||||
|
|
||||||
machine := target.CreateTargetMachine(config.Triple(), config.CPU(), features, llvm.CodeGenLevelDefault, relocationModel, codeModel)
|
machine := target.CreateTargetMachine(config.Triple, config.CPU, features, llvm.CodeGenLevelDefault, relocationModel, codeModel)
|
||||||
return machine, nil
|
return machine, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -218,8 +244,8 @@ func Sizes(machine llvm.TargetMachine) types.Sizes {
|
||||||
// CompileProgram compiles the given package path or .go file path. Return an
|
// CompileProgram compiles the given package path or .go file path. Return an
|
||||||
// error when this fails (in any stage). If successful it returns the LLVM
|
// error when this fails (in any stage). If successful it returns the LLVM
|
||||||
// module. If not, one or more errors will be returned.
|
// module. If not, one or more errors will be returned.
|
||||||
func CompileProgram(pkgName string, lprogram *loader.Program, machine llvm.TargetMachine, config *compileopts.Config) (llvm.Module, []error) {
|
func CompileProgram(lprogram *loader.Program, machine llvm.TargetMachine, config *Config, dumpSSA bool) (llvm.Module, []error) {
|
||||||
c := newCompilerContext(pkgName, machine, config)
|
c := newCompilerContext("", machine, config, dumpSSA)
|
||||||
|
|
||||||
c.program = lprogram.LoadSSA()
|
c.program = lprogram.LoadSSA()
|
||||||
c.program.Build()
|
c.program.Build()
|
||||||
|
@ -232,10 +258,10 @@ func CompileProgram(pkgName string, lprogram *loader.Program, machine llvm.Targe
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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: pkgName,
|
File: "<unknown>",
|
||||||
Dir: "",
|
Dir: "",
|
||||||
Producer: "TinyGo",
|
Producer: "TinyGo",
|
||||||
Optimized: true,
|
Optimized: true,
|
||||||
|
@ -271,7 +297,7 @@ func CompileProgram(pkgName string, lprogram *loader.Program, machine llvm.Targe
|
||||||
llvmInitFn := c.getFunction(initFn)
|
llvmInitFn := c.getFunction(initFn)
|
||||||
llvmInitFn.SetLinkage(llvm.InternalLinkage)
|
llvmInitFn.SetLinkage(llvm.InternalLinkage)
|
||||||
llvmInitFn.SetUnnamedAddr(true)
|
llvmInitFn.SetUnnamedAddr(true)
|
||||||
if c.Debug() {
|
if c.Debug {
|
||||||
difunc := c.attachDebugInfo(initFn)
|
difunc := c.attachDebugInfo(initFn)
|
||||||
pos := c.program.Fset.Position(initFn.Pos())
|
pos := c.program.Fset.Position(initFn.Pos())
|
||||||
irbuilder.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), difunc, llvm.Metadata{})
|
irbuilder.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), difunc, llvm.Metadata{})
|
||||||
|
@ -286,7 +312,7 @@ func CompileProgram(pkgName string, lprogram *loader.Program, machine llvm.Targe
|
||||||
irbuilder.CreateRetVoid()
|
irbuilder.CreateRetVoid()
|
||||||
|
|
||||||
// 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
|
||||||
|
@ -308,8 +334,8 @@ func CompileProgram(pkgName string, lprogram *loader.Program, machine llvm.Targe
|
||||||
}
|
}
|
||||||
|
|
||||||
// CompilePackage compiles a single package to a LLVM module.
|
// CompilePackage compiles a single package to a LLVM module.
|
||||||
func CompilePackage(moduleName string, pkg *loader.Package, machine llvm.TargetMachine, config *compileopts.Config) (llvm.Module, []error) {
|
func CompilePackage(moduleName string, pkg *loader.Package, machine llvm.TargetMachine, config *Config, dumpSSA bool) (llvm.Module, []error) {
|
||||||
c := newCompilerContext(moduleName, machine, config)
|
c := newCompilerContext(moduleName, machine, config, dumpSSA)
|
||||||
|
|
||||||
// Build SSA from AST.
|
// Build SSA from AST.
|
||||||
ssaPkg := pkg.LoadSSA()
|
ssaPkg := pkg.LoadSSA()
|
||||||
|
@ -737,7 +763,7 @@ func (c *compilerContext) getDIFile(filename string) llvm.Metadata {
|
||||||
// function must not yet be defined, otherwise this function will create a
|
// function must not yet be defined, otherwise this function will create a
|
||||||
// diagnostic.
|
// diagnostic.
|
||||||
func (b *builder) createFunction() {
|
func (b *builder) createFunction() {
|
||||||
if b.DumpSSA() {
|
if b.DumpSSA {
|
||||||
fmt.Printf("\nfunc %s:\n", b.fn)
|
fmt.Printf("\nfunc %s:\n", b.fn)
|
||||||
}
|
}
|
||||||
if !b.llvmFn.IsDeclaration() {
|
if !b.llvmFn.IsDeclaration() {
|
||||||
|
@ -767,7 +793,7 @@ func (b *builder) createFunction() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add debug info, if needed.
|
// Add debug info, if needed.
|
||||||
if b.Debug() {
|
if b.Debug {
|
||||||
if b.fn.Synthetic == "package initializer" {
|
if b.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*.
|
||||||
|
@ -804,7 +830,7 @@ func (b *builder) createFunction() {
|
||||||
b.locals[param] = b.collapseFormalParam(llvmType, fields)
|
b.locals[param] = b.collapseFormalParam(llvmType, fields)
|
||||||
|
|
||||||
// Add debug information to this parameter (if available)
|
// Add debug information to this parameter (if available)
|
||||||
if b.Debug() && b.fn.Syntax() != nil {
|
if b.Debug && b.fn.Syntax() != nil {
|
||||||
dbgParam := b.getLocalVariable(param.Object().(*types.Var))
|
dbgParam := b.getLocalVariable(param.Object().(*types.Var))
|
||||||
loc := b.GetCurrentDebugLocation()
|
loc := b.GetCurrentDebugLocation()
|
||||||
if len(fields) == 1 {
|
if len(fields) == 1 {
|
||||||
|
@ -857,14 +883,14 @@ func (b *builder) createFunction() {
|
||||||
|
|
||||||
// Fill blocks with instructions.
|
// Fill blocks with instructions.
|
||||||
for _, block := range b.fn.DomPreorder() {
|
for _, block := range b.fn.DomPreorder() {
|
||||||
if b.DumpSSA() {
|
if b.DumpSSA {
|
||||||
fmt.Printf("%d: %s:\n", block.Index, block.Comment)
|
fmt.Printf("%d: %s:\n", block.Index, block.Comment)
|
||||||
}
|
}
|
||||||
b.SetInsertPointAtEnd(b.blockEntries[block])
|
b.SetInsertPointAtEnd(b.blockEntries[block])
|
||||||
b.currentBlock = block
|
b.currentBlock = block
|
||||||
for _, instr := range block.Instrs {
|
for _, instr := range block.Instrs {
|
||||||
if instr, ok := instr.(*ssa.DebugRef); ok {
|
if instr, ok := instr.(*ssa.DebugRef); ok {
|
||||||
if !b.Debug() {
|
if !b.Debug {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
object := instr.Object()
|
object := instr.Object()
|
||||||
|
@ -887,7 +913,7 @@ func (b *builder) createFunction() {
|
||||||
}, b.GetInsertBlock())
|
}, b.GetInsertBlock())
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if b.DumpSSA() {
|
if b.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 {
|
||||||
|
@ -911,7 +937,7 @@ func (b *builder) createFunction() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if b.NeedsStackObjects() {
|
if b.NeedsStackObjects {
|
||||||
// Track phi nodes.
|
// Track phi nodes.
|
||||||
for _, phi := range b.phis {
|
for _, phi := range b.phis {
|
||||||
insertPoint := llvm.NextInstruction(phi.llvm)
|
insertPoint := llvm.NextInstruction(phi.llvm)
|
||||||
|
@ -927,7 +953,7 @@ func (b *builder) createFunction() {
|
||||||
// createInstruction builds the LLVM IR equivalent instructions for the
|
// createInstruction builds the LLVM IR equivalent instructions for the
|
||||||
// particular Go SSA instruction.
|
// particular Go SSA instruction.
|
||||||
func (b *builder) createInstruction(instr ssa.Instruction) {
|
func (b *builder) createInstruction(instr ssa.Instruction) {
|
||||||
if b.Debug() {
|
if b.Debug {
|
||||||
pos := b.program.Fset.Position(instr.Pos())
|
pos := b.program.Fset.Position(instr.Pos())
|
||||||
b.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), b.difunc, llvm.Metadata{})
|
b.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), b.difunc, llvm.Metadata{})
|
||||||
}
|
}
|
||||||
|
@ -945,7 +971,7 @@ func (b *builder) createInstruction(instr ssa.Instruction) {
|
||||||
b.locals[instr] = llvm.Undef(b.getLLVMType(instr.Type()))
|
b.locals[instr] = llvm.Undef(b.getLLVMType(instr.Type()))
|
||||||
} else {
|
} else {
|
||||||
b.locals[instr] = value
|
b.locals[instr] = value
|
||||||
if len(*instr.Referrers()) != 0 && b.NeedsStackObjects() {
|
if len(*instr.Referrers()) != 0 && b.NeedsStackObjects {
|
||||||
b.trackExpr(instr, value)
|
b.trackExpr(instr, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -987,7 +1013,7 @@ func (b *builder) createInstruction(instr ssa.Instruction) {
|
||||||
// * The function pointer (for tasks).
|
// * The function pointer (for tasks).
|
||||||
funcPtr, context := b.decodeFuncValue(b.getValue(instr.Call.Value), instr.Call.Value.Type().Underlying().(*types.Signature))
|
funcPtr, context := b.decodeFuncValue(b.getValue(instr.Call.Value), instr.Call.Value.Type().Underlying().(*types.Signature))
|
||||||
params = append(params, context) // context parameter
|
params = append(params, context) // context parameter
|
||||||
switch b.Scheduler() {
|
switch b.Scheduler {
|
||||||
case "none", "coroutines":
|
case "none", "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":
|
||||||
|
|
|
@ -28,7 +28,17 @@ func TestCompiler(t *testing.T) {
|
||||||
Options: &compileopts.Options{},
|
Options: &compileopts.Options{},
|
||||||
Target: target,
|
Target: target,
|
||||||
}
|
}
|
||||||
machine, err := NewTargetMachine(config)
|
compilerConfig := &Config{
|
||||||
|
Triple: config.Triple(),
|
||||||
|
GOOS: config.GOOS(),
|
||||||
|
GOARCH: config.GOARCH(),
|
||||||
|
CodeModel: config.CodeModel(),
|
||||||
|
RelocationModel: config.RelocationModel(),
|
||||||
|
Scheduler: config.Scheduler(),
|
||||||
|
FuncImplementation: config.FuncImplementation(),
|
||||||
|
AutomaticStackSize: config.AutomaticStackSize(),
|
||||||
|
}
|
||||||
|
machine, err := NewTargetMachine(compilerConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("failed to create target machine:", err)
|
t.Fatal("failed to create target machine:", err)
|
||||||
}
|
}
|
||||||
|
@ -56,7 +66,7 @@ func TestCompiler(t *testing.T) {
|
||||||
|
|
||||||
// Compile AST to IR.
|
// Compile AST to IR.
|
||||||
pkg := lprogram.MainPkg()
|
pkg := lprogram.MainPkg()
|
||||||
mod, errs := CompilePackage(testCase, pkg, machine, config)
|
mod, errs := CompilePackage(testCase, pkg, machine, compilerConfig, false)
|
||||||
if errs != nil {
|
if errs != nil {
|
||||||
for _, err := range errs {
|
for _, err := range errs {
|
||||||
t.Log("error:", err)
|
t.Log("error:", err)
|
||||||
|
|
|
@ -220,7 +220,7 @@ func (b *builder) createDefer(instr *ssa.Defer) {
|
||||||
allocCall := b.createRuntimeCall("alloc", []llvm.Value{sizeValue}, "defer.alloc.call")
|
allocCall := b.createRuntimeCall("alloc", []llvm.Value{sizeValue}, "defer.alloc.call")
|
||||||
alloca = b.CreateBitCast(allocCall, llvm.PointerType(deferFrameType, 0), "defer.alloc")
|
alloca = b.CreateBitCast(allocCall, llvm.PointerType(deferFrameType, 0), "defer.alloc")
|
||||||
}
|
}
|
||||||
if b.NeedsStackObjects() {
|
if b.NeedsStackObjects {
|
||||||
b.trackPointer(alloca)
|
b.trackPointer(alloca)
|
||||||
}
|
}
|
||||||
b.CreateStore(deferFrame, alloca)
|
b.CreateStore(deferFrame, alloca)
|
||||||
|
|
|
@ -6,7 +6,6 @@ package compiler
|
||||||
import (
|
import (
|
||||||
"go/types"
|
"go/types"
|
||||||
|
|
||||||
"github.com/tinygo-org/tinygo/compileopts"
|
|
||||||
"golang.org/x/tools/go/ssa"
|
"golang.org/x/tools/go/ssa"
|
||||||
"tinygo.org/x/go-llvm"
|
"tinygo.org/x/go-llvm"
|
||||||
)
|
)
|
||||||
|
@ -21,11 +20,11 @@ func (b *builder) createFuncValue(funcPtr, context llvm.Value, sig *types.Signat
|
||||||
// context.
|
// context.
|
||||||
func (c *compilerContext) createFuncValue(builder llvm.Builder, funcPtr, context llvm.Value, sig *types.Signature) llvm.Value {
|
func (c *compilerContext) createFuncValue(builder llvm.Builder, funcPtr, context llvm.Value, sig *types.Signature) llvm.Value {
|
||||||
var funcValueScalar llvm.Value
|
var funcValueScalar llvm.Value
|
||||||
switch c.FuncImplementation() {
|
switch c.FuncImplementation {
|
||||||
case compileopts.FuncValueDoubleword:
|
case "doubleword":
|
||||||
// Closure is: {context, function pointer}
|
// Closure is: {context, function pointer}
|
||||||
funcValueScalar = funcPtr
|
funcValueScalar = funcPtr
|
||||||
case compileopts.FuncValueSwitch:
|
case "switch":
|
||||||
sigGlobal := c.getTypeCode(sig)
|
sigGlobal := c.getTypeCode(sig)
|
||||||
funcValueWithSignatureGlobalName := funcPtr.Name() + "$withSignature"
|
funcValueWithSignatureGlobalName := funcPtr.Name() + "$withSignature"
|
||||||
funcValueWithSignatureGlobal := c.mod.NamedGlobal(funcValueWithSignatureGlobalName)
|
funcValueWithSignatureGlobal := c.mod.NamedGlobal(funcValueWithSignatureGlobalName)
|
||||||
|
@ -67,10 +66,10 @@ func (b *builder) extractFuncContext(funcValue llvm.Value) llvm.Value {
|
||||||
// value. This may be an expensive operation.
|
// value. This may be an expensive operation.
|
||||||
func (b *builder) decodeFuncValue(funcValue llvm.Value, sig *types.Signature) (funcPtr, context llvm.Value) {
|
func (b *builder) decodeFuncValue(funcValue llvm.Value, sig *types.Signature) (funcPtr, context llvm.Value) {
|
||||||
context = b.CreateExtractValue(funcValue, 0, "")
|
context = b.CreateExtractValue(funcValue, 0, "")
|
||||||
switch b.FuncImplementation() {
|
switch b.FuncImplementation {
|
||||||
case compileopts.FuncValueDoubleword:
|
case "doubleword":
|
||||||
funcPtr = b.CreateExtractValue(funcValue, 1, "")
|
funcPtr = b.CreateExtractValue(funcValue, 1, "")
|
||||||
case compileopts.FuncValueSwitch:
|
case "switch":
|
||||||
llvmSig := b.getRawFuncType(sig)
|
llvmSig := b.getRawFuncType(sig)
|
||||||
sigGlobal := b.getTypeCode(sig)
|
sigGlobal := b.getTypeCode(sig)
|
||||||
funcPtr = b.createRuntimeCall("getFuncPtr", []llvm.Value{funcValue, sigGlobal}, "")
|
funcPtr = b.createRuntimeCall("getFuncPtr", []llvm.Value{funcValue, sigGlobal}, "")
|
||||||
|
@ -83,11 +82,11 @@ func (b *builder) decodeFuncValue(funcValue llvm.Value, sig *types.Signature) (f
|
||||||
|
|
||||||
// getFuncType returns the type of a func value given a signature.
|
// getFuncType returns the type of a func value given a signature.
|
||||||
func (c *compilerContext) getFuncType(typ *types.Signature) llvm.Type {
|
func (c *compilerContext) getFuncType(typ *types.Signature) llvm.Type {
|
||||||
switch c.FuncImplementation() {
|
switch c.FuncImplementation {
|
||||||
case compileopts.FuncValueDoubleword:
|
case "doubleword":
|
||||||
rawPtr := c.getRawFuncType(typ)
|
rawPtr := c.getRawFuncType(typ)
|
||||||
return c.ctx.StructType([]llvm.Type{c.i8ptrType, rawPtr}, false)
|
return c.ctx.StructType([]llvm.Type{c.i8ptrType, rawPtr}, false)
|
||||||
case compileopts.FuncValueSwitch:
|
case "switch":
|
||||||
return c.getLLVMRuntimeType("funcValue")
|
return c.getLLVMRuntimeType("funcValue")
|
||||||
default:
|
default:
|
||||||
panic("unimplemented func value variant")
|
panic("unimplemented func value variant")
|
||||||
|
|
|
@ -21,10 +21,10 @@ import (
|
||||||
func (b *builder) createGoInstruction(funcPtr llvm.Value, params []llvm.Value, prefix string, pos token.Pos) llvm.Value {
|
func (b *builder) createGoInstruction(funcPtr llvm.Value, params []llvm.Value, prefix string, pos token.Pos) llvm.Value {
|
||||||
paramBundle := b.emitPointerPack(params)
|
paramBundle := b.emitPointerPack(params)
|
||||||
var callee, stackSize llvm.Value
|
var callee, stackSize llvm.Value
|
||||||
switch b.Scheduler() {
|
switch b.Scheduler {
|
||||||
case "none", "tasks":
|
case "none", "tasks":
|
||||||
callee = b.createGoroutineStartWrapper(funcPtr, prefix, pos)
|
callee = b.createGoroutineStartWrapper(funcPtr, prefix, pos)
|
||||||
if b.AutomaticStackSize() {
|
if b.AutomaticStackSize {
|
||||||
// The stack size is not known until after linking. Call a dummy
|
// The stack size is not known until after linking. Call a dummy
|
||||||
// function that will be replaced with a load from a special ELF
|
// function that will be replaced with a load from a special ELF
|
||||||
// section that contains the stack size (and is modified after
|
// section that contains the stack size (and is modified after
|
||||||
|
@ -34,7 +34,7 @@ func (b *builder) createGoInstruction(funcPtr llvm.Value, params []llvm.Value, p
|
||||||
} else {
|
} else {
|
||||||
// The stack size is fixed at compile time. By emitting it here as a
|
// The stack size is fixed at compile time. By emitting it here as a
|
||||||
// constant, it can be optimized.
|
// constant, it can be optimized.
|
||||||
stackSize = llvm.ConstInt(b.uintptrType, b.Target.DefaultStackSize, false)
|
stackSize = llvm.ConstInt(b.uintptrType, b.DefaultStackSize, false)
|
||||||
}
|
}
|
||||||
case "coroutines":
|
case "coroutines":
|
||||||
callee = b.CreatePtrToInt(funcPtr, b.uintptrType, "")
|
callee = b.CreatePtrToInt(funcPtr, b.uintptrType, "")
|
||||||
|
@ -90,7 +90,7 @@ func (c *compilerContext) createGoroutineStartWrapper(fn llvm.Value, prefix stri
|
||||||
entry := c.ctx.AddBasicBlock(wrapper, "entry")
|
entry := c.ctx.AddBasicBlock(wrapper, "entry")
|
||||||
builder.SetInsertPointAtEnd(entry)
|
builder.SetInsertPointAtEnd(entry)
|
||||||
|
|
||||||
if c.Debug() {
|
if c.Debug {
|
||||||
pos := c.program.Fset.Position(pos)
|
pos := c.program.Fset.Position(pos)
|
||||||
diFuncType := c.dibuilder.CreateSubroutineType(llvm.DISubroutineType{
|
diFuncType := c.dibuilder.CreateSubroutineType(llvm.DISubroutineType{
|
||||||
File: c.getDIFile(pos.Filename),
|
File: c.getDIFile(pos.Filename),
|
||||||
|
@ -147,7 +147,7 @@ func (c *compilerContext) createGoroutineStartWrapper(fn llvm.Value, prefix stri
|
||||||
entry := c.ctx.AddBasicBlock(wrapper, "entry")
|
entry := c.ctx.AddBasicBlock(wrapper, "entry")
|
||||||
builder.SetInsertPointAtEnd(entry)
|
builder.SetInsertPointAtEnd(entry)
|
||||||
|
|
||||||
if c.Debug() {
|
if c.Debug {
|
||||||
pos := c.program.Fset.Position(pos)
|
pos := c.program.Fset.Position(pos)
|
||||||
diFuncType := c.dibuilder.CreateSubroutineType(llvm.DISubroutineType{
|
diFuncType := c.dibuilder.CreateSubroutineType(llvm.DISubroutineType{
|
||||||
File: c.getDIFile(pos.Filename),
|
File: c.getDIFile(pos.Filename),
|
||||||
|
|
|
@ -478,7 +478,7 @@ func (c *compilerContext) getInterfaceInvokeWrapper(fn *ssa.Function, llvmFn llv
|
||||||
defer b.Builder.Dispose()
|
defer b.Builder.Dispose()
|
||||||
|
|
||||||
// add debug info if needed
|
// add debug info if needed
|
||||||
if c.Debug() {
|
if c.Debug {
|
||||||
pos := c.program.Fset.Position(fn.Pos())
|
pos := c.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)
|
||||||
b.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), difunc, llvm.Metadata{})
|
b.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), difunc, llvm.Metadata{})
|
||||||
|
|
|
@ -55,7 +55,7 @@ func (b *builder) createInterruptGlobal(instr *ssa.CallCommon) (llvm.Value, erro
|
||||||
global.SetInitializer(initializer)
|
global.SetInitializer(initializer)
|
||||||
|
|
||||||
// Add debug info to the interrupt global.
|
// Add debug info to the interrupt global.
|
||||||
if b.Debug() {
|
if b.Debug {
|
||||||
pos := b.program.Fset.Position(instr.Pos())
|
pos := b.program.Fset.Position(instr.Pos())
|
||||||
diglobal := b.dibuilder.CreateGlobalVariableExpression(b.getDIFile(pos.Filename), llvm.DIGlobalVariableExpression{
|
diglobal := b.dibuilder.CreateGlobalVariableExpression(b.getDIFile(pos.Filename), llvm.DIGlobalVariableExpression{
|
||||||
Name: "interrupt" + strconv.FormatInt(id.Int64(), 10),
|
Name: "interrupt" + strconv.FormatInt(id.Int64(), 10),
|
||||||
|
@ -79,7 +79,7 @@ func (b *builder) createInterruptGlobal(instr *ssa.CallCommon) (llvm.Value, erro
|
||||||
// PLIC where each interrupt must be enabled using the interrupt number, and
|
// PLIC where each interrupt must be enabled using the interrupt number, and
|
||||||
// thus keeps the Interrupt object alive.
|
// thus keeps the Interrupt object alive.
|
||||||
// This call is removed during interrupt lowering.
|
// This call is removed during interrupt lowering.
|
||||||
if strings.HasPrefix(b.Triple(), "avr") {
|
if strings.HasPrefix(b.Triple, "avr") {
|
||||||
useFn := b.mod.NamedFunction("runtime/interrupt.use")
|
useFn := b.mod.NamedFunction("runtime/interrupt.use")
|
||||||
if useFn.IsNil() {
|
if useFn.IsNil() {
|
||||||
useFnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{interrupt.Type()}, false)
|
useFnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{interrupt.Type()}, false)
|
||||||
|
|
|
@ -44,7 +44,7 @@ func (b *builder) emitLifetimeEnd(ptr, size llvm.Value) {
|
||||||
// bitcasts, or else allocates a value on the heap if it cannot be packed in the
|
// bitcasts, or else allocates a value on the heap if it cannot be packed in the
|
||||||
// pointer value directly. It returns the pointer with the packed data.
|
// pointer value directly. It returns the pointer with the packed data.
|
||||||
func (b *builder) emitPointerPack(values []llvm.Value) llvm.Value {
|
func (b *builder) emitPointerPack(values []llvm.Value) llvm.Value {
|
||||||
return llvmutil.EmitPointerPack(b.Builder, b.mod, b.Config, values)
|
return llvmutil.EmitPointerPack(b.Builder, b.mod, b.NeedsStackObjects, values)
|
||||||
}
|
}
|
||||||
|
|
||||||
// emitPointerUnpack extracts a list of values packed using emitPointerPack.
|
// emitPointerUnpack extracts a list of values packed using emitPointerPack.
|
||||||
|
|
|
@ -5,7 +5,6 @@ package llvmutil
|
||||||
// itself if possible and legal.
|
// itself if possible and legal.
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/tinygo-org/tinygo/compileopts"
|
|
||||||
"tinygo.org/x/go-llvm"
|
"tinygo.org/x/go-llvm"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -13,7 +12,7 @@ import (
|
||||||
// bitcasts, or else allocates a value on the heap if it cannot be packed in the
|
// bitcasts, or else allocates a value on the heap if it cannot be packed in the
|
||||||
// pointer value directly. It returns the pointer with the packed data.
|
// pointer value directly. It returns the pointer with the packed data.
|
||||||
// If the values are all constants, they are be stored in a constant global and deduplicated.
|
// If the values are all constants, they are be stored in a constant global and deduplicated.
|
||||||
func EmitPointerPack(builder llvm.Builder, mod llvm.Module, config *compileopts.Config, values []llvm.Value) llvm.Value {
|
func EmitPointerPack(builder llvm.Builder, mod llvm.Module, needsStackObjects bool, values []llvm.Value) llvm.Value {
|
||||||
ctx := mod.Context()
|
ctx := mod.Context()
|
||||||
targetData := llvm.NewTargetData(mod.DataLayout())
|
targetData := llvm.NewTargetData(mod.DataLayout())
|
||||||
i8ptrType := llvm.PointerType(mod.Context().Int8Type(), 0)
|
i8ptrType := llvm.PointerType(mod.Context().Int8Type(), 0)
|
||||||
|
@ -101,7 +100,7 @@ func EmitPointerPack(builder llvm.Builder, mod llvm.Module, config *compileopts.
|
||||||
llvm.Undef(i8ptrType), // unused context parameter
|
llvm.Undef(i8ptrType), // unused context parameter
|
||||||
llvm.ConstPointerNull(i8ptrType), // coroutine handle
|
llvm.ConstPointerNull(i8ptrType), // coroutine handle
|
||||||
}, "")
|
}, "")
|
||||||
if config.NeedsStackObjects() {
|
if needsStackObjects {
|
||||||
trackPointer := mod.NamedFunction("runtime.trackPointer")
|
trackPointer := mod.NamedFunction("runtime.trackPointer")
|
||||||
builder.CreateCall(trackPointer, []llvm.Value{
|
builder.CreateCall(trackPointer, []llvm.Value{
|
||||||
packedHeapAlloc,
|
packedHeapAlloc,
|
||||||
|
|
|
@ -295,7 +295,7 @@ func (c *compilerContext) getGlobal(g *ssa.Global) llvm.Value {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.Debug() && !info.extern {
|
if c.Debug && !info.extern {
|
||||||
// Add debug info.
|
// Add debug info.
|
||||||
// TODO: this should be done for every global in the program, not just
|
// TODO: this should be done for every global in the program, not just
|
||||||
// the ones that are referenced from some code.
|
// the ones that are referenced from some code.
|
||||||
|
|
|
@ -16,8 +16,8 @@ func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
||||||
num := b.getValue(call.Args[0])
|
num := b.getValue(call.Args[0])
|
||||||
var syscallResult llvm.Value
|
var syscallResult llvm.Value
|
||||||
switch {
|
switch {
|
||||||
case b.GOARCH() == "amd64":
|
case b.GOARCH == "amd64":
|
||||||
if b.GOOS() == "darwin" {
|
if b.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 (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
||||||
fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
|
fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
|
||||||
target := llvm.InlineAsm(fnType, "syscall", constraints, true, false, llvm.InlineAsmDialectIntel)
|
target := llvm.InlineAsm(fnType, "syscall", constraints, true, false, llvm.InlineAsmDialectIntel)
|
||||||
syscallResult = b.CreateCall(target, args, "")
|
syscallResult = b.CreateCall(target, args, "")
|
||||||
case b.GOARCH() == "386" && b.GOOS() == "linux":
|
case b.GOARCH == "386" && b.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 (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
||||||
fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
|
fnType := llvm.FunctionType(b.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 = b.CreateCall(target, args, "")
|
syscallResult = b.CreateCall(target, args, "")
|
||||||
case b.GOARCH() == "arm" && b.GOOS() == "linux":
|
case b.GOARCH == "arm" && b.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 (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
||||||
fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
|
fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
|
||||||
target := llvm.InlineAsm(fnType, "svc #0", constraints, true, false, 0)
|
target := llvm.InlineAsm(fnType, "svc #0", constraints, true, false, 0)
|
||||||
syscallResult = b.CreateCall(target, args, "")
|
syscallResult = b.CreateCall(target, args, "")
|
||||||
case b.GOARCH() == "arm64" && b.GOOS() == "linux":
|
case b.GOARCH == "arm64" && b.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 (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
||||||
target := llvm.InlineAsm(fnType, "svc #0", constraints, true, false, 0)
|
target := llvm.InlineAsm(fnType, "svc #0", constraints, true, false, 0)
|
||||||
syscallResult = b.CreateCall(target, args, "")
|
syscallResult = b.CreateCall(target, args, "")
|
||||||
default:
|
default:
|
||||||
return llvm.Value{}, b.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+b.GOOS()+"/"+b.GOARCH())
|
return llvm.Value{}, b.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+b.GOOS+"/"+b.GOARCH)
|
||||||
}
|
}
|
||||||
switch b.GOOS() {
|
switch b.GOOS {
|
||||||
case "linux", "freebsd":
|
case "linux", "freebsd":
|
||||||
// Return values: r0, r1 uintptr, err Errno
|
// Return values: r0, r1 uintptr, err Errno
|
||||||
// Pseudocode:
|
// Pseudocode:
|
||||||
|
@ -187,6 +187,6 @@ func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
||||||
retval = b.CreateInsertValue(retval, errResult, 2, "")
|
retval = b.CreateInsertValue(retval, errResult, 2, "")
|
||||||
return retval, nil
|
return retval, nil
|
||||||
default:
|
default:
|
||||||
return llvm.Value{}, b.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+b.GOOS()+"/"+b.GOARCH())
|
return llvm.Value{}, b.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+b.GOOS+"/"+b.GOARCH)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,7 +85,7 @@ func Optimize(mod llvm.Module, config *compileopts.Config, optLevel, sizeLevel i
|
||||||
return errs
|
return errs
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.FuncImplementation() == compileopts.FuncValueSwitch {
|
if config.FuncImplementation() == "switch" {
|
||||||
LowerFuncValues(mod)
|
LowerFuncValues(mod)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,7 +104,7 @@ func Optimize(mod llvm.Module, config *compileopts.Config, optLevel, sizeLevel i
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []error{err}
|
return []error{err}
|
||||||
}
|
}
|
||||||
if config.FuncImplementation() == compileopts.FuncValueSwitch {
|
if config.FuncImplementation() == "switch" {
|
||||||
LowerFuncValues(mod)
|
LowerFuncValues(mod)
|
||||||
}
|
}
|
||||||
errs := LowerInterrupts(mod)
|
errs := LowerInterrupts(mod)
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче