diff --git a/builder/build.go b/builder/build.go index fa2834be..6df86956 100644 --- a/builder/build.go +++ b/builder/build.go @@ -45,10 +45,27 @@ type BuildResult struct { // The error value may be of type *MultiError. Callers will likely want to check // for this case and print such errors individually. 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 // details of a target (alignment restrictions, pointer size, default // address spaces, etc). - machine, err := compiler.NewTargetMachine(config) + machine, err := compiler.NewTargetMachine(compilerConfig) if err != nil { return err } @@ -77,7 +94,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil programJob := &compileJob{ description: "compile Go files", run: func() (err error) { - mod, err = compileWholeProgram(pkgName, config, lprogram, machine) + mod, err = compileWholeProgram(pkgName, config, compilerConfig, lprogram, machine) if err != nil { 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 // 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. - mod, errs := compiler.CompileProgram(pkgName, lprogram, machine, config) + mod, errs := compiler.CompileProgram(lprogram, machine, compilerConfig, config.DumpSSA()) if errs != nil { return mod, newMultiError(errs) } diff --git a/compileopts/config.go b/compileopts/config.go index e642b1f8..279f6a2a 100644 --- a/compileopts/config.go +++ b/compileopts/config.go @@ -21,29 +21,6 @@ type Config struct { 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. func (c *Config) Triple() string { return c.Target.Triple @@ -143,14 +120,24 @@ func (c *Config) Scheduler() string { // FuncImplementation picks an appropriate func value implementation for the // target. -func (c *Config) FuncImplementation() FuncValueImplementation { - // Always pick the switch implementation, as it allows the use of blocking - // inside a function that is used as a func value. +func (c *Config) FuncImplementation() string { switch c.Scheduler() { - case "none", "coroutines": - return FuncValueSwitch 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: panic("unknown scheduler type") } diff --git a/compiler/compiler.go b/compiler/compiler.go index 8bf9fcac..a7887f36 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -14,7 +14,6 @@ import ( "strconv" "strings" - "github.com/tinygo-org/tinygo/compileopts" "github.com/tinygo-org/tinygo/compiler/llvmutil" "github.com/tinygo-org/tinygo/loader" "golang.org/x/tools/go/ssa" @@ -32,11 +31,37 @@ func init() { // The TinyGo import path. 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 // available while compiling every function. It is not strictly read-only, but // must not contain function-dependent data such as an IR builder. type compilerContext struct { - *compileopts.Config + *Config + DumpSSA bool mod llvm.Module ctx llvm.Context dibuilder *llvm.DIBuilder @@ -57,9 +82,10 @@ type compilerContext struct { // newCompilerContext returns a new compiler context ready for use, most // 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{ Config: config, + DumpSSA: dumpSSA, difiles: make(map[string]llvm.Metadata), ditypes: make(map[types.Type]llvm.Metadata), machine: machine, @@ -68,9 +94,9 @@ func newCompilerContext(moduleName string, machine llvm.TargetMachine, config *c c.ctx = llvm.NewContext() c.mod = c.ctx.NewModule(moduleName) - c.mod.SetTarget(config.Triple()) + c.mod.SetTarget(config.Triple) c.mod.SetDataLayout(c.targetData.String()) - if c.Debug() { + if c.Debug { c.dibuilder = llvm.NewDIBuilder(c.mod) } @@ -148,17 +174,17 @@ type phiNode struct { // NewTargetMachine returns a new llvm.TargetMachine based on the passed-in // configuration. It is used by the compiler and is needed for machine code // emission. -func NewTargetMachine(config *compileopts.Config) (llvm.TargetMachine, error) { - target, err := llvm.GetTargetFromTriple(config.Triple()) +func NewTargetMachine(config *Config) (llvm.TargetMachine, error) { + target, err := llvm.GetTargetFromTriple(config.Triple) if err != nil { return llvm.TargetMachine{}, err } - features := strings.Join(config.Features(), ",") + features := strings.Join(config.Features, ",") var codeModel llvm.CodeModel var relocationModel llvm.RelocMode - switch config.CodeModel() { + switch config.CodeModel { case "default": codeModel = llvm.CodeModelDefault case "tiny": @@ -173,7 +199,7 @@ func NewTargetMachine(config *compileopts.Config) (llvm.TargetMachine, error) { codeModel = llvm.CodeModelLarge } - switch config.RelocationModel() { + switch config.RelocationModel { case "static": relocationModel = llvm.RelocStatic case "pic": @@ -182,7 +208,7 @@ func NewTargetMachine(config *compileopts.Config) (llvm.TargetMachine, error) { 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 } @@ -218,8 +244,8 @@ func Sizes(machine llvm.TargetMachine) types.Sizes { // 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 // 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) { - c := newCompilerContext(pkgName, machine, config) +func CompileProgram(lprogram *loader.Program, machine llvm.TargetMachine, config *Config, dumpSSA bool) (llvm.Module, []error) { + c := newCompilerContext("", machine, config, dumpSSA) c.program = lprogram.LoadSSA() c.program.Build() @@ -232,10 +258,10 @@ func CompileProgram(pkgName string, lprogram *loader.Program, machine llvm.Targe } // Initialize debug information. - if c.Debug() { + if c.Debug { c.cu = c.dibuilder.CreateCompileUnit(llvm.DICompileUnit{ Language: 0xb, // DW_LANG_C99 (0xc, off-by-one?) - File: pkgName, + File: "", Dir: "", Producer: "TinyGo", Optimized: true, @@ -271,7 +297,7 @@ func CompileProgram(pkgName string, lprogram *loader.Program, machine llvm.Targe llvmInitFn := c.getFunction(initFn) llvmInitFn.SetLinkage(llvm.InternalLinkage) llvmInitFn.SetUnnamedAddr(true) - if c.Debug() { + if c.Debug { difunc := c.attachDebugInfo(initFn) pos := c.program.Fset.Position(initFn.Pos()) 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() // see: https://reviews.llvm.org/D18355 - if c.Debug() { + if c.Debug { c.mod.AddNamedMetadataOperand("llvm.module.flags", c.ctx.MDNode([]llvm.Metadata{ 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. -func CompilePackage(moduleName string, pkg *loader.Package, machine llvm.TargetMachine, config *compileopts.Config) (llvm.Module, []error) { - c := newCompilerContext(moduleName, machine, config) +func CompilePackage(moduleName string, pkg *loader.Package, machine llvm.TargetMachine, config *Config, dumpSSA bool) (llvm.Module, []error) { + c := newCompilerContext(moduleName, machine, config, dumpSSA) // Build SSA from AST. 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 // diagnostic. func (b *builder) createFunction() { - if b.DumpSSA() { + if b.DumpSSA { fmt.Printf("\nfunc %s:\n", b.fn) } if !b.llvmFn.IsDeclaration() { @@ -767,7 +793,7 @@ func (b *builder) createFunction() { } // Add debug info, if needed. - if b.Debug() { + if b.Debug { if b.fn.Synthetic == "package initializer" { // Package initializers have no debug info. Create some fake debug // info to at least have *something*. @@ -804,7 +830,7 @@ func (b *builder) createFunction() { b.locals[param] = b.collapseFormalParam(llvmType, fields) // 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)) loc := b.GetCurrentDebugLocation() if len(fields) == 1 { @@ -857,14 +883,14 @@ func (b *builder) createFunction() { // Fill blocks with instructions. for _, block := range b.fn.DomPreorder() { - if b.DumpSSA() { + if b.DumpSSA { fmt.Printf("%d: %s:\n", block.Index, block.Comment) } b.SetInsertPointAtEnd(b.blockEntries[block]) b.currentBlock = block for _, instr := range block.Instrs { if instr, ok := instr.(*ssa.DebugRef); ok { - if !b.Debug() { + if !b.Debug { continue } object := instr.Object() @@ -887,7 +913,7 @@ func (b *builder) createFunction() { }, b.GetInsertBlock()) continue } - if b.DumpSSA() { + if b.DumpSSA { if val, ok := instr.(ssa.Value); ok && val.Name() != "" { fmt.Printf("\t%s = %s\n", val.Name(), val.String()) } else { @@ -911,7 +937,7 @@ func (b *builder) createFunction() { } } - if b.NeedsStackObjects() { + if b.NeedsStackObjects { // Track phi nodes. for _, phi := range b.phis { insertPoint := llvm.NextInstruction(phi.llvm) @@ -927,7 +953,7 @@ func (b *builder) createFunction() { // createInstruction builds the LLVM IR equivalent instructions for the // particular Go SSA instruction. func (b *builder) createInstruction(instr ssa.Instruction) { - if b.Debug() { + if b.Debug { pos := b.program.Fset.Position(instr.Pos()) 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())) } else { b.locals[instr] = value - if len(*instr.Referrers()) != 0 && b.NeedsStackObjects() { + if len(*instr.Referrers()) != 0 && b.NeedsStackObjects { b.trackExpr(instr, value) } } @@ -987,7 +1013,7 @@ func (b *builder) createInstruction(instr ssa.Instruction) { // * The function pointer (for tasks). funcPtr, context := b.decodeFuncValue(b.getValue(instr.Call.Value), instr.Call.Value.Type().Underlying().(*types.Signature)) params = append(params, context) // context parameter - switch b.Scheduler() { + switch b.Scheduler { case "none", "coroutines": // There are no additional parameters needed for the goroutine start operation. case "tasks": diff --git a/compiler/compiler_test.go b/compiler/compiler_test.go index 58199248..3919dd81 100644 --- a/compiler/compiler_test.go +++ b/compiler/compiler_test.go @@ -28,7 +28,17 @@ func TestCompiler(t *testing.T) { Options: &compileopts.Options{}, 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 { t.Fatal("failed to create target machine:", err) } @@ -56,7 +66,7 @@ func TestCompiler(t *testing.T) { // Compile AST to IR. pkg := lprogram.MainPkg() - mod, errs := CompilePackage(testCase, pkg, machine, config) + mod, errs := CompilePackage(testCase, pkg, machine, compilerConfig, false) if errs != nil { for _, err := range errs { t.Log("error:", err) diff --git a/compiler/defer.go b/compiler/defer.go index 04651151..5d356f52 100644 --- a/compiler/defer.go +++ b/compiler/defer.go @@ -220,7 +220,7 @@ func (b *builder) createDefer(instr *ssa.Defer) { allocCall := b.createRuntimeCall("alloc", []llvm.Value{sizeValue}, "defer.alloc.call") alloca = b.CreateBitCast(allocCall, llvm.PointerType(deferFrameType, 0), "defer.alloc") } - if b.NeedsStackObjects() { + if b.NeedsStackObjects { b.trackPointer(alloca) } b.CreateStore(deferFrame, alloca) diff --git a/compiler/func.go b/compiler/func.go index 7f5e4398..37e8dac7 100644 --- a/compiler/func.go +++ b/compiler/func.go @@ -6,7 +6,6 @@ package compiler import ( "go/types" - "github.com/tinygo-org/tinygo/compileopts" "golang.org/x/tools/go/ssa" "tinygo.org/x/go-llvm" ) @@ -21,11 +20,11 @@ func (b *builder) createFuncValue(funcPtr, context llvm.Value, sig *types.Signat // context. func (c *compilerContext) createFuncValue(builder llvm.Builder, funcPtr, context llvm.Value, sig *types.Signature) llvm.Value { var funcValueScalar llvm.Value - switch c.FuncImplementation() { - case compileopts.FuncValueDoubleword: + switch c.FuncImplementation { + case "doubleword": // Closure is: {context, function pointer} funcValueScalar = funcPtr - case compileopts.FuncValueSwitch: + case "switch": sigGlobal := c.getTypeCode(sig) funcValueWithSignatureGlobalName := funcPtr.Name() + "$withSignature" 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. func (b *builder) decodeFuncValue(funcValue llvm.Value, sig *types.Signature) (funcPtr, context llvm.Value) { context = b.CreateExtractValue(funcValue, 0, "") - switch b.FuncImplementation() { - case compileopts.FuncValueDoubleword: + switch b.FuncImplementation { + case "doubleword": funcPtr = b.CreateExtractValue(funcValue, 1, "") - case compileopts.FuncValueSwitch: + case "switch": llvmSig := b.getRawFuncType(sig) sigGlobal := b.getTypeCode(sig) 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. func (c *compilerContext) getFuncType(typ *types.Signature) llvm.Type { - switch c.FuncImplementation() { - case compileopts.FuncValueDoubleword: + switch c.FuncImplementation { + case "doubleword": rawPtr := c.getRawFuncType(typ) return c.ctx.StructType([]llvm.Type{c.i8ptrType, rawPtr}, false) - case compileopts.FuncValueSwitch: + case "switch": return c.getLLVMRuntimeType("funcValue") default: panic("unimplemented func value variant") diff --git a/compiler/goroutine.go b/compiler/goroutine.go index 2afa5b01..94965833 100644 --- a/compiler/goroutine.go +++ b/compiler/goroutine.go @@ -21,10 +21,10 @@ import ( func (b *builder) createGoInstruction(funcPtr llvm.Value, params []llvm.Value, prefix string, pos token.Pos) llvm.Value { paramBundle := b.emitPointerPack(params) var callee, stackSize llvm.Value - switch b.Scheduler() { + switch b.Scheduler { case "none", "tasks": callee = b.createGoroutineStartWrapper(funcPtr, prefix, pos) - if b.AutomaticStackSize() { + if b.AutomaticStackSize { // The stack size is not known until after linking. Call a dummy // function that will be replaced with a load from a special ELF // 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 { // The stack size is fixed at compile time. By emitting it here as a // constant, it can be optimized. - stackSize = llvm.ConstInt(b.uintptrType, b.Target.DefaultStackSize, false) + stackSize = llvm.ConstInt(b.uintptrType, b.DefaultStackSize, false) } case "coroutines": 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") builder.SetInsertPointAtEnd(entry) - if c.Debug() { + if c.Debug { pos := c.program.Fset.Position(pos) diFuncType := c.dibuilder.CreateSubroutineType(llvm.DISubroutineType{ File: c.getDIFile(pos.Filename), @@ -147,7 +147,7 @@ func (c *compilerContext) createGoroutineStartWrapper(fn llvm.Value, prefix stri entry := c.ctx.AddBasicBlock(wrapper, "entry") builder.SetInsertPointAtEnd(entry) - if c.Debug() { + if c.Debug { pos := c.program.Fset.Position(pos) diFuncType := c.dibuilder.CreateSubroutineType(llvm.DISubroutineType{ File: c.getDIFile(pos.Filename), diff --git a/compiler/interface.go b/compiler/interface.go index 24986dd1..9427c638 100644 --- a/compiler/interface.go +++ b/compiler/interface.go @@ -478,7 +478,7 @@ func (c *compilerContext) getInterfaceInvokeWrapper(fn *ssa.Function, llvmFn llv defer b.Builder.Dispose() // add debug info if needed - if c.Debug() { + if c.Debug { pos := c.program.Fset.Position(fn.Pos()) difunc := c.attachDebugInfoRaw(fn, wrapper, "$invoke", pos.Filename, pos.Line) b.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), difunc, llvm.Metadata{}) diff --git a/compiler/interrupt.go b/compiler/interrupt.go index 82585d56..15278751 100644 --- a/compiler/interrupt.go +++ b/compiler/interrupt.go @@ -55,7 +55,7 @@ func (b *builder) createInterruptGlobal(instr *ssa.CallCommon) (llvm.Value, erro global.SetInitializer(initializer) // Add debug info to the interrupt global. - if b.Debug() { + if b.Debug { pos := b.program.Fset.Position(instr.Pos()) diglobal := b.dibuilder.CreateGlobalVariableExpression(b.getDIFile(pos.Filename), llvm.DIGlobalVariableExpression{ 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 // thus keeps the Interrupt object alive. // 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") if useFn.IsNil() { useFnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{interrupt.Type()}, false) diff --git a/compiler/llvm.go b/compiler/llvm.go index 1f8f7f93..b8e0808a 100644 --- a/compiler/llvm.go +++ b/compiler/llvm.go @@ -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 // pointer value directly. It returns the pointer with the packed data. 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. diff --git a/compiler/llvmutil/wordpack.go b/compiler/llvmutil/wordpack.go index 230f5dda..05fc4ee8 100644 --- a/compiler/llvmutil/wordpack.go +++ b/compiler/llvmutil/wordpack.go @@ -5,7 +5,6 @@ package llvmutil // itself if possible and legal. import ( - "github.com/tinygo-org/tinygo/compileopts" "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 // 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. -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() targetData := llvm.NewTargetData(mod.DataLayout()) 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.ConstPointerNull(i8ptrType), // coroutine handle }, "") - if config.NeedsStackObjects() { + if needsStackObjects { trackPointer := mod.NamedFunction("runtime.trackPointer") builder.CreateCall(trackPointer, []llvm.Value{ packedHeapAlloc, diff --git a/compiler/symbol.go b/compiler/symbol.go index 05be92f2..10051be6 100644 --- a/compiler/symbol.go +++ b/compiler/symbol.go @@ -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. // TODO: this should be done for every global in the program, not just // the ones that are referenced from some code. diff --git a/compiler/syscall.go b/compiler/syscall.go index 8bb873fa..5b93e9ec 100644 --- a/compiler/syscall.go +++ b/compiler/syscall.go @@ -16,8 +16,8 @@ func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) { num := b.getValue(call.Args[0]) var syscallResult llvm.Value switch { - case b.GOARCH() == "amd64": - if b.GOOS() == "darwin" { + case b.GOARCH == "amd64": + if b.GOOS == "darwin" { // Darwin adds this magic number to system call numbers: // // > 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) target := llvm.InlineAsm(fnType, "syscall", constraints, true, false, llvm.InlineAsmDialectIntel) syscallResult = b.CreateCall(target, args, "") - case b.GOARCH() == "386" && b.GOOS() == "linux": + case b.GOARCH == "386" && b.GOOS == "linux": // Sources: // syscall(2) man page // 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) target := llvm.InlineAsm(fnType, "int 0x80", constraints, true, false, llvm.InlineAsmDialectIntel) 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. // Source: syscall(2) man page. args := []llvm.Value{} @@ -116,7 +116,7 @@ func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) { fnType := llvm.FunctionType(b.uintptrType, argTypes, false) target := llvm.InlineAsm(fnType, "svc #0", constraints, true, false, 0) syscallResult = b.CreateCall(target, args, "") - case b.GOARCH() == "arm64" && b.GOOS() == "linux": + case b.GOARCH == "arm64" && b.GOOS == "linux": // Source: syscall(2) man page. args := []llvm.Value{} 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) syscallResult = b.CreateCall(target, args, "") 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": // Return values: r0, r1 uintptr, err Errno // Pseudocode: @@ -187,6 +187,6 @@ func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) { retval = b.CreateInsertValue(retval, errResult, 2, "") return retval, nil 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) } } diff --git a/transform/optimizer.go b/transform/optimizer.go index 917083ce..dc9e1ffb 100644 --- a/transform/optimizer.go +++ b/transform/optimizer.go @@ -85,7 +85,7 @@ func Optimize(mod llvm.Module, config *compileopts.Config, optLevel, sizeLevel i return errs } - if config.FuncImplementation() == compileopts.FuncValueSwitch { + if config.FuncImplementation() == "switch" { LowerFuncValues(mod) } @@ -104,7 +104,7 @@ func Optimize(mod llvm.Module, config *compileopts.Config, optLevel, sizeLevel i if err != nil { return []error{err} } - if config.FuncImplementation() == compileopts.FuncValueSwitch { + if config.FuncImplementation() == "switch" { LowerFuncValues(mod) } errs := LowerInterrupts(mod)