diff --git a/builder/build.go b/builder/build.go index 3ce7f644ee..2e42ea5ebf 100644 --- a/builder/build.go +++ b/builder/build.go @@ -17,6 +17,7 @@ import ( "github.com/tinygo-org/tinygo/goenv" "github.com/tinygo-org/tinygo/interp" "github.com/tinygo-org/tinygo/transform" + "tinygo.org/x/go-llvm" ) // Build performs a single package to executable Go build. It takes in a package @@ -26,34 +27,34 @@ import ( // 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(string) error) error { - c, err := compiler.NewCompiler(pkgName, config) + // Compile Go code to IR. + machine, err := compiler.NewTargetMachine(config) if err != nil { return err } - - // Compile Go code to IR. - errs := c.Compile(pkgName) - if len(errs) != 0 { + mod, extraFiles, errs := compiler.Compile(pkgName, machine, config) + if errs != nil { return newMultiError(errs) } + if config.Options.PrintIR { fmt.Println("; Generated LLVM IR:") - fmt.Println(c.IR()) + fmt.Println(mod.String()) } - if err := c.Verify(); err != nil { + if err := llvm.VerifyModule(mod, llvm.PrintMessageAction); err != nil { return errors.New("verification error after IR construction") } - err = interp.Run(c.Module(), config.DumpSSA()) + err = interp.Run(mod, config.DumpSSA()) if err != nil { return err } - if err := c.Verify(); err != nil { + if err := llvm.VerifyModule(mod, llvm.PrintMessageAction); err != nil { return errors.New("verification error after interpreting runtime.initAll") } if config.GOOS() != "darwin" { - transform.ApplyFunctionSections(c.Module()) // -ffunction-sections + transform.ApplyFunctionSections(mod) // -ffunction-sections } // Browsers cannot handle external functions that have type i64 because it @@ -62,7 +63,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(stri // stack-allocated values. // Use -wasm-abi=generic to disable this behaviour. if config.Options.WasmAbi == "js" && strings.HasPrefix(config.Triple(), "wasm") { - err := transform.ExternalInt64AsPtr(c.Module()) + err := transform.ExternalInt64AsPtr(mod) if err != nil { return err } @@ -73,22 +74,22 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(stri errs = nil switch config.Options.Opt { case "none", "0": - errs = transform.Optimize(c.Module(), config, 0, 0, 0) // -O0 + errs = transform.Optimize(mod, config, 0, 0, 0) // -O0 case "1": - errs = transform.Optimize(c.Module(), config, 1, 0, 0) // -O1 + errs = transform.Optimize(mod, config, 1, 0, 0) // -O1 case "2": - errs = transform.Optimize(c.Module(), config, 2, 0, 225) // -O2 + errs = transform.Optimize(mod, config, 2, 0, 225) // -O2 case "s": - errs = transform.Optimize(c.Module(), config, 2, 1, 225) // -Os + errs = transform.Optimize(mod, config, 2, 1, 225) // -Os case "z": - errs = transform.Optimize(c.Module(), config, 2, 2, 5) // -Oz, default + errs = transform.Optimize(mod, config, 2, 2, 5) // -Oz, default default: errs = []error{errors.New("unknown optimization level: -opt=" + config.Options.Opt)} } if len(errs) > 0 { return newMultiError(errs) } - if err := c.Verify(); err != nil { + if err := llvm.VerifyModule(mod, llvm.PrintMessageAction); err != nil { return errors.New("verification failure after LLVM optimization passes") } @@ -98,8 +99,8 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(stri // pointers are flash and which are in RAM so that pointers can have a // correct address space parameter (address space 1 is for flash). if strings.HasPrefix(config.Triple(), "avr") { - transform.NonConstGlobals(c.Module()) - if err := c.Verify(); err != nil { + transform.NonConstGlobals(mod) + if err := llvm.VerifyModule(mod, llvm.PrintMessageAction); err != nil { return errors.New("verification error after making all globals non-constant on AVR") } } @@ -108,11 +109,17 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(stri outext := filepath.Ext(outpath) switch outext { case ".o": - return c.EmitObject(outpath) + llvmBuf, err := machine.EmitToMemoryBuffer(mod, llvm.ObjectFile) + if err != nil { + return err + } + return ioutil.WriteFile(outpath, llvmBuf.Bytes(), 0666) case ".bc": - return c.EmitBitcode(outpath) + data := llvm.WriteBitcodeToMemoryBuffer(mod).Bytes() + return ioutil.WriteFile(outpath, data, 0666) case ".ll": - return c.EmitText(outpath) + data := []byte(mod.String()) + return ioutil.WriteFile(outpath, data, 0666) default: // Act as a compiler driver. @@ -125,7 +132,11 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(stri // Write the object file. objfile := filepath.Join(dir, "main.o") - err = c.EmitObject(objfile) + llvmBuf, err := machine.EmitToMemoryBuffer(mod, llvm.ObjectFile) + if err != nil { + return err + } + err = ioutil.WriteFile(objfile, llvmBuf.Bytes(), 0666) if err != nil { return err } @@ -161,16 +172,13 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(stri } // Compile C files in packages. - for i, pkg := range c.Packages() { - for _, file := range pkg.CFiles { - path := filepath.Join(pkg.Package.Dir, file) - outpath := filepath.Join(dir, "pkg"+strconv.Itoa(i)+"-"+file+".o") - err := runCCompiler(config.Target.Compiler, append(config.CFlags(), "-c", "-o", outpath, path)...) - if err != nil { - return &commandError{"failed to build", path, err} - } - ldflags = append(ldflags, outpath) + for i, file := range extraFiles { + outpath := filepath.Join(dir, "pkg"+strconv.Itoa(i)+"-"+filepath.Base(file)+".o") + err := runCCompiler(config.Target.Compiler, append(config.CFlags(), "-c", "-o", outpath, file)...) + if err != nil { + return &commandError{"failed to build", file, err} } + ldflags = append(ldflags, outpath) } // Link the object files together. diff --git a/compiler/asserts.go b/compiler/asserts.go index daeecadb9c..6b25478177 100644 --- a/compiler/asserts.go +++ b/compiler/asserts.go @@ -11,11 +11,11 @@ import ( "tinygo.org/x/go-llvm" ) -// emitLookupBoundsCheck emits a bounds check before doing a lookup into a +// createLookupBoundsCheck emits a bounds check before doing a lookup into a // slice. This is required by the Go language spec: an index out of bounds must // cause a panic. -func (c *Compiler) emitLookupBoundsCheck(frame *Frame, arrayLen, index llvm.Value, indexType types.Type) { - if frame.fn.IsNoBounds() { +func (b *builder) createLookupBoundsCheck(arrayLen, index llvm.Value, indexType types.Type) { + if b.fn.IsNoBounds() { // The //go:nobounds pragma was added to the function to avoid bounds // checking. return @@ -25,29 +25,29 @@ func (c *Compiler) emitLookupBoundsCheck(frame *Frame, arrayLen, index llvm.Valu // Sometimes, the index can be e.g. an uint8 or int8, and we have to // correctly extend that type. if indexType.Underlying().(*types.Basic).Info()&types.IsUnsigned == 0 { - index = c.builder.CreateZExt(index, arrayLen.Type(), "") + index = b.CreateZExt(index, arrayLen.Type(), "") } else { - index = c.builder.CreateSExt(index, arrayLen.Type(), "") + index = b.CreateSExt(index, arrayLen.Type(), "") } } else if index.Type().IntTypeWidth() > arrayLen.Type().IntTypeWidth() { // The index is bigger than the array length type, so extend it. - arrayLen = c.builder.CreateZExt(arrayLen, index.Type(), "") + arrayLen = b.CreateZExt(arrayLen, index.Type(), "") } // Now do the bounds check: index >= arrayLen - outOfBounds := c.builder.CreateICmp(llvm.IntUGE, index, arrayLen, "") - c.createRuntimeAssert(frame, outOfBounds, "lookup", "lookupPanic") + outOfBounds := b.CreateICmp(llvm.IntUGE, index, arrayLen, "") + b.createRuntimeAssert(outOfBounds, "lookup", "lookupPanic") } -// emitSliceBoundsCheck emits a bounds check before a slicing operation to make +// createSliceBoundsCheck emits a bounds check before a slicing operation to make // sure it is within bounds. // // This function is both used for slicing a slice (low and high have their // normal meaning) and for creating a new slice, where 'capacity' means the // biggest possible slice capacity, 'low' means len and 'high' means cap. The // logic is the same in both cases. -func (c *Compiler) emitSliceBoundsCheck(frame *Frame, capacity, low, high, max llvm.Value, lowType, highType, maxType *types.Basic) { - if frame.fn.IsNoBounds() { +func (b *builder) createSliceBoundsCheck(capacity, low, high, max llvm.Value, lowType, highType, maxType *types.Basic) { + if b.fn.IsNoBounds() { // The //go:nobounds pragma was added to the function to avoid bounds // checking. return @@ -65,45 +65,45 @@ func (c *Compiler) emitSliceBoundsCheck(frame *Frame, capacity, low, high, max l capacityType = max.Type() } if capacityType != capacity.Type() { - capacity = c.builder.CreateZExt(capacity, capacityType, "") + capacity = b.CreateZExt(capacity, capacityType, "") } // Extend low and high to be the same size as capacity. if low.Type().IntTypeWidth() < capacityType.IntTypeWidth() { if lowType.Info()&types.IsUnsigned != 0 { - low = c.builder.CreateZExt(low, capacityType, "") + low = b.CreateZExt(low, capacityType, "") } else { - low = c.builder.CreateSExt(low, capacityType, "") + low = b.CreateSExt(low, capacityType, "") } } if high.Type().IntTypeWidth() < capacityType.IntTypeWidth() { if highType.Info()&types.IsUnsigned != 0 { - high = c.builder.CreateZExt(high, capacityType, "") + high = b.CreateZExt(high, capacityType, "") } else { - high = c.builder.CreateSExt(high, capacityType, "") + high = b.CreateSExt(high, capacityType, "") } } if max.Type().IntTypeWidth() < capacityType.IntTypeWidth() { if maxType.Info()&types.IsUnsigned != 0 { - max = c.builder.CreateZExt(max, capacityType, "") + max = b.CreateZExt(max, capacityType, "") } else { - max = c.builder.CreateSExt(max, capacityType, "") + max = b.CreateSExt(max, capacityType, "") } } // Now do the bounds check: low > high || high > capacity - outOfBounds1 := c.builder.CreateICmp(llvm.IntUGT, low, high, "slice.lowhigh") - outOfBounds2 := c.builder.CreateICmp(llvm.IntUGT, high, max, "slice.highmax") - outOfBounds3 := c.builder.CreateICmp(llvm.IntUGT, max, capacity, "slice.maxcap") - outOfBounds := c.builder.CreateOr(outOfBounds1, outOfBounds2, "slice.lowmax") - outOfBounds = c.builder.CreateOr(outOfBounds, outOfBounds3, "slice.lowcap") - c.createRuntimeAssert(frame, outOfBounds, "slice", "slicePanic") + outOfBounds1 := b.CreateICmp(llvm.IntUGT, low, high, "slice.lowhigh") + outOfBounds2 := b.CreateICmp(llvm.IntUGT, high, max, "slice.highmax") + outOfBounds3 := b.CreateICmp(llvm.IntUGT, max, capacity, "slice.maxcap") + outOfBounds := b.CreateOr(outOfBounds1, outOfBounds2, "slice.lowmax") + outOfBounds = b.CreateOr(outOfBounds, outOfBounds3, "slice.lowcap") + b.createRuntimeAssert(outOfBounds, "slice", "slicePanic") } -// emitChanBoundsCheck emits a bounds check before creating a new channel to +// createChanBoundsCheck creates a bounds check before creating a new channel to // check that the value is not too big for runtime.chanMake. -func (c *Compiler) emitChanBoundsCheck(frame *Frame, elementSize uint64, bufSize llvm.Value, bufSizeType *types.Basic, pos token.Pos) { - if frame.fn.IsNoBounds() { +func (b *builder) createChanBoundsCheck(elementSize uint64, bufSize llvm.Value, bufSizeType *types.Basic, pos token.Pos) { + if b.fn.IsNoBounds() { // The //go:nobounds pragma was added to the function to avoid bounds // checking. return @@ -111,23 +111,23 @@ func (c *Compiler) emitChanBoundsCheck(frame *Frame, elementSize uint64, bufSize // Check whether the bufSize parameter must be cast to a wider integer for // comparison. - if bufSize.Type().IntTypeWidth() < c.uintptrType.IntTypeWidth() { + if bufSize.Type().IntTypeWidth() < b.uintptrType.IntTypeWidth() { if bufSizeType.Info()&types.IsUnsigned != 0 { // Unsigned, so zero-extend to uint type. bufSizeType = types.Typ[types.Uint] - bufSize = c.builder.CreateZExt(bufSize, c.intType, "") + bufSize = b.CreateZExt(bufSize, b.intType, "") } else { // Signed, so sign-extend to int type. bufSizeType = types.Typ[types.Int] - bufSize = c.builder.CreateSExt(bufSize, c.intType, "") + bufSize = b.CreateSExt(bufSize, b.intType, "") } } // Calculate (^uintptr(0)) >> 1, which is the max value that fits in an // uintptr if uintptrs were signed. - maxBufSize := llvm.ConstLShr(llvm.ConstNot(llvm.ConstInt(c.uintptrType, 0, false)), llvm.ConstInt(c.uintptrType, 1, false)) + maxBufSize := llvm.ConstLShr(llvm.ConstNot(llvm.ConstInt(b.uintptrType, 0, false)), llvm.ConstInt(b.uintptrType, 1, false)) if elementSize > maxBufSize.ZExtValue() { - c.addError(pos, fmt.Sprintf("channel element type is too big (%v bytes)", elementSize)) + b.addError(pos, fmt.Sprintf("channel element type is too big (%v bytes)", elementSize)) return } // Avoid divide-by-zero. @@ -136,7 +136,7 @@ func (c *Compiler) emitChanBoundsCheck(frame *Frame, elementSize uint64, bufSize } // Make the maxBufSize actually the maximum allowed value (in number of // elements in the channel buffer). - maxBufSize = llvm.ConstUDiv(maxBufSize, llvm.ConstInt(c.uintptrType, elementSize, false)) + maxBufSize = llvm.ConstUDiv(maxBufSize, llvm.ConstInt(b.uintptrType, elementSize, false)) // Make sure maxBufSize has the same type as bufSize. if maxBufSize.Type() != bufSize.Type() { @@ -144,14 +144,14 @@ func (c *Compiler) emitChanBoundsCheck(frame *Frame, elementSize uint64, bufSize } // Do the check for a too large (or negative) buffer size. - bufSizeTooBig := c.builder.CreateICmp(llvm.IntUGE, bufSize, maxBufSize, "") - c.createRuntimeAssert(frame, bufSizeTooBig, "chan", "chanMakePanic") + bufSizeTooBig := b.CreateICmp(llvm.IntUGE, bufSize, maxBufSize, "") + b.createRuntimeAssert(bufSizeTooBig, "chan", "chanMakePanic") } -// emitNilCheck checks whether the given pointer is nil, and panics if it is. It -// has no effect in well-behaved programs, but makes sure no uncaught nil +// createNilCheck checks whether the given pointer is nil, and panics if it is. +// It has no effect in well-behaved programs, but makes sure no uncaught nil // pointer dereferences exist in valid Go code. -func (c *Compiler) emitNilCheck(frame *Frame, ptr llvm.Value, blockPrefix string) { +func (b *builder) createNilCheck(ptr llvm.Value, blockPrefix string) { // Check whether we need to emit this check at all. if !ptr.IsAGlobalValue().IsNil() { return @@ -167,22 +167,22 @@ func (c *Compiler) emitNilCheck(frame *Frame, ptr llvm.Value, blockPrefix string // https://reviews.llvm.org/D60047 for details. Pointer capturing // unfortunately breaks escape analysis, so we use this trick to let the // functionattr pass know that this pointer doesn't really escape. - ptr = c.builder.CreateBitCast(ptr, c.i8ptrType, "") - isnil = c.createRuntimeCall("isnil", []llvm.Value{ptr}, "") + ptr = b.CreateBitCast(ptr, b.i8ptrType, "") + isnil = b.createRuntimeCall("isnil", []llvm.Value{ptr}, "") } else { // Do the nil check using a regular icmp. This can happen with function // pointers on AVR, which don't benefit from escape analysis anyway. nilptr := llvm.ConstPointerNull(ptr.Type()) - isnil = c.builder.CreateICmp(llvm.IntEQ, ptr, nilptr, "") + isnil = b.CreateICmp(llvm.IntEQ, ptr, nilptr, "") } // Emit the nil check in IR. - c.createRuntimeAssert(frame, isnil, blockPrefix, "nilPanic") + b.createRuntimeAssert(isnil, blockPrefix, "nilPanic") } // createRuntimeAssert is a common function to create a new branch on an assert // bool, calling an assert func if the assert value is true (1). -func (c *Compiler) createRuntimeAssert(frame *Frame, assert llvm.Value, blockPrefix, assertFunc string) { +func (b *builder) createRuntimeAssert(assert llvm.Value, blockPrefix, assertFunc string) { // Check whether we can resolve this check at compile time. if !assert.IsAConstantInt().IsNil() { val := assert.ZExtValue() @@ -193,18 +193,18 @@ func (c *Compiler) createRuntimeAssert(frame *Frame, assert llvm.Value, blockPre } } - faultBlock := c.ctx.AddBasicBlock(frame.fn.LLVMFn, blockPrefix+".throw") - nextBlock := c.ctx.AddBasicBlock(frame.fn.LLVMFn, blockPrefix+".next") - frame.blockExits[frame.currentBlock] = nextBlock // adjust outgoing block for phi nodes + faultBlock := b.ctx.AddBasicBlock(b.fn.LLVMFn, blockPrefix+".throw") + nextBlock := b.ctx.AddBasicBlock(b.fn.LLVMFn, blockPrefix+".next") + b.blockExits[b.currentBlock] = nextBlock // adjust outgoing block for phi nodes // Now branch to the out-of-bounds or the regular block. - c.builder.CreateCondBr(assert, faultBlock, nextBlock) + b.CreateCondBr(assert, faultBlock, nextBlock) // Fail: the assert triggered so panic. - c.builder.SetInsertPointAtEnd(faultBlock) - c.createRuntimeCall(assertFunc, nil, "") - c.builder.CreateUnreachable() + b.SetInsertPointAtEnd(faultBlock) + b.createRuntimeCall(assertFunc, nil, "") + b.CreateUnreachable() // Ok: assert didn't trigger so continue normally. - c.builder.SetInsertPointAtEnd(nextBlock) + b.SetInsertPointAtEnd(nextBlock) } diff --git a/compiler/calls.go b/compiler/calls.go index 40d9443aa3..1de28b24ad 100644 --- a/compiler/calls.go +++ b/compiler/calls.go @@ -1,8 +1,6 @@ package compiler import ( - "fmt" - "golang.org/x/tools/go/ssa" "tinygo.org/x/go-llvm" ) @@ -13,40 +11,35 @@ import ( // a struct contains more fields, it is passed as a struct without expanding. const MaxFieldsPerParam = 3 -// Shortcut: create a call to runtime. with the given arguments. -func (c *Compiler) createRuntimeCall(fnName string, args []llvm.Value, name string) llvm.Value { - runtimePkg := c.ir.Program.ImportedPackage("runtime") - member := runtimePkg.Members[fnName] - if member == nil { - panic("trying to call runtime." + fnName) +// createCall creates a new call to runtime. with the given arguments. +func (b *builder) createRuntimeCall(fnName string, args []llvm.Value, name string) llvm.Value { + fullName := "runtime." + fnName + fn := b.mod.NamedFunction(fullName) + if fn.IsNil() { + panic("trying to call non-existent function: " + fullName) } - fn := c.ir.GetFunction(member.(*ssa.Function)) - if fn.LLVMFn.IsNil() { - panic(fmt.Errorf("function %s does not appear in LLVM IR", fnName)) - } - if !fn.IsExported() { - args = append(args, llvm.Undef(c.i8ptrType)) // unused context parameter - args = append(args, llvm.ConstPointerNull(c.i8ptrType)) // coroutine handle - } - return c.createCall(fn.LLVMFn, args, name) + args = append(args, llvm.Undef(b.i8ptrType)) // unused context parameter + args = append(args, llvm.ConstPointerNull(b.i8ptrType)) // coroutine handle + return b.createCall(fn, args, name) } -// Create a call to the given function with the arguments possibly expanded. -func (c *Compiler) createCall(fn llvm.Value, args []llvm.Value, name string) llvm.Value { +// createCall creates a call to the given function with the arguments possibly +// expanded. +func (b *builder) createCall(fn llvm.Value, args []llvm.Value, name string) llvm.Value { expanded := make([]llvm.Value, 0, len(args)) for _, arg := range args { - fragments := c.expandFormalParam(arg) + fragments := b.expandFormalParam(arg) expanded = append(expanded, fragments...) } - return c.builder.CreateCall(fn, expanded, name) + return b.CreateCall(fn, expanded, name) } // Expand an argument type to a list that can be used in a function call // parameter list. -func (c *Compiler) expandFormalParamType(t llvm.Type) []llvm.Type { +func expandFormalParamType(t llvm.Type) []llvm.Type { switch t.TypeKind() { case llvm.StructTypeKind: - fields := c.flattenAggregateType(t) + fields := flattenAggregateType(t) if len(fields) <= MaxFieldsPerParam { return fields } else { @@ -59,13 +52,14 @@ func (c *Compiler) expandFormalParamType(t llvm.Type) []llvm.Type { } } -// Expand an argument type to a list of offsets from the start of the object. -// Used together with expandFormalParam to get the offset of each value from the -// start of the non-expanded value. -func (c *Compiler) expandFormalParamOffsets(t llvm.Type) []uint64 { +// expandFormalParamOffsets returns a list of offsets from the start of an +// object of type t after it would have been split up by expandFormalParam. This +// is useful for debug information, where it is necessary to know the offset +// from the start of the combined object. +func (b *builder) expandFormalParamOffsets(t llvm.Type) []uint64 { switch t.TypeKind() { case llvm.StructTypeKind: - fields := c.flattenAggregateTypeOffsets(t) + fields := b.flattenAggregateTypeOffsets(t) if len(fields) <= MaxFieldsPerParam { return fields } else { @@ -78,13 +72,16 @@ func (c *Compiler) expandFormalParamOffsets(t llvm.Type) []uint64 { } } -// Equivalent of expandFormalParamType for parameter values. -func (c *Compiler) expandFormalParam(v llvm.Value) []llvm.Value { +// expandFormalParam splits a formal param value into pieces, so it can be +// passed directly as part of a function call. For example, it splits up small +// structs into individual fields. It is the equivalent of expandFormalParamType +// for parameter values. +func (b *builder) expandFormalParam(v llvm.Value) []llvm.Value { switch v.Type().TypeKind() { case llvm.StructTypeKind: - fieldTypes := c.flattenAggregateType(v.Type()) + fieldTypes := flattenAggregateType(v.Type()) if len(fieldTypes) <= MaxFieldsPerParam { - fields := c.flattenAggregate(v) + fields := b.flattenAggregate(v) if len(fields) != len(fieldTypes) { panic("type and value param lowering don't match") } @@ -101,12 +98,12 @@ func (c *Compiler) expandFormalParam(v llvm.Value) []llvm.Value { // Try to flatten a struct type to a list of types. Returns a 1-element slice // with the passed in type if this is not possible. -func (c *Compiler) flattenAggregateType(t llvm.Type) []llvm.Type { +func flattenAggregateType(t llvm.Type) []llvm.Type { switch t.TypeKind() { case llvm.StructTypeKind: fields := make([]llvm.Type, 0, t.StructElementTypesCount()) for _, subfield := range t.StructElementTypes() { - subfields := c.flattenAggregateType(subfield) + subfields := flattenAggregateType(subfield) fields = append(fields, subfields...) } return fields @@ -115,10 +112,13 @@ func (c *Compiler) flattenAggregateType(t llvm.Type) []llvm.Type { } } -// Return the offsets from the start of the object if this object type were -// flattened like in flattenAggregate. Used together with flattenAggregate to -// know the start indices of each value in the non-flattened object. -func (c *Compiler) flattenAggregateTypeOffsets(t llvm.Type) []uint64 { +// flattenAggregateTypeOffset returns the offsets from the start of an object of +// type t if this object were flattened like in flattenAggregate. Used together +// with flattenAggregate to know the start indices of each value in the +// non-flattened object. +// +// Note: this is an implementation detail, use expandFormalParamOffsets instead. +func (c *compilerContext) flattenAggregateTypeOffsets(t llvm.Type) []uint64 { switch t.TypeKind() { case llvm.StructTypeKind: fields := make([]uint64, 0, t.StructElementTypesCount()) @@ -136,15 +136,15 @@ func (c *Compiler) flattenAggregateTypeOffsets(t llvm.Type) []uint64 { } } -// Break down a struct into its elementary types for argument passing. The value -// equivalent of flattenAggregateType -func (c *Compiler) flattenAggregate(v llvm.Value) []llvm.Value { +// flattenAggregate breaks down a struct into its elementary values for argument +// passing. It is the value equivalent of flattenAggregateType +func (b *builder) flattenAggregate(v llvm.Value) []llvm.Value { switch v.Type().TypeKind() { case llvm.StructTypeKind: fields := make([]llvm.Value, 0, v.Type().StructElementTypesCount()) for i := range v.Type().StructElementTypes() { - subfield := c.builder.CreateExtractValue(v, i, "") - subfields := c.flattenAggregate(subfield) + subfield := b.CreateExtractValue(v, i, "") + subfields := b.flattenAggregate(subfield) fields = append(fields, subfields...) } return fields @@ -153,25 +153,28 @@ func (c *Compiler) flattenAggregate(v llvm.Value) []llvm.Value { } } -// Collapse a list of fields into its original value. -func (c *Compiler) collapseFormalParam(t llvm.Type, fields []llvm.Value) llvm.Value { - param, remaining := c.collapseFormalParamInternal(t, fields) +// collapseFormalParam combines an aggregate object back into the original +// value. This is used to join multiple LLVM parameters into a single Go value +// in the function entry block. +func (b *builder) collapseFormalParam(t llvm.Type, fields []llvm.Value) llvm.Value { + param, remaining := b.collapseFormalParamInternal(t, fields) if len(remaining) != 0 { panic("failed to expand back all fields") } return param } -// Returns (value, remainingFields). Used by collapseFormalParam. -func (c *Compiler) collapseFormalParamInternal(t llvm.Type, fields []llvm.Value) (llvm.Value, []llvm.Value) { +// collapseFormalParamInternal is an implementation detail of +// collapseFormalParam: it works by recursing until there are no fields left. +func (b *builder) collapseFormalParamInternal(t llvm.Type, fields []llvm.Value) (llvm.Value, []llvm.Value) { switch t.TypeKind() { case llvm.StructTypeKind: - if len(c.flattenAggregateType(t)) <= MaxFieldsPerParam { + if len(flattenAggregateType(t)) <= MaxFieldsPerParam { value := llvm.ConstNull(t) for i, subtyp := range t.StructElementTypes() { - structField, remaining := c.collapseFormalParamInternal(subtyp, fields) + structField, remaining := b.collapseFormalParamInternal(subtyp, fields) fields = remaining - value = c.builder.CreateInsertValue(value, structField, i, "") + value = b.CreateInsertValue(value, structField, i, "") } return value, fields } else { diff --git a/compiler/channel.go b/compiler/channel.go index de7b12e291..3686c98f37 100644 --- a/compiler/channel.go +++ b/compiler/channel.go @@ -11,79 +11,79 @@ import ( "tinygo.org/x/go-llvm" ) -func (c *Compiler) emitMakeChan(frame *Frame, expr *ssa.MakeChan) llvm.Value { - elementSize := c.targetData.TypeAllocSize(c.getLLVMType(expr.Type().(*types.Chan).Elem())) - elementSizeValue := llvm.ConstInt(c.uintptrType, elementSize, false) - bufSize := c.getValue(frame, expr.Size) - c.emitChanBoundsCheck(frame, elementSize, bufSize, expr.Size.Type().Underlying().(*types.Basic), expr.Pos()) - if bufSize.Type().IntTypeWidth() < c.uintptrType.IntTypeWidth() { - bufSize = c.builder.CreateZExt(bufSize, c.uintptrType, "") - } else if bufSize.Type().IntTypeWidth() > c.uintptrType.IntTypeWidth() { - bufSize = c.builder.CreateTrunc(bufSize, c.uintptrType, "") +func (b *builder) createMakeChan(expr *ssa.MakeChan) llvm.Value { + elementSize := b.targetData.TypeAllocSize(b.getLLVMType(expr.Type().(*types.Chan).Elem())) + elementSizeValue := llvm.ConstInt(b.uintptrType, elementSize, false) + bufSize := b.getValue(expr.Size) + b.createChanBoundsCheck(elementSize, bufSize, expr.Size.Type().Underlying().(*types.Basic), expr.Pos()) + if bufSize.Type().IntTypeWidth() < b.uintptrType.IntTypeWidth() { + bufSize = b.CreateZExt(bufSize, b.uintptrType, "") + } else if bufSize.Type().IntTypeWidth() > b.uintptrType.IntTypeWidth() { + bufSize = b.CreateTrunc(bufSize, b.uintptrType, "") } - return c.createRuntimeCall("chanMake", []llvm.Value{elementSizeValue, bufSize}, "") + return b.createRuntimeCall("chanMake", []llvm.Value{elementSizeValue, bufSize}, "") } -// emitChanSend emits a pseudo chan send operation. It is lowered to the actual -// channel send operation during goroutine lowering. -func (c *Compiler) emitChanSend(frame *Frame, instr *ssa.Send) { - ch := c.getValue(frame, instr.Chan) - chanValue := c.getValue(frame, instr.X) +// createChanSend emits a pseudo chan send operation. It is lowered to the +// actual channel send operation during goroutine lowering. +func (b *builder) createChanSend(instr *ssa.Send) { + ch := b.getValue(instr.Chan) + chanValue := b.getValue(instr.X) // store value-to-send - valueType := c.getLLVMType(instr.X.Type()) - valueAlloca, valueAllocaCast, valueAllocaSize := c.createTemporaryAlloca(valueType, "chan.value") - c.builder.CreateStore(chanValue, valueAlloca) + valueType := b.getLLVMType(instr.X.Type()) + valueAlloca, valueAllocaCast, valueAllocaSize := b.createTemporaryAlloca(valueType, "chan.value") + b.CreateStore(chanValue, valueAlloca) // Do the send. - c.createRuntimeCall("chanSend", []llvm.Value{ch, valueAllocaCast}, "") + b.createRuntimeCall("chanSend", []llvm.Value{ch, valueAllocaCast}, "") // End the lifetime of the alloca. // This also works around a bug in CoroSplit, at least in LLVM 8: // https://bugs.llvm.org/show_bug.cgi?id=41742 - c.emitLifetimeEnd(valueAllocaCast, valueAllocaSize) + b.emitLifetimeEnd(valueAllocaCast, valueAllocaSize) } -// emitChanRecv emits a pseudo chan receive operation. It is lowered to the +// createChanRecv emits a pseudo chan receive operation. It is lowered to the // actual channel receive operation during goroutine lowering. -func (c *Compiler) emitChanRecv(frame *Frame, unop *ssa.UnOp) llvm.Value { - valueType := c.getLLVMType(unop.X.Type().(*types.Chan).Elem()) - ch := c.getValue(frame, unop.X) +func (b *builder) createChanRecv(unop *ssa.UnOp) llvm.Value { + valueType := b.getLLVMType(unop.X.Type().(*types.Chan).Elem()) + ch := b.getValue(unop.X) // Allocate memory to receive into. - valueAlloca, valueAllocaCast, valueAllocaSize := c.createTemporaryAlloca(valueType, "chan.value") + valueAlloca, valueAllocaCast, valueAllocaSize := b.createTemporaryAlloca(valueType, "chan.value") // Do the receive. - commaOk := c.createRuntimeCall("chanRecv", []llvm.Value{ch, valueAllocaCast}, "") - received := c.builder.CreateLoad(valueAlloca, "chan.received") - c.emitLifetimeEnd(valueAllocaCast, valueAllocaSize) + commaOk := b.createRuntimeCall("chanRecv", []llvm.Value{ch, valueAllocaCast}, "") + received := b.CreateLoad(valueAlloca, "chan.received") + b.emitLifetimeEnd(valueAllocaCast, valueAllocaSize) if unop.CommaOk { - tuple := llvm.Undef(c.ctx.StructType([]llvm.Type{valueType, c.ctx.Int1Type()}, false)) - tuple = c.builder.CreateInsertValue(tuple, received, 0, "") - tuple = c.builder.CreateInsertValue(tuple, commaOk, 1, "") + tuple := llvm.Undef(b.ctx.StructType([]llvm.Type{valueType, b.ctx.Int1Type()}, false)) + tuple = b.CreateInsertValue(tuple, received, 0, "") + tuple = b.CreateInsertValue(tuple, commaOk, 1, "") return tuple } else { return received } } -// emitChanClose closes the given channel. -func (c *Compiler) emitChanClose(frame *Frame, param ssa.Value) { - ch := c.getValue(frame, param) - c.createRuntimeCall("chanClose", []llvm.Value{ch}, "") +// createChanClose closes the given channel. +func (b *builder) createChanClose(param ssa.Value) { + ch := b.getValue(param) + b.createRuntimeCall("chanClose", []llvm.Value{ch}, "") } -// emitSelect emits all IR necessary for a select statements. That's a +// createSelect emits all IR necessary for a select statements. That's a // non-trivial amount of code because select is very complex to implement. -func (c *Compiler) emitSelect(frame *Frame, expr *ssa.Select) llvm.Value { +func (b *builder) createSelect(expr *ssa.Select) llvm.Value { if len(expr.States) == 0 { // Shortcuts for some simple selects. - llvmType := c.getLLVMType(expr.Type()) + llvmType := b.getLLVMType(expr.Type()) if expr.Blocking { // Blocks forever: // select {} - c.createRuntimeCall("deadlock", nil, "") + b.createRuntimeCall("deadlock", nil, "") return llvm.Undef(llvmType) } else { // No-op: @@ -91,7 +91,7 @@ func (c *Compiler) emitSelect(frame *Frame, expr *ssa.Select) llvm.Value { // default: // } retval := llvm.Undef(llvmType) - retval = c.builder.CreateInsertValue(retval, llvm.ConstInt(c.intType, 0xffffffffffffffff, true), 0, "") + retval = b.CreateInsertValue(retval, llvm.ConstInt(b.intType, 0xffffffffffffffff, true), 0, "") return retval // {-1, false} } } @@ -109,30 +109,30 @@ func (c *Compiler) emitSelect(frame *Frame, expr *ssa.Select) llvm.Value { recvbufAlign := 0 hasReceives := false var selectStates []llvm.Value - chanSelectStateType := c.getLLVMRuntimeType("chanSelectState") + chanSelectStateType := b.getLLVMRuntimeType("chanSelectState") for _, state := range expr.States { - ch := c.getValue(frame, state.Chan) + ch := b.getValue(state.Chan) selectState := llvm.ConstNull(chanSelectStateType) - selectState = c.builder.CreateInsertValue(selectState, ch, 0, "") + selectState = b.CreateInsertValue(selectState, ch, 0, "") switch state.Dir { case types.RecvOnly: // Make sure the receive buffer is big enough and has the correct alignment. - llvmType := c.getLLVMType(state.Chan.Type().(*types.Chan).Elem()) - if size := c.targetData.TypeAllocSize(llvmType); size > recvbufSize { + llvmType := b.getLLVMType(state.Chan.Type().(*types.Chan).Elem()) + if size := b.targetData.TypeAllocSize(llvmType); size > recvbufSize { recvbufSize = size } - if align := c.targetData.ABITypeAlignment(llvmType); align > recvbufAlign { + if align := b.targetData.ABITypeAlignment(llvmType); align > recvbufAlign { recvbufAlign = align } hasReceives = true case types.SendOnly: // Store this value in an alloca and put a pointer to this alloca // in the send state. - sendValue := c.getValue(frame, state.Send) - alloca := llvmutil.CreateEntryBlockAlloca(c.builder, sendValue.Type(), "select.send.value") - c.builder.CreateStore(sendValue, alloca) - ptr := c.builder.CreateBitCast(alloca, c.i8ptrType, "") - selectState = c.builder.CreateInsertValue(selectState, ptr, 1, "") + sendValue := b.getValue(state.Send) + alloca := llvmutil.CreateEntryBlockAlloca(b.Builder, sendValue.Type(), "select.send.value") + b.CreateStore(sendValue, alloca) + ptr := b.CreateBitCast(alloca, b.i8ptrType, "") + selectState = b.CreateInsertValue(selectState, ptr, 1, "") default: panic("unreachable") } @@ -140,74 +140,74 @@ func (c *Compiler) emitSelect(frame *Frame, expr *ssa.Select) llvm.Value { } // Create a receive buffer, where the received value will be stored. - recvbuf := llvm.Undef(c.i8ptrType) + recvbuf := llvm.Undef(b.i8ptrType) if hasReceives { - allocaType := llvm.ArrayType(c.ctx.Int8Type(), int(recvbufSize)) - recvbufAlloca, _, _ := c.createTemporaryAlloca(allocaType, "select.recvbuf.alloca") + allocaType := llvm.ArrayType(b.ctx.Int8Type(), int(recvbufSize)) + recvbufAlloca, _, _ := b.createTemporaryAlloca(allocaType, "select.recvbuf.alloca") recvbufAlloca.SetAlignment(recvbufAlign) - recvbuf = c.builder.CreateGEP(recvbufAlloca, []llvm.Value{ - llvm.ConstInt(c.ctx.Int32Type(), 0, false), - llvm.ConstInt(c.ctx.Int32Type(), 0, false), + recvbuf = b.CreateGEP(recvbufAlloca, []llvm.Value{ + llvm.ConstInt(b.ctx.Int32Type(), 0, false), + llvm.ConstInt(b.ctx.Int32Type(), 0, false), }, "select.recvbuf") } // Create the states slice (allocated on the stack). statesAllocaType := llvm.ArrayType(chanSelectStateType, len(selectStates)) - statesAlloca, statesI8, statesSize := c.createTemporaryAlloca(statesAllocaType, "select.states.alloca") + statesAlloca, statesI8, statesSize := b.createTemporaryAlloca(statesAllocaType, "select.states.alloca") for i, state := range selectStates { // Set each slice element to the appropriate channel. - gep := c.builder.CreateGEP(statesAlloca, []llvm.Value{ - llvm.ConstInt(c.ctx.Int32Type(), 0, false), - llvm.ConstInt(c.ctx.Int32Type(), uint64(i), false), + gep := b.CreateGEP(statesAlloca, []llvm.Value{ + llvm.ConstInt(b.ctx.Int32Type(), 0, false), + llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false), }, "") - c.builder.CreateStore(state, gep) + b.CreateStore(state, gep) } - statesPtr := c.builder.CreateGEP(statesAlloca, []llvm.Value{ - llvm.ConstInt(c.ctx.Int32Type(), 0, false), - llvm.ConstInt(c.ctx.Int32Type(), 0, false), + statesPtr := b.CreateGEP(statesAlloca, []llvm.Value{ + llvm.ConstInt(b.ctx.Int32Type(), 0, false), + llvm.ConstInt(b.ctx.Int32Type(), 0, false), }, "select.states") - statesLen := llvm.ConstInt(c.uintptrType, uint64(len(selectStates)), false) + statesLen := llvm.ConstInt(b.uintptrType, uint64(len(selectStates)), false) // Do the select in the runtime. var results llvm.Value if expr.Blocking { // Stack-allocate operation structures. // If these were simply created as a slice, they would heap-allocate. - chBlockAllocaType := llvm.ArrayType(c.getLLVMRuntimeType("channelBlockedList"), len(selectStates)) - chBlockAlloca, chBlockAllocaPtr, chBlockSize := c.createTemporaryAlloca(chBlockAllocaType, "select.block.alloca") - chBlockLen := llvm.ConstInt(c.uintptrType, uint64(len(selectStates)), false) - chBlockPtr := c.builder.CreateGEP(chBlockAlloca, []llvm.Value{ - llvm.ConstInt(c.ctx.Int32Type(), 0, false), - llvm.ConstInt(c.ctx.Int32Type(), 0, false), + chBlockAllocaType := llvm.ArrayType(b.getLLVMRuntimeType("channelBlockedList"), len(selectStates)) + chBlockAlloca, chBlockAllocaPtr, chBlockSize := b.createTemporaryAlloca(chBlockAllocaType, "select.block.alloca") + chBlockLen := llvm.ConstInt(b.uintptrType, uint64(len(selectStates)), false) + chBlockPtr := b.CreateGEP(chBlockAlloca, []llvm.Value{ + llvm.ConstInt(b.ctx.Int32Type(), 0, false), + llvm.ConstInt(b.ctx.Int32Type(), 0, false), }, "select.block") - results = c.createRuntimeCall("chanSelect", []llvm.Value{ + results = b.createRuntimeCall("chanSelect", []llvm.Value{ recvbuf, statesPtr, statesLen, statesLen, // []chanSelectState chBlockPtr, chBlockLen, chBlockLen, // []channelBlockList }, "select.result") // Terminate the lifetime of the operation structures. - c.emitLifetimeEnd(chBlockAllocaPtr, chBlockSize) + b.emitLifetimeEnd(chBlockAllocaPtr, chBlockSize) } else { - results = c.createRuntimeCall("tryChanSelect", []llvm.Value{ + results = b.createRuntimeCall("tryChanSelect", []llvm.Value{ recvbuf, statesPtr, statesLen, statesLen, // []chanSelectState }, "select.result") } // Terminate the lifetime of the states alloca. - c.emitLifetimeEnd(statesI8, statesSize) + b.emitLifetimeEnd(statesI8, statesSize) // The result value does not include all the possible received values, // because we can't load them in advance. Instead, the *ssa.Extract // instruction will treat a *ssa.Select specially and load it there inline. // Store the receive alloca in a sidetable until we hit this extract // instruction. - if frame.selectRecvBuf == nil { - frame.selectRecvBuf = make(map[*ssa.Select]llvm.Value) + if b.selectRecvBuf == nil { + b.selectRecvBuf = make(map[*ssa.Select]llvm.Value) } - frame.selectRecvBuf[expr] = recvbuf + b.selectRecvBuf[expr] = recvbuf return results } @@ -216,28 +216,28 @@ func (c *Compiler) emitSelect(frame *Frame, expr *ssa.Select) llvm.Value { // when extracting a value from a select statement (*ssa.Select). Because // *ssa.Select cannot load all values in advance, it does this later in the // *ssa.Extract expression. -func (c *Compiler) getChanSelectResult(frame *Frame, expr *ssa.Extract) llvm.Value { +func (b *builder) getChanSelectResult(expr *ssa.Extract) llvm.Value { if expr.Index == 0 { // index - value := c.getValue(frame, expr.Tuple) - index := c.builder.CreateExtractValue(value, expr.Index, "") - if index.Type().IntTypeWidth() < c.intType.IntTypeWidth() { - index = c.builder.CreateSExt(index, c.intType, "") + value := b.getValue(expr.Tuple) + index := b.CreateExtractValue(value, expr.Index, "") + if index.Type().IntTypeWidth() < b.intType.IntTypeWidth() { + index = b.CreateSExt(index, b.intType, "") } return index } else if expr.Index == 1 { // comma-ok - value := c.getValue(frame, expr.Tuple) - return c.builder.CreateExtractValue(value, expr.Index, "") + value := b.getValue(expr.Tuple) + return b.CreateExtractValue(value, expr.Index, "") } else { // Select statements are (index, ok, ...) where ... is a number of // received values, depending on how many receive statements there // are. They are all combined into one alloca (because only one // receive can proceed at a time) so we'll get that alloca, bitcast // it to the correct type, and dereference it. - recvbuf := frame.selectRecvBuf[expr.Tuple.(*ssa.Select)] - typ := llvm.PointerType(c.getLLVMType(expr.Type()), 0) - ptr := c.builder.CreateBitCast(recvbuf, typ, "") - return c.builder.CreateLoad(ptr, "") + recvbuf := b.selectRecvBuf[expr.Tuple.(*ssa.Select)] + typ := llvm.PointerType(b.getLLVMType(expr.Type()), 0) + ptr := b.CreateBitCast(recvbuf, typ, "") + return b.CreateLoad(ptr, "") } } diff --git a/compiler/compiler.go b/compiler/compiler.go index 63b1dc9205..966b0f62c8 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -34,29 +34,32 @@ func init() { // The TinyGo import path. const tinygoPath = "github.com/tinygo-org/tinygo" -type Compiler struct { +// 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 - mod llvm.Module - ctx llvm.Context - builder llvm.Builder - dibuilder *llvm.DIBuilder - cu llvm.Metadata - difiles map[string]llvm.Metadata - ditypes map[types.Type]llvm.Metadata - machine llvm.TargetMachine - targetData llvm.TargetData - intType llvm.Type - i8ptrType llvm.Type // for convenience - funcPtrAddrSpace int - uintptrType llvm.Type - initFuncs []llvm.Value - interfaceInvokeWrappers []interfaceInvokeWrapper - ir *ir.Program - diagnostics []error - astComments map[string]*ast.CommentGroup + mod llvm.Module + ctx llvm.Context + dibuilder *llvm.DIBuilder + cu llvm.Metadata + difiles map[string]llvm.Metadata + ditypes map[types.Type]llvm.Metadata + machine llvm.TargetMachine + targetData llvm.TargetData + intType llvm.Type + i8ptrType llvm.Type // for convenience + funcPtrAddrSpace int + uintptrType llvm.Type + ir *ir.Program + diagnostics []error + astComments map[string]*ast.CommentGroup } -type Frame struct { +// builder contains all information relevant to build a single function. +type builder struct { + *compilerContext + llvm.Builder fn *ir.Function locals map[ssa.Value]llvm.Value // local variables blockEntries map[*ssa.BasicBlock]llvm.BasicBlock // a *ssa.BasicBlock may be split up @@ -79,26 +82,40 @@ type Phi struct { llvm llvm.Value } -func NewCompiler(pkgName string, config *compileopts.Config) (*Compiler, error) { - c := &Compiler{ - Config: config, - difiles: make(map[string]llvm.Metadata), - ditypes: make(map[types.Type]llvm.Metadata), - } - +// 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()) if err != nil { - return nil, err + return llvm.TargetMachine{}, err } 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() + machine := target.CreateTargetMachine(config.Triple(), config.CPU(), features, llvm.CodeGenLevelDefault, llvm.RelocStatic, llvm.CodeModelDefault) + return machine, nil +} + +// Compile the given package path or .go file path. Return an error when this +// fails (in any stage). If successful it returns the LLVM module and a list of +// extra C files to be compiled. If not, one or more errors will be returned. +// +// The fact that it returns a list of filenames to compile is a layering +// violation. Eventually, this Compile function should only compile a single +// package and not the whole program, and loading of the program (including CGo +// processing) should be moved outside the compiler package. +func Compile(pkgName string, machine llvm.TargetMachine, config *compileopts.Config) (llvm.Module, []string, []error) { + c := &compilerContext{ + Config: config, + difiles: make(map[string]llvm.Metadata), + ditypes: make(map[types.Type]llvm.Metadata), + machine: machine, + targetData: machine.CreateTargetData(), + } c.ctx = llvm.NewContext() c.mod = c.ctx.NewModule(pkgName) c.mod.SetTarget(config.Triple()) c.mod.SetDataLayout(c.targetData.String()) - c.builder = c.ctx.NewBuilder() if c.Debug() { c.dibuilder = llvm.NewDIBuilder(c.mod) } @@ -120,21 +137,6 @@ func NewCompiler(pkgName string, config *compileopts.Config) (*Compiler, error) c.funcPtrAddrSpace = dummyFunc.Type().PointerAddressSpace() dummyFunc.EraseFromParentAsFunction() - return c, nil -} - -func (c *Compiler) Packages() []*loader.Package { - return c.ir.LoaderProgram.Sorted() -} - -// Return the LLVM module. Only valid after a successful compile. -func (c *Compiler) Module() llvm.Module { - return c.mod -} - -// Compile the given package path or .go file path. Return an error when this -// fails (in any stage). -func (c *Compiler) Compile(mainPath string) []error { // Prefix the GOPATH with the system GOROOT, as GOROOT is already set to // the TinyGo root. overlayGopath := goenv.Get("GOPATH") @@ -146,7 +148,7 @@ func (c *Compiler) Compile(mainPath string) []error { wd, err := os.Getwd() if err != nil { - return []error{err} + return c.mod, nil, []error{err} } lprogram := &loader.Program{ Build: &build.Context{ @@ -206,17 +208,17 @@ func (c *Compiler) Compile(mainPath string) []error { ClangHeaders: c.ClangHeaders, } - if strings.HasSuffix(mainPath, ".go") { - _, err = lprogram.ImportFile(mainPath) + if strings.HasSuffix(pkgName, ".go") { + _, err = lprogram.ImportFile(pkgName) if err != nil { - return []error{err} + return c.mod, nil, []error{err} } } else { - _, err = lprogram.Import(mainPath, wd, token.Position{ + _, err = lprogram.Import(pkgName, wd, token.Position{ Filename: "build command-line-arguments", }) if err != nil { - return []error{err} + return c.mod, nil, []error{err} } } @@ -224,15 +226,15 @@ func (c *Compiler) Compile(mainPath string) []error { Filename: "build default import", }) if err != nil { - return []error{err} + return c.mod, nil, []error{err} } err = lprogram.Parse(c.TestConfig.CompileTestBinary) if err != nil { - return []error{err} + return c.mod, nil, []error{err} } - c.ir = ir.NewProgram(lprogram, mainPath) + c.ir = ir.NewProgram(lprogram, pkgName) // Run a simple dead code elimination pass. c.ir.SimpleDCE() @@ -241,40 +243,60 @@ func (c *Compiler) Compile(mainPath string) []error { if c.Debug() { c.cu = c.dibuilder.CreateCompileUnit(llvm.DICompileUnit{ Language: 0xb, // DW_LANG_C99 (0xc, off-by-one?) - File: mainPath, + File: pkgName, Dir: "", Producer: "TinyGo", Optimized: true, }) } - var frames []*Frame - c.loadASTComments(lprogram) + // Declare runtime types. + // TODO: lazily create runtime types in getLLVMRuntimeType when they are + // needed. Eventually this will be required anyway, when packages are + // compiled independently (and the runtime types are not available). + for _, member := range c.ir.Program.ImportedPackage("runtime").Members { + if member, ok := member.(*ssa.Type); ok { + if typ, ok := member.Type().(*types.Named); ok { + if _, ok := typ.Underlying().(*types.Struct); ok { + c.getLLVMType(typ) + } + } + } + } + // Declare all functions. for _, f := range c.ir.Functions { - frames = append(frames, c.parseFuncDecl(f)) + c.createFunctionDeclaration(f) } // Add definitions to declarations. - for _, frame := range frames { - if frame.fn.Synthetic == "package initializer" { - c.initFuncs = append(c.initFuncs, frame.fn.LLVMFn) + var initFuncs []llvm.Value + irbuilder := c.ctx.NewBuilder() + defer irbuilder.Dispose() + for _, f := range c.ir.Functions { + if f.Synthetic == "package initializer" { + initFuncs = append(initFuncs, f.LLVMFn) } - if frame.fn.CName() != "" { + if f.CName() != "" { continue } - if frame.fn.Blocks == nil { + if f.Blocks == nil { continue // external function } - c.parseFunc(frame) - } - // Define the already declared functions that wrap methods for use in - // interfaces. - for _, state := range c.interfaceInvokeWrappers { - c.createInterfaceInvokeWrapper(state) + // Create the function definition. + b := builder{ + compilerContext: c, + Builder: irbuilder, + fn: f, + locals: make(map[ssa.Value]llvm.Value), + dilocals: make(map[*types.Var]llvm.Metadata), + blockEntries: make(map[*ssa.BasicBlock]llvm.BasicBlock), + blockExits: make(map[*ssa.BasicBlock]llvm.BasicBlock), + } + b.createFunctionDefinition() } // After all packages are imported, add a synthetic initializer function @@ -285,14 +307,14 @@ func (c *Compiler) Compile(mainPath string) []error { if c.Debug() { difunc := c.attachDebugInfo(initFn) pos := c.ir.Program.Fset.Position(initFn.Pos()) - c.builder.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), difunc, llvm.Metadata{}) + irbuilder.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), difunc, llvm.Metadata{}) } block := c.ctx.AddBasicBlock(initFn.LLVMFn, "entry") - c.builder.SetInsertPointAtEnd(block) - for _, fn := range c.initFuncs { - c.builder.CreateCall(fn, []llvm.Value{llvm.Undef(c.i8ptrType), llvm.Undef(c.i8ptrType)}, "") + irbuilder.SetInsertPointAtEnd(block) + for _, fn := range initFuncs { + irbuilder.CreateCall(fn, []llvm.Value{llvm.Undef(c.i8ptrType), llvm.Undef(c.i8ptrType)}, "") } - c.builder.CreateRetVoid() + irbuilder.CreateRetVoid() // Conserve for goroutine lowering. Without marking these as external, they // would be optimized away. @@ -317,7 +339,7 @@ func (c *Compiler) Compile(mainPath string) []error { c.mod.NamedFunction("runtime.alloc").AddAttributeAtIndex(0, getAttr(attrName)) } - // See emitNilCheck in asserts.go. + // See createNilCheck in asserts.go. c.mod.NamedFunction("runtime.isnil").AddAttributeAtIndex(1, nocapture) // On *nix systems, the "abort" functuion in libc is used to handle fatal panics. @@ -364,26 +386,34 @@ func (c *Compiler) Compile(mainPath string) []error { c.dibuilder.Finalize() } - return c.diagnostics -} + // Gather the list of (C) file paths that should be included in the build. + var extraFiles []string + for _, pkg := range c.ir.LoaderProgram.Sorted() { + for _, file := range pkg.CFiles { + extraFiles = append(extraFiles, filepath.Join(pkg.Package.Dir, file)) + } + } -// getRuntimeType obtains a named type from the runtime package and returns it -// as a Go type. -func (c *Compiler) getRuntimeType(name string) types.Type { - return c.ir.Program.ImportedPackage("runtime").Type(name).Type() + return c.mod, extraFiles, c.diagnostics } // getLLVMRuntimeType obtains a named type from the runtime package and returns // it as a LLVM type, creating it if necessary. It is a shorthand for // getLLVMType(getRuntimeType(name)). -func (c *Compiler) getLLVMRuntimeType(name string) llvm.Type { - return c.getLLVMType(c.getRuntimeType(name)) +func (c *compilerContext) getLLVMRuntimeType(name string) llvm.Type { + fullName := "runtime." + name + typ := c.mod.GetTypeByName(fullName) + if typ.IsNil() { + println(c.mod.String()) + panic("could not find runtime type: " + fullName) + } + return typ } // getLLVMType creates and returns a LLVM type for a Go type. In the case of // named struct types (or Go types implemented as named LLVM structs such as // strings) it also creates it first if necessary. -func (c *Compiler) getLLVMType(goType types.Type) llvm.Type { +func (c *compilerContext) getLLVMType(goType types.Type) llvm.Type { switch typ := goType.(type) { case *types.Array: elemType := c.getLLVMType(typ.Elem()) @@ -483,7 +513,7 @@ func isPointer(typ types.Type) bool { } // Get the DWARF type for this Go type. -func (c *Compiler) getDIType(typ types.Type) llvm.Metadata { +func (c *compilerContext) getDIType(typ types.Type) llvm.Metadata { if md, ok := c.ditypes[typ]; ok { return md } @@ -494,7 +524,7 @@ func (c *Compiler) getDIType(typ types.Type) llvm.Metadata { // createDIType creates a new DWARF type. Don't call this function directly, // call getDIType instead. -func (c *Compiler) createDIType(typ types.Type) llvm.Metadata { +func (c *compilerContext) createDIType(typ types.Type) llvm.Metadata { llvmType := c.getLLVMType(typ) sizeInBytes := c.targetData.TypeAllocSize(llvmType) switch typ := typ.(type) { @@ -666,52 +696,46 @@ func (c *Compiler) createDIType(typ types.Type) llvm.Metadata { // getLocalVariable returns a debug info entry for a local variable, which may // either be a parameter or a regular variable. It will create a new metadata // entry if there isn't one for the variable yet. -func (c *Compiler) getLocalVariable(frame *Frame, variable *types.Var) llvm.Metadata { - if dilocal, ok := frame.dilocals[variable]; ok { +func (b *builder) getLocalVariable(variable *types.Var) llvm.Metadata { + if dilocal, ok := b.dilocals[variable]; ok { // DILocalVariable was already created, return it directly. return dilocal } - pos := c.ir.Program.Fset.Position(variable.Pos()) + pos := b.ir.Program.Fset.Position(variable.Pos()) // Check whether this is a function parameter. - for i, param := range frame.fn.Params { + for i, param := range b.fn.Params { if param.Object().(*types.Var) == variable { // Yes it is, create it as a function parameter. - dilocal := c.dibuilder.CreateParameterVariable(frame.difunc, llvm.DIParameterVariable{ + dilocal := b.dibuilder.CreateParameterVariable(b.difunc, llvm.DIParameterVariable{ Name: param.Name(), - File: c.getDIFile(pos.Filename), + File: b.getDIFile(pos.Filename), Line: pos.Line, - Type: c.getDIType(variable.Type()), + Type: b.getDIType(variable.Type()), AlwaysPreserve: true, ArgNo: i + 1, }) - frame.dilocals[variable] = dilocal + b.dilocals[variable] = dilocal return dilocal } } // No, it's not a parameter. Create a regular (auto) variable. - dilocal := c.dibuilder.CreateAutoVariable(frame.difunc, llvm.DIAutoVariable{ + dilocal := b.dibuilder.CreateAutoVariable(b.difunc, llvm.DIAutoVariable{ Name: variable.Name(), - File: c.getDIFile(pos.Filename), + File: b.getDIFile(pos.Filename), Line: pos.Line, - Type: c.getDIType(variable.Type()), + Type: b.getDIType(variable.Type()), AlwaysPreserve: true, }) - frame.dilocals[variable] = dilocal + b.dilocals[variable] = dilocal return dilocal } -func (c *Compiler) parseFuncDecl(f *ir.Function) *Frame { - frame := &Frame{ - fn: f, - locals: make(map[ssa.Value]llvm.Value), - dilocals: make(map[*types.Var]llvm.Metadata), - blockEntries: make(map[*ssa.BasicBlock]llvm.BasicBlock), - blockExits: make(map[*ssa.BasicBlock]llvm.BasicBlock), - } - +// createFunctionDeclaration creates a LLVM function declaration without body. +// It can later be filled with frame.createFunctionDefinition(). +func (c *compilerContext) createFunctionDeclaration(f *ir.Function) { var retType llvm.Type if f.Signature.Results() == nil { retType = c.ctx.VoidType() @@ -728,7 +752,7 @@ func (c *Compiler) parseFuncDecl(f *ir.Function) *Frame { var paramTypes []llvm.Type for _, param := range f.Params { paramType := c.getLLVMType(param.Type()) - paramTypeFragments := c.expandFormalParamType(paramType) + paramTypeFragments := expandFormalParamType(paramType) paramTypes = append(paramTypes, paramTypeFragments...) } @@ -742,9 +766,9 @@ func (c *Compiler) parseFuncDecl(f *ir.Function) *Frame { fnType := llvm.FunctionType(retType, paramTypes, false) name := f.LinkName() - frame.fn.LLVMFn = c.mod.NamedFunction(name) - if frame.fn.LLVMFn.IsNil() { - frame.fn.LLVMFn = llvm.AddFunction(c.mod, name, fnType) + f.LLVMFn = c.mod.NamedFunction(name) + if f.LLVMFn.IsNil() { + f.LLVMFn = llvm.AddFunction(c.mod, name, fnType) } // External/exported functions may not retain pointer values. @@ -753,26 +777,29 @@ func (c *Compiler) parseFuncDecl(f *ir.Function) *Frame { // Set the wasm-import-module attribute if the function's module is set. if f.Module() != "" { wasmImportModuleAttr := c.ctx.CreateStringAttribute("wasm-import-module", f.Module()) - frame.fn.LLVMFn.AddFunctionAttr(wasmImportModuleAttr) + f.LLVMFn.AddFunctionAttr(wasmImportModuleAttr) } nocaptureKind := llvm.AttributeKindID("nocapture") nocapture := c.ctx.CreateEnumAttribute(nocaptureKind, 0) for i, typ := range paramTypes { if typ.TypeKind() == llvm.PointerTypeKind { - frame.fn.LLVMFn.AddAttributeAtIndex(i+1, nocapture) + f.LLVMFn.AddAttributeAtIndex(i+1, nocapture) } } } - - return frame } -func (c *Compiler) attachDebugInfo(f *ir.Function) llvm.Metadata { +// attachDebugInfo adds debug info to a function declaration. It returns the +// DISubprogram metadata node. +func (c *compilerContext) attachDebugInfo(f *ir.Function) llvm.Metadata { pos := c.ir.Program.Fset.Position(f.Syntax().Pos()) return c.attachDebugInfoRaw(f, f.LLVMFn, "", pos.Filename, pos.Line) } -func (c *Compiler) attachDebugInfoRaw(f *ir.Function, llvmFn llvm.Value, suffix, filename string, line int) llvm.Metadata { +// attachDebugInfo adds debug info to a function declaration. It returns the +// DISubprogram metadata node. This method allows some more control over how +// debug info is added to the function. +func (c *compilerContext) attachDebugInfoRaw(f *ir.Function, llvmFn llvm.Value, suffix, filename string, line int) llvm.Metadata { // Debug info for this function. diparams := make([]llvm.Metadata, 0, len(f.Params)) for _, param := range f.Params { @@ -802,7 +829,7 @@ func (c *Compiler) attachDebugInfoRaw(f *ir.Function, llvmFn llvm.Value, suffix, // getDIFile returns a DIFile metadata node for the given filename. It tries to // use one that was already created, otherwise it falls back to creating a new // one. -func (c *Compiler) getDIFile(filename string) llvm.Metadata { +func (c *compilerContext) getDIFile(filename string) llvm.Metadata { if _, ok := c.difiles[filename]; !ok { dir, file := filepath.Split(filename) if dir != "" { @@ -813,86 +840,89 @@ func (c *Compiler) getDIFile(filename string) llvm.Metadata { return c.difiles[filename] } -func (c *Compiler) parseFunc(frame *Frame) { - if c.DumpSSA() { - fmt.Printf("\nfunc %s:\n", frame.fn.Function) +// createFunctionDefinition builds the LLVM IR implementation for this function. +// The function must be declared but not yet defined, otherwise this function +// will create a diagnostic. +func (b *builder) createFunctionDefinition() { + if b.DumpSSA() { + fmt.Printf("\nfunc %s:\n", b.fn.Function) } - if !frame.fn.LLVMFn.IsDeclaration() { - errValue := frame.fn.LLVMFn.Name() + " redeclared in this program" - fnPos := getPosition(frame.fn.LLVMFn) + if !b.fn.LLVMFn.IsDeclaration() { + errValue := b.fn.Name() + " redeclared in this program" + fnPos := getPosition(b.fn.LLVMFn) if fnPos.IsValid() { errValue += "\n\tprevious declaration at " + fnPos.String() } - c.addError(frame.fn.Pos(), errValue) + b.addError(b.fn.Pos(), errValue) return } - if !frame.fn.IsExported() { - frame.fn.LLVMFn.SetLinkage(llvm.InternalLinkage) - frame.fn.LLVMFn.SetUnnamedAddr(true) + if !b.fn.IsExported() { + b.fn.LLVMFn.SetLinkage(llvm.InternalLinkage) + b.fn.LLVMFn.SetUnnamedAddr(true) } // Some functions have a pragma controlling the inlining level. - switch frame.fn.Inline() { + switch b.fn.Inline() { case ir.InlineHint: // Add LLVM inline hint to functions with //go:inline pragma. - inline := c.ctx.CreateEnumAttribute(llvm.AttributeKindID("inlinehint"), 0) - frame.fn.LLVMFn.AddFunctionAttr(inline) + inline := b.ctx.CreateEnumAttribute(llvm.AttributeKindID("inlinehint"), 0) + b.fn.LLVMFn.AddFunctionAttr(inline) case ir.InlineNone: // Add LLVM attribute to always avoid inlining this function. - noinline := c.ctx.CreateEnumAttribute(llvm.AttributeKindID("noinline"), 0) - frame.fn.LLVMFn.AddFunctionAttr(noinline) + noinline := b.ctx.CreateEnumAttribute(llvm.AttributeKindID("noinline"), 0) + b.fn.LLVMFn.AddFunctionAttr(noinline) } // Add debug info, if needed. - if c.Debug() { - if frame.fn.Synthetic == "package initializer" { + 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*. - frame.difunc = c.attachDebugInfoRaw(frame.fn, frame.fn.LLVMFn, "", "", 0) - } else if frame.fn.Syntax() != nil { + b.difunc = b.attachDebugInfoRaw(b.fn, b.fn.LLVMFn, "", "", 0) + } else if b.fn.Syntax() != nil { // Create debug info file if needed. - frame.difunc = c.attachDebugInfo(frame.fn) + b.difunc = b.attachDebugInfo(b.fn) } - pos := c.ir.Program.Fset.Position(frame.fn.Pos()) - c.builder.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), frame.difunc, llvm.Metadata{}) + pos := b.ir.Program.Fset.Position(b.fn.Pos()) + b.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), b.difunc, llvm.Metadata{}) } // Pre-create all basic blocks in the function. - for _, block := range frame.fn.DomPreorder() { - llvmBlock := c.ctx.AddBasicBlock(frame.fn.LLVMFn, block.Comment) - frame.blockEntries[block] = llvmBlock - frame.blockExits[block] = llvmBlock + for _, block := range b.fn.DomPreorder() { + llvmBlock := b.ctx.AddBasicBlock(b.fn.LLVMFn, block.Comment) + b.blockEntries[block] = llvmBlock + b.blockExits[block] = llvmBlock } - entryBlock := frame.blockEntries[frame.fn.Blocks[0]] - c.builder.SetInsertPointAtEnd(entryBlock) + entryBlock := b.blockEntries[b.fn.Blocks[0]] + b.SetInsertPointAtEnd(entryBlock) // Load function parameters llvmParamIndex := 0 - for _, param := range frame.fn.Params { - llvmType := c.getLLVMType(param.Type()) + for _, param := range b.fn.Params { + llvmType := b.getLLVMType(param.Type()) fields := make([]llvm.Value, 0, 1) - for range c.expandFormalParamType(llvmType) { - fields = append(fields, frame.fn.LLVMFn.Param(llvmParamIndex)) + for range expandFormalParamType(llvmType) { + fields = append(fields, b.fn.LLVMFn.Param(llvmParamIndex)) llvmParamIndex++ } - frame.locals[param] = c.collapseFormalParam(llvmType, fields) + b.locals[param] = b.collapseFormalParam(llvmType, fields) // Add debug information to this parameter (if available) - if c.Debug() && frame.fn.Syntax() != nil { - dbgParam := c.getLocalVariable(frame, param.Object().(*types.Var)) - loc := c.builder.GetCurrentDebugLocation() + if b.Debug() && b.fn.Syntax() != nil { + dbgParam := b.getLocalVariable(param.Object().(*types.Var)) + loc := b.GetCurrentDebugLocation() if len(fields) == 1 { - expr := c.dibuilder.CreateExpression(nil) - c.dibuilder.InsertValueAtEnd(fields[0], dbgParam, expr, loc, entryBlock) + expr := b.dibuilder.CreateExpression(nil) + b.dibuilder.InsertValueAtEnd(fields[0], dbgParam, expr, loc, entryBlock) } else { - fieldOffsets := c.expandFormalParamOffsets(llvmType) + fieldOffsets := b.expandFormalParamOffsets(llvmType) for i, field := range fields { - expr := c.dibuilder.CreateExpression([]int64{ + expr := b.dibuilder.CreateExpression([]int64{ 0x1000, // DW_OP_LLVM_fragment int64(fieldOffsets[i]) * 8, // offset in bits - int64(c.targetData.TypeAllocSize(field.Type())) * 8, // size in bits + int64(b.targetData.TypeAllocSize(field.Type())) * 8, // size in bits }) - c.dibuilder.InsertValueAtEnd(field, dbgParam, expr, loc, entryBlock) + b.dibuilder.InsertValueAtEnd(field, dbgParam, expr, loc, entryBlock) } } } @@ -901,44 +931,44 @@ func (c *Compiler) parseFunc(frame *Frame) { // Load free variables from the context. This is a closure (or bound // method). var context llvm.Value - if !frame.fn.IsExported() { - parentHandle := frame.fn.LLVMFn.LastParam() + if !b.fn.IsExported() { + parentHandle := b.fn.LLVMFn.LastParam() parentHandle.SetName("parentHandle") context = llvm.PrevParam(parentHandle) context.SetName("context") } - if len(frame.fn.FreeVars) != 0 { + if len(b.fn.FreeVars) != 0 { // Get a list of all variable types in the context. - freeVarTypes := make([]llvm.Type, len(frame.fn.FreeVars)) - for i, freeVar := range frame.fn.FreeVars { - freeVarTypes[i] = c.getLLVMType(freeVar.Type()) + freeVarTypes := make([]llvm.Type, len(b.fn.FreeVars)) + for i, freeVar := range b.fn.FreeVars { + freeVarTypes[i] = b.getLLVMType(freeVar.Type()) } // Load each free variable from the context pointer. // A free variable is always a pointer when this is a closure, but it // can be another type when it is a wrapper for a bound method (these // wrappers are generated by the ssa package). - for i, val := range c.emitPointerUnpack(context, freeVarTypes) { - frame.locals[frame.fn.FreeVars[i]] = val + for i, val := range b.emitPointerUnpack(context, freeVarTypes) { + b.locals[b.fn.FreeVars[i]] = val } } - if frame.fn.Recover != nil { + if b.fn.Recover != nil { // This function has deferred function calls. Set some things up for // them. - c.deferInitFunc(frame) + b.deferInitFunc() } // Fill blocks with instructions. - for _, block := range frame.fn.DomPreorder() { - if c.DumpSSA() { + for _, block := range b.fn.DomPreorder() { + if b.DumpSSA() { fmt.Printf("%d: %s:\n", block.Index, block.Comment) } - c.builder.SetInsertPointAtEnd(frame.blockEntries[block]) - frame.currentBlock = block + b.SetInsertPointAtEnd(b.blockEntries[block]) + b.currentBlock = block for _, instr := range block.Instrs { if instr, ok := instr.(*ssa.DebugRef); ok { - if !c.Debug() { + if !b.Debug() { continue } object := instr.Object() @@ -952,103 +982,105 @@ func (c *Compiler) parseFunc(frame *Frame) { // for example. continue } - dbgVar := c.getLocalVariable(frame, variable) - pos := c.ir.Program.Fset.Position(instr.Pos()) - c.dibuilder.InsertValueAtEnd(c.getValue(frame, instr.X), dbgVar, c.dibuilder.CreateExpression(nil), llvm.DebugLoc{ + dbgVar := b.getLocalVariable(variable) + pos := b.ir.Program.Fset.Position(instr.Pos()) + b.dibuilder.InsertValueAtEnd(b.getValue(instr.X), dbgVar, b.dibuilder.CreateExpression(nil), llvm.DebugLoc{ Line: uint(pos.Line), Col: uint(pos.Column), - Scope: frame.difunc, - }, c.builder.GetInsertBlock()) + Scope: b.difunc, + }, b.GetInsertBlock()) continue } - if c.DumpSSA() { + if b.DumpSSA() { if val, ok := instr.(ssa.Value); ok && val.Name() != "" { fmt.Printf("\t%s = %s\n", val.Name(), val.String()) } else { fmt.Printf("\t%s\n", instr.String()) } } - c.parseInstr(frame, instr) + b.createInstruction(instr) } - if frame.fn.Name() == "init" && len(block.Instrs) == 0 { - c.builder.CreateRetVoid() + if b.fn.Name() == "init" && len(block.Instrs) == 0 { + b.CreateRetVoid() } } // Resolve phi nodes - for _, phi := range frame.phis { + for _, phi := range b.phis { block := phi.ssa.Block() for i, edge := range phi.ssa.Edges { - llvmVal := c.getValue(frame, edge) - llvmBlock := frame.blockExits[block.Preds[i]] + llvmVal := b.getValue(edge) + llvmBlock := b.blockExits[block.Preds[i]] phi.llvm.AddIncoming([]llvm.Value{llvmVal}, []llvm.BasicBlock{llvmBlock}) } } } -func (c *Compiler) parseInstr(frame *Frame, instr ssa.Instruction) { - if c.Debug() { - pos := c.ir.Program.Fset.Position(instr.Pos()) - c.builder.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), frame.difunc, llvm.Metadata{}) +// createInstruction builds the LLVM IR equivalent instructions for the +// particular Go SSA instruction. +func (b *builder) createInstruction(instr ssa.Instruction) { + if b.Debug() { + pos := b.ir.Program.Fset.Position(instr.Pos()) + b.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), b.difunc, llvm.Metadata{}) } switch instr := instr.(type) { case ssa.Value: - if value, err := c.parseExpr(frame, instr); err != nil { + if value, err := b.createExpr(instr); err != nil { // This expression could not be parsed. Add the error to the list // of diagnostics and continue with an undef value. // The resulting IR will be incorrect (but valid). However, // compilation can proceed which is useful because there may be // more compilation errors which can then all be shown together to // the user. - c.diagnostics = append(c.diagnostics, err) - frame.locals[instr] = llvm.Undef(c.getLLVMType(instr.Type())) + b.diagnostics = append(b.diagnostics, err) + b.locals[instr] = llvm.Undef(b.getLLVMType(instr.Type())) } else { - frame.locals[instr] = value - if len(*instr.Referrers()) != 0 && c.NeedsStackObjects() { - c.trackExpr(frame, instr, value) + b.locals[instr] = value + if len(*instr.Referrers()) != 0 && b.NeedsStackObjects() { + b.trackExpr(instr, value) } } case *ssa.DebugRef: // ignore case *ssa.Defer: - c.emitDefer(frame, instr) + b.createDefer(instr) case *ssa.Go: // Get all function parameters to pass to the goroutine. var params []llvm.Value for _, param := range instr.Call.Args { - params = append(params, c.getValue(frame, param)) + params = append(params, b.getValue(param)) } // Start a new goroutine. if callee := instr.Call.StaticCallee(); callee != nil { // Static callee is known. This makes it easier to start a new // goroutine. - calleeFn := c.ir.GetFunction(callee) + calleeFn := b.ir.GetFunction(callee) var context llvm.Value switch value := instr.Call.Value.(type) { case *ssa.Function: // Goroutine call is regular function call. No context is necessary. - context = llvm.Undef(c.i8ptrType) + context = llvm.Undef(b.i8ptrType) case *ssa.MakeClosure: // A goroutine call on a func value, but the callee is trivial to find. For // example: immediately applied functions. - funcValue := c.getValue(frame, value) - context = c.extractFuncContext(funcValue) + funcValue := b.getValue(value) + context = b.extractFuncContext(funcValue) default: panic("StaticCallee returned an unexpected value") } params = append(params, context) // context parameter - c.emitStartGoroutine(calleeFn.LLVMFn, params) + b.createGoInstruction(calleeFn.LLVMFn, params) } else if !instr.Call.IsInvoke() { // This is a function pointer. // At the moment, two extra params are passed to the newly started // goroutine: // * The function context, for closures. // * The function pointer (for tasks). - funcPtr, context := c.decodeFuncValue(c.getValue(frame, instr.Call.Value), instr.Call.Value.Type().(*types.Signature)) + funcPtr, context := b.decodeFuncValue(b.getValue(instr.Call.Value), instr.Call.Value.Type().(*types.Signature)) params = append(params, context) // context parameter - switch c.Scheduler() { + switch b.Scheduler() { case "none", "coroutines": // There are no additional parameters needed for the goroutine start operation. case "tasks": @@ -1057,177 +1089,179 @@ func (c *Compiler) parseInstr(frame *Frame, instr ssa.Instruction) { default: panic("unknown scheduler type") } - c.emitStartGoroutine(funcPtr, params) + b.createGoInstruction(funcPtr, params) } else { - c.addError(instr.Pos(), "todo: go on interface call") + b.addError(instr.Pos(), "todo: go on interface call") } case *ssa.If: - cond := c.getValue(frame, instr.Cond) + cond := b.getValue(instr.Cond) block := instr.Block() - blockThen := frame.blockEntries[block.Succs[0]] - blockElse := frame.blockEntries[block.Succs[1]] - c.builder.CreateCondBr(cond, blockThen, blockElse) + blockThen := b.blockEntries[block.Succs[0]] + blockElse := b.blockEntries[block.Succs[1]] + b.CreateCondBr(cond, blockThen, blockElse) case *ssa.Jump: - blockJump := frame.blockEntries[instr.Block().Succs[0]] - c.builder.CreateBr(blockJump) + blockJump := b.blockEntries[instr.Block().Succs[0]] + b.CreateBr(blockJump) case *ssa.MapUpdate: - m := c.getValue(frame, instr.Map) - key := c.getValue(frame, instr.Key) - value := c.getValue(frame, instr.Value) + m := b.getValue(instr.Map) + key := b.getValue(instr.Key) + value := b.getValue(instr.Value) mapType := instr.Map.Type().Underlying().(*types.Map) - c.emitMapUpdate(mapType.Key(), m, key, value, instr.Pos()) + b.createMapUpdate(mapType.Key(), m, key, value, instr.Pos()) case *ssa.Panic: - value := c.getValue(frame, instr.X) - c.createRuntimeCall("_panic", []llvm.Value{value}, "") - c.builder.CreateUnreachable() + value := b.getValue(instr.X) + b.createRuntimeCall("_panic", []llvm.Value{value}, "") + b.CreateUnreachable() case *ssa.Return: if len(instr.Results) == 0 { - c.builder.CreateRetVoid() + b.CreateRetVoid() } else if len(instr.Results) == 1 { - c.builder.CreateRet(c.getValue(frame, instr.Results[0])) + b.CreateRet(b.getValue(instr.Results[0])) } else { // Multiple return values. Put them all in a struct. - retVal := llvm.ConstNull(frame.fn.LLVMFn.Type().ElementType().ReturnType()) + retVal := llvm.ConstNull(b.fn.LLVMFn.Type().ElementType().ReturnType()) for i, result := range instr.Results { - val := c.getValue(frame, result) - retVal = c.builder.CreateInsertValue(retVal, val, i, "") + val := b.getValue(result) + retVal = b.CreateInsertValue(retVal, val, i, "") } - c.builder.CreateRet(retVal) + b.CreateRet(retVal) } case *ssa.RunDefers: - c.emitRunDefers(frame) + b.createRunDefers() case *ssa.Send: - c.emitChanSend(frame, instr) + b.createChanSend(instr) case *ssa.Store: - llvmAddr := c.getValue(frame, instr.Addr) - llvmVal := c.getValue(frame, instr.Val) - c.emitNilCheck(frame, llvmAddr, "store") - if c.targetData.TypeAllocSize(llvmVal.Type()) == 0 { + llvmAddr := b.getValue(instr.Addr) + llvmVal := b.getValue(instr.Val) + b.createNilCheck(llvmAddr, "store") + if b.targetData.TypeAllocSize(llvmVal.Type()) == 0 { // nothing to store return } - c.builder.CreateStore(llvmVal, llvmAddr) + b.CreateStore(llvmVal, llvmAddr) default: - c.addError(instr.Pos(), "unknown instruction: "+instr.String()) + b.addError(instr.Pos(), "unknown instruction: "+instr.String()) } } -func (c *Compiler) parseBuiltin(frame *Frame, args []ssa.Value, callName string, pos token.Pos) (llvm.Value, error) { +// createBuiltin lowers a builtin Go function (append, close, delete, etc.) to +// LLVM IR. It uses runtime calls for some builtins. +func (b *builder) createBuiltin(args []ssa.Value, callName string, pos token.Pos) (llvm.Value, error) { switch callName { case "append": - src := c.getValue(frame, args[0]) - elems := c.getValue(frame, args[1]) - srcBuf := c.builder.CreateExtractValue(src, 0, "append.srcBuf") - srcPtr := c.builder.CreateBitCast(srcBuf, c.i8ptrType, "append.srcPtr") - srcLen := c.builder.CreateExtractValue(src, 1, "append.srcLen") - srcCap := c.builder.CreateExtractValue(src, 2, "append.srcCap") - elemsBuf := c.builder.CreateExtractValue(elems, 0, "append.elemsBuf") - elemsPtr := c.builder.CreateBitCast(elemsBuf, c.i8ptrType, "append.srcPtr") - elemsLen := c.builder.CreateExtractValue(elems, 1, "append.elemsLen") + src := b.getValue(args[0]) + elems := b.getValue(args[1]) + srcBuf := b.CreateExtractValue(src, 0, "append.srcBuf") + srcPtr := b.CreateBitCast(srcBuf, b.i8ptrType, "append.srcPtr") + srcLen := b.CreateExtractValue(src, 1, "append.srcLen") + srcCap := b.CreateExtractValue(src, 2, "append.srcCap") + elemsBuf := b.CreateExtractValue(elems, 0, "append.elemsBuf") + elemsPtr := b.CreateBitCast(elemsBuf, b.i8ptrType, "append.srcPtr") + elemsLen := b.CreateExtractValue(elems, 1, "append.elemsLen") elemType := srcBuf.Type().ElementType() - elemSize := llvm.ConstInt(c.uintptrType, c.targetData.TypeAllocSize(elemType), false) - result := c.createRuntimeCall("sliceAppend", []llvm.Value{srcPtr, elemsPtr, srcLen, srcCap, elemsLen, elemSize}, "append.new") - newPtr := c.builder.CreateExtractValue(result, 0, "append.newPtr") - newBuf := c.builder.CreateBitCast(newPtr, srcBuf.Type(), "append.newBuf") - newLen := c.builder.CreateExtractValue(result, 1, "append.newLen") - newCap := c.builder.CreateExtractValue(result, 2, "append.newCap") + elemSize := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(elemType), false) + result := b.createRuntimeCall("sliceAppend", []llvm.Value{srcPtr, elemsPtr, srcLen, srcCap, elemsLen, elemSize}, "append.new") + newPtr := b.CreateExtractValue(result, 0, "append.newPtr") + newBuf := b.CreateBitCast(newPtr, srcBuf.Type(), "append.newBuf") + newLen := b.CreateExtractValue(result, 1, "append.newLen") + newCap := b.CreateExtractValue(result, 2, "append.newCap") newSlice := llvm.Undef(src.Type()) - newSlice = c.builder.CreateInsertValue(newSlice, newBuf, 0, "") - newSlice = c.builder.CreateInsertValue(newSlice, newLen, 1, "") - newSlice = c.builder.CreateInsertValue(newSlice, newCap, 2, "") + newSlice = b.CreateInsertValue(newSlice, newBuf, 0, "") + newSlice = b.CreateInsertValue(newSlice, newLen, 1, "") + newSlice = b.CreateInsertValue(newSlice, newCap, 2, "") return newSlice, nil case "cap": - value := c.getValue(frame, args[0]) + value := b.getValue(args[0]) var llvmCap llvm.Value switch args[0].Type().(type) { case *types.Chan: // Channel. Buffered channels haven't been implemented yet so always // return 0. - llvmCap = llvm.ConstInt(c.intType, 0, false) + llvmCap = llvm.ConstInt(b.intType, 0, false) case *types.Slice: - llvmCap = c.builder.CreateExtractValue(value, 2, "cap") + llvmCap = b.CreateExtractValue(value, 2, "cap") default: - return llvm.Value{}, c.makeError(pos, "todo: cap: unknown type") + return llvm.Value{}, b.makeError(pos, "todo: cap: unknown type") } - if c.targetData.TypeAllocSize(llvmCap.Type()) < c.targetData.TypeAllocSize(c.intType) { - llvmCap = c.builder.CreateZExt(llvmCap, c.intType, "len.int") + if b.targetData.TypeAllocSize(llvmCap.Type()) < b.targetData.TypeAllocSize(b.intType) { + llvmCap = b.CreateZExt(llvmCap, b.intType, "len.int") } return llvmCap, nil case "close": - c.emitChanClose(frame, args[0]) + b.createChanClose(args[0]) return llvm.Value{}, nil case "complex": - r := c.getValue(frame, args[0]) - i := c.getValue(frame, args[1]) + r := b.getValue(args[0]) + i := b.getValue(args[1]) t := args[0].Type().Underlying().(*types.Basic) var cplx llvm.Value switch t.Kind() { case types.Float32: - cplx = llvm.Undef(c.ctx.StructType([]llvm.Type{c.ctx.FloatType(), c.ctx.FloatType()}, false)) + cplx = llvm.Undef(b.ctx.StructType([]llvm.Type{b.ctx.FloatType(), b.ctx.FloatType()}, false)) case types.Float64: - cplx = llvm.Undef(c.ctx.StructType([]llvm.Type{c.ctx.DoubleType(), c.ctx.DoubleType()}, false)) + cplx = llvm.Undef(b.ctx.StructType([]llvm.Type{b.ctx.DoubleType(), b.ctx.DoubleType()}, false)) default: - return llvm.Value{}, c.makeError(pos, "unsupported type in complex builtin: "+t.String()) + return llvm.Value{}, b.makeError(pos, "unsupported type in complex builtin: "+t.String()) } - cplx = c.builder.CreateInsertValue(cplx, r, 0, "") - cplx = c.builder.CreateInsertValue(cplx, i, 1, "") + cplx = b.CreateInsertValue(cplx, r, 0, "") + cplx = b.CreateInsertValue(cplx, i, 1, "") return cplx, nil case "copy": - dst := c.getValue(frame, args[0]) - src := c.getValue(frame, args[1]) - dstLen := c.builder.CreateExtractValue(dst, 1, "copy.dstLen") - srcLen := c.builder.CreateExtractValue(src, 1, "copy.srcLen") - dstBuf := c.builder.CreateExtractValue(dst, 0, "copy.dstArray") - srcBuf := c.builder.CreateExtractValue(src, 0, "copy.srcArray") + dst := b.getValue(args[0]) + src := b.getValue(args[1]) + dstLen := b.CreateExtractValue(dst, 1, "copy.dstLen") + srcLen := b.CreateExtractValue(src, 1, "copy.srcLen") + dstBuf := b.CreateExtractValue(dst, 0, "copy.dstArray") + srcBuf := b.CreateExtractValue(src, 0, "copy.srcArray") elemType := dstBuf.Type().ElementType() - dstBuf = c.builder.CreateBitCast(dstBuf, c.i8ptrType, "copy.dstPtr") - srcBuf = c.builder.CreateBitCast(srcBuf, c.i8ptrType, "copy.srcPtr") - elemSize := llvm.ConstInt(c.uintptrType, c.targetData.TypeAllocSize(elemType), false) - return c.createRuntimeCall("sliceCopy", []llvm.Value{dstBuf, srcBuf, dstLen, srcLen, elemSize}, "copy.n"), nil + dstBuf = b.CreateBitCast(dstBuf, b.i8ptrType, "copy.dstPtr") + srcBuf = b.CreateBitCast(srcBuf, b.i8ptrType, "copy.srcPtr") + elemSize := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(elemType), false) + return b.createRuntimeCall("sliceCopy", []llvm.Value{dstBuf, srcBuf, dstLen, srcLen, elemSize}, "copy.n"), nil case "delete": - m := c.getValue(frame, args[0]) - key := c.getValue(frame, args[1]) - return llvm.Value{}, c.emitMapDelete(args[1].Type(), m, key, pos) + m := b.getValue(args[0]) + key := b.getValue(args[1]) + return llvm.Value{}, b.createMapDelete(args[1].Type(), m, key, pos) case "imag": - cplx := c.getValue(frame, args[0]) - return c.builder.CreateExtractValue(cplx, 1, "imag"), nil + cplx := b.getValue(args[0]) + return b.CreateExtractValue(cplx, 1, "imag"), nil case "len": - value := c.getValue(frame, args[0]) + value := b.getValue(args[0]) var llvmLen llvm.Value switch args[0].Type().Underlying().(type) { case *types.Basic, *types.Slice: // string or slice - llvmLen = c.builder.CreateExtractValue(value, 1, "len") + llvmLen = b.CreateExtractValue(value, 1, "len") case *types.Chan: // Channel. Buffered channels haven't been implemented yet so always // return 0. - llvmLen = llvm.ConstInt(c.intType, 0, false) + llvmLen = llvm.ConstInt(b.intType, 0, false) case *types.Map: - llvmLen = c.createRuntimeCall("hashmapLen", []llvm.Value{value}, "len") + llvmLen = b.createRuntimeCall("hashmapLen", []llvm.Value{value}, "len") default: - return llvm.Value{}, c.makeError(pos, "todo: len: unknown type") + return llvm.Value{}, b.makeError(pos, "todo: len: unknown type") } - if c.targetData.TypeAllocSize(llvmLen.Type()) < c.targetData.TypeAllocSize(c.intType) { - llvmLen = c.builder.CreateZExt(llvmLen, c.intType, "len.int") + if b.targetData.TypeAllocSize(llvmLen.Type()) < b.targetData.TypeAllocSize(b.intType) { + llvmLen = b.CreateZExt(llvmLen, b.intType, "len.int") } return llvmLen, nil case "print", "println": for i, arg := range args { if i >= 1 && callName == "println" { - c.createRuntimeCall("printspace", nil, "") + b.createRuntimeCall("printspace", nil, "") } - value := c.getValue(frame, arg) + value := b.getValue(arg) typ := arg.Type().Underlying() switch typ := typ.(type) { case *types.Basic: switch typ.Kind() { case types.String, types.UntypedString: - c.createRuntimeCall("printstring", []llvm.Value{value}, "") + b.createRuntimeCall("printstring", []llvm.Value{value}, "") case types.Uintptr: - c.createRuntimeCall("printptr", []llvm.Value{value}, "") + b.createRuntimeCall("printptr", []llvm.Value{value}, "") case types.UnsafePointer: - ptrValue := c.builder.CreatePtrToInt(value, c.uintptrType, "") - c.createRuntimeCall("printptr", []llvm.Value{ptrValue}, "") + ptrValue := b.CreatePtrToInt(value, b.uintptrType, "") + b.createRuntimeCall("printptr", []llvm.Value{ptrValue}, "") default: // runtime.print{int,uint}{8,16,32,64} if typ.Info()&types.IsInteger != 0 { @@ -1237,155 +1271,159 @@ func (c *Compiler) parseBuiltin(frame *Frame, args []ssa.Value, callName string, } else { name += "int" } - name += strconv.FormatUint(c.targetData.TypeAllocSize(value.Type())*8, 10) - c.createRuntimeCall(name, []llvm.Value{value}, "") + name += strconv.FormatUint(b.targetData.TypeAllocSize(value.Type())*8, 10) + b.createRuntimeCall(name, []llvm.Value{value}, "") } else if typ.Kind() == types.Bool { - c.createRuntimeCall("printbool", []llvm.Value{value}, "") + b.createRuntimeCall("printbool", []llvm.Value{value}, "") } else if typ.Kind() == types.Float32 { - c.createRuntimeCall("printfloat32", []llvm.Value{value}, "") + b.createRuntimeCall("printfloat32", []llvm.Value{value}, "") } else if typ.Kind() == types.Float64 { - c.createRuntimeCall("printfloat64", []llvm.Value{value}, "") + b.createRuntimeCall("printfloat64", []llvm.Value{value}, "") } else if typ.Kind() == types.Complex64 { - c.createRuntimeCall("printcomplex64", []llvm.Value{value}, "") + b.createRuntimeCall("printcomplex64", []llvm.Value{value}, "") } else if typ.Kind() == types.Complex128 { - c.createRuntimeCall("printcomplex128", []llvm.Value{value}, "") + b.createRuntimeCall("printcomplex128", []llvm.Value{value}, "") } else { - return llvm.Value{}, c.makeError(pos, "unknown basic arg type: "+typ.String()) + return llvm.Value{}, b.makeError(pos, "unknown basic arg type: "+typ.String()) } } case *types.Interface: - c.createRuntimeCall("printitf", []llvm.Value{value}, "") + b.createRuntimeCall("printitf", []llvm.Value{value}, "") case *types.Map: - c.createRuntimeCall("printmap", []llvm.Value{value}, "") + b.createRuntimeCall("printmap", []llvm.Value{value}, "") case *types.Pointer: - ptrValue := c.builder.CreatePtrToInt(value, c.uintptrType, "") - c.createRuntimeCall("printptr", []llvm.Value{ptrValue}, "") + ptrValue := b.CreatePtrToInt(value, b.uintptrType, "") + b.createRuntimeCall("printptr", []llvm.Value{ptrValue}, "") default: - return llvm.Value{}, c.makeError(pos, "unknown arg type: "+typ.String()) + return llvm.Value{}, b.makeError(pos, "unknown arg type: "+typ.String()) } } if callName == "println" { - c.createRuntimeCall("printnl", nil, "") + b.createRuntimeCall("printnl", nil, "") } return llvm.Value{}, nil // print() or println() returns void case "real": - cplx := c.getValue(frame, args[0]) - return c.builder.CreateExtractValue(cplx, 0, "real"), nil + cplx := b.getValue(args[0]) + return b.CreateExtractValue(cplx, 0, "real"), nil case "recover": - return c.createRuntimeCall("_recover", nil, ""), nil + return b.createRuntimeCall("_recover", nil, ""), nil case "ssa:wrapnilchk": // TODO: do an actual nil check? - return c.getValue(frame, args[0]), nil + return b.getValue(args[0]), nil default: - return llvm.Value{}, c.makeError(pos, "todo: builtin: "+callName) + return llvm.Value{}, b.makeError(pos, "todo: builtin: "+callName) } } -func (c *Compiler) parseFunctionCall(frame *Frame, args []ssa.Value, llvmFn, context llvm.Value, exported bool) llvm.Value { - var params []llvm.Value - for _, param := range args { - params = append(params, c.getValue(frame, param)) - } - - if !exported { - // This function takes a context parameter. - // Add it to the end of the parameter list. - params = append(params, context) - - // Parent coroutine handle. - params = append(params, llvm.Undef(c.i8ptrType)) - } - - return c.createCall(llvmFn, params, "") -} - -func (c *Compiler) parseCall(frame *Frame, instr *ssa.CallCommon) (llvm.Value, error) { +// createFunctionCall lowers a Go SSA call instruction (to a simple function, +// closure, function pointer, builtin, method, etc.) to LLVM IR, usually a call +// instruction. +// +// This is also where compiler intrinsics are implemented. +func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error) { if instr.IsInvoke() { - fnCast, args := c.getInvokeCall(frame, instr) - return c.createCall(fnCast, args, ""), nil + fnCast, args := b.getInvokeCall(instr) + return b.createCall(fnCast, args, ""), nil } // Try to call the function directly for trivially static calls. + var callee, context llvm.Value + exported := false if fn := instr.StaticCallee(); fn != nil { + // Direct function call, either to a named or anonymous (directly + // applied) function call. If it is anonymous, it may be a closure. name := fn.RelString(nil) switch { case name == "device/arm.ReadRegister" || name == "device/riscv.ReadRegister": - return c.emitReadRegister(name, instr.Args) + return b.createReadRegister(name, instr.Args) case name == "device/arm.Asm" || name == "device/avr.Asm" || name == "device/riscv.Asm": - return c.emitAsm(instr.Args) + return b.createInlineAsm(instr.Args) case name == "device/arm.AsmFull" || name == "device/avr.AsmFull" || name == "device/riscv.AsmFull": - return c.emitAsmFull(frame, instr) + return b.createInlineAsmFull(instr) case strings.HasPrefix(name, "device/arm.SVCall"): - return c.emitSVCall(frame, instr.Args) + return b.emitSVCall(instr.Args) case strings.HasPrefix(name, "(device/riscv.CSR)."): - return c.emitCSROperation(frame, instr) + return b.emitCSROperation(instr) case strings.HasPrefix(name, "syscall.Syscall"): - return c.emitSyscall(frame, instr) + return b.createSyscall(instr) case strings.HasPrefix(name, "runtime/volatile.Load"): - return c.emitVolatileLoad(frame, instr) + return b.createVolatileLoad(instr) case strings.HasPrefix(name, "runtime/volatile.Store"): - return c.emitVolatileStore(frame, instr) + return b.createVolatileStore(instr) case name == "runtime/interrupt.New": - return c.emitInterruptGlobal(frame, instr) + return b.createInterruptGlobal(instr) } - targetFunc := c.ir.GetFunction(fn) + targetFunc := b.ir.GetFunction(fn) if targetFunc.LLVMFn.IsNil() { - return llvm.Value{}, c.makeError(instr.Pos(), "undefined function: "+targetFunc.LinkName()) + return llvm.Value{}, b.makeError(instr.Pos(), "undefined function: "+targetFunc.LinkName()) } - var context llvm.Value switch value := instr.Value.(type) { case *ssa.Function: // Regular function call. No context is necessary. - context = llvm.Undef(c.i8ptrType) + context = llvm.Undef(b.i8ptrType) case *ssa.MakeClosure: // A call on a func value, but the callee is trivial to find. For // example: immediately applied functions. - funcValue := c.getValue(frame, value) - context = c.extractFuncContext(funcValue) + funcValue := b.getValue(value) + context = b.extractFuncContext(funcValue) default: panic("StaticCallee returned an unexpected value") } - return c.parseFunctionCall(frame, instr.Args, targetFunc.LLVMFn, context, targetFunc.IsExported()), nil - } - - // Builtin or function pointer. - switch call := instr.Value.(type) { - case *ssa.Builtin: - return c.parseBuiltin(frame, instr.Args, call.Name(), instr.Pos()) - default: // function pointer - value := c.getValue(frame, instr.Value) + callee = targetFunc.LLVMFn + exported = targetFunc.IsExported() + } else if call, ok := instr.Value.(*ssa.Builtin); ok { + // Builtin function (append, close, delete, etc.).) + return b.createBuiltin(instr.Args, call.Name(), instr.Pos()) + } else { + // Function pointer. + value := b.getValue(instr.Value) // This is a func value, which cannot be called directly. We have to // extract the function pointer and context first from the func value. - funcPtr, context := c.decodeFuncValue(value, instr.Value.Type().Underlying().(*types.Signature)) - c.emitNilCheck(frame, funcPtr, "fpcall") - return c.parseFunctionCall(frame, instr.Args, funcPtr, context, false), nil + callee, context = b.decodeFuncValue(value, instr.Value.Type().Underlying().(*types.Signature)) + b.createNilCheck(callee, "fpcall") + } + + var params []llvm.Value + for _, param := range instr.Args { + params = append(params, b.getValue(param)) } + + if !exported { + // This function takes a context parameter. + // Add it to the end of the parameter list. + params = append(params, context) + + // Parent coroutine handle. + params = append(params, llvm.Undef(b.i8ptrType)) + } + + return b.createCall(callee, params, ""), nil } // getValue returns the LLVM value of a constant, function value, global, or // already processed SSA expression. -func (c *Compiler) getValue(frame *Frame, expr ssa.Value) llvm.Value { +func (b *builder) getValue(expr ssa.Value) llvm.Value { switch expr := expr.(type) { case *ssa.Const: - return c.parseConst(frame.fn.LinkName(), expr) + return b.createConst(b.fn.LinkName(), expr) case *ssa.Function: - fn := c.ir.GetFunction(expr) + fn := b.ir.GetFunction(expr) if fn.IsExported() { - c.addError(expr.Pos(), "cannot use an exported function as value: "+expr.String()) - return llvm.Undef(c.getLLVMType(expr.Type())) + b.addError(expr.Pos(), "cannot use an exported function as value: "+expr.String()) + return llvm.Undef(b.getLLVMType(expr.Type())) } - return c.createFuncValue(fn.LLVMFn, llvm.Undef(c.i8ptrType), fn.Signature) + return b.createFuncValue(fn.LLVMFn, llvm.Undef(b.i8ptrType), fn.Signature) case *ssa.Global: - value := c.getGlobal(expr) + value := b.getGlobal(expr) if value.IsNil() { - c.addError(expr.Pos(), "global not found: "+expr.RelString(nil)) - return llvm.Undef(c.getLLVMType(expr.Type())) + b.addError(expr.Pos(), "global not found: "+expr.RelString(nil)) + return llvm.Undef(b.getLLVMType(expr.Type())) } return value default: // other (local) SSA value - if value, ok := frame.locals[expr]; ok { + if value, ok := b.locals[expr]; ok { return value } else { // indicates a compiler bug @@ -1394,43 +1432,42 @@ func (c *Compiler) getValue(frame *Frame, expr ssa.Value) llvm.Value { } } -// parseExpr translates a Go SSA expression to a LLVM instruction. -func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) { - if _, ok := frame.locals[expr]; ok { +// createExpr translates a Go SSA expression to LLVM IR. This can be zero, one, +// or multiple LLVM IR instructions and/or runtime calls. +func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) { + if _, ok := b.locals[expr]; ok { // sanity check - panic("local has already been parsed: " + expr.String()) + panic("instruction has already been created: " + expr.String()) } switch expr := expr.(type) { case *ssa.Alloc: - typ := c.getLLVMType(expr.Type().Underlying().(*types.Pointer).Elem()) + typ := b.getLLVMType(expr.Type().Underlying().(*types.Pointer).Elem()) if expr.Heap { - size := c.targetData.TypeAllocSize(typ) + size := b.targetData.TypeAllocSize(typ) // Calculate ^uintptr(0) - maxSize := llvm.ConstNot(llvm.ConstInt(c.uintptrType, 0, false)).ZExtValue() + maxSize := llvm.ConstNot(llvm.ConstInt(b.uintptrType, 0, false)).ZExtValue() if size > maxSize { // Size would be truncated if truncated to uintptr. - return llvm.Value{}, c.makeError(expr.Pos(), fmt.Sprintf("value is too big (%v bytes)", size)) + return llvm.Value{}, b.makeError(expr.Pos(), fmt.Sprintf("value is too big (%v bytes)", size)) } - sizeValue := llvm.ConstInt(c.uintptrType, size, false) - buf := c.createRuntimeCall("alloc", []llvm.Value{sizeValue}, expr.Comment) - buf = c.builder.CreateBitCast(buf, llvm.PointerType(typ, 0), "") + sizeValue := llvm.ConstInt(b.uintptrType, size, false) + buf := b.createRuntimeCall("alloc", []llvm.Value{sizeValue}, expr.Comment) + buf = b.CreateBitCast(buf, llvm.PointerType(typ, 0), "") return buf, nil } else { - buf := llvmutil.CreateEntryBlockAlloca(c.builder, typ, expr.Comment) - if c.targetData.TypeAllocSize(typ) != 0 { - c.builder.CreateStore(llvm.ConstNull(typ), buf) // zero-initialize var + buf := llvmutil.CreateEntryBlockAlloca(b.Builder, typ, expr.Comment) + if b.targetData.TypeAllocSize(typ) != 0 { + b.CreateStore(llvm.ConstNull(typ), buf) // zero-initialize var } return buf, nil } case *ssa.BinOp: - x := c.getValue(frame, expr.X) - y := c.getValue(frame, expr.Y) - return c.parseBinOp(expr.Op, expr.X.Type(), x, y, expr.Pos()) + x := b.getValue(expr.X) + y := b.getValue(expr.Y) + return b.createBinOp(expr.Op, expr.X.Type(), x, y, expr.Pos()) case *ssa.Call: - // Passing the current task here to the subroutine. It is only used when - // the subroutine is blocking. - return c.parseCall(frame, expr.Common()) + return b.createFunctionCall(expr.Common()) case *ssa.ChangeInterface: // Do not change between interface types: always use the underlying // (concrete) type in the type number of the interface. Every method @@ -1438,13 +1475,13 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) { // This is different from how the official Go compiler works, because of // heap allocation and because it's easier to implement, see: // https://research.swtch.com/interfaces - return c.getValue(frame, expr.X), nil + return b.getValue(expr.X), nil case *ssa.ChangeType: // This instruction changes the type, but the underlying value remains // the same. This is often a no-op, but sometimes we have to change the // LLVM type as well. - x := c.getValue(frame, expr.X) - llvmType := c.getLLVMType(expr.Type()) + x := b.getValue(expr.X) + llvmType := b.getLLVMType(expr.Type()) if x.Type() == llvmType { // Different Go type but same LLVM type (for example, named int). // This is the common case. @@ -1458,70 +1495,70 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) { // values from the previous struct in there. value := llvm.Undef(llvmType) for i := 0; i < llvmType.StructElementTypesCount(); i++ { - field := c.builder.CreateExtractValue(x, i, "changetype.field") - value = c.builder.CreateInsertValue(value, field, i, "changetype.struct") + field := b.CreateExtractValue(x, i, "changetype.field") + value = b.CreateInsertValue(value, field, i, "changetype.struct") } return value, nil case llvm.PointerTypeKind: // This can happen with pointers to structs. This case is easy: // simply bitcast the pointer to the destination type. - return c.builder.CreateBitCast(x, llvmType, "changetype.pointer"), nil + return b.CreateBitCast(x, llvmType, "changetype.pointer"), nil default: return llvm.Value{}, errors.New("todo: unknown ChangeType type: " + expr.X.Type().String()) } case *ssa.Const: panic("const is not an expression") case *ssa.Convert: - x := c.getValue(frame, expr.X) - return c.parseConvert(expr.X.Type(), expr.Type(), x, expr.Pos()) + x := b.getValue(expr.X) + return b.createConvert(expr.X.Type(), expr.Type(), x, expr.Pos()) case *ssa.Extract: if _, ok := expr.Tuple.(*ssa.Select); ok { - return c.getChanSelectResult(frame, expr), nil + return b.getChanSelectResult(expr), nil } - value := c.getValue(frame, expr.Tuple) - return c.builder.CreateExtractValue(value, expr.Index, ""), nil + value := b.getValue(expr.Tuple) + return b.CreateExtractValue(value, expr.Index, ""), nil case *ssa.Field: - value := c.getValue(frame, expr.X) - result := c.builder.CreateExtractValue(value, expr.Field, "") + value := b.getValue(expr.X) + result := b.CreateExtractValue(value, expr.Field, "") return result, nil case *ssa.FieldAddr: - val := c.getValue(frame, expr.X) + val := b.getValue(expr.X) // Check for nil pointer before calculating the address, from the spec: // > For an operand x of type T, the address operation &x generates a // > pointer of type *T to x. [...] If the evaluation of x would cause a // > run-time panic, then the evaluation of &x does too. - c.emitNilCheck(frame, val, "gep") + b.createNilCheck(val, "gep") // Do a GEP on the pointer to get the field address. indices := []llvm.Value{ - llvm.ConstInt(c.ctx.Int32Type(), 0, false), - llvm.ConstInt(c.ctx.Int32Type(), uint64(expr.Field), false), + llvm.ConstInt(b.ctx.Int32Type(), 0, false), + llvm.ConstInt(b.ctx.Int32Type(), uint64(expr.Field), false), } - return c.builder.CreateInBoundsGEP(val, indices, ""), nil + return b.CreateInBoundsGEP(val, indices, ""), nil case *ssa.Function: panic("function is not an expression") case *ssa.Global: panic("global is not an expression") case *ssa.Index: - array := c.getValue(frame, expr.X) - index := c.getValue(frame, expr.Index) + array := b.getValue(expr.X) + index := b.getValue(expr.Index) // Check bounds. arrayLen := expr.X.Type().(*types.Array).Len() - arrayLenLLVM := llvm.ConstInt(c.uintptrType, uint64(arrayLen), false) - c.emitLookupBoundsCheck(frame, arrayLenLLVM, index, expr.Index.Type()) + arrayLenLLVM := llvm.ConstInt(b.uintptrType, uint64(arrayLen), false) + b.createLookupBoundsCheck(arrayLenLLVM, index, expr.Index.Type()) // Can't load directly from array (as index is non-constant), so have to // do it using an alloca+gep+load. - alloca, allocaPtr, allocaSize := c.createTemporaryAlloca(array.Type(), "index.alloca") - c.builder.CreateStore(array, alloca) - zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false) - ptr := c.builder.CreateInBoundsGEP(alloca, []llvm.Value{zero, index}, "index.gep") - result := c.builder.CreateLoad(ptr, "index.load") - c.emitLifetimeEnd(allocaPtr, allocaSize) + alloca, allocaPtr, allocaSize := b.createTemporaryAlloca(array.Type(), "index.alloca") + b.CreateStore(array, alloca) + zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false) + ptr := b.CreateInBoundsGEP(alloca, []llvm.Value{zero, index}, "index.gep") + result := b.CreateLoad(ptr, "index.load") + b.emitLifetimeEnd(allocaPtr, allocaSize) return result, nil case *ssa.IndexAddr: - val := c.getValue(frame, expr.X) - index := c.getValue(frame, expr.Index) + val := b.getValue(expr.X) + index := b.getValue(expr.Index) // Get buffer pointer and length var bufptr, buflen llvm.Value @@ -1531,42 +1568,42 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) { switch typ := typ.(type) { case *types.Array: bufptr = val - buflen = llvm.ConstInt(c.uintptrType, uint64(typ.Len()), false) + buflen = llvm.ConstInt(b.uintptrType, uint64(typ.Len()), false) // Check for nil pointer before calculating the address, from // the spec: // > For an operand x of type T, the address operation &x // > generates a pointer of type *T to x. [...] If the // > evaluation of x would cause a run-time panic, then the // > evaluation of &x does too. - c.emitNilCheck(frame, bufptr, "gep") + b.createNilCheck(bufptr, "gep") default: - return llvm.Value{}, c.makeError(expr.Pos(), "todo: indexaddr: "+typ.String()) + return llvm.Value{}, b.makeError(expr.Pos(), "todo: indexaddr: "+typ.String()) } case *types.Slice: - bufptr = c.builder.CreateExtractValue(val, 0, "indexaddr.ptr") - buflen = c.builder.CreateExtractValue(val, 1, "indexaddr.len") + bufptr = b.CreateExtractValue(val, 0, "indexaddr.ptr") + buflen = b.CreateExtractValue(val, 1, "indexaddr.len") default: - return llvm.Value{}, c.makeError(expr.Pos(), "todo: indexaddr: "+ptrTyp.String()) + return llvm.Value{}, b.makeError(expr.Pos(), "todo: indexaddr: "+ptrTyp.String()) } // Bounds check. - c.emitLookupBoundsCheck(frame, buflen, index, expr.Index.Type()) + b.createLookupBoundsCheck(buflen, index, expr.Index.Type()) switch expr.X.Type().Underlying().(type) { case *types.Pointer: indices := []llvm.Value{ - llvm.ConstInt(c.ctx.Int32Type(), 0, false), + llvm.ConstInt(b.ctx.Int32Type(), 0, false), index, } - return c.builder.CreateInBoundsGEP(bufptr, indices, ""), nil + return b.CreateInBoundsGEP(bufptr, indices, ""), nil case *types.Slice: - return c.builder.CreateInBoundsGEP(bufptr, []llvm.Value{index}, ""), nil + return b.CreateInBoundsGEP(bufptr, []llvm.Value{index}, ""), nil default: panic("unreachable") } case *ssa.Lookup: - value := c.getValue(frame, expr.X) - index := c.getValue(frame, expr.Index) + value := b.getValue(expr.X) + index := b.getValue(expr.Index) switch xType := expr.X.Type().Underlying().(type) { case *types.Basic: // Value type must be a string, which is a basic type. @@ -1575,153 +1612,153 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) { } // Bounds check. - length := c.builder.CreateExtractValue(value, 1, "len") - c.emitLookupBoundsCheck(frame, length, index, expr.Index.Type()) + length := b.CreateExtractValue(value, 1, "len") + b.createLookupBoundsCheck(length, index, expr.Index.Type()) // Lookup byte - buf := c.builder.CreateExtractValue(value, 0, "") - bufPtr := c.builder.CreateInBoundsGEP(buf, []llvm.Value{index}, "") - return c.builder.CreateLoad(bufPtr, ""), nil + buf := b.CreateExtractValue(value, 0, "") + bufPtr := b.CreateInBoundsGEP(buf, []llvm.Value{index}, "") + return b.CreateLoad(bufPtr, ""), nil case *types.Map: valueType := expr.Type() if expr.CommaOk { valueType = valueType.(*types.Tuple).At(0).Type() } - return c.emitMapLookup(xType.Key(), valueType, value, index, expr.CommaOk, expr.Pos()) + return b.createMapLookup(xType.Key(), valueType, value, index, expr.CommaOk, expr.Pos()) default: panic("unknown lookup type: " + expr.String()) } case *ssa.MakeChan: - return c.emitMakeChan(frame, expr), nil + return b.createMakeChan(expr), nil case *ssa.MakeClosure: - return c.parseMakeClosure(frame, expr) + return b.parseMakeClosure(expr) case *ssa.MakeInterface: - val := c.getValue(frame, expr.X) - return c.parseMakeInterface(val, expr.X.Type(), expr.Pos()), nil + val := b.getValue(expr.X) + return b.createMakeInterface(val, expr.X.Type(), expr.Pos()), nil case *ssa.MakeMap: - return c.emitMakeMap(frame, expr) + return b.createMakeMap(expr) case *ssa.MakeSlice: - sliceLen := c.getValue(frame, expr.Len) - sliceCap := c.getValue(frame, expr.Cap) + sliceLen := b.getValue(expr.Len) + sliceCap := b.getValue(expr.Cap) sliceType := expr.Type().Underlying().(*types.Slice) - llvmElemType := c.getLLVMType(sliceType.Elem()) - elemSize := c.targetData.TypeAllocSize(llvmElemType) - elemSizeValue := llvm.ConstInt(c.uintptrType, elemSize, false) + llvmElemType := b.getLLVMType(sliceType.Elem()) + elemSize := b.targetData.TypeAllocSize(llvmElemType) + elemSizeValue := llvm.ConstInt(b.uintptrType, elemSize, false) // Calculate (^uintptr(0)) >> 1, which is the max value that fits in // uintptr if uintptr were signed. - maxSize := llvm.ConstLShr(llvm.ConstNot(llvm.ConstInt(c.uintptrType, 0, false)), llvm.ConstInt(c.uintptrType, 1, false)) + maxSize := llvm.ConstLShr(llvm.ConstNot(llvm.ConstInt(b.uintptrType, 0, false)), llvm.ConstInt(b.uintptrType, 1, false)) if elemSize > maxSize.ZExtValue() { // This seems to be checked by the typechecker already, but let's // check it again just to be sure. - return llvm.Value{}, c.makeError(expr.Pos(), fmt.Sprintf("slice element type is too big (%v bytes)", elemSize)) + return llvm.Value{}, b.makeError(expr.Pos(), fmt.Sprintf("slice element type is too big (%v bytes)", elemSize)) } // Bounds checking. lenType := expr.Len.Type().(*types.Basic) capType := expr.Cap.Type().(*types.Basic) - c.emitSliceBoundsCheck(frame, maxSize, sliceLen, sliceCap, sliceCap, lenType, capType, capType) + b.createSliceBoundsCheck(maxSize, sliceLen, sliceCap, sliceCap, lenType, capType, capType) // Allocate the backing array. - sliceCapCast, err := c.parseConvert(expr.Cap.Type(), types.Typ[types.Uintptr], sliceCap, expr.Pos()) + sliceCapCast, err := b.createConvert(expr.Cap.Type(), types.Typ[types.Uintptr], sliceCap, expr.Pos()) if err != nil { return llvm.Value{}, err } - sliceSize := c.builder.CreateBinOp(llvm.Mul, elemSizeValue, sliceCapCast, "makeslice.cap") - slicePtr := c.createRuntimeCall("alloc", []llvm.Value{sliceSize}, "makeslice.buf") - slicePtr = c.builder.CreateBitCast(slicePtr, llvm.PointerType(llvmElemType, 0), "makeslice.array") + sliceSize := b.CreateBinOp(llvm.Mul, elemSizeValue, sliceCapCast, "makeslice.cap") + slicePtr := b.createRuntimeCall("alloc", []llvm.Value{sliceSize}, "makeslice.buf") + slicePtr = b.CreateBitCast(slicePtr, llvm.PointerType(llvmElemType, 0), "makeslice.array") // Extend or truncate if necessary. This is safe as we've already done // the bounds check. - sliceLen, err = c.parseConvert(expr.Len.Type(), types.Typ[types.Uintptr], sliceLen, expr.Pos()) + sliceLen, err = b.createConvert(expr.Len.Type(), types.Typ[types.Uintptr], sliceLen, expr.Pos()) if err != nil { return llvm.Value{}, err } - sliceCap, err = c.parseConvert(expr.Cap.Type(), types.Typ[types.Uintptr], sliceCap, expr.Pos()) + sliceCap, err = b.createConvert(expr.Cap.Type(), types.Typ[types.Uintptr], sliceCap, expr.Pos()) if err != nil { return llvm.Value{}, err } // Create the slice. - slice := c.ctx.ConstStruct([]llvm.Value{ + slice := b.ctx.ConstStruct([]llvm.Value{ llvm.Undef(slicePtr.Type()), - llvm.Undef(c.uintptrType), - llvm.Undef(c.uintptrType), + llvm.Undef(b.uintptrType), + llvm.Undef(b.uintptrType), }, false) - slice = c.builder.CreateInsertValue(slice, slicePtr, 0, "") - slice = c.builder.CreateInsertValue(slice, sliceLen, 1, "") - slice = c.builder.CreateInsertValue(slice, sliceCap, 2, "") + slice = b.CreateInsertValue(slice, slicePtr, 0, "") + slice = b.CreateInsertValue(slice, sliceLen, 1, "") + slice = b.CreateInsertValue(slice, sliceCap, 2, "") return slice, nil case *ssa.Next: rangeVal := expr.Iter.(*ssa.Range).X - llvmRangeVal := c.getValue(frame, rangeVal) - it := c.getValue(frame, expr.Iter) + llvmRangeVal := b.getValue(rangeVal) + it := b.getValue(expr.Iter) if expr.IsString { - return c.createRuntimeCall("stringNext", []llvm.Value{llvmRangeVal, it}, "range.next"), nil + return b.createRuntimeCall("stringNext", []llvm.Value{llvmRangeVal, it}, "range.next"), nil } else { // map - llvmKeyType := c.getLLVMType(rangeVal.Type().Underlying().(*types.Map).Key()) - llvmValueType := c.getLLVMType(rangeVal.Type().Underlying().(*types.Map).Elem()) - - mapKeyAlloca, mapKeyPtr, mapKeySize := c.createTemporaryAlloca(llvmKeyType, "range.key") - mapValueAlloca, mapValuePtr, mapValueSize := c.createTemporaryAlloca(llvmValueType, "range.value") - ok := c.createRuntimeCall("hashmapNext", []llvm.Value{llvmRangeVal, it, mapKeyPtr, mapValuePtr}, "range.next") - - tuple := llvm.Undef(c.ctx.StructType([]llvm.Type{c.ctx.Int1Type(), llvmKeyType, llvmValueType}, false)) - tuple = c.builder.CreateInsertValue(tuple, ok, 0, "") - tuple = c.builder.CreateInsertValue(tuple, c.builder.CreateLoad(mapKeyAlloca, ""), 1, "") - tuple = c.builder.CreateInsertValue(tuple, c.builder.CreateLoad(mapValueAlloca, ""), 2, "") - c.emitLifetimeEnd(mapKeyPtr, mapKeySize) - c.emitLifetimeEnd(mapValuePtr, mapValueSize) + llvmKeyType := b.getLLVMType(rangeVal.Type().Underlying().(*types.Map).Key()) + llvmValueType := b.getLLVMType(rangeVal.Type().Underlying().(*types.Map).Elem()) + + mapKeyAlloca, mapKeyPtr, mapKeySize := b.createTemporaryAlloca(llvmKeyType, "range.key") + mapValueAlloca, mapValuePtr, mapValueSize := b.createTemporaryAlloca(llvmValueType, "range.value") + ok := b.createRuntimeCall("hashmapNext", []llvm.Value{llvmRangeVal, it, mapKeyPtr, mapValuePtr}, "range.next") + + tuple := llvm.Undef(b.ctx.StructType([]llvm.Type{b.ctx.Int1Type(), llvmKeyType, llvmValueType}, false)) + tuple = b.CreateInsertValue(tuple, ok, 0, "") + tuple = b.CreateInsertValue(tuple, b.CreateLoad(mapKeyAlloca, ""), 1, "") + tuple = b.CreateInsertValue(tuple, b.CreateLoad(mapValueAlloca, ""), 2, "") + b.emitLifetimeEnd(mapKeyPtr, mapKeySize) + b.emitLifetimeEnd(mapValuePtr, mapValueSize) return tuple, nil } case *ssa.Phi: - phi := c.builder.CreatePHI(c.getLLVMType(expr.Type()), "") - frame.phis = append(frame.phis, Phi{expr, phi}) + phi := b.CreatePHI(b.getLLVMType(expr.Type()), "") + b.phis = append(b.phis, Phi{expr, phi}) return phi, nil case *ssa.Range: var iteratorType llvm.Type switch typ := expr.X.Type().Underlying().(type) { case *types.Basic: // string - iteratorType = c.getLLVMRuntimeType("stringIterator") + iteratorType = b.getLLVMRuntimeType("stringIterator") case *types.Map: - iteratorType = c.getLLVMRuntimeType("hashmapIterator") + iteratorType = b.getLLVMRuntimeType("hashmapIterator") default: panic("unknown type in range: " + typ.String()) } - it, _, _ := c.createTemporaryAlloca(iteratorType, "range.it") - c.builder.CreateStore(llvm.ConstNull(iteratorType), it) + it, _, _ := b.createTemporaryAlloca(iteratorType, "range.it") + b.CreateStore(llvm.ConstNull(iteratorType), it) return it, nil case *ssa.Select: - return c.emitSelect(frame, expr), nil + return b.createSelect(expr), nil case *ssa.Slice: - value := c.getValue(frame, expr.X) + value := b.getValue(expr.X) var lowType, highType, maxType *types.Basic var low, high, max llvm.Value if expr.Low != nil { lowType = expr.Low.Type().Underlying().(*types.Basic) - low = c.getValue(frame, expr.Low) - if low.Type().IntTypeWidth() < c.uintptrType.IntTypeWidth() { + low = b.getValue(expr.Low) + if low.Type().IntTypeWidth() < b.uintptrType.IntTypeWidth() { if lowType.Info()&types.IsUnsigned != 0 { - low = c.builder.CreateZExt(low, c.uintptrType, "") + low = b.CreateZExt(low, b.uintptrType, "") } else { - low = c.builder.CreateSExt(low, c.uintptrType, "") + low = b.CreateSExt(low, b.uintptrType, "") } } } else { lowType = types.Typ[types.Uintptr] - low = llvm.ConstInt(c.uintptrType, 0, false) + low = llvm.ConstInt(b.uintptrType, 0, false) } if expr.High != nil { highType = expr.High.Type().Underlying().(*types.Basic) - high = c.getValue(frame, expr.High) - if high.Type().IntTypeWidth() < c.uintptrType.IntTypeWidth() { + high = b.getValue(expr.High) + if high.Type().IntTypeWidth() < b.uintptrType.IntTypeWidth() { if highType.Info()&types.IsUnsigned != 0 { - high = c.builder.CreateZExt(high, c.uintptrType, "") + high = b.CreateZExt(high, b.uintptrType, "") } else { - high = c.builder.CreateSExt(high, c.uintptrType, "") + high = b.CreateSExt(high, b.uintptrType, "") } } } else { @@ -1730,12 +1767,12 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) { if expr.Max != nil { maxType = expr.Max.Type().Underlying().(*types.Basic) - max = c.getValue(frame, expr.Max) - if max.Type().IntTypeWidth() < c.uintptrType.IntTypeWidth() { + max = b.getValue(expr.Max) + if max.Type().IntTypeWidth() < b.uintptrType.IntTypeWidth() { if maxType.Info()&types.IsUnsigned != 0 { - max = c.builder.CreateZExt(max, c.uintptrType, "") + max = b.CreateZExt(max, b.uintptrType, "") } else { - max = c.builder.CreateSExt(max, c.uintptrType, "") + max = b.CreateSExt(max, b.uintptrType, "") } } } else { @@ -1746,7 +1783,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) { case *types.Pointer: // pointer to array // slice an array length := typ.Elem().Underlying().(*types.Array).Len() - llvmLen := llvm.ConstInt(c.uintptrType, uint64(length), false) + llvmLen := llvm.ConstInt(b.uintptrType, uint64(length), false) if high.IsNil() { high = llvmLen } @@ -1754,43 +1791,43 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) { max = llvmLen } indices := []llvm.Value{ - llvm.ConstInt(c.ctx.Int32Type(), 0, false), + llvm.ConstInt(b.ctx.Int32Type(), 0, false), low, } - c.emitSliceBoundsCheck(frame, llvmLen, low, high, max, lowType, highType, maxType) + b.createSliceBoundsCheck(llvmLen, low, high, max, lowType, highType, maxType) // Truncate ints bigger than uintptr. This is after the bounds // check so it's safe. - if c.targetData.TypeAllocSize(low.Type()) > c.targetData.TypeAllocSize(c.uintptrType) { - low = c.builder.CreateTrunc(low, c.uintptrType, "") + if b.targetData.TypeAllocSize(low.Type()) > b.targetData.TypeAllocSize(b.uintptrType) { + low = b.CreateTrunc(low, b.uintptrType, "") } - if c.targetData.TypeAllocSize(high.Type()) > c.targetData.TypeAllocSize(c.uintptrType) { - high = c.builder.CreateTrunc(high, c.uintptrType, "") + if b.targetData.TypeAllocSize(high.Type()) > b.targetData.TypeAllocSize(b.uintptrType) { + high = b.CreateTrunc(high, b.uintptrType, "") } - if c.targetData.TypeAllocSize(max.Type()) > c.targetData.TypeAllocSize(c.uintptrType) { - max = c.builder.CreateTrunc(max, c.uintptrType, "") + if b.targetData.TypeAllocSize(max.Type()) > b.targetData.TypeAllocSize(b.uintptrType) { + max = b.CreateTrunc(max, b.uintptrType, "") } - sliceLen := c.builder.CreateSub(high, low, "slice.len") - slicePtr := c.builder.CreateInBoundsGEP(value, indices, "slice.ptr") - sliceCap := c.builder.CreateSub(max, low, "slice.cap") + sliceLen := b.CreateSub(high, low, "slice.len") + slicePtr := b.CreateInBoundsGEP(value, indices, "slice.ptr") + sliceCap := b.CreateSub(max, low, "slice.cap") - slice := c.ctx.ConstStruct([]llvm.Value{ + slice := b.ctx.ConstStruct([]llvm.Value{ llvm.Undef(slicePtr.Type()), - llvm.Undef(c.uintptrType), - llvm.Undef(c.uintptrType), + llvm.Undef(b.uintptrType), + llvm.Undef(b.uintptrType), }, false) - slice = c.builder.CreateInsertValue(slice, slicePtr, 0, "") - slice = c.builder.CreateInsertValue(slice, sliceLen, 1, "") - slice = c.builder.CreateInsertValue(slice, sliceCap, 2, "") + slice = b.CreateInsertValue(slice, slicePtr, 0, "") + slice = b.CreateInsertValue(slice, sliceLen, 1, "") + slice = b.CreateInsertValue(slice, sliceCap, 2, "") return slice, nil case *types.Slice: // slice a slice - oldPtr := c.builder.CreateExtractValue(value, 0, "") - oldLen := c.builder.CreateExtractValue(value, 1, "") - oldCap := c.builder.CreateExtractValue(value, 2, "") + oldPtr := b.CreateExtractValue(value, 0, "") + oldLen := b.CreateExtractValue(value, 1, "") + oldCap := b.CreateExtractValue(value, 2, "") if high.IsNil() { high = oldLen } @@ -1798,80 +1835,86 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) { max = oldCap } - c.emitSliceBoundsCheck(frame, oldCap, low, high, max, lowType, highType, maxType) + b.createSliceBoundsCheck(oldCap, low, high, max, lowType, highType, maxType) // Truncate ints bigger than uintptr. This is after the bounds // check so it's safe. - if c.targetData.TypeAllocSize(low.Type()) > c.targetData.TypeAllocSize(c.uintptrType) { - low = c.builder.CreateTrunc(low, c.uintptrType, "") + if b.targetData.TypeAllocSize(low.Type()) > b.targetData.TypeAllocSize(b.uintptrType) { + low = b.CreateTrunc(low, b.uintptrType, "") } - if c.targetData.TypeAllocSize(high.Type()) > c.targetData.TypeAllocSize(c.uintptrType) { - high = c.builder.CreateTrunc(high, c.uintptrType, "") + if b.targetData.TypeAllocSize(high.Type()) > b.targetData.TypeAllocSize(b.uintptrType) { + high = b.CreateTrunc(high, b.uintptrType, "") } - if c.targetData.TypeAllocSize(max.Type()) > c.targetData.TypeAllocSize(c.uintptrType) { - max = c.builder.CreateTrunc(max, c.uintptrType, "") + if b.targetData.TypeAllocSize(max.Type()) > b.targetData.TypeAllocSize(b.uintptrType) { + max = b.CreateTrunc(max, b.uintptrType, "") } - newPtr := c.builder.CreateInBoundsGEP(oldPtr, []llvm.Value{low}, "") - newLen := c.builder.CreateSub(high, low, "") - newCap := c.builder.CreateSub(max, low, "") - slice := c.ctx.ConstStruct([]llvm.Value{ + newPtr := b.CreateInBoundsGEP(oldPtr, []llvm.Value{low}, "") + newLen := b.CreateSub(high, low, "") + newCap := b.CreateSub(max, low, "") + slice := b.ctx.ConstStruct([]llvm.Value{ llvm.Undef(newPtr.Type()), - llvm.Undef(c.uintptrType), - llvm.Undef(c.uintptrType), + llvm.Undef(b.uintptrType), + llvm.Undef(b.uintptrType), }, false) - slice = c.builder.CreateInsertValue(slice, newPtr, 0, "") - slice = c.builder.CreateInsertValue(slice, newLen, 1, "") - slice = c.builder.CreateInsertValue(slice, newCap, 2, "") + slice = b.CreateInsertValue(slice, newPtr, 0, "") + slice = b.CreateInsertValue(slice, newLen, 1, "") + slice = b.CreateInsertValue(slice, newCap, 2, "") return slice, nil case *types.Basic: if typ.Info()&types.IsString == 0 { - return llvm.Value{}, c.makeError(expr.Pos(), "unknown slice type: "+typ.String()) + return llvm.Value{}, b.makeError(expr.Pos(), "unknown slice type: "+typ.String()) } // slice a string if expr.Max != nil { // This might as well be a panic, as the frontend should have // handled this already. - return llvm.Value{}, c.makeError(expr.Pos(), "slicing a string with a max parameter is not allowed by the spec") + return llvm.Value{}, b.makeError(expr.Pos(), "slicing a string with a max parameter is not allowed by the spec") } - oldPtr := c.builder.CreateExtractValue(value, 0, "") - oldLen := c.builder.CreateExtractValue(value, 1, "") + oldPtr := b.CreateExtractValue(value, 0, "") + oldLen := b.CreateExtractValue(value, 1, "") if high.IsNil() { high = oldLen } - c.emitSliceBoundsCheck(frame, oldLen, low, high, high, lowType, highType, maxType) + b.createSliceBoundsCheck(oldLen, low, high, high, lowType, highType, maxType) // Truncate ints bigger than uintptr. This is after the bounds // check so it's safe. - if c.targetData.TypeAllocSize(low.Type()) > c.targetData.TypeAllocSize(c.uintptrType) { - low = c.builder.CreateTrunc(low, c.uintptrType, "") + if b.targetData.TypeAllocSize(low.Type()) > b.targetData.TypeAllocSize(b.uintptrType) { + low = b.CreateTrunc(low, b.uintptrType, "") } - if c.targetData.TypeAllocSize(high.Type()) > c.targetData.TypeAllocSize(c.uintptrType) { - high = c.builder.CreateTrunc(high, c.uintptrType, "") + if b.targetData.TypeAllocSize(high.Type()) > b.targetData.TypeAllocSize(b.uintptrType) { + high = b.CreateTrunc(high, b.uintptrType, "") } - newPtr := c.builder.CreateInBoundsGEP(oldPtr, []llvm.Value{low}, "") - newLen := c.builder.CreateSub(high, low, "") - str := llvm.Undef(c.getLLVMRuntimeType("_string")) - str = c.builder.CreateInsertValue(str, newPtr, 0, "") - str = c.builder.CreateInsertValue(str, newLen, 1, "") + newPtr := b.CreateInBoundsGEP(oldPtr, []llvm.Value{low}, "") + newLen := b.CreateSub(high, low, "") + str := llvm.Undef(b.getLLVMRuntimeType("_string")) + str = b.CreateInsertValue(str, newPtr, 0, "") + str = b.CreateInsertValue(str, newLen, 1, "") return str, nil default: - return llvm.Value{}, c.makeError(expr.Pos(), "unknown slice type: "+typ.String()) + return llvm.Value{}, b.makeError(expr.Pos(), "unknown slice type: "+typ.String()) } case *ssa.TypeAssert: - return c.parseTypeAssert(frame, expr), nil + return b.createTypeAssert(expr), nil case *ssa.UnOp: - return c.parseUnOp(frame, expr) + return b.createUnOp(expr) default: - return llvm.Value{}, c.makeError(expr.Pos(), "todo: unknown expression: "+expr.String()) + return llvm.Value{}, b.makeError(expr.Pos(), "todo: unknown expression: "+expr.String()) } } -func (c *Compiler) parseBinOp(op token.Token, typ types.Type, x, y llvm.Value, pos token.Pos) (llvm.Value, error) { +// createBinOp creates a LLVM binary operation (add, sub, mul, etc) for a Go +// binary operation. This is almost a direct mapping, but there are some subtle +// differences such as the requirement in LLVM IR that both sides must have the +// same type, even for bitshifts. Also, signedness in Go is encoded in the type +// and is encoded in the operation in LLVM IR: this is important for some +// operations such as divide. +func (b *builder) createBinOp(op token.Token, typ types.Type, x, y llvm.Value, pos token.Pos) (llvm.Value, error) { switch typ := typ.Underlying().(type) { case *types.Basic: if typ.Info()&types.IsInteger != 0 { @@ -1879,87 +1922,87 @@ func (c *Compiler) parseBinOp(op token.Token, typ types.Type, x, y llvm.Value, p signed := typ.Info()&types.IsUnsigned == 0 switch op { case token.ADD: // + - return c.builder.CreateAdd(x, y, ""), nil + return b.CreateAdd(x, y, ""), nil case token.SUB: // - - return c.builder.CreateSub(x, y, ""), nil + return b.CreateSub(x, y, ""), nil case token.MUL: // * - return c.builder.CreateMul(x, y, ""), nil + return b.CreateMul(x, y, ""), nil case token.QUO: // / if signed { - return c.builder.CreateSDiv(x, y, ""), nil + return b.CreateSDiv(x, y, ""), nil } else { - return c.builder.CreateUDiv(x, y, ""), nil + return b.CreateUDiv(x, y, ""), nil } case token.REM: // % if signed { - return c.builder.CreateSRem(x, y, ""), nil + return b.CreateSRem(x, y, ""), nil } else { - return c.builder.CreateURem(x, y, ""), nil + return b.CreateURem(x, y, ""), nil } case token.AND: // & - return c.builder.CreateAnd(x, y, ""), nil + return b.CreateAnd(x, y, ""), nil case token.OR: // | - return c.builder.CreateOr(x, y, ""), nil + return b.CreateOr(x, y, ""), nil case token.XOR: // ^ - return c.builder.CreateXor(x, y, ""), nil + return b.CreateXor(x, y, ""), nil case token.SHL, token.SHR: - sizeX := c.targetData.TypeAllocSize(x.Type()) - sizeY := c.targetData.TypeAllocSize(y.Type()) + sizeX := b.targetData.TypeAllocSize(x.Type()) + sizeY := b.targetData.TypeAllocSize(y.Type()) if sizeX > sizeY { // x and y must have equal sizes, make Y bigger in this case. // y is unsigned, this has been checked by the Go type checker. - y = c.builder.CreateZExt(y, x.Type(), "") + y = b.CreateZExt(y, x.Type(), "") } else if sizeX < sizeY { // What about shifting more than the integer width? // I'm not entirely sure what the Go spec is on that, but as // Intel CPUs have undefined behavior when shifting more // than the integer width I'm assuming it is also undefined // in Go. - y = c.builder.CreateTrunc(y, x.Type(), "") + y = b.CreateTrunc(y, x.Type(), "") } switch op { case token.SHL: // << - return c.builder.CreateShl(x, y, ""), nil + return b.CreateShl(x, y, ""), nil case token.SHR: // >> if signed { - return c.builder.CreateAShr(x, y, ""), nil + return b.CreateAShr(x, y, ""), nil } else { - return c.builder.CreateLShr(x, y, ""), nil + return b.CreateLShr(x, y, ""), nil } default: panic("unreachable") } case token.EQL: // == - return c.builder.CreateICmp(llvm.IntEQ, x, y, ""), nil + return b.CreateICmp(llvm.IntEQ, x, y, ""), nil case token.NEQ: // != - return c.builder.CreateICmp(llvm.IntNE, x, y, ""), nil + return b.CreateICmp(llvm.IntNE, x, y, ""), nil case token.AND_NOT: // &^ // Go specific. Calculate "and not" with x & (~y) - inv := c.builder.CreateNot(y, "") // ~y - return c.builder.CreateAnd(x, inv, ""), nil + inv := b.CreateNot(y, "") // ~y + return b.CreateAnd(x, inv, ""), nil case token.LSS: // < if signed { - return c.builder.CreateICmp(llvm.IntSLT, x, y, ""), nil + return b.CreateICmp(llvm.IntSLT, x, y, ""), nil } else { - return c.builder.CreateICmp(llvm.IntULT, x, y, ""), nil + return b.CreateICmp(llvm.IntULT, x, y, ""), nil } case token.LEQ: // <= if signed { - return c.builder.CreateICmp(llvm.IntSLE, x, y, ""), nil + return b.CreateICmp(llvm.IntSLE, x, y, ""), nil } else { - return c.builder.CreateICmp(llvm.IntULE, x, y, ""), nil + return b.CreateICmp(llvm.IntULE, x, y, ""), nil } case token.GTR: // > if signed { - return c.builder.CreateICmp(llvm.IntSGT, x, y, ""), nil + return b.CreateICmp(llvm.IntSGT, x, y, ""), nil } else { - return c.builder.CreateICmp(llvm.IntUGT, x, y, ""), nil + return b.CreateICmp(llvm.IntUGT, x, y, ""), nil } case token.GEQ: // >= if signed { - return c.builder.CreateICmp(llvm.IntSGE, x, y, ""), nil + return b.CreateICmp(llvm.IntSGE, x, y, ""), nil } else { - return c.builder.CreateICmp(llvm.IntUGE, x, y, ""), nil + return b.CreateICmp(llvm.IntUGE, x, y, ""), nil } default: panic("binop on integer: " + op.String()) @@ -1968,58 +2011,58 @@ func (c *Compiler) parseBinOp(op token.Token, typ types.Type, x, y llvm.Value, p // Operations on floats switch op { case token.ADD: // + - return c.builder.CreateFAdd(x, y, ""), nil + return b.CreateFAdd(x, y, ""), nil case token.SUB: // - - return c.builder.CreateFSub(x, y, ""), nil + return b.CreateFSub(x, y, ""), nil case token.MUL: // * - return c.builder.CreateFMul(x, y, ""), nil + return b.CreateFMul(x, y, ""), nil case token.QUO: // / - return c.builder.CreateFDiv(x, y, ""), nil + return b.CreateFDiv(x, y, ""), nil case token.EQL: // == - return c.builder.CreateFCmp(llvm.FloatUEQ, x, y, ""), nil + return b.CreateFCmp(llvm.FloatUEQ, x, y, ""), nil case token.NEQ: // != - return c.builder.CreateFCmp(llvm.FloatUNE, x, y, ""), nil + return b.CreateFCmp(llvm.FloatUNE, x, y, ""), nil case token.LSS: // < - return c.builder.CreateFCmp(llvm.FloatULT, x, y, ""), nil + return b.CreateFCmp(llvm.FloatULT, x, y, ""), nil case token.LEQ: // <= - return c.builder.CreateFCmp(llvm.FloatULE, x, y, ""), nil + return b.CreateFCmp(llvm.FloatULE, x, y, ""), nil case token.GTR: // > - return c.builder.CreateFCmp(llvm.FloatUGT, x, y, ""), nil + return b.CreateFCmp(llvm.FloatUGT, x, y, ""), nil case token.GEQ: // >= - return c.builder.CreateFCmp(llvm.FloatUGE, x, y, ""), nil + return b.CreateFCmp(llvm.FloatUGE, x, y, ""), nil default: panic("binop on float: " + op.String()) } } else if typ.Info()&types.IsComplex != 0 { - r1 := c.builder.CreateExtractValue(x, 0, "r1") - r2 := c.builder.CreateExtractValue(y, 0, "r2") - i1 := c.builder.CreateExtractValue(x, 1, "i1") - i2 := c.builder.CreateExtractValue(y, 1, "i2") + r1 := b.CreateExtractValue(x, 0, "r1") + r2 := b.CreateExtractValue(y, 0, "r2") + i1 := b.CreateExtractValue(x, 1, "i1") + i2 := b.CreateExtractValue(y, 1, "i2") switch op { case token.EQL: // == - req := c.builder.CreateFCmp(llvm.FloatOEQ, r1, r2, "") - ieq := c.builder.CreateFCmp(llvm.FloatOEQ, i1, i2, "") - return c.builder.CreateAnd(req, ieq, ""), nil + req := b.CreateFCmp(llvm.FloatOEQ, r1, r2, "") + ieq := b.CreateFCmp(llvm.FloatOEQ, i1, i2, "") + return b.CreateAnd(req, ieq, ""), nil case token.NEQ: // != - req := c.builder.CreateFCmp(llvm.FloatOEQ, r1, r2, "") - ieq := c.builder.CreateFCmp(llvm.FloatOEQ, i1, i2, "") - neq := c.builder.CreateAnd(req, ieq, "") - return c.builder.CreateNot(neq, ""), nil + req := b.CreateFCmp(llvm.FloatOEQ, r1, r2, "") + ieq := b.CreateFCmp(llvm.FloatOEQ, i1, i2, "") + neq := b.CreateAnd(req, ieq, "") + return b.CreateNot(neq, ""), nil case token.ADD, token.SUB: var r, i llvm.Value switch op { case token.ADD: - r = c.builder.CreateFAdd(r1, r2, "") - i = c.builder.CreateFAdd(i1, i2, "") + r = b.CreateFAdd(r1, r2, "") + i = b.CreateFAdd(i1, i2, "") case token.SUB: - r = c.builder.CreateFSub(r1, r2, "") - i = c.builder.CreateFSub(i1, i2, "") + r = b.CreateFSub(r1, r2, "") + i = b.CreateFSub(i1, i2, "") default: panic("unreachable") } - cplx := llvm.Undef(c.ctx.StructType([]llvm.Type{r.Type(), i.Type()}, false)) - cplx = c.builder.CreateInsertValue(cplx, r, 0, "") - cplx = c.builder.CreateInsertValue(cplx, i, 1, "") + cplx := llvm.Undef(b.ctx.StructType([]llvm.Type{r.Type(), i.Type()}, false)) + cplx = b.CreateInsertValue(cplx, r, 0, "") + cplx = b.CreateInsertValue(cplx, i, 1, "") return cplx, nil case token.MUL: // Complex multiplication follows the current implementation in @@ -2034,11 +2077,11 @@ func (c *Compiler) parseBinOp(op token.Token, typ types.Type, x, y llvm.Value, p // http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1548.pdf#page=549 // See https://github.com/golang/go/issues/29846 for a related // discussion. - r := c.builder.CreateFSub(c.builder.CreateFMul(r1, r2, ""), c.builder.CreateFMul(i1, i2, ""), "") - i := c.builder.CreateFAdd(c.builder.CreateFMul(r1, i2, ""), c.builder.CreateFMul(i1, r2, ""), "") - cplx := llvm.Undef(c.ctx.StructType([]llvm.Type{r.Type(), i.Type()}, false)) - cplx = c.builder.CreateInsertValue(cplx, r, 0, "") - cplx = c.builder.CreateInsertValue(cplx, i, 1, "") + r := b.CreateFSub(b.CreateFMul(r1, r2, ""), b.CreateFMul(i1, i2, ""), "") + i := b.CreateFAdd(b.CreateFMul(r1, i2, ""), b.CreateFMul(i1, r2, ""), "") + cplx := llvm.Undef(b.ctx.StructType([]llvm.Type{r.Type(), i.Type()}, false)) + cplx = b.CreateInsertValue(cplx, r, 0, "") + cplx = b.CreateInsertValue(cplx, i, 1, "") return cplx, nil case token.QUO: // Complex division. @@ -2046,9 +2089,9 @@ func (c *Compiler) parseBinOp(op token.Token, typ types.Type, x, y llvm.Value, p // inline. switch r1.Type().TypeKind() { case llvm.FloatTypeKind: - return c.createRuntimeCall("complex64div", []llvm.Value{x, y}, ""), nil + return b.createRuntimeCall("complex64div", []llvm.Value{x, y}, ""), nil case llvm.DoubleTypeKind: - return c.createRuntimeCall("complex128div", []llvm.Value{x, y}, ""), nil + return b.createRuntimeCall("complex128div", []llvm.Value{x, y}, ""), nil default: panic("unexpected complex type") } @@ -2059,9 +2102,9 @@ func (c *Compiler) parseBinOp(op token.Token, typ types.Type, x, y llvm.Value, p // Operations on booleans switch op { case token.EQL: // == - return c.builder.CreateICmp(llvm.IntEQ, x, y, ""), nil + return b.CreateICmp(llvm.IntEQ, x, y, ""), nil case token.NEQ: // != - return c.builder.CreateICmp(llvm.IntNE, x, y, ""), nil + return b.CreateICmp(llvm.IntNE, x, y, ""), nil default: panic("binop on bool: " + op.String()) } @@ -2069,9 +2112,9 @@ func (c *Compiler) parseBinOp(op token.Token, typ types.Type, x, y llvm.Value, p // Operations on pointers switch op { case token.EQL: // == - return c.builder.CreateICmp(llvm.IntEQ, x, y, ""), nil + return b.CreateICmp(llvm.IntEQ, x, y, ""), nil case token.NEQ: // != - return c.builder.CreateICmp(llvm.IntNE, x, y, ""), nil + return b.CreateICmp(llvm.IntNE, x, y, ""), nil default: panic("binop on pointer: " + op.String()) } @@ -2079,27 +2122,27 @@ func (c *Compiler) parseBinOp(op token.Token, typ types.Type, x, y llvm.Value, p // Operations on strings switch op { case token.ADD: // + - return c.createRuntimeCall("stringConcat", []llvm.Value{x, y}, ""), nil + return b.createRuntimeCall("stringConcat", []llvm.Value{x, y}, ""), nil case token.EQL: // == - return c.createRuntimeCall("stringEqual", []llvm.Value{x, y}, ""), nil + return b.createRuntimeCall("stringEqual", []llvm.Value{x, y}, ""), nil case token.NEQ: // != - result := c.createRuntimeCall("stringEqual", []llvm.Value{x, y}, "") - return c.builder.CreateNot(result, ""), nil + result := b.createRuntimeCall("stringEqual", []llvm.Value{x, y}, "") + return b.CreateNot(result, ""), nil case token.LSS: // < - return c.createRuntimeCall("stringLess", []llvm.Value{x, y}, ""), nil + return b.createRuntimeCall("stringLess", []llvm.Value{x, y}, ""), nil case token.LEQ: // <= - result := c.createRuntimeCall("stringLess", []llvm.Value{y, x}, "") - return c.builder.CreateNot(result, ""), nil + result := b.createRuntimeCall("stringLess", []llvm.Value{y, x}, "") + return b.CreateNot(result, ""), nil case token.GTR: // > - result := c.createRuntimeCall("stringLess", []llvm.Value{x, y}, "") - return c.builder.CreateNot(result, ""), nil + result := b.createRuntimeCall("stringLess", []llvm.Value{x, y}, "") + return b.CreateNot(result, ""), nil case token.GEQ: // >= - return c.createRuntimeCall("stringLess", []llvm.Value{y, x}, ""), nil + return b.createRuntimeCall("stringLess", []llvm.Value{y, x}, ""), nil default: panic("binop on string: " + op.String()) } } else { - return llvm.Value{}, c.makeError(pos, "todo: unknown basic type in binop: "+typ.String()) + return llvm.Value{}, b.makeError(pos, "todo: unknown basic type in binop: "+typ.String()) } case *types.Signature: // Get raw scalars from the function value and compare those. @@ -2107,26 +2150,26 @@ func (c *Compiler) parseBinOp(op token.Token, typ types.Type, x, y llvm.Value, p // have some way of getting a scalar value identifying the function. // This is safe: function pointers are generally not comparable // against each other, only against nil. So one of these has to be nil. - x = c.extractFuncScalar(x) - y = c.extractFuncScalar(y) + x = b.extractFuncScalar(x) + y = b.extractFuncScalar(y) switch op { case token.EQL: // == - return c.builder.CreateICmp(llvm.IntEQ, x, y, ""), nil + return b.CreateICmp(llvm.IntEQ, x, y, ""), nil case token.NEQ: // != - return c.builder.CreateICmp(llvm.IntNE, x, y, ""), nil + return b.CreateICmp(llvm.IntNE, x, y, ""), nil default: - return llvm.Value{}, c.makeError(pos, "binop on signature: "+op.String()) + return llvm.Value{}, b.makeError(pos, "binop on signature: "+op.String()) } case *types.Interface: switch op { case token.EQL, token.NEQ: // ==, != - result := c.createRuntimeCall("interfaceEqual", []llvm.Value{x, y}, "") + result := b.createRuntimeCall("interfaceEqual", []llvm.Value{x, y}, "") if op == token.NEQ { - result = c.builder.CreateNot(result, "") + result = b.CreateNot(result, "") } return result, nil default: - return llvm.Value{}, c.makeError(pos, "binop on interface: "+op.String()) + return llvm.Value{}, b.makeError(pos, "binop on interface: "+op.String()) } case *types.Chan, *types.Map, *types.Pointer: // Maps are in general not comparable, but can be compared against nil @@ -2136,85 +2179,86 @@ func (c *Compiler) parseBinOp(op token.Token, typ types.Type, x, y llvm.Value, p // are created with the same call to make or if both are nil. switch op { case token.EQL: // == - return c.builder.CreateICmp(llvm.IntEQ, x, y, ""), nil + return b.CreateICmp(llvm.IntEQ, x, y, ""), nil case token.NEQ: // != - return c.builder.CreateICmp(llvm.IntNE, x, y, ""), nil + return b.CreateICmp(llvm.IntNE, x, y, ""), nil default: - return llvm.Value{}, c.makeError(pos, "todo: binop on pointer: "+op.String()) + return llvm.Value{}, b.makeError(pos, "todo: binop on pointer: "+op.String()) } case *types.Slice: // Slices are in general not comparable, but can be compared against // nil. Assume at least one of them is nil to make the code easier. - xPtr := c.builder.CreateExtractValue(x, 0, "") - yPtr := c.builder.CreateExtractValue(y, 0, "") + xPtr := b.CreateExtractValue(x, 0, "") + yPtr := b.CreateExtractValue(y, 0, "") switch op { case token.EQL: // == - return c.builder.CreateICmp(llvm.IntEQ, xPtr, yPtr, ""), nil + return b.CreateICmp(llvm.IntEQ, xPtr, yPtr, ""), nil case token.NEQ: // != - return c.builder.CreateICmp(llvm.IntNE, xPtr, yPtr, ""), nil + return b.CreateICmp(llvm.IntNE, xPtr, yPtr, ""), nil default: - return llvm.Value{}, c.makeError(pos, "todo: binop on slice: "+op.String()) + return llvm.Value{}, b.makeError(pos, "todo: binop on slice: "+op.String()) } case *types.Array: // Compare each array element and combine the result. From the spec: // Array values are comparable if values of the array element type // are comparable. Two array values are equal if their corresponding // elements are equal. - result := llvm.ConstInt(c.ctx.Int1Type(), 1, true) + result := llvm.ConstInt(b.ctx.Int1Type(), 1, true) for i := 0; i < int(typ.Len()); i++ { - xField := c.builder.CreateExtractValue(x, i, "") - yField := c.builder.CreateExtractValue(y, i, "") - fieldEqual, err := c.parseBinOp(token.EQL, typ.Elem(), xField, yField, pos) + xField := b.CreateExtractValue(x, i, "") + yField := b.CreateExtractValue(y, i, "") + fieldEqual, err := b.createBinOp(token.EQL, typ.Elem(), xField, yField, pos) if err != nil { return llvm.Value{}, err } - result = c.builder.CreateAnd(result, fieldEqual, "") + result = b.CreateAnd(result, fieldEqual, "") } switch op { case token.EQL: // == return result, nil case token.NEQ: // != - return c.builder.CreateNot(result, ""), nil + return b.CreateNot(result, ""), nil default: - return llvm.Value{}, c.makeError(pos, "unknown: binop on struct: "+op.String()) + return llvm.Value{}, b.makeError(pos, "unknown: binop on struct: "+op.String()) } case *types.Struct: // Compare each struct field and combine the result. From the spec: // Struct values are comparable if all their fields are comparable. // Two struct values are equal if their corresponding non-blank // fields are equal. - result := llvm.ConstInt(c.ctx.Int1Type(), 1, true) + result := llvm.ConstInt(b.ctx.Int1Type(), 1, true) for i := 0; i < typ.NumFields(); i++ { if typ.Field(i).Name() == "_" { // skip blank fields continue } fieldType := typ.Field(i).Type() - xField := c.builder.CreateExtractValue(x, i, "") - yField := c.builder.CreateExtractValue(y, i, "") - fieldEqual, err := c.parseBinOp(token.EQL, fieldType, xField, yField, pos) + xField := b.CreateExtractValue(x, i, "") + yField := b.CreateExtractValue(y, i, "") + fieldEqual, err := b.createBinOp(token.EQL, fieldType, xField, yField, pos) if err != nil { return llvm.Value{}, err } - result = c.builder.CreateAnd(result, fieldEqual, "") + result = b.CreateAnd(result, fieldEqual, "") } switch op { case token.EQL: // == return result, nil case token.NEQ: // != - return c.builder.CreateNot(result, ""), nil + return b.CreateNot(result, ""), nil default: - return llvm.Value{}, c.makeError(pos, "unknown: binop on struct: "+op.String()) + return llvm.Value{}, b.makeError(pos, "unknown: binop on struct: "+op.String()) } default: - return llvm.Value{}, c.makeError(pos, "todo: binop type: "+typ.String()) + return llvm.Value{}, b.makeError(pos, "todo: binop type: "+typ.String()) } } -func (c *Compiler) parseConst(prefix string, expr *ssa.Const) llvm.Value { +// createConst creates a LLVM constant value from a Go constant. +func (b *builder) createConst(prefix string, expr *ssa.Const) llvm.Value { switch typ := expr.Type().Underlying().(type) { case *types.Basic: - llvmType := c.getLLVMType(typ) + llvmType := b.getLLVMType(typ) if typ.Info()&types.IsBoolean != 0 { b := constant.BoolVal(expr.Value) n := uint64(0) @@ -2224,23 +2268,23 @@ func (c *Compiler) parseConst(prefix string, expr *ssa.Const) llvm.Value { return llvm.ConstInt(llvmType, n, false) } else if typ.Info()&types.IsString != 0 { str := constant.StringVal(expr.Value) - strLen := llvm.ConstInt(c.uintptrType, uint64(len(str)), false) + strLen := llvm.ConstInt(b.uintptrType, uint64(len(str)), false) objname := prefix + "$string" - global := llvm.AddGlobal(c.mod, llvm.ArrayType(c.ctx.Int8Type(), len(str)), objname) - global.SetInitializer(c.ctx.ConstString(str, false)) + global := llvm.AddGlobal(b.mod, llvm.ArrayType(b.ctx.Int8Type(), len(str)), objname) + global.SetInitializer(b.ctx.ConstString(str, false)) global.SetLinkage(llvm.InternalLinkage) global.SetGlobalConstant(true) global.SetUnnamedAddr(true) - zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false) - strPtr := c.builder.CreateInBoundsGEP(global, []llvm.Value{zero, zero}, "") - strObj := llvm.ConstNamedStruct(c.getLLVMRuntimeType("_string"), []llvm.Value{strPtr, strLen}) + zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false) + strPtr := b.CreateInBoundsGEP(global, []llvm.Value{zero, zero}, "") + strObj := llvm.ConstNamedStruct(b.getLLVMRuntimeType("_string"), []llvm.Value{strPtr, strLen}) return strObj } else if typ.Kind() == types.UnsafePointer { if !expr.IsNil() { value, _ := constant.Uint64Val(expr.Value) - return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, value, false), c.i8ptrType) + return llvm.ConstIntToPtr(llvm.ConstInt(b.uintptrType, value, false), b.i8ptrType) } - return llvm.ConstNull(c.i8ptrType) + return llvm.ConstNull(b.i8ptrType) } else if typ.Info()&types.IsUnsigned != 0 { n, _ := constant.Uint64Val(expr.Value) return llvm.ConstInt(llvmType, n, false) @@ -2251,18 +2295,18 @@ func (c *Compiler) parseConst(prefix string, expr *ssa.Const) llvm.Value { n, _ := constant.Float64Val(expr.Value) return llvm.ConstFloat(llvmType, n) } else if typ.Kind() == types.Complex64 { - r := c.parseConst(prefix, ssa.NewConst(constant.Real(expr.Value), types.Typ[types.Float32])) - i := c.parseConst(prefix, ssa.NewConst(constant.Imag(expr.Value), types.Typ[types.Float32])) - cplx := llvm.Undef(c.ctx.StructType([]llvm.Type{c.ctx.FloatType(), c.ctx.FloatType()}, false)) - cplx = c.builder.CreateInsertValue(cplx, r, 0, "") - cplx = c.builder.CreateInsertValue(cplx, i, 1, "") + r := b.createConst(prefix, ssa.NewConst(constant.Real(expr.Value), types.Typ[types.Float32])) + i := b.createConst(prefix, ssa.NewConst(constant.Imag(expr.Value), types.Typ[types.Float32])) + cplx := llvm.Undef(b.ctx.StructType([]llvm.Type{b.ctx.FloatType(), b.ctx.FloatType()}, false)) + cplx = b.CreateInsertValue(cplx, r, 0, "") + cplx = b.CreateInsertValue(cplx, i, 1, "") return cplx } else if typ.Kind() == types.Complex128 { - r := c.parseConst(prefix, ssa.NewConst(constant.Real(expr.Value), types.Typ[types.Float64])) - i := c.parseConst(prefix, ssa.NewConst(constant.Imag(expr.Value), types.Typ[types.Float64])) - cplx := llvm.Undef(c.ctx.StructType([]llvm.Type{c.ctx.DoubleType(), c.ctx.DoubleType()}, false)) - cplx = c.builder.CreateInsertValue(cplx, r, 0, "") - cplx = c.builder.CreateInsertValue(cplx, i, 1, "") + r := b.createConst(prefix, ssa.NewConst(constant.Real(expr.Value), types.Typ[types.Float64])) + i := b.createConst(prefix, ssa.NewConst(constant.Imag(expr.Value), types.Typ[types.Float64])) + cplx := llvm.Undef(b.ctx.StructType([]llvm.Type{b.ctx.DoubleType(), b.ctx.DoubleType()}, false)) + cplx = b.CreateInsertValue(cplx, r, 0, "") + cplx = b.CreateInsertValue(cplx, i, 1, "") return cplx } else { panic("unknown constant of basic type: " + expr.String()) @@ -2271,35 +2315,35 @@ func (c *Compiler) parseConst(prefix string, expr *ssa.Const) llvm.Value { if expr.Value != nil { panic("expected nil chan constant") } - return llvm.ConstNull(c.getLLVMType(expr.Type())) + return llvm.ConstNull(b.getLLVMType(expr.Type())) case *types.Signature: if expr.Value != nil { panic("expected nil signature constant") } - return llvm.ConstNull(c.getLLVMType(expr.Type())) + return llvm.ConstNull(b.getLLVMType(expr.Type())) case *types.Interface: if expr.Value != nil { panic("expected nil interface constant") } // Create a generic nil interface with no dynamic type (typecode=0). fields := []llvm.Value{ - llvm.ConstInt(c.uintptrType, 0, false), - llvm.ConstPointerNull(c.i8ptrType), + llvm.ConstInt(b.uintptrType, 0, false), + llvm.ConstPointerNull(b.i8ptrType), } - return llvm.ConstNamedStruct(c.getLLVMRuntimeType("_interface"), fields) + return llvm.ConstNamedStruct(b.getLLVMRuntimeType("_interface"), fields) case *types.Pointer: if expr.Value != nil { panic("expected nil pointer constant") } - return llvm.ConstPointerNull(c.getLLVMType(typ)) + return llvm.ConstPointerNull(b.getLLVMType(typ)) case *types.Slice: if expr.Value != nil { panic("expected nil slice constant") } - elemType := c.getLLVMType(typ.Elem()) + elemType := b.getLLVMType(typ.Elem()) llvmPtr := llvm.ConstPointerNull(llvm.PointerType(elemType, 0)) - llvmLen := llvm.ConstInt(c.uintptrType, 0, false) - slice := c.ctx.ConstStruct([]llvm.Value{ + llvmLen := llvm.ConstInt(b.uintptrType, 0, false) + slice := b.ctx.ConstStruct([]llvm.Value{ llvmPtr, // backing array llvmLen, // len llvmLen, // cap @@ -2310,22 +2354,23 @@ func (c *Compiler) parseConst(prefix string, expr *ssa.Const) llvm.Value { // I believe this is not allowed by the Go spec. panic("non-nil map constant") } - llvmType := c.getLLVMType(typ) + llvmType := b.getLLVMType(typ) return llvm.ConstNull(llvmType) default: panic("unknown constant: " + expr.String()) } } -func (c *Compiler) parseConvert(typeFrom, typeTo types.Type, value llvm.Value, pos token.Pos) (llvm.Value, error) { +// createConvert creates a Go type conversion instruction. +func (b *builder) createConvert(typeFrom, typeTo types.Type, value llvm.Value, pos token.Pos) (llvm.Value, error) { llvmTypeFrom := value.Type() - llvmTypeTo := c.getLLVMType(typeTo) + llvmTypeTo := b.getLLVMType(typeTo) // Conversion between unsafe.Pointer and uintptr. isPtrFrom := isPointer(typeFrom.Underlying()) isPtrTo := isPointer(typeTo.Underlying()) if isPtrFrom && !isPtrTo { - return c.builder.CreatePtrToInt(value, llvmTypeTo, ""), nil + return b.CreatePtrToInt(value, llvmTypeTo, ""), nil } else if !isPtrFrom && isPtrTo { if !value.IsABinaryOperator().IsNil() && value.InstructionOpcode() == llvm.Add { // This is probably a pattern like the following: @@ -2340,7 +2385,7 @@ func (c *Compiler) parseConvert(typeFrom, typeTo types.Type, value llvm.Value, p } if !ptr.IsAPtrToIntInst().IsNil() { origptr := ptr.Operand(0) - if origptr.Type() == c.i8ptrType { + if origptr.Type() == b.i8ptrType { // This pointer can be calculated from the original // ptrtoint instruction with a GEP. The leftover inttoptr // instruction is trivial to optimize away. @@ -2348,21 +2393,21 @@ func (c *Compiler) parseConvert(typeFrom, typeTo types.Type, value llvm.Value, p // create a GEP that is not in bounds. However, we're // talking about unsafe code here so the programmer has to // be careful anyway. - return c.builder.CreateInBoundsGEP(origptr, []llvm.Value{index}, ""), nil + return b.CreateInBoundsGEP(origptr, []llvm.Value{index}, ""), nil } } } - return c.builder.CreateIntToPtr(value, llvmTypeTo, ""), nil + return b.CreateIntToPtr(value, llvmTypeTo, ""), nil } // Conversion between pointers and unsafe.Pointer. if isPtrFrom && isPtrTo { - return c.builder.CreateBitCast(value, llvmTypeTo, ""), nil + return b.CreateBitCast(value, llvmTypeTo, ""), nil } switch typeTo := typeTo.Underlying().(type) { case *types.Basic: - sizeFrom := c.targetData.TypeAllocSize(llvmTypeFrom) + sizeFrom := b.targetData.TypeAllocSize(llvmTypeFrom) if typeTo.Info()&types.IsString != 0 { switch typeFrom := typeFrom.Underlying().(type) { @@ -2372,47 +2417,47 @@ func (c *Compiler) parseConvert(typeFrom, typeTo types.Type, value llvm.Value, p // Cast to an i32 value as expected by // runtime.stringFromUnicode. if sizeFrom > 4 { - value = c.builder.CreateTrunc(value, c.ctx.Int32Type(), "") + value = b.CreateTrunc(value, b.ctx.Int32Type(), "") } else if sizeFrom < 4 && typeTo.Info()&types.IsUnsigned != 0 { - value = c.builder.CreateZExt(value, c.ctx.Int32Type(), "") + value = b.CreateZExt(value, b.ctx.Int32Type(), "") } else if sizeFrom < 4 { - value = c.builder.CreateSExt(value, c.ctx.Int32Type(), "") + value = b.CreateSExt(value, b.ctx.Int32Type(), "") } - return c.createRuntimeCall("stringFromUnicode", []llvm.Value{value}, ""), nil + return b.createRuntimeCall("stringFromUnicode", []llvm.Value{value}, ""), nil case *types.Slice: switch typeFrom.Elem().(*types.Basic).Kind() { case types.Byte: - return c.createRuntimeCall("stringFromBytes", []llvm.Value{value}, ""), nil + return b.createRuntimeCall("stringFromBytes", []llvm.Value{value}, ""), nil case types.Rune: - return c.createRuntimeCall("stringFromRunes", []llvm.Value{value}, ""), nil + return b.createRuntimeCall("stringFromRunes", []llvm.Value{value}, ""), nil default: - return llvm.Value{}, c.makeError(pos, "todo: convert to string: "+typeFrom.String()) + return llvm.Value{}, b.makeError(pos, "todo: convert to string: "+typeFrom.String()) } default: - return llvm.Value{}, c.makeError(pos, "todo: convert to string: "+typeFrom.String()) + return llvm.Value{}, b.makeError(pos, "todo: convert to string: "+typeFrom.String()) } } typeFrom := typeFrom.Underlying().(*types.Basic) - sizeTo := c.targetData.TypeAllocSize(llvmTypeTo) + sizeTo := b.targetData.TypeAllocSize(llvmTypeTo) if typeFrom.Info()&types.IsInteger != 0 && typeTo.Info()&types.IsInteger != 0 { // Conversion between two integers. if sizeFrom > sizeTo { - return c.builder.CreateTrunc(value, llvmTypeTo, ""), nil + return b.CreateTrunc(value, llvmTypeTo, ""), nil } else if typeFrom.Info()&types.IsUnsigned != 0 { // if unsigned - return c.builder.CreateZExt(value, llvmTypeTo, ""), nil + return b.CreateZExt(value, llvmTypeTo, ""), nil } else { // if signed - return c.builder.CreateSExt(value, llvmTypeTo, ""), nil + return b.CreateSExt(value, llvmTypeTo, ""), nil } } if typeFrom.Info()&types.IsFloat != 0 && typeTo.Info()&types.IsFloat != 0 { // Conversion between two floats. if sizeFrom > sizeTo { - return c.builder.CreateFPTrunc(value, llvmTypeTo, ""), nil + return b.CreateFPTrunc(value, llvmTypeTo, ""), nil } else if sizeFrom < sizeTo { - return c.builder.CreateFPExt(value, llvmTypeTo, ""), nil + return b.CreateFPExt(value, llvmTypeTo, ""), nil } else { return value, nil } @@ -2421,46 +2466,46 @@ func (c *Compiler) parseConvert(typeFrom, typeTo types.Type, value llvm.Value, p if typeFrom.Info()&types.IsFloat != 0 && typeTo.Info()&types.IsInteger != 0 { // Conversion from float to int. if typeTo.Info()&types.IsUnsigned != 0 { // if unsigned - return c.builder.CreateFPToUI(value, llvmTypeTo, ""), nil + return b.CreateFPToUI(value, llvmTypeTo, ""), nil } else { // if signed - return c.builder.CreateFPToSI(value, llvmTypeTo, ""), nil + return b.CreateFPToSI(value, llvmTypeTo, ""), nil } } if typeFrom.Info()&types.IsInteger != 0 && typeTo.Info()&types.IsFloat != 0 { // Conversion from int to float. if typeFrom.Info()&types.IsUnsigned != 0 { // if unsigned - return c.builder.CreateUIToFP(value, llvmTypeTo, ""), nil + return b.CreateUIToFP(value, llvmTypeTo, ""), nil } else { // if signed - return c.builder.CreateSIToFP(value, llvmTypeTo, ""), nil + return b.CreateSIToFP(value, llvmTypeTo, ""), nil } } if typeFrom.Kind() == types.Complex128 && typeTo.Kind() == types.Complex64 { // Conversion from complex128 to complex64. - r := c.builder.CreateExtractValue(value, 0, "real.f64") - i := c.builder.CreateExtractValue(value, 1, "imag.f64") - r = c.builder.CreateFPTrunc(r, c.ctx.FloatType(), "real.f32") - i = c.builder.CreateFPTrunc(i, c.ctx.FloatType(), "imag.f32") - cplx := llvm.Undef(c.ctx.StructType([]llvm.Type{c.ctx.FloatType(), c.ctx.FloatType()}, false)) - cplx = c.builder.CreateInsertValue(cplx, r, 0, "") - cplx = c.builder.CreateInsertValue(cplx, i, 1, "") + r := b.CreateExtractValue(value, 0, "real.f64") + i := b.CreateExtractValue(value, 1, "imag.f64") + r = b.CreateFPTrunc(r, b.ctx.FloatType(), "real.f32") + i = b.CreateFPTrunc(i, b.ctx.FloatType(), "imag.f32") + cplx := llvm.Undef(b.ctx.StructType([]llvm.Type{b.ctx.FloatType(), b.ctx.FloatType()}, false)) + cplx = b.CreateInsertValue(cplx, r, 0, "") + cplx = b.CreateInsertValue(cplx, i, 1, "") return cplx, nil } if typeFrom.Kind() == types.Complex64 && typeTo.Kind() == types.Complex128 { // Conversion from complex64 to complex128. - r := c.builder.CreateExtractValue(value, 0, "real.f32") - i := c.builder.CreateExtractValue(value, 1, "imag.f32") - r = c.builder.CreateFPExt(r, c.ctx.DoubleType(), "real.f64") - i = c.builder.CreateFPExt(i, c.ctx.DoubleType(), "imag.f64") - cplx := llvm.Undef(c.ctx.StructType([]llvm.Type{c.ctx.DoubleType(), c.ctx.DoubleType()}, false)) - cplx = c.builder.CreateInsertValue(cplx, r, 0, "") - cplx = c.builder.CreateInsertValue(cplx, i, 1, "") + r := b.CreateExtractValue(value, 0, "real.f32") + i := b.CreateExtractValue(value, 1, "imag.f32") + r = b.CreateFPExt(r, b.ctx.DoubleType(), "real.f64") + i = b.CreateFPExt(i, b.ctx.DoubleType(), "imag.f64") + cplx := llvm.Undef(b.ctx.StructType([]llvm.Type{b.ctx.DoubleType(), b.ctx.DoubleType()}, false)) + cplx = b.CreateInsertValue(cplx, r, 0, "") + cplx = b.CreateInsertValue(cplx, i, 1, "") return cplx, nil } - return llvm.Value{}, c.makeError(pos, "todo: convert: basic non-integer type: "+typeFrom.String()+" -> "+typeTo.String()) + return llvm.Value{}, b.makeError(pos, "todo: convert: basic non-integer type: "+typeFrom.String()+" -> "+typeTo.String()) case *types.Slice: if basic, ok := typeFrom.(*types.Basic); !ok || basic.Info()&types.IsString == 0 { @@ -2470,38 +2515,42 @@ func (c *Compiler) parseConvert(typeFrom, typeTo types.Type, value llvm.Value, p elemType := typeTo.Elem().Underlying().(*types.Basic) // must be byte or rune switch elemType.Kind() { case types.Byte: - return c.createRuntimeCall("stringToBytes", []llvm.Value{value}, ""), nil + return b.createRuntimeCall("stringToBytes", []llvm.Value{value}, ""), nil case types.Rune: - return c.createRuntimeCall("stringToRunes", []llvm.Value{value}, ""), nil + return b.createRuntimeCall("stringToRunes", []llvm.Value{value}, ""), nil default: panic("unexpected type in string to slice conversion") } default: - return llvm.Value{}, c.makeError(pos, "todo: convert "+typeTo.String()+" <- "+typeFrom.String()) + return llvm.Value{}, b.makeError(pos, "todo: convert "+typeTo.String()+" <- "+typeFrom.String()) } } -func (c *Compiler) parseUnOp(frame *Frame, unop *ssa.UnOp) (llvm.Value, error) { - x := c.getValue(frame, unop.X) +// createUnOp creates LLVM IR for a given Go unary operation. +// Most unary operators are pretty simple, such as the not and minus operator +// which can all be directly lowered to IR. However, there is also the channel +// receive operator which is handled in the runtime directly. +func (b *builder) createUnOp(unop *ssa.UnOp) (llvm.Value, error) { + x := b.getValue(unop.X) switch unop.Op { case token.NOT: // !x - return c.builder.CreateNot(x, ""), nil + return b.CreateNot(x, ""), nil case token.SUB: // -x if typ, ok := unop.X.Type().Underlying().(*types.Basic); ok { if typ.Info()&types.IsInteger != 0 { - return c.builder.CreateSub(llvm.ConstInt(x.Type(), 0, false), x, ""), nil + return b.CreateSub(llvm.ConstInt(x.Type(), 0, false), x, ""), nil } else if typ.Info()&types.IsFloat != 0 { - return c.builder.CreateFSub(llvm.ConstFloat(x.Type(), 0.0), x, ""), nil + return b.CreateFSub(llvm.ConstFloat(x.Type(), 0.0), x, ""), nil } else { - return llvm.Value{}, c.makeError(unop.Pos(), "todo: unknown basic type for negate: "+typ.String()) + return llvm.Value{}, b.makeError(unop.Pos(), "todo: unknown basic type for negate: "+typ.String()) } } else { - return llvm.Value{}, c.makeError(unop.Pos(), "todo: unknown type for negate: "+unop.X.Type().Underlying().String()) + return llvm.Value{}, b.makeError(unop.Pos(), "todo: unknown type for negate: "+unop.X.Type().Underlying().String()) } case token.MUL: // *x, dereference pointer unop.X.Type().Underlying().(*types.Pointer).Elem() - if c.targetData.TypeAllocSize(x.Type().ElementType()) == 0 { + if b.targetData.TypeAllocSize(x.Type().ElementType()) == 0 { // zero-length data return llvm.ConstNull(x.Type().ElementType()), nil } else if strings.HasSuffix(unop.X.String(), "$funcaddr") { @@ -2510,67 +2559,23 @@ func (c *Compiler) parseUnOp(frame *Frame, unop *ssa.UnOp) (llvm.Value, error) { // var C.add unsafe.Pointer // Instead of a load from the global, create a bitcast of the // function pointer itself. - globalName := c.getGlobalInfo(unop.X.(*ssa.Global)).linkName + globalName := b.getGlobalInfo(unop.X.(*ssa.Global)).linkName name := globalName[:len(globalName)-len("$funcaddr")] - fn := c.mod.NamedFunction(name) + fn := b.mod.NamedFunction(name) if fn.IsNil() { - return llvm.Value{}, c.makeError(unop.Pos(), "cgo function not found: "+name) + return llvm.Value{}, b.makeError(unop.Pos(), "cgo function not found: "+name) } - return c.builder.CreateBitCast(fn, c.i8ptrType, ""), nil + return b.CreateBitCast(fn, b.i8ptrType, ""), nil } else { - c.emitNilCheck(frame, x, "deref") - load := c.builder.CreateLoad(x, "") + b.createNilCheck(x, "deref") + load := b.CreateLoad(x, "") return load, nil } case token.XOR: // ^x, toggle all bits in integer - return c.builder.CreateXor(x, llvm.ConstInt(x.Type(), ^uint64(0), false), ""), nil + return b.CreateXor(x, llvm.ConstInt(x.Type(), ^uint64(0), false), ""), nil case token.ARROW: // <-x, receive from channel - return c.emitChanRecv(frame, unop), nil + return b.createChanRecv(unop), nil default: - return llvm.Value{}, c.makeError(unop.Pos(), "todo: unknown unop") - } -} - -// IR returns the whole IR as a human-readable string. -func (c *Compiler) IR() string { - return c.mod.String() -} - -func (c *Compiler) Verify() error { - return llvm.VerifyModule(c.mod, llvm.PrintMessageAction) -} - -// Emit object file (.o). -func (c *Compiler) EmitObject(path string) error { - llvmBuf, err := c.machine.EmitToMemoryBuffer(c.mod, llvm.ObjectFile) - if err != nil { - return err - } - return c.writeFile(llvmBuf.Bytes(), path) -} - -// Emit LLVM bitcode file (.bc). -func (c *Compiler) EmitBitcode(path string) error { - data := llvm.WriteBitcodeToMemoryBuffer(c.mod).Bytes() - return c.writeFile(data, path) -} - -// Emit LLVM IR source file (.ll). -func (c *Compiler) EmitText(path string) error { - data := []byte(c.mod.String()) - return c.writeFile(data, path) -} - -// Write the data to the file specified by path. -func (c *Compiler) writeFile(data []byte, path string) error { - // Write output to file - f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666) - if err != nil { - return err - } - _, err = f.Write(data) - if err != nil { - return err + return llvm.Value{}, b.makeError(unop.Pos(), "todo: unknown unop") } - return f.Close() } diff --git a/compiler/defer.go b/compiler/defer.go index bc82603200..fa7d7623b5 100644 --- a/compiler/defer.go +++ b/compiler/defer.go @@ -23,16 +23,16 @@ import ( // deferInitFunc sets up this function for future deferred calls. It must be // called from within the entry block when this function contains deferred // calls. -func (c *Compiler) deferInitFunc(frame *Frame) { +func (b *builder) deferInitFunc() { // Some setup. - frame.deferFuncs = make(map[*ir.Function]int) - frame.deferInvokeFuncs = make(map[string]int) - frame.deferClosureFuncs = make(map[*ir.Function]int) + b.deferFuncs = make(map[*ir.Function]int) + b.deferInvokeFuncs = make(map[string]int) + b.deferClosureFuncs = make(map[*ir.Function]int) // Create defer list pointer. - deferType := llvm.PointerType(c.getLLVMRuntimeType("_defer"), 0) - frame.deferPtr = c.builder.CreateAlloca(deferType, "deferPtr") - c.builder.CreateStore(llvm.ConstPointerNull(deferType), frame.deferPtr) + deferType := llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0) + b.deferPtr = b.CreateAlloca(deferType, "deferPtr") + b.CreateStore(llvm.ConstPointerNull(deferType), b.deferPtr) } // isInLoop checks if there is a path from a basic block to itself. @@ -69,53 +69,53 @@ func isInLoop(start *ssa.BasicBlock) bool { return false } -// emitDefer emits a single defer instruction, to be run when this function +// createDefer emits a single defer instruction, to be run when this function // returns. -func (c *Compiler) emitDefer(frame *Frame, instr *ssa.Defer) { +func (b *builder) createDefer(instr *ssa.Defer) { // The pointer to the previous defer struct, which we will replace to // make a linked list. - next := c.builder.CreateLoad(frame.deferPtr, "defer.next") + next := b.CreateLoad(b.deferPtr, "defer.next") var values []llvm.Value - valueTypes := []llvm.Type{c.uintptrType, next.Type()} + valueTypes := []llvm.Type{b.uintptrType, next.Type()} if instr.Call.IsInvoke() { // Method call on an interface. // Get callback type number. methodName := instr.Call.Method.FullName() - if _, ok := frame.deferInvokeFuncs[methodName]; !ok { - frame.deferInvokeFuncs[methodName] = len(frame.allDeferFuncs) - frame.allDeferFuncs = append(frame.allDeferFuncs, &instr.Call) + if _, ok := b.deferInvokeFuncs[methodName]; !ok { + b.deferInvokeFuncs[methodName] = len(b.allDeferFuncs) + b.allDeferFuncs = append(b.allDeferFuncs, &instr.Call) } - callback := llvm.ConstInt(c.uintptrType, uint64(frame.deferInvokeFuncs[methodName]), false) + callback := llvm.ConstInt(b.uintptrType, uint64(b.deferInvokeFuncs[methodName]), false) // Collect all values to be put in the struct (starting with // runtime._defer fields, followed by the call parameters). - itf := c.getValue(frame, instr.Call.Value) // interface - receiverValue := c.builder.CreateExtractValue(itf, 1, "invoke.func.receiver") + itf := b.getValue(instr.Call.Value) // interface + receiverValue := b.CreateExtractValue(itf, 1, "invoke.func.receiver") values = []llvm.Value{callback, next, receiverValue} - valueTypes = append(valueTypes, c.i8ptrType) + valueTypes = append(valueTypes, b.i8ptrType) for _, arg := range instr.Call.Args { - val := c.getValue(frame, arg) + val := b.getValue(arg) values = append(values, val) valueTypes = append(valueTypes, val.Type()) } } else if callee, ok := instr.Call.Value.(*ssa.Function); ok { // Regular function call. - fn := c.ir.GetFunction(callee) + fn := b.ir.GetFunction(callee) - if _, ok := frame.deferFuncs[fn]; !ok { - frame.deferFuncs[fn] = len(frame.allDeferFuncs) - frame.allDeferFuncs = append(frame.allDeferFuncs, fn) + if _, ok := b.deferFuncs[fn]; !ok { + b.deferFuncs[fn] = len(b.allDeferFuncs) + b.allDeferFuncs = append(b.allDeferFuncs, fn) } - callback := llvm.ConstInt(c.uintptrType, uint64(frame.deferFuncs[fn]), false) + callback := llvm.ConstInt(b.uintptrType, uint64(b.deferFuncs[fn]), false) // Collect all values to be put in the struct (starting with // runtime._defer fields). values = []llvm.Value{callback, next} for _, param := range instr.Call.Args { - llvmParam := c.getValue(frame, param) + llvmParam := b.getValue(param) values = append(values, llvmParam) valueTypes = append(valueTypes, llvmParam.Type()) } @@ -127,23 +127,23 @@ func (c *Compiler) emitDefer(frame *Frame, instr *ssa.Defer) { // pointer. // TODO: ignore this closure entirely and put pointers to the free // variables directly in the defer struct, avoiding a memory allocation. - closure := c.getValue(frame, instr.Call.Value) - context := c.builder.CreateExtractValue(closure, 0, "") + closure := b.getValue(instr.Call.Value) + context := b.CreateExtractValue(closure, 0, "") // Get the callback number. - fn := c.ir.GetFunction(makeClosure.Fn.(*ssa.Function)) - if _, ok := frame.deferClosureFuncs[fn]; !ok { - frame.deferClosureFuncs[fn] = len(frame.allDeferFuncs) - frame.allDeferFuncs = append(frame.allDeferFuncs, makeClosure) + fn := b.ir.GetFunction(makeClosure.Fn.(*ssa.Function)) + if _, ok := b.deferClosureFuncs[fn]; !ok { + b.deferClosureFuncs[fn] = len(b.allDeferFuncs) + b.allDeferFuncs = append(b.allDeferFuncs, makeClosure) } - callback := llvm.ConstInt(c.uintptrType, uint64(frame.deferClosureFuncs[fn]), false) + callback := llvm.ConstInt(b.uintptrType, uint64(b.deferClosureFuncs[fn]), false) // Collect all values to be put in the struct (starting with // runtime._defer fields, followed by all parameters including the // context pointer). values = []llvm.Value{callback, next} for _, param := range instr.Call.Args { - llvmParam := c.getValue(frame, param) + llvmParam := b.getValue(param) values = append(values, llvmParam) valueTypes = append(valueTypes, llvmParam.Type()) } @@ -151,41 +151,41 @@ func (c *Compiler) emitDefer(frame *Frame, instr *ssa.Defer) { valueTypes = append(valueTypes, context.Type()) } else { - c.addError(instr.Pos(), "todo: defer on uncommon function call type") + b.addError(instr.Pos(), "todo: defer on uncommon function call type") return } // Make a struct out of the collected values to put in the defer frame. - deferFrameType := c.ctx.StructType(valueTypes, false) + deferFrameType := b.ctx.StructType(valueTypes, false) deferFrame := llvm.ConstNull(deferFrameType) for i, value := range values { - deferFrame = c.builder.CreateInsertValue(deferFrame, value, i, "") + deferFrame = b.CreateInsertValue(deferFrame, value, i, "") } // Put this struct in an allocation. var alloca llvm.Value if !isInLoop(instr.Block()) { // This can safely use a stack allocation. - alloca = llvmutil.CreateEntryBlockAlloca(c.builder, deferFrameType, "defer.alloca") + alloca = llvmutil.CreateEntryBlockAlloca(b.Builder, deferFrameType, "defer.alloca") } else { // This may be hit a variable number of times, so use a heap allocation. - size := c.targetData.TypeAllocSize(deferFrameType) - sizeValue := llvm.ConstInt(c.uintptrType, size, false) - allocCall := c.createRuntimeCall("alloc", []llvm.Value{sizeValue}, "defer.alloc.call") - alloca = c.builder.CreateBitCast(allocCall, llvm.PointerType(deferFrameType, 0), "defer.alloc") + size := b.targetData.TypeAllocSize(deferFrameType) + sizeValue := llvm.ConstInt(b.uintptrType, size, false) + allocCall := b.createRuntimeCall("alloc", []llvm.Value{sizeValue}, "defer.alloc.call") + alloca = b.CreateBitCast(allocCall, llvm.PointerType(deferFrameType, 0), "defer.alloc") } - if c.NeedsStackObjects() { - c.trackPointer(alloca) + if b.NeedsStackObjects() { + b.trackPointer(alloca) } - c.builder.CreateStore(deferFrame, alloca) + b.CreateStore(deferFrame, alloca) // Push it on top of the linked list by replacing deferPtr. - allocaCast := c.builder.CreateBitCast(alloca, next.Type(), "defer.alloca.cast") - c.builder.CreateStore(allocaCast, frame.deferPtr) + allocaCast := b.CreateBitCast(alloca, next.Type(), "defer.alloca.cast") + b.CreateStore(allocaCast, b.deferPtr) } -// emitRunDefers emits code to run all deferred functions. -func (c *Compiler) emitRunDefers(frame *Frame) { +// createRunDefers emits code to run all deferred functions. +func (b *builder) createRunDefers() { // Add a loop like the following: // for stack != nil { // _stack := stack @@ -202,44 +202,44 @@ func (c *Compiler) emitRunDefers(frame *Frame) { // } // Create loop. - loophead := c.ctx.AddBasicBlock(frame.fn.LLVMFn, "rundefers.loophead") - loop := c.ctx.AddBasicBlock(frame.fn.LLVMFn, "rundefers.loop") - unreachable := c.ctx.AddBasicBlock(frame.fn.LLVMFn, "rundefers.default") - end := c.ctx.AddBasicBlock(frame.fn.LLVMFn, "rundefers.end") - c.builder.CreateBr(loophead) + loophead := b.ctx.AddBasicBlock(b.fn.LLVMFn, "rundefers.loophead") + loop := b.ctx.AddBasicBlock(b.fn.LLVMFn, "rundefers.loop") + unreachable := b.ctx.AddBasicBlock(b.fn.LLVMFn, "rundefers.default") + end := b.ctx.AddBasicBlock(b.fn.LLVMFn, "rundefers.end") + b.CreateBr(loophead) // Create loop head: // for stack != nil { - c.builder.SetInsertPointAtEnd(loophead) - deferData := c.builder.CreateLoad(frame.deferPtr, "") - stackIsNil := c.builder.CreateICmp(llvm.IntEQ, deferData, llvm.ConstPointerNull(deferData.Type()), "stackIsNil") - c.builder.CreateCondBr(stackIsNil, end, loop) + b.SetInsertPointAtEnd(loophead) + deferData := b.CreateLoad(b.deferPtr, "") + stackIsNil := b.CreateICmp(llvm.IntEQ, deferData, llvm.ConstPointerNull(deferData.Type()), "stackIsNil") + b.CreateCondBr(stackIsNil, end, loop) // Create loop body: // _stack := stack // stack = stack.next // switch stack.callback { - c.builder.SetInsertPointAtEnd(loop) - nextStackGEP := c.builder.CreateInBoundsGEP(deferData, []llvm.Value{ - llvm.ConstInt(c.ctx.Int32Type(), 0, false), - llvm.ConstInt(c.ctx.Int32Type(), 1, false), // .next field + b.SetInsertPointAtEnd(loop) + nextStackGEP := b.CreateInBoundsGEP(deferData, []llvm.Value{ + llvm.ConstInt(b.ctx.Int32Type(), 0, false), + llvm.ConstInt(b.ctx.Int32Type(), 1, false), // .next field }, "stack.next.gep") - nextStack := c.builder.CreateLoad(nextStackGEP, "stack.next") - c.builder.CreateStore(nextStack, frame.deferPtr) - gep := c.builder.CreateInBoundsGEP(deferData, []llvm.Value{ - llvm.ConstInt(c.ctx.Int32Type(), 0, false), - llvm.ConstInt(c.ctx.Int32Type(), 0, false), // .callback field + nextStack := b.CreateLoad(nextStackGEP, "stack.next") + b.CreateStore(nextStack, b.deferPtr) + gep := b.CreateInBoundsGEP(deferData, []llvm.Value{ + llvm.ConstInt(b.ctx.Int32Type(), 0, false), + llvm.ConstInt(b.ctx.Int32Type(), 0, false), // .callback field }, "callback.gep") - callback := c.builder.CreateLoad(gep, "callback") - sw := c.builder.CreateSwitch(callback, unreachable, len(frame.allDeferFuncs)) + callback := b.CreateLoad(gep, "callback") + sw := b.CreateSwitch(callback, unreachable, len(b.allDeferFuncs)) - for i, callback := range frame.allDeferFuncs { + for i, callback := range b.allDeferFuncs { // Create switch case, for example: // case 0: // // run first deferred call - block := c.ctx.AddBasicBlock(frame.fn.LLVMFn, "rundefers.callback") - sw.AddCase(llvm.ConstInt(c.uintptrType, uint64(i), false), block) - c.builder.SetInsertPointAtEnd(block) + block := b.ctx.AddBasicBlock(b.fn.LLVMFn, "rundefers.callback") + sw.AddCase(llvm.ConstInt(b.uintptrType, uint64(i), false), block) + b.SetInsertPointAtEnd(block) switch callback := callback.(type) { case *ssa.CallCommon: // Call on an interface value. @@ -248,50 +248,50 @@ func (c *Compiler) emitRunDefers(frame *Frame) { } // Get the real defer struct type and cast to it. - valueTypes := []llvm.Type{c.uintptrType, llvm.PointerType(c.getLLVMRuntimeType("_defer"), 0), c.i8ptrType} + valueTypes := []llvm.Type{b.uintptrType, llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0), b.i8ptrType} for _, arg := range callback.Args { - valueTypes = append(valueTypes, c.getLLVMType(arg.Type())) + valueTypes = append(valueTypes, b.getLLVMType(arg.Type())) } - deferFrameType := c.ctx.StructType(valueTypes, false) - deferFramePtr := c.builder.CreateBitCast(deferData, llvm.PointerType(deferFrameType, 0), "deferFrame") + deferFrameType := b.ctx.StructType(valueTypes, false) + deferFramePtr := b.CreateBitCast(deferData, llvm.PointerType(deferFrameType, 0), "deferFrame") // Extract the params from the struct (including receiver). forwardParams := []llvm.Value{} - zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false) + zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false) for i := 2; i < len(valueTypes); i++ { - gep := c.builder.CreateInBoundsGEP(deferFramePtr, []llvm.Value{zero, llvm.ConstInt(c.ctx.Int32Type(), uint64(i), false)}, "gep") - forwardParam := c.builder.CreateLoad(gep, "param") + gep := b.CreateInBoundsGEP(deferFramePtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)}, "gep") + forwardParam := b.CreateLoad(gep, "param") forwardParams = append(forwardParams, forwardParam) } // Add the context parameter. An interface call cannot also be a // closure but we have to supply the parameter anyway for platforms // with a strict calling convention. - forwardParams = append(forwardParams, llvm.Undef(c.i8ptrType)) + forwardParams = append(forwardParams, llvm.Undef(b.i8ptrType)) // Parent coroutine handle. - forwardParams = append(forwardParams, llvm.Undef(c.i8ptrType)) + forwardParams = append(forwardParams, llvm.Undef(b.i8ptrType)) - fnPtr, _ := c.getInvokeCall(frame, callback) - c.createCall(fnPtr, forwardParams, "") + fnPtr, _ := b.getInvokeCall(callback) + b.createCall(fnPtr, forwardParams, "") case *ir.Function: // Direct call. // Get the real defer struct type and cast to it. - valueTypes := []llvm.Type{c.uintptrType, llvm.PointerType(c.getLLVMRuntimeType("_defer"), 0)} + valueTypes := []llvm.Type{b.uintptrType, llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0)} for _, param := range callback.Params { - valueTypes = append(valueTypes, c.getLLVMType(param.Type())) + valueTypes = append(valueTypes, b.getLLVMType(param.Type())) } - deferFrameType := c.ctx.StructType(valueTypes, false) - deferFramePtr := c.builder.CreateBitCast(deferData, llvm.PointerType(deferFrameType, 0), "deferFrame") + deferFrameType := b.ctx.StructType(valueTypes, false) + deferFramePtr := b.CreateBitCast(deferData, llvm.PointerType(deferFrameType, 0), "deferFrame") // Extract the params from the struct. forwardParams := []llvm.Value{} - zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false) + zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false) for i := range callback.Params { - gep := c.builder.CreateInBoundsGEP(deferFramePtr, []llvm.Value{zero, llvm.ConstInt(c.ctx.Int32Type(), uint64(i+2), false)}, "gep") - forwardParam := c.builder.CreateLoad(gep, "param") + gep := b.CreateInBoundsGEP(deferFramePtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i+2), false)}, "gep") + forwardParam := b.CreateLoad(gep, "param") forwardParams = append(forwardParams, forwardParam) } @@ -300,57 +300,57 @@ func (c *Compiler) emitRunDefers(frame *Frame) { if !callback.IsExported() { // Add the context parameter. We know it is ignored by the receiving // function, but we have to pass one anyway. - forwardParams = append(forwardParams, llvm.Undef(c.i8ptrType)) + forwardParams = append(forwardParams, llvm.Undef(b.i8ptrType)) // Parent coroutine handle. - forwardParams = append(forwardParams, llvm.Undef(c.i8ptrType)) + forwardParams = append(forwardParams, llvm.Undef(b.i8ptrType)) } // Call real function. - c.createCall(callback.LLVMFn, forwardParams, "") + b.createCall(callback.LLVMFn, forwardParams, "") case *ssa.MakeClosure: // Get the real defer struct type and cast to it. - fn := c.ir.GetFunction(callback.Fn.(*ssa.Function)) - valueTypes := []llvm.Type{c.uintptrType, llvm.PointerType(c.getLLVMRuntimeType("_defer"), 0)} + fn := b.ir.GetFunction(callback.Fn.(*ssa.Function)) + valueTypes := []llvm.Type{b.uintptrType, llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0)} params := fn.Signature.Params() for i := 0; i < params.Len(); i++ { - valueTypes = append(valueTypes, c.getLLVMType(params.At(i).Type())) + valueTypes = append(valueTypes, b.getLLVMType(params.At(i).Type())) } - valueTypes = append(valueTypes, c.i8ptrType) // closure - deferFrameType := c.ctx.StructType(valueTypes, false) - deferFramePtr := c.builder.CreateBitCast(deferData, llvm.PointerType(deferFrameType, 0), "deferFrame") + valueTypes = append(valueTypes, b.i8ptrType) // closure + deferFrameType := b.ctx.StructType(valueTypes, false) + deferFramePtr := b.CreateBitCast(deferData, llvm.PointerType(deferFrameType, 0), "deferFrame") // Extract the params from the struct. forwardParams := []llvm.Value{} - zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false) + zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false) for i := 2; i < len(valueTypes); i++ { - gep := c.builder.CreateInBoundsGEP(deferFramePtr, []llvm.Value{zero, llvm.ConstInt(c.ctx.Int32Type(), uint64(i), false)}, "") - forwardParam := c.builder.CreateLoad(gep, "param") + gep := b.CreateInBoundsGEP(deferFramePtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)}, "") + forwardParam := b.CreateLoad(gep, "param") forwardParams = append(forwardParams, forwardParam) } // Parent coroutine handle. - forwardParams = append(forwardParams, llvm.Undef(c.i8ptrType)) + forwardParams = append(forwardParams, llvm.Undef(b.i8ptrType)) // Call deferred function. - c.createCall(fn.LLVMFn, forwardParams, "") + b.createCall(fn.LLVMFn, forwardParams, "") default: panic("unknown deferred function type") } // Branch back to the start of the loop. - c.builder.CreateBr(loophead) + b.CreateBr(loophead) } // Create default unreachable block: // default: // unreachable // } - c.builder.SetInsertPointAtEnd(unreachable) - c.builder.CreateUnreachable() + b.SetInsertPointAtEnd(unreachable) + b.CreateUnreachable() // End of loop. - c.builder.SetInsertPointAtEnd(end) + b.SetInsertPointAtEnd(end) } diff --git a/compiler/errors.go b/compiler/errors.go index b2d8cc766d..85de4d161e 100644 --- a/compiler/errors.go +++ b/compiler/errors.go @@ -1,5 +1,7 @@ package compiler +// This file contains some utility functions related to error handling. + import ( "go/scanner" "go/token" @@ -9,7 +11,8 @@ import ( "tinygo.org/x/go-llvm" ) -func (c *Compiler) makeError(pos token.Pos, msg string) types.Error { +// makeError makes it easy to create an error from a token.Pos with a message. +func (c *compilerContext) makeError(pos token.Pos, msg string) types.Error { return types.Error{ Fset: c.ir.Program.Fset, Pos: pos, @@ -17,7 +20,7 @@ func (c *Compiler) makeError(pos token.Pos, msg string) types.Error { } } -func (c *Compiler) addError(pos token.Pos, msg string) { +func (c *compilerContext) addError(pos token.Pos, msg string) { c.diagnostics = append(c.diagnostics, c.makeError(pos, msg)) } diff --git a/compiler/func.go b/compiler/func.go index b79b17f822..544f3e5fa3 100644 --- a/compiler/func.go +++ b/compiler/func.go @@ -13,7 +13,13 @@ import ( // createFuncValue creates a function value from a raw function pointer with no // context. -func (c *Compiler) createFuncValue(funcPtr, context llvm.Value, sig *types.Signature) llvm.Value { +func (b *builder) createFuncValue(funcPtr, context llvm.Value, sig *types.Signature) llvm.Value { + return b.compilerContext.createFuncValue(b.Builder, funcPtr, context, sig) +} + +// createFuncValue creates a function value from a raw function pointer with no +// 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: @@ -40,35 +46,35 @@ func (c *Compiler) createFuncValue(funcPtr, context llvm.Value, sig *types.Signa } funcValueType := c.getFuncType(sig) funcValue := llvm.Undef(funcValueType) - funcValue = c.builder.CreateInsertValue(funcValue, context, 0, "") - funcValue = c.builder.CreateInsertValue(funcValue, funcValueScalar, 1, "") + funcValue = builder.CreateInsertValue(funcValue, context, 0, "") + funcValue = builder.CreateInsertValue(funcValue, funcValueScalar, 1, "") return funcValue } // extractFuncScalar returns some scalar that can be used in comparisons. It is // a cheap operation. -func (c *Compiler) extractFuncScalar(funcValue llvm.Value) llvm.Value { - return c.builder.CreateExtractValue(funcValue, 1, "") +func (b *builder) extractFuncScalar(funcValue llvm.Value) llvm.Value { + return b.CreateExtractValue(funcValue, 1, "") } // extractFuncContext extracts the context pointer from this function value. It // is a cheap operation. -func (c *Compiler) extractFuncContext(funcValue llvm.Value) llvm.Value { - return c.builder.CreateExtractValue(funcValue, 0, "") +func (b *builder) extractFuncContext(funcValue llvm.Value) llvm.Value { + return b.CreateExtractValue(funcValue, 0, "") } // decodeFuncValue extracts the context and the function pointer from this func // value. This may be an expensive operation. -func (c *Compiler) decodeFuncValue(funcValue llvm.Value, sig *types.Signature) (funcPtr, context llvm.Value) { - context = c.builder.CreateExtractValue(funcValue, 0, "") - switch c.FuncImplementation() { +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: - funcPtr = c.builder.CreateExtractValue(funcValue, 1, "") + funcPtr = b.CreateExtractValue(funcValue, 1, "") case compileopts.FuncValueSwitch: - llvmSig := c.getRawFuncType(sig) - sigGlobal := c.getTypeCode(sig) - funcPtr = c.createRuntimeCall("getFuncPtr", []llvm.Value{funcValue, sigGlobal}, "") - funcPtr = c.builder.CreateIntToPtr(funcPtr, llvmSig, "") + llvmSig := b.getRawFuncType(sig) + sigGlobal := b.getTypeCode(sig) + funcPtr = b.createRuntimeCall("getFuncPtr", []llvm.Value{funcValue, sigGlobal}, "") + funcPtr = b.CreateIntToPtr(funcPtr, llvmSig, "") default: panic("unimplemented func value variant") } @@ -76,7 +82,7 @@ func (c *Compiler) decodeFuncValue(funcValue llvm.Value, sig *types.Signature) ( } // getFuncType returns the type of a func value given a signature. -func (c *Compiler) getFuncType(typ *types.Signature) llvm.Type { +func (c *compilerContext) getFuncType(typ *types.Signature) llvm.Type { switch c.FuncImplementation() { case compileopts.FuncValueDoubleword: rawPtr := c.getRawFuncType(typ) @@ -89,7 +95,7 @@ func (c *Compiler) getFuncType(typ *types.Signature) llvm.Type { } // getRawFuncType returns a LLVM function pointer type for a given signature. -func (c *Compiler) getRawFuncType(typ *types.Signature) llvm.Type { +func (c *compilerContext) getRawFuncType(typ *types.Signature) llvm.Type { // Get the return type. var returnType llvm.Type switch typ.Results().Len() { @@ -119,11 +125,11 @@ func (c *Compiler) getRawFuncType(typ *types.Signature) llvm.Type { // The receiver is not an interface, but a i8* type. recv = c.i8ptrType } - paramTypes = append(paramTypes, c.expandFormalParamType(recv)...) + paramTypes = append(paramTypes, expandFormalParamType(recv)...) } for i := 0; i < typ.Params().Len(); i++ { subType := c.getLLVMType(typ.Params().At(i).Type()) - paramTypes = append(paramTypes, c.expandFormalParamType(subType)...) + paramTypes = append(paramTypes, expandFormalParamType(subType)...) } // All functions take these parameters at the end. paramTypes = append(paramTypes, c.i8ptrType) // context @@ -135,24 +141,24 @@ func (c *Compiler) getRawFuncType(typ *types.Signature) llvm.Type { // parseMakeClosure makes a function value (with context) from the given // closure expression. -func (c *Compiler) parseMakeClosure(frame *Frame, expr *ssa.MakeClosure) (llvm.Value, error) { +func (b *builder) parseMakeClosure(expr *ssa.MakeClosure) (llvm.Value, error) { if len(expr.Bindings) == 0 { panic("unexpected: MakeClosure without bound variables") } - f := c.ir.GetFunction(expr.Fn.(*ssa.Function)) + f := b.ir.GetFunction(expr.Fn.(*ssa.Function)) // Collect all bound variables. boundVars := make([]llvm.Value, len(expr.Bindings)) for i, binding := range expr.Bindings { // The context stores the bound variables. - llvmBoundVar := c.getValue(frame, binding) + llvmBoundVar := b.getValue(binding) boundVars[i] = llvmBoundVar } // Store the bound variables in a single object, allocating it on the heap // if necessary. - context := c.emitPointerPack(boundVars) + context := b.emitPointerPack(boundVars) // Create the closure. - return c.createFuncValue(f.LLVMFn, context, f.Signature), nil + return b.createFuncValue(f.LLVMFn, context, f.Signature), nil } diff --git a/compiler/gc.go b/compiler/gc.go index 0992c01d57..ceb071bf73 100644 --- a/compiler/gc.go +++ b/compiler/gc.go @@ -12,53 +12,53 @@ import ( // trackExpr inserts pointer tracking intrinsics for the GC if the expression is // one of the expressions that need this. -func (c *Compiler) trackExpr(frame *Frame, expr ssa.Value, value llvm.Value) { +func (b *builder) trackExpr(expr ssa.Value, value llvm.Value) { // There are uses of this expression, Make sure the pointers // are tracked during GC. switch expr := expr.(type) { case *ssa.Alloc, *ssa.MakeChan, *ssa.MakeMap: // These values are always of pointer type in IR. - c.trackPointer(value) + b.trackPointer(value) case *ssa.Call, *ssa.Convert, *ssa.MakeClosure, *ssa.MakeInterface, *ssa.MakeSlice, *ssa.Next: if !value.IsNil() { - c.trackValue(value) + b.trackValue(value) } case *ssa.Select: - if alloca, ok := frame.selectRecvBuf[expr]; ok { + if alloca, ok := b.selectRecvBuf[expr]; ok { if alloca.IsAUndefValue().IsNil() { - c.trackPointer(alloca) + b.trackPointer(alloca) } } case *ssa.UnOp: switch expr.Op { case token.MUL: // Pointer dereference. - c.trackValue(value) + b.trackValue(value) case token.ARROW: // Channel receive operator. // It's not necessary to look at commaOk here, because in that // case it's just an aggregate and trackValue will extract the // pointer in there (if there is one). - c.trackValue(value) + b.trackValue(value) } } } // trackValue locates pointers in a value (possibly an aggregate) and tracks the // individual pointers -func (c *Compiler) trackValue(value llvm.Value) { +func (b *builder) trackValue(value llvm.Value) { typ := value.Type() switch typ.TypeKind() { case llvm.PointerTypeKind: - c.trackPointer(value) + b.trackPointer(value) case llvm.StructTypeKind: if !typeHasPointers(typ) { return } numElements := typ.StructElementTypesCount() for i := 0; i < numElements; i++ { - subValue := c.builder.CreateExtractValue(value, i, "") - c.trackValue(subValue) + subValue := b.CreateExtractValue(value, i, "") + b.trackValue(subValue) } case llvm.ArrayTypeKind: if !typeHasPointers(typ) { @@ -66,19 +66,19 @@ func (c *Compiler) trackValue(value llvm.Value) { } numElements := typ.ArrayLength() for i := 0; i < numElements; i++ { - subValue := c.builder.CreateExtractValue(value, i, "") - c.trackValue(subValue) + subValue := b.CreateExtractValue(value, i, "") + b.trackValue(subValue) } } } // trackPointer creates a call to runtime.trackPointer, bitcasting the poitner // first if needed. The input value must be of LLVM pointer type. -func (c *Compiler) trackPointer(value llvm.Value) { - if value.Type() != c.i8ptrType { - value = c.builder.CreateBitCast(value, c.i8ptrType, "") +func (b *builder) trackPointer(value llvm.Value) { + if value.Type() != b.i8ptrType { + value = b.CreateBitCast(value, b.i8ptrType, "") } - c.createRuntimeCall("trackPointer", []llvm.Value{value}, "") + b.createRuntimeCall("trackPointer", []llvm.Value{value}, "") } // typeHasPointers returns whether this type is a pointer or contains pointers. diff --git a/compiler/goroutine.go b/compiler/goroutine.go index 2abb8bb0fe..dd114b3219 100644 --- a/compiler/goroutine.go +++ b/compiler/goroutine.go @@ -3,27 +3,30 @@ package compiler // This file implements the 'go' keyword to start a new goroutine. See // goroutine-lowering.go for more details. -import "tinygo.org/x/go-llvm" +import ( + "github.com/tinygo-org/tinygo/compiler/llvmutil" + "tinygo.org/x/go-llvm" +) -// emitStartGoroutine starts a new goroutine with the provided function pointer +// createGoInstruction starts a new goroutine with the provided function pointer // and parameters. // In general, you should pass all regular parameters plus the context parameter. // There is one exception: the task-based scheduler needs to have the function // pointer passed in as a parameter too in addition to the context. // // Because a go statement doesn't return anything, return undef. -func (c *Compiler) emitStartGoroutine(funcPtr llvm.Value, params []llvm.Value) llvm.Value { - paramBundle := c.emitPointerPack(params) +func (b *builder) createGoInstruction(funcPtr llvm.Value, params []llvm.Value) llvm.Value { + paramBundle := b.emitPointerPack(params) var callee llvm.Value - switch c.Scheduler() { + switch b.Scheduler() { case "none", "tasks": - callee = c.createGoroutineStartWrapper(funcPtr) + callee = b.createGoroutineStartWrapper(funcPtr) case "coroutines": - callee = c.builder.CreatePtrToInt(funcPtr, c.uintptrType, "") + callee = b.CreatePtrToInt(funcPtr, b.uintptrType, "") default: panic("unreachable") } - c.createCall(c.mod.NamedFunction("internal/task.start"), []llvm.Value{callee, paramBundle, llvm.Undef(c.i8ptrType), llvm.ConstPointerNull(c.i8ptrType)}, "") + b.createCall(b.mod.NamedFunction("internal/task.start"), []llvm.Value{callee, paramBundle, llvm.Undef(b.i8ptrType), llvm.ConstPointerNull(b.i8ptrType)}, "") return llvm.Undef(funcPtr.Type().ElementType().ReturnType()) } @@ -45,36 +48,34 @@ func (c *Compiler) emitStartGoroutine(funcPtr llvm.Value, params []llvm.Value) l // allows a single (pointer) argument to the newly started goroutine. Also, it // ignores the return value because newly started goroutines do not have a // return value. -func (c *Compiler) createGoroutineStartWrapper(fn llvm.Value) llvm.Value { +func (c *compilerContext) createGoroutineStartWrapper(fn llvm.Value) llvm.Value { var wrapper llvm.Value + builder := c.ctx.NewBuilder() + if !fn.IsAFunction().IsNil() { // See whether this wrapper has already been created. If so, return it. name := fn.Name() wrapper = c.mod.NamedFunction(name + "$gowrapper") if !wrapper.IsNil() { - return c.builder.CreatePtrToInt(wrapper, c.uintptrType, "") + return llvm.ConstPtrToInt(wrapper, c.uintptrType) } - // Save the current position in the IR builder. - currentBlock := c.builder.GetInsertBlock() - defer c.builder.SetInsertPointAtEnd(currentBlock) - // Create the wrapper. wrapperType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.i8ptrType}, false) wrapper = llvm.AddFunction(c.mod, name+"$gowrapper", wrapperType) wrapper.SetLinkage(llvm.PrivateLinkage) wrapper.SetUnnamedAddr(true) entry := c.ctx.AddBasicBlock(wrapper, "entry") - c.builder.SetInsertPointAtEnd(entry) + builder.SetInsertPointAtEnd(entry) // Create the list of params for the call. paramTypes := fn.Type().ElementType().ParamTypes() - params := c.emitPointerUnpack(wrapper.Param(0), paramTypes[:len(paramTypes)-1]) + params := llvmutil.EmitPointerUnpack(builder, c.mod, wrapper.Param(0), paramTypes[:len(paramTypes)-1]) params = append(params, llvm.Undef(c.i8ptrType)) // Create the call. - c.builder.CreateCall(fn, params, "") + builder.CreateCall(fn, params, "") } else { // For a function pointer like this: @@ -94,22 +95,18 @@ func (c *Compiler) createGoroutineStartWrapper(fn llvm.Value) llvm.Value { // With a bit of luck, identical wrapper functions like these can be // merged into one. - // Save the current position in the IR builder. - currentBlock := c.builder.GetInsertBlock() - defer c.builder.SetInsertPointAtEnd(currentBlock) - // Create the wrapper. wrapperType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.i8ptrType}, false) wrapper = llvm.AddFunction(c.mod, ".gowrapper", wrapperType) wrapper.SetLinkage(llvm.InternalLinkage) wrapper.SetUnnamedAddr(true) entry := c.ctx.AddBasicBlock(wrapper, "entry") - c.builder.SetInsertPointAtEnd(entry) + builder.SetInsertPointAtEnd(entry) // Get the list of parameters, with the extra parameters at the end. paramTypes := fn.Type().ElementType().ParamTypes() paramTypes[len(paramTypes)-1] = fn.Type() // the last element is the function pointer - params := c.emitPointerUnpack(wrapper.Param(0), paramTypes) + params := llvmutil.EmitPointerUnpack(builder, c.mod, wrapper.Param(0), paramTypes) // Get the function pointer. fnPtr := params[len(params)-1] @@ -119,13 +116,13 @@ func (c *Compiler) createGoroutineStartWrapper(fn llvm.Value) llvm.Value { params[len(params)-1] = llvm.Undef(c.i8ptrType) // Create the call. - c.builder.CreateCall(fnPtr, params, "") + builder.CreateCall(fnPtr, params, "") } // Finish the function. Every basic block must end in a terminator, and // because goroutines never return a value we can simply return void. - c.builder.CreateRetVoid() + builder.CreateRetVoid() // Return a ptrtoint of the wrapper, not the function itself. - return c.builder.CreatePtrToInt(wrapper, c.uintptrType, "") + return builder.CreatePtrToInt(wrapper, c.uintptrType, "") } diff --git a/compiler/inlineasm.go b/compiler/inlineasm.go index e9f093a7b7..2727228a18 100644 --- a/compiler/inlineasm.go +++ b/compiler/inlineasm.go @@ -18,8 +18,8 @@ import ( // func ReadRegister(name string) uintptr // // The register name must be a constant, for example "sp". -func (c *Compiler) emitReadRegister(name string, args []ssa.Value) (llvm.Value, error) { - fnType := llvm.FunctionType(c.uintptrType, []llvm.Type{}, false) +func (b *builder) createReadRegister(name string, args []ssa.Value) (llvm.Value, error) { + fnType := llvm.FunctionType(b.uintptrType, []llvm.Type{}, false) regname := constant.StringVal(args[0].(*ssa.Const).Value) var asm string switch name { @@ -31,7 +31,7 @@ func (c *Compiler) emitReadRegister(name string, args []ssa.Value) (llvm.Value, panic("unknown architecture") } target := llvm.InlineAsm(fnType, asm, "=r", false, false, 0) - return c.builder.CreateCall(target, nil, ""), nil + return b.CreateCall(target, nil, ""), nil } // This is a compiler builtin, which emits a piece of inline assembly with no @@ -41,12 +41,12 @@ func (c *Compiler) emitReadRegister(name string, args []ssa.Value) (llvm.Value, // func Asm(asm string) // // The provided assembly must be a constant. -func (c *Compiler) emitAsm(args []ssa.Value) (llvm.Value, error) { +func (b *builder) createInlineAsm(args []ssa.Value) (llvm.Value, error) { // Magic function: insert inline assembly instead of calling it. - fnType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{}, false) + fnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{}, false) asm := constant.StringVal(args[0].(*ssa.Const).Value) target := llvm.InlineAsm(fnType, asm, "", true, false, 0) - return c.builder.CreateCall(target, nil, ""), nil + return b.CreateCall(target, nil, ""), nil } // This is a compiler builtin, which allows assembly to be called in a flexible @@ -63,7 +63,7 @@ func (c *Compiler) emitAsm(args []ssa.Value) (llvm.Value, error) { // "value": 1 // "result": &dest, // }) -func (c *Compiler) emitAsmFull(frame *Frame, instr *ssa.CallCommon) (llvm.Value, error) { +func (b *builder) createInlineAsmFull(instr *ssa.CallCommon) (llvm.Value, error) { asmString := constant.StringVal(instr.Args[0].(*ssa.Const).Value) registers := map[string]llvm.Value{} registerMap := instr.Args[1].(*ssa.MakeMap) @@ -73,17 +73,17 @@ func (c *Compiler) emitAsmFull(frame *Frame, instr *ssa.CallCommon) (llvm.Value, // ignore case *ssa.MapUpdate: if r.Block() != registerMap.Block() { - return llvm.Value{}, c.makeError(instr.Pos(), "register value map must be created in the same basic block") + return llvm.Value{}, b.makeError(instr.Pos(), "register value map must be created in the same basic block") } key := constant.StringVal(r.Key.(*ssa.Const).Value) //println("value:", r.Value.(*ssa.MakeInterface).X.String()) - registers[key] = c.getValue(frame, r.Value.(*ssa.MakeInterface).X) + registers[key] = b.getValue(r.Value.(*ssa.MakeInterface).X) case *ssa.Call: if r.Common() == instr { break } default: - return llvm.Value{}, c.makeError(instr.Pos(), "don't know how to handle argument to inline assembly: "+r.String()) + return llvm.Value{}, b.makeError(instr.Pos(), "don't know how to handle argument to inline assembly: "+r.String()) } } // TODO: handle dollar signs in asm string @@ -98,7 +98,7 @@ func (c *Compiler) emitAsmFull(frame *Frame, instr *ssa.CallCommon) (llvm.Value, name := s[1 : len(s)-1] if _, ok := registers[name]; !ok { if err == nil { - err = c.makeError(instr.Pos(), "unknown register name: "+name) + err = b.makeError(instr.Pos(), "unknown register name: "+name) } return s } @@ -112,7 +112,7 @@ func (c *Compiler) emitAsmFull(frame *Frame, instr *ssa.CallCommon) (llvm.Value, case llvm.PointerTypeKind: constraints = append(constraints, "*m") default: - err = c.makeError(instr.Pos(), "unknown type in inline assembly for value: "+name) + err = b.makeError(instr.Pos(), "unknown type in inline assembly for value: "+name) return s } } @@ -121,9 +121,9 @@ func (c *Compiler) emitAsmFull(frame *Frame, instr *ssa.CallCommon) (llvm.Value, if err != nil { return llvm.Value{}, err } - fnType := llvm.FunctionType(c.ctx.VoidType(), argTypes, false) + fnType := llvm.FunctionType(b.ctx.VoidType(), argTypes, false) target := llvm.InlineAsm(fnType, asmString, strings.Join(constraints, ","), true, false, 0) - return c.builder.CreateCall(target, args, ""), nil + return b.CreateCall(target, args, ""), nil } // This is a compiler builtin which emits an inline SVCall instruction. It can @@ -137,7 +137,7 @@ func (c *Compiler) emitAsmFull(frame *Frame, instr *ssa.CallCommon) (llvm.Value, // // The num parameter must be a constant. All other parameters may be any scalar // value supported by LLVM inline assembly. -func (c *Compiler) emitSVCall(frame *Frame, args []ssa.Value) (llvm.Value, error) { +func (b *builder) emitSVCall(args []ssa.Value) (llvm.Value, error) { num, _ := constant.Uint64Val(args[0].(*ssa.Const).Value) llvmArgs := []llvm.Value{} argTypes := []llvm.Type{} @@ -150,7 +150,7 @@ func (c *Compiler) emitSVCall(frame *Frame, args []ssa.Value) (llvm.Value, error } else { constraints += ",{r" + strconv.Itoa(i) + "}" } - llvmValue := c.getValue(frame, arg) + llvmValue := b.getValue(arg) llvmArgs = append(llvmArgs, llvmValue) argTypes = append(argTypes, llvmValue.Type()) } @@ -158,9 +158,9 @@ func (c *Compiler) emitSVCall(frame *Frame, args []ssa.Value) (llvm.Value, error // clobbered. r0 is used as an output register so doesn't have to be // marked as clobbered. constraints += ",~{r1},~{r2},~{r3}" - fnType := llvm.FunctionType(c.uintptrType, argTypes, false) + fnType := llvm.FunctionType(b.uintptrType, argTypes, false) target := llvm.InlineAsm(fnType, asm, constraints, true, false, 0) - return c.builder.CreateCall(target, llvmArgs, ""), nil + return b.CreateCall(target, llvmArgs, ""), nil } // This is a compiler builtin which emits CSR instructions. It can be one of: @@ -172,38 +172,38 @@ func (c *Compiler) emitSVCall(frame *Frame, args []ssa.Value) (llvm.Value, error // // The csr parameter (method receiver) must be a constant. Other parameter can // be any value. -func (c *Compiler) emitCSROperation(frame *Frame, call *ssa.CallCommon) (llvm.Value, error) { +func (b *builder) emitCSROperation(call *ssa.CallCommon) (llvm.Value, error) { csrConst, ok := call.Args[0].(*ssa.Const) if !ok { - return llvm.Value{}, c.makeError(call.Pos(), "CSR must be constant") + return llvm.Value{}, b.makeError(call.Pos(), "CSR must be constant") } csr := csrConst.Uint64() switch name := call.StaticCallee().Name(); name { case "Get": // Note that this instruction may have side effects, and thus must be // marked as such. - fnType := llvm.FunctionType(c.uintptrType, nil, false) + fnType := llvm.FunctionType(b.uintptrType, nil, false) asm := fmt.Sprintf("csrr $0, %d", csr) target := llvm.InlineAsm(fnType, asm, "=r", true, false, 0) - return c.builder.CreateCall(target, nil, ""), nil + return b.CreateCall(target, nil, ""), nil case "Set": - fnType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.uintptrType}, false) + fnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.uintptrType}, false) asm := fmt.Sprintf("csrw %d, $0", csr) target := llvm.InlineAsm(fnType, asm, "r", true, false, 0) - return c.builder.CreateCall(target, []llvm.Value{c.getValue(frame, call.Args[1])}, ""), nil + return b.CreateCall(target, []llvm.Value{b.getValue(call.Args[1])}, ""), nil case "SetBits": // Note: it may be possible to optimize this to csrrsi in many cases. - fnType := llvm.FunctionType(c.uintptrType, []llvm.Type{c.uintptrType}, false) + fnType := llvm.FunctionType(b.uintptrType, []llvm.Type{b.uintptrType}, false) asm := fmt.Sprintf("csrrs $0, %d, $1", csr) target := llvm.InlineAsm(fnType, asm, "=r,r", true, false, 0) - return c.builder.CreateCall(target, []llvm.Value{c.getValue(frame, call.Args[1])}, ""), nil + return b.CreateCall(target, []llvm.Value{b.getValue(call.Args[1])}, ""), nil case "ClearBits": // Note: it may be possible to optimize this to csrrci in many cases. - fnType := llvm.FunctionType(c.uintptrType, []llvm.Type{c.uintptrType}, false) + fnType := llvm.FunctionType(b.uintptrType, []llvm.Type{b.uintptrType}, false) asm := fmt.Sprintf("csrrc $0, %d, $1", csr) target := llvm.InlineAsm(fnType, asm, "=r,r", true, false, 0) - return c.builder.CreateCall(target, []llvm.Value{c.getValue(frame, call.Args[1])}, ""), nil + return b.CreateCall(target, []llvm.Value{b.getValue(call.Args[1])}, ""), nil default: - return llvm.Value{}, c.makeError(call.Pos(), "unknown CSR operation: "+name) + return llvm.Value{}, b.makeError(call.Pos(), "unknown CSR operation: "+name) } } diff --git a/compiler/interface.go b/compiler/interface.go index 761a45c5ca..c6a8a61ac9 100644 --- a/compiler/interface.go +++ b/compiler/interface.go @@ -16,35 +16,35 @@ import ( "tinygo.org/x/go-llvm" ) -// parseMakeInterface emits the LLVM IR for the *ssa.MakeInterface instruction. +// createMakeInterface emits the LLVM IR for the *ssa.MakeInterface instruction. // It tries to put the type in the interface value, but if that's not possible, // it will do an allocation of the right size and put that in the interface // value field. // -// An interface value is a {typecode, value} tuple, or {i16, i8*} to be exact. -func (c *Compiler) parseMakeInterface(val llvm.Value, typ types.Type, pos token.Pos) llvm.Value { - itfValue := c.emitPointerPack([]llvm.Value{val}) - itfTypeCodeGlobal := c.getTypeCode(typ) - itfMethodSetGlobal := c.getTypeMethodSet(typ) - itfConcreteTypeGlobal := c.mod.NamedGlobal("typeInInterface:" + itfTypeCodeGlobal.Name()) +// An interface value is a {typecode, value} tuple named runtime._interface. +func (b *builder) createMakeInterface(val llvm.Value, typ types.Type, pos token.Pos) llvm.Value { + itfValue := b.emitPointerPack([]llvm.Value{val}) + itfTypeCodeGlobal := b.getTypeCode(typ) + itfMethodSetGlobal := b.getTypeMethodSet(typ) + itfConcreteTypeGlobal := b.mod.NamedGlobal("typeInInterface:" + itfTypeCodeGlobal.Name()) if itfConcreteTypeGlobal.IsNil() { - typeInInterface := c.getLLVMRuntimeType("typeInInterface") - itfConcreteTypeGlobal = llvm.AddGlobal(c.mod, typeInInterface, "typeInInterface:"+itfTypeCodeGlobal.Name()) + typeInInterface := b.getLLVMRuntimeType("typeInInterface") + itfConcreteTypeGlobal = llvm.AddGlobal(b.mod, typeInInterface, "typeInInterface:"+itfTypeCodeGlobal.Name()) itfConcreteTypeGlobal.SetInitializer(llvm.ConstNamedStruct(typeInInterface, []llvm.Value{itfTypeCodeGlobal, itfMethodSetGlobal})) itfConcreteTypeGlobal.SetGlobalConstant(true) itfConcreteTypeGlobal.SetLinkage(llvm.PrivateLinkage) } - itfTypeCode := c.builder.CreatePtrToInt(itfConcreteTypeGlobal, c.uintptrType, "") - itf := llvm.Undef(c.getLLVMRuntimeType("_interface")) - itf = c.builder.CreateInsertValue(itf, itfTypeCode, 0, "") - itf = c.builder.CreateInsertValue(itf, itfValue, 1, "") + itfTypeCode := b.CreatePtrToInt(itfConcreteTypeGlobal, b.uintptrType, "") + itf := llvm.Undef(b.getLLVMRuntimeType("_interface")) + itf = b.CreateInsertValue(itf, itfTypeCode, 0, "") + itf = b.CreateInsertValue(itf, itfValue, 1, "") return itf } // getTypeCode returns a reference to a type code. // It returns a pointer to an external global which should be replaced with the // real type in the interface lowering pass. -func (c *Compiler) getTypeCode(typ types.Type) llvm.Value { +func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value { globalName := "reflect/types.type:" + getTypeCodeName(typ) global := c.mod.NamedGlobal(globalName) if global.IsNil() { @@ -91,7 +91,7 @@ func (c *Compiler) getTypeCode(typ types.Type) llvm.Value { // makeStructTypeFields creates a new global that stores all type information // related to this struct type, and returns the resulting global. This global is // actually an array of all the fields in the structs. -func (c *Compiler) makeStructTypeFields(typ *types.Struct) llvm.Value { +func (c *compilerContext) makeStructTypeFields(typ *types.Struct) llvm.Value { // The global is an array of runtime.structField structs. runtimeStructField := c.getLLVMRuntimeType("structField") structGlobalType := llvm.ArrayType(runtimeStructField, typ.NumFields()) @@ -228,7 +228,7 @@ func getTypeCodeName(t types.Type) string { // getTypeMethodSet returns a reference (GEP) to a global method set. This // method set should be unreferenced after the interface lowering pass. -func (c *Compiler) getTypeMethodSet(typ types.Type) llvm.Value { +func (c *compilerContext) getTypeMethodSet(typ types.Type) llvm.Value { global := c.mod.NamedGlobal(typ.String() + "$methodset") zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false) if !global.IsNil() { @@ -271,7 +271,7 @@ func (c *Compiler) getTypeMethodSet(typ types.Type) llvm.Value { // getInterfaceMethodSet returns a global variable with the method set of the // given named interface type. This method set is used by the interface lowering // pass. -func (c *Compiler) getInterfaceMethodSet(typ *types.Named) llvm.Value { +func (c *compilerContext) getInterfaceMethodSet(typ *types.Named) llvm.Value { global := c.mod.NamedGlobal(typ.String() + "$interface") zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false) if !global.IsNil() { @@ -297,7 +297,7 @@ func (c *Compiler) getInterfaceMethodSet(typ *types.Named) llvm.Value { // getMethodSignature returns a global variable which is a reference to an // external *i8 indicating the indicating the signature of this method. It is // used during the interface lowering pass. -func (c *Compiler) getMethodSignature(method *types.Func) llvm.Value { +func (c *compilerContext) getMethodSignature(method *types.Func) llvm.Value { signature := ir.MethodSignature(method) signatureGlobal := c.mod.NamedGlobal("func " + signature) if signatureGlobal.IsNil() { @@ -307,18 +307,18 @@ func (c *Compiler) getMethodSignature(method *types.Func) llvm.Value { return signatureGlobal } -// parseTypeAssert will emit the code for a typeassert, used in if statements +// createTypeAssert will emit the code for a typeassert, used in if statements // and in type switches (Go SSA does not have type switches, only if/else // chains). Note that even though the Go SSA does not contain type switches, // LLVM will recognize the pattern and make it a real switch in many cases. // // Type asserts on concrete types are trivial: just compare type numbers. Type // asserts on interfaces are more difficult, see the comments in the function. -func (c *Compiler) parseTypeAssert(frame *Frame, expr *ssa.TypeAssert) llvm.Value { - itf := c.getValue(frame, expr.X) - assertedType := c.getLLVMType(expr.AssertedType) +func (b *builder) createTypeAssert(expr *ssa.TypeAssert) llvm.Value { + itf := b.getValue(expr.X) + assertedType := b.getLLVMType(expr.AssertedType) - actualTypeNum := c.builder.CreateExtractValue(itf, 0, "interface.type") + actualTypeNum := b.CreateExtractValue(itf, 0, "interface.type") commaOk := llvm.Value{} if _, ok := expr.AssertedType.Underlying().(*types.Interface); ok { // Type assert on interface type. @@ -329,15 +329,15 @@ func (c *Compiler) parseTypeAssert(frame *Frame, expr *ssa.TypeAssert) llvm.Valu // the main Go compiler, where the runtime checks whether the type // implements each method of the interface. See: // https://research.swtch.com/interfaces - methodSet := c.getInterfaceMethodSet(expr.AssertedType.(*types.Named)) - commaOk = c.createRuntimeCall("interfaceImplements", []llvm.Value{actualTypeNum, methodSet}, "") + methodSet := b.getInterfaceMethodSet(expr.AssertedType.(*types.Named)) + commaOk = b.createRuntimeCall("interfaceImplements", []llvm.Value{actualTypeNum, methodSet}, "") } else { // Type assert on concrete type. // Call runtime.typeAssert, which will be lowered to a simple icmp or // const false in the interface lowering pass. - assertedTypeCodeGlobal := c.getTypeCode(expr.AssertedType) - commaOk = c.createRuntimeCall("typeAssert", []llvm.Value{actualTypeNum, assertedTypeCodeGlobal}, "typecode") + assertedTypeCodeGlobal := b.getTypeCode(expr.AssertedType) + commaOk = b.createRuntimeCall("typeAssert", []llvm.Value{actualTypeNum, assertedTypeCodeGlobal}, "typecode") } // Add 2 new basic blocks (that should get optimized away): one for the @@ -351,15 +351,15 @@ func (c *Compiler) parseTypeAssert(frame *Frame, expr *ssa.TypeAssert) llvm.Valu // typeassert should return a zero value, not an incorrectly casted // value. - prevBlock := c.builder.GetInsertBlock() - okBlock := c.ctx.AddBasicBlock(frame.fn.LLVMFn, "typeassert.ok") - nextBlock := c.ctx.AddBasicBlock(frame.fn.LLVMFn, "typeassert.next") - frame.blockExits[frame.currentBlock] = nextBlock // adjust outgoing block for phi nodes - c.builder.CreateCondBr(commaOk, okBlock, nextBlock) + prevBlock := b.GetInsertBlock() + okBlock := b.ctx.AddBasicBlock(b.fn.LLVMFn, "typeassert.ok") + nextBlock := b.ctx.AddBasicBlock(b.fn.LLVMFn, "typeassert.next") + b.blockExits[b.currentBlock] = nextBlock // adjust outgoing block for phi nodes + b.CreateCondBr(commaOk, okBlock, nextBlock) // Retrieve the value from the interface if the type assert was // successful. - c.builder.SetInsertPointAtEnd(okBlock) + b.SetInsertPointAtEnd(okBlock) var valueOk llvm.Value if _, ok := expr.AssertedType.Underlying().(*types.Interface); ok { // Type assert on interface type. Easy: just return the same @@ -368,74 +368,66 @@ func (c *Compiler) parseTypeAssert(frame *Frame, expr *ssa.TypeAssert) llvm.Valu } else { // Type assert on concrete type. Extract the underlying type from // the interface (but only after checking it matches). - valuePtr := c.builder.CreateExtractValue(itf, 1, "typeassert.value.ptr") - valueOk = c.emitPointerUnpack(valuePtr, []llvm.Type{assertedType})[0] + valuePtr := b.CreateExtractValue(itf, 1, "typeassert.value.ptr") + valueOk = b.emitPointerUnpack(valuePtr, []llvm.Type{assertedType})[0] } - c.builder.CreateBr(nextBlock) + b.CreateBr(nextBlock) // Continue after the if statement. - c.builder.SetInsertPointAtEnd(nextBlock) - phi := c.builder.CreatePHI(assertedType, "typeassert.value") + b.SetInsertPointAtEnd(nextBlock) + phi := b.CreatePHI(assertedType, "typeassert.value") phi.AddIncoming([]llvm.Value{llvm.ConstNull(assertedType), valueOk}, []llvm.BasicBlock{prevBlock, okBlock}) if expr.CommaOk { - tuple := c.ctx.ConstStruct([]llvm.Value{llvm.Undef(assertedType), llvm.Undef(c.ctx.Int1Type())}, false) // create empty tuple - tuple = c.builder.CreateInsertValue(tuple, phi, 0, "") // insert value - tuple = c.builder.CreateInsertValue(tuple, commaOk, 1, "") // insert 'comma ok' boolean + tuple := b.ctx.ConstStruct([]llvm.Value{llvm.Undef(assertedType), llvm.Undef(b.ctx.Int1Type())}, false) // create empty tuple + tuple = b.CreateInsertValue(tuple, phi, 0, "") // insert value + tuple = b.CreateInsertValue(tuple, commaOk, 1, "") // insert 'comma ok' boolean return tuple } else { // This is kind of dirty as the branch above becomes mostly useless, // but hopefully this gets optimized away. - c.createRuntimeCall("interfaceTypeAssert", []llvm.Value{commaOk}, "") + b.createRuntimeCall("interfaceTypeAssert", []llvm.Value{commaOk}, "") return phi } } // getInvokeCall creates and returns the function pointer and parameters of an // interface call. It can be used in a call or defer instruction. -func (c *Compiler) getInvokeCall(frame *Frame, instr *ssa.CallCommon) (llvm.Value, []llvm.Value) { +func (b *builder) getInvokeCall(instr *ssa.CallCommon) (llvm.Value, []llvm.Value) { // Call an interface method with dynamic dispatch. - itf := c.getValue(frame, instr.Value) // interface + itf := b.getValue(instr.Value) // interface - llvmFnType := c.getRawFuncType(instr.Method.Type().(*types.Signature)) + llvmFnType := b.getRawFuncType(instr.Method.Type().(*types.Signature)) - typecode := c.builder.CreateExtractValue(itf, 0, "invoke.typecode") + typecode := b.CreateExtractValue(itf, 0, "invoke.typecode") values := []llvm.Value{ typecode, - c.getInterfaceMethodSet(instr.Value.Type().(*types.Named)), - c.getMethodSignature(instr.Method), + b.getInterfaceMethodSet(instr.Value.Type().(*types.Named)), + b.getMethodSignature(instr.Method), } - fn := c.createRuntimeCall("interfaceMethod", values, "invoke.func") - fnCast := c.builder.CreateIntToPtr(fn, llvmFnType, "invoke.func.cast") - receiverValue := c.builder.CreateExtractValue(itf, 1, "invoke.func.receiver") + fn := b.createRuntimeCall("interfaceMethod", values, "invoke.func") + fnCast := b.CreateIntToPtr(fn, llvmFnType, "invoke.func.cast") + receiverValue := b.CreateExtractValue(itf, 1, "invoke.func.receiver") args := []llvm.Value{receiverValue} for _, arg := range instr.Args { - args = append(args, c.getValue(frame, arg)) + args = append(args, b.getValue(arg)) } // Add the context parameter. An interface call never takes a context but we // have to supply the parameter anyway. - args = append(args, llvm.Undef(c.i8ptrType)) + args = append(args, llvm.Undef(b.i8ptrType)) // Add the parent goroutine handle. - args = append(args, llvm.Undef(c.i8ptrType)) + args = append(args, llvm.Undef(b.i8ptrType)) return fnCast, args } -// interfaceInvokeWrapper keeps some state between getInterfaceInvokeWrapper and -// createInterfaceInvokeWrapper. The former is called during IR construction -// itself and the latter is called when finishing up the IR. -type interfaceInvokeWrapper struct { - fn *ir.Function - wrapper llvm.Value - receiverType llvm.Type -} - -// Wrap an interface method function pointer. The wrapper takes in a pointer to -// the underlying value, dereferences it, and calls the real method. This -// wrapper is only needed when the interface value actually doesn't fit in a -// pointer and a pointer to the value must be created. -func (c *Compiler) getInterfaceInvokeWrapper(f *ir.Function) llvm.Value { +// getInterfaceInvokeWrapper returns a wrapper for the given method so it can be +// invoked from an interface. The wrapper takes in a pointer to the underlying +// value, dereferences or unpacks it if necessary, and calls the real method. +// If the method to wrap has a pointer receiver, no wrapping is necessary and +// the function is returned directly. +func (c *compilerContext) getInterfaceInvokeWrapper(f *ir.Function) llvm.Value { wrapperName := f.LinkName() + "$invoke" wrapper := c.mod.NamedFunction(wrapperName) if !wrapper.IsNil() { @@ -445,7 +437,7 @@ func (c *Compiler) getInterfaceInvokeWrapper(f *ir.Function) llvm.Value { // Get the expanded receiver type. receiverType := c.getLLVMType(f.Params[0].Type()) - expandedReceiverType := c.expandFormalParamType(receiverType) + expandedReceiverType := expandFormalParamType(receiverType) // Does this method even need any wrapping? if len(expandedReceiverType) == 1 && receiverType.TypeKind() == llvm.PointerTypeKind { @@ -464,41 +456,37 @@ func (c *Compiler) getInterfaceInvokeWrapper(f *ir.Function) llvm.Value { if f.LLVMFn.LastParam().Name() == "parentHandle" { wrapper.LastParam().SetName("parentHandle") } - c.interfaceInvokeWrappers = append(c.interfaceInvokeWrappers, interfaceInvokeWrapper{ - fn: f, - wrapper: wrapper, - receiverType: receiverType, - }) - return wrapper -} -// createInterfaceInvokeWrapper finishes the work of getInterfaceInvokeWrapper, -// see that function for details. -func (c *Compiler) createInterfaceInvokeWrapper(state interfaceInvokeWrapper) { - wrapper := state.wrapper - fn := state.fn - receiverType := state.receiverType wrapper.SetLinkage(llvm.InternalLinkage) wrapper.SetUnnamedAddr(true) + // Create a new builder just to create this wrapper. + b := builder{ + compilerContext: c, + Builder: c.ctx.NewBuilder(), + } + defer b.Builder.Dispose() + // add debug info if needed if c.Debug() { - pos := c.ir.Program.Fset.Position(fn.Pos()) - difunc := c.attachDebugInfoRaw(fn, wrapper, "$invoke", pos.Filename, pos.Line) - c.builder.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), difunc, llvm.Metadata{}) + pos := c.ir.Program.Fset.Position(f.Pos()) + difunc := c.attachDebugInfoRaw(f, wrapper, "$invoke", pos.Filename, pos.Line) + b.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), difunc, llvm.Metadata{}) } // set up IR builder - block := c.ctx.AddBasicBlock(wrapper, "entry") - c.builder.SetInsertPointAtEnd(block) - - receiverValue := c.emitPointerUnpack(wrapper.Param(0), []llvm.Type{receiverType})[0] - params := append(c.expandFormalParam(receiverValue), wrapper.Params()[1:]...) - if fn.LLVMFn.Type().ElementType().ReturnType().TypeKind() == llvm.VoidTypeKind { - c.builder.CreateCall(fn.LLVMFn, params, "") - c.builder.CreateRetVoid() + block := b.ctx.AddBasicBlock(wrapper, "entry") + b.SetInsertPointAtEnd(block) + + receiverValue := b.emitPointerUnpack(wrapper.Param(0), []llvm.Type{receiverType})[0] + params := append(b.expandFormalParam(receiverValue), wrapper.Params()[1:]...) + if f.LLVMFn.Type().ElementType().ReturnType().TypeKind() == llvm.VoidTypeKind { + b.CreateCall(f.LLVMFn, params, "") + b.CreateRetVoid() } else { - ret := c.builder.CreateCall(fn.LLVMFn, params, "ret") - c.builder.CreateRet(ret) + ret := b.CreateCall(f.LLVMFn, params, "ret") + b.CreateRet(ret) } + + return wrapper } diff --git a/compiler/interrupt.go b/compiler/interrupt.go index 67cb8eca62..e4e31c0807 100644 --- a/compiler/interrupt.go +++ b/compiler/interrupt.go @@ -8,62 +8,62 @@ import ( "tinygo.org/x/go-llvm" ) -// emitInterruptGlobal creates a new runtime/interrupt.Interrupt struct that +// createInterruptGlobal creates a new runtime/interrupt.Interrupt struct that // will be lowered to a real interrupt during interrupt lowering. // // This two-stage approach allows unused interrupts to be optimized away if // necessary. -func (c *Compiler) emitInterruptGlobal(frame *Frame, instr *ssa.CallCommon) (llvm.Value, error) { +func (b *builder) createInterruptGlobal(instr *ssa.CallCommon) (llvm.Value, error) { // Get the interrupt number, which must be a compile-time constant. id, ok := instr.Args[0].(*ssa.Const) if !ok { - return llvm.Value{}, c.makeError(instr.Pos(), "interrupt ID is not a constant") + return llvm.Value{}, b.makeError(instr.Pos(), "interrupt ID is not a constant") } // Get the func value, which also must be a compile time constant. // Note that bound functions are allowed if the function has a pointer // receiver and is a global. This is rather strict but still allows for // idiomatic Go code. - funcValue := c.getValue(frame, instr.Args[1]) + funcValue := b.getValue(instr.Args[1]) if funcValue.IsAConstant().IsNil() { // Try to determine the cause of the non-constantness for a nice error // message. switch instr.Args[1].(type) { case *ssa.MakeClosure: // This may also be a bound method. - return llvm.Value{}, c.makeError(instr.Pos(), "closures are not supported in interrupt.New") + return llvm.Value{}, b.makeError(instr.Pos(), "closures are not supported in interrupt.New") } // Fall back to a generic error. - return llvm.Value{}, c.makeError(instr.Pos(), "interrupt function must be constant") + return llvm.Value{}, b.makeError(instr.Pos(), "interrupt function must be constant") } // Create a new global of type runtime/interrupt.handle. Globals of this // type are lowered in the interrupt lowering pass. - globalType := c.ir.Program.ImportedPackage("runtime/interrupt").Type("handle").Type() - globalLLVMType := c.getLLVMType(globalType) + globalType := b.ir.Program.ImportedPackage("runtime/interrupt").Type("handle").Type() + globalLLVMType := b.getLLVMType(globalType) globalName := "runtime/interrupt.$interrupt" + strconv.FormatInt(id.Int64(), 10) - if global := c.mod.NamedGlobal(globalName); !global.IsNil() { - return llvm.Value{}, c.makeError(instr.Pos(), "interrupt redeclared in this program") + if global := b.mod.NamedGlobal(globalName); !global.IsNil() { + return llvm.Value{}, b.makeError(instr.Pos(), "interrupt redeclared in this program") } - global := llvm.AddGlobal(c.mod, globalLLVMType, globalName) + global := llvm.AddGlobal(b.mod, globalLLVMType, globalName) global.SetLinkage(llvm.PrivateLinkage) global.SetGlobalConstant(true) global.SetUnnamedAddr(true) initializer := llvm.ConstNull(globalLLVMType) initializer = llvm.ConstInsertValue(initializer, funcValue, []uint32{0}) - initializer = llvm.ConstInsertValue(initializer, llvm.ConstInt(c.intType, uint64(id.Int64()), true), []uint32{1, 0}) + initializer = llvm.ConstInsertValue(initializer, llvm.ConstInt(b.intType, uint64(id.Int64()), true), []uint32{1, 0}) global.SetInitializer(initializer) // Add debug info to the interrupt global. - if c.Debug() { - pos := c.ir.Program.Fset.Position(instr.Pos()) - diglobal := c.dibuilder.CreateGlobalVariableExpression(c.difiles[pos.Filename], llvm.DIGlobalVariableExpression{ + if b.Debug() { + pos := b.ir.Program.Fset.Position(instr.Pos()) + diglobal := b.dibuilder.CreateGlobalVariableExpression(b.getDIFile(pos.Filename), llvm.DIGlobalVariableExpression{ Name: "interrupt" + strconv.FormatInt(id.Int64(), 10), LinkageName: globalName, - File: c.getDIFile(pos.Filename), + File: b.getDIFile(pos.Filename), Line: pos.Line, - Type: c.getDIType(globalType), - Expr: c.dibuilder.CreateExpression(nil), + Type: b.getDIType(globalType), + Expr: b.dibuilder.CreateExpression(nil), LocalToUnit: false, }) global.AddMetadata(0, diglobal) @@ -71,21 +71,21 @@ func (c *Compiler) emitInterruptGlobal(frame *Frame, instr *ssa.CallCommon) (llv // Create the runtime/interrupt.Interrupt type. It is a struct with a single // member of type int. - num := llvm.ConstPtrToInt(global, c.intType) - interrupt := llvm.ConstNamedStruct(c.mod.GetTypeByName("runtime/interrupt.Interrupt"), []llvm.Value{num}) + num := llvm.ConstPtrToInt(global, b.intType) + interrupt := llvm.ConstNamedStruct(b.mod.GetTypeByName("runtime/interrupt.Interrupt"), []llvm.Value{num}) // Add dummy "use" call for AVR, because interrupts may be used even though // they are never referenced again. This is unlike Cortex-M or the RISC-V // 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(c.Triple(), "avr") { - useFn := c.mod.NamedFunction("runtime/interrupt.use") + if strings.HasPrefix(b.Triple(), "avr") { + useFn := b.mod.NamedFunction("runtime/interrupt.use") if useFn.IsNil() { - useFnType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{interrupt.Type()}, false) - useFn = llvm.AddFunction(c.mod, "runtime/interrupt.use", useFnType) + useFnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{interrupt.Type()}, false) + useFn = llvm.AddFunction(b.mod, "runtime/interrupt.use", useFnType) } - c.builder.CreateCall(useFn, []llvm.Value{interrupt}, "") + b.CreateCall(useFn, []llvm.Value{interrupt}, "") } return interrupt, nil diff --git a/compiler/llvm.go b/compiler/llvm.go index 53d52bbaef..e69c622276 100644 --- a/compiler/llvm.go +++ b/compiler/llvm.go @@ -29,34 +29,34 @@ func getUses(value llvm.Value) []llvm.Value { // // This is useful for creating temporary allocas for intrinsics. Don't forget to // end the lifetime using emitLifetimeEnd after you're done with it. -func (c *Compiler) createTemporaryAlloca(t llvm.Type, name string) (alloca, bitcast, size llvm.Value) { - return llvmutil.CreateTemporaryAlloca(c.builder, c.mod, t, name) +func (b *builder) createTemporaryAlloca(t llvm.Type, name string) (alloca, bitcast, size llvm.Value) { + return llvmutil.CreateTemporaryAlloca(b.Builder, b.mod, t, name) } // emitLifetimeEnd signals the end of an (alloca) lifetime by calling the // llvm.lifetime.end intrinsic. It is commonly used together with // createTemporaryAlloca. -func (c *Compiler) emitLifetimeEnd(ptr, size llvm.Value) { - llvmutil.EmitLifetimeEnd(c.builder, c.mod, ptr, size) +func (b *builder) emitLifetimeEnd(ptr, size llvm.Value) { + llvmutil.EmitLifetimeEnd(b.Builder, b.mod, ptr, size) } // emitPointerPack packs the list of values into a single pointer value using // 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 (c *Compiler) emitPointerPack(values []llvm.Value) llvm.Value { - return llvmutil.EmitPointerPack(c.builder, c.mod, c.Config, values) +func (b *builder) emitPointerPack(values []llvm.Value) llvm.Value { + return llvmutil.EmitPointerPack(b.Builder, b.mod, b.Config, values) } // emitPointerUnpack extracts a list of values packed using emitPointerPack. -func (c *Compiler) emitPointerUnpack(ptr llvm.Value, valueTypes []llvm.Type) []llvm.Value { - return llvmutil.EmitPointerUnpack(c.builder, c.mod, ptr, valueTypes) +func (b *builder) emitPointerUnpack(ptr llvm.Value, valueTypes []llvm.Type) []llvm.Value { + return llvmutil.EmitPointerUnpack(b.Builder, b.mod, ptr, valueTypes) } // makeGlobalArray creates a new LLVM global with the given name and integers as // contents, and returns the global. // Note that it is left with the default linkage etc., you should set // linkage/constant/etc properties yourself. -func (c *Compiler) makeGlobalArray(buf []byte, name string, elementType llvm.Type) llvm.Value { +func (c *compilerContext) makeGlobalArray(buf []byte, name string, elementType llvm.Type) llvm.Value { globalType := llvm.ArrayType(elementType, len(buf)) global := llvm.AddGlobal(c.mod, globalType, name) value := llvm.Undef(globalType) diff --git a/compiler/map.go b/compiler/map.go index 5faf7aedf2..d469db5bb9 100644 --- a/compiler/map.go +++ b/compiler/map.go @@ -10,56 +10,58 @@ import ( "tinygo.org/x/go-llvm" ) -// emitMakeMap creates a new map object (runtime.hashmap) by allocating and +// createMakeMap creates a new map object (runtime.hashmap) by allocating and // initializing an appropriately sized object. -func (c *Compiler) emitMakeMap(frame *Frame, expr *ssa.MakeMap) (llvm.Value, error) { +func (b *builder) createMakeMap(expr *ssa.MakeMap) (llvm.Value, error) { mapType := expr.Type().Underlying().(*types.Map) keyType := mapType.Key().Underlying() - llvmValueType := c.getLLVMType(mapType.Elem().Underlying()) + llvmValueType := b.getLLVMType(mapType.Elem().Underlying()) var llvmKeyType llvm.Type if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 { // String keys. - llvmKeyType = c.getLLVMType(keyType) + llvmKeyType = b.getLLVMType(keyType) } else if hashmapIsBinaryKey(keyType) { // Trivially comparable keys. - llvmKeyType = c.getLLVMType(keyType) + llvmKeyType = b.getLLVMType(keyType) } else { // All other keys. Implemented as map[interface{}]valueType for ease of // implementation. - llvmKeyType = c.getLLVMRuntimeType("_interface") + llvmKeyType = b.getLLVMRuntimeType("_interface") } - keySize := c.targetData.TypeAllocSize(llvmKeyType) - valueSize := c.targetData.TypeAllocSize(llvmValueType) - llvmKeySize := llvm.ConstInt(c.ctx.Int8Type(), keySize, false) - llvmValueSize := llvm.ConstInt(c.ctx.Int8Type(), valueSize, false) - sizeHint := llvm.ConstInt(c.uintptrType, 8, false) + keySize := b.targetData.TypeAllocSize(llvmKeyType) + valueSize := b.targetData.TypeAllocSize(llvmValueType) + llvmKeySize := llvm.ConstInt(b.ctx.Int8Type(), keySize, false) + llvmValueSize := llvm.ConstInt(b.ctx.Int8Type(), valueSize, false) + sizeHint := llvm.ConstInt(b.uintptrType, 8, false) if expr.Reserve != nil { - sizeHint = c.getValue(frame, expr.Reserve) + sizeHint = b.getValue(expr.Reserve) var err error - sizeHint, err = c.parseConvert(expr.Reserve.Type(), types.Typ[types.Uintptr], sizeHint, expr.Pos()) + sizeHint, err = b.createConvert(expr.Reserve.Type(), types.Typ[types.Uintptr], sizeHint, expr.Pos()) if err != nil { return llvm.Value{}, err } } - hashmap := c.createRuntimeCall("hashmapMake", []llvm.Value{llvmKeySize, llvmValueSize, sizeHint}, "") + hashmap := b.createRuntimeCall("hashmapMake", []llvm.Value{llvmKeySize, llvmValueSize, sizeHint}, "") return hashmap, nil } -func (c *Compiler) emitMapLookup(keyType, valueType types.Type, m, key llvm.Value, commaOk bool, pos token.Pos) (llvm.Value, error) { - llvmValueType := c.getLLVMType(valueType) +// createMapLookup returns the value in a map. It calls a runtime function +// depending on the map key type to load the map value and its comma-ok value. +func (b *builder) createMapLookup(keyType, valueType types.Type, m, key llvm.Value, commaOk bool, pos token.Pos) (llvm.Value, error) { + llvmValueType := b.getLLVMType(valueType) // Allocate the memory for the resulting type. Do not zero this memory: it // will be zeroed by the hashmap get implementation if the key is not // present in the map. - mapValueAlloca, mapValuePtr, mapValueAllocaSize := c.createTemporaryAlloca(llvmValueType, "hashmap.value") + mapValueAlloca, mapValuePtr, mapValueAllocaSize := b.createTemporaryAlloca(llvmValueType, "hashmap.value") // We need the map size (with type uintptr) to pass to the hashmap*Get // functions. This is necessary because those *Get functions are valid on // nil maps, and they'll need to zero the value pointer by that number of // bytes. mapValueSize := mapValueAllocaSize - if mapValueSize.Type().IntTypeWidth() > c.uintptrType.IntTypeWidth() { - mapValueSize = llvm.ConstTrunc(mapValueSize, c.uintptrType) + if mapValueSize.Type().IntTypeWidth() > b.uintptrType.IntTypeWidth() { + mapValueSize = llvm.ConstTrunc(mapValueSize, b.uintptrType) } // Do the lookup. How it is done depends on the key type. @@ -68,84 +70,88 @@ func (c *Compiler) emitMapLookup(keyType, valueType types.Type, m, key llvm.Valu if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 { // key is a string params := []llvm.Value{m, key, mapValuePtr, mapValueSize} - commaOkValue = c.createRuntimeCall("hashmapStringGet", params, "") + commaOkValue = b.createRuntimeCall("hashmapStringGet", params, "") } else if hashmapIsBinaryKey(keyType) { // key can be compared with runtime.memequal // Store the key in an alloca, in the entry block to avoid dynamic stack // growth. - mapKeyAlloca, mapKeyPtr, mapKeySize := c.createTemporaryAlloca(key.Type(), "hashmap.key") - c.builder.CreateStore(key, mapKeyAlloca) + mapKeyAlloca, mapKeyPtr, mapKeySize := b.createTemporaryAlloca(key.Type(), "hashmap.key") + b.CreateStore(key, mapKeyAlloca) // Fetch the value from the hashmap. params := []llvm.Value{m, mapKeyPtr, mapValuePtr, mapValueSize} - commaOkValue = c.createRuntimeCall("hashmapBinaryGet", params, "") - c.emitLifetimeEnd(mapKeyPtr, mapKeySize) + commaOkValue = b.createRuntimeCall("hashmapBinaryGet", params, "") + b.emitLifetimeEnd(mapKeyPtr, mapKeySize) } else { // Not trivially comparable using memcmp. Make it an interface instead. itfKey := key if _, ok := keyType.(*types.Interface); !ok { // Not already an interface, so convert it to an interface now. - itfKey = c.parseMakeInterface(key, keyType, pos) + itfKey = b.createMakeInterface(key, keyType, pos) } params := []llvm.Value{m, itfKey, mapValuePtr, mapValueSize} - commaOkValue = c.createRuntimeCall("hashmapInterfaceGet", params, "") + commaOkValue = b.createRuntimeCall("hashmapInterfaceGet", params, "") } // Load the resulting value from the hashmap. The value is set to the zero // value if the key doesn't exist in the hashmap. - mapValue := c.builder.CreateLoad(mapValueAlloca, "") - c.emitLifetimeEnd(mapValuePtr, mapValueAllocaSize) + mapValue := b.CreateLoad(mapValueAlloca, "") + b.emitLifetimeEnd(mapValuePtr, mapValueAllocaSize) if commaOk { - tuple := llvm.Undef(c.ctx.StructType([]llvm.Type{llvmValueType, c.ctx.Int1Type()}, false)) - tuple = c.builder.CreateInsertValue(tuple, mapValue, 0, "") - tuple = c.builder.CreateInsertValue(tuple, commaOkValue, 1, "") + tuple := llvm.Undef(b.ctx.StructType([]llvm.Type{llvmValueType, b.ctx.Int1Type()}, false)) + tuple = b.CreateInsertValue(tuple, mapValue, 0, "") + tuple = b.CreateInsertValue(tuple, commaOkValue, 1, "") return tuple, nil } else { return mapValue, nil } } -func (c *Compiler) emitMapUpdate(keyType types.Type, m, key, value llvm.Value, pos token.Pos) { - valueAlloca, valuePtr, valueSize := c.createTemporaryAlloca(value.Type(), "hashmap.value") - c.builder.CreateStore(value, valueAlloca) +// createMapUpdate updates a map key to a given value, by creating an +// appropriate runtime call. +func (b *builder) createMapUpdate(keyType types.Type, m, key, value llvm.Value, pos token.Pos) { + valueAlloca, valuePtr, valueSize := b.createTemporaryAlloca(value.Type(), "hashmap.value") + b.CreateStore(value, valueAlloca) keyType = keyType.Underlying() if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 { // key is a string params := []llvm.Value{m, key, valuePtr} - c.createRuntimeCall("hashmapStringSet", params, "") + b.createRuntimeCall("hashmapStringSet", params, "") } else if hashmapIsBinaryKey(keyType) { // key can be compared with runtime.memequal - keyAlloca, keyPtr, keySize := c.createTemporaryAlloca(key.Type(), "hashmap.key") - c.builder.CreateStore(key, keyAlloca) + keyAlloca, keyPtr, keySize := b.createTemporaryAlloca(key.Type(), "hashmap.key") + b.CreateStore(key, keyAlloca) params := []llvm.Value{m, keyPtr, valuePtr} - c.createRuntimeCall("hashmapBinarySet", params, "") - c.emitLifetimeEnd(keyPtr, keySize) + b.createRuntimeCall("hashmapBinarySet", params, "") + b.emitLifetimeEnd(keyPtr, keySize) } else { // Key is not trivially comparable, so compare it as an interface instead. itfKey := key if _, ok := keyType.(*types.Interface); !ok { // Not already an interface, so convert it to an interface first. - itfKey = c.parseMakeInterface(key, keyType, pos) + itfKey = b.createMakeInterface(key, keyType, pos) } params := []llvm.Value{m, itfKey, valuePtr} - c.createRuntimeCall("hashmapInterfaceSet", params, "") + b.createRuntimeCall("hashmapInterfaceSet", params, "") } - c.emitLifetimeEnd(valuePtr, valueSize) + b.emitLifetimeEnd(valuePtr, valueSize) } -func (c *Compiler) emitMapDelete(keyType types.Type, m, key llvm.Value, pos token.Pos) error { +// createMapDelete deletes a key from a map by calling the appropriate runtime +// function. It is the implementation of the Go delete() builtin. +func (b *builder) createMapDelete(keyType types.Type, m, key llvm.Value, pos token.Pos) error { keyType = keyType.Underlying() if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 { // key is a string params := []llvm.Value{m, key} - c.createRuntimeCall("hashmapStringDelete", params, "") + b.createRuntimeCall("hashmapStringDelete", params, "") return nil } else if hashmapIsBinaryKey(keyType) { - keyAlloca, keyPtr, keySize := c.createTemporaryAlloca(key.Type(), "hashmap.key") - c.builder.CreateStore(key, keyAlloca) + keyAlloca, keyPtr, keySize := b.createTemporaryAlloca(key.Type(), "hashmap.key") + b.CreateStore(key, keyAlloca) params := []llvm.Value{m, keyPtr} - c.createRuntimeCall("hashmapBinaryDelete", params, "") - c.emitLifetimeEnd(keyPtr, keySize) + b.createRuntimeCall("hashmapBinaryDelete", params, "") + b.emitLifetimeEnd(keyPtr, keySize) return nil } else { // Key is not trivially comparable, so compare it as an interface @@ -153,10 +159,10 @@ func (c *Compiler) emitMapDelete(keyType types.Type, m, key llvm.Value, pos toke itfKey := key if _, ok := keyType.(*types.Interface); !ok { // Not already an interface, so convert it to an interface first. - itfKey = c.parseMakeInterface(key, keyType, pos) + itfKey = b.createMakeInterface(key, keyType, pos) } params := []llvm.Value{m, itfKey} - c.createRuntimeCall("hashmapInterfaceDelete", params, "") + b.createRuntimeCall("hashmapInterfaceDelete", params, "") return nil } } diff --git a/compiler/symbol.go b/compiler/symbol.go index ae853f1886..ec7cd34092 100644 --- a/compiler/symbol.go +++ b/compiler/symbol.go @@ -26,7 +26,7 @@ type globalInfo struct { // loadASTComments loads comments on globals from the AST, for use later in the // program. In particular, they are required for //go:extern pragmas on globals. -func (c *Compiler) loadASTComments(lprogram *loader.Program) { +func (c *compilerContext) loadASTComments(lprogram *loader.Program) { c.astComments = map[string]*ast.CommentGroup{} for _, pkgInfo := range lprogram.Sorted() { for _, file := range pkgInfo.Files { @@ -56,7 +56,7 @@ func (c *Compiler) loadASTComments(lprogram *loader.Program) { // getGlobal returns a LLVM IR global value for a Go SSA global. It is added to // the LLVM IR if it has not been added already. -func (c *Compiler) getGlobal(g *ssa.Global) llvm.Value { +func (c *compilerContext) getGlobal(g *ssa.Global) llvm.Value { info := c.getGlobalInfo(g) llvmGlobal := c.mod.NamedGlobal(info.linkName) if llvmGlobal.IsNil() { @@ -104,7 +104,7 @@ func (c *Compiler) getGlobal(g *ssa.Global) llvm.Value { } // getGlobalInfo returns some information about a specific global. -func (c *Compiler) getGlobalInfo(g *ssa.Global) globalInfo { +func (c *compilerContext) getGlobalInfo(g *ssa.Global) globalInfo { info := globalInfo{} if strings.HasPrefix(g.Name(), "C.") { // Created by CGo: such a name cannot be created by regular C code. diff --git a/compiler/syscall.go b/compiler/syscall.go index 8dbc81928e..8bb873facf 100644 --- a/compiler/syscall.go +++ b/compiler/syscall.go @@ -10,14 +10,14 @@ import ( "tinygo.org/x/go-llvm" ) -// emitSyscall emits an inline system call instruction, depending on the target -// OS/arch. -func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value, error) { - num := c.getValue(frame, call.Args[0]) +// createSyscall emits an inline system call instruction, depending on the +// target OS/arch. +func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) { + num := b.getValue(call.Args[0]) var syscallResult llvm.Value switch { - case c.GOARCH() == "amd64": - if c.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. @@ -28,13 +28,13 @@ func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value, // > All system classes enter the kernel via the syscall instruction. // // Source: https://opensource.apple.com/source/xnu/xnu-792.13.8/osfmk/mach/i386/syscall_sw.h - num = c.builder.CreateOr(num, llvm.ConstInt(c.uintptrType, 0x2000000, false), "") + num = b.CreateOr(num, llvm.ConstInt(b.uintptrType, 0x2000000, false), "") } // Sources: // https://stackoverflow.com/a/2538212 // https://en.wikibooks.org/wiki/X86_Assembly/Interfacing_with_Linux#syscall args := []llvm.Value{num} - argTypes := []llvm.Type{c.uintptrType} + argTypes := []llvm.Type{b.uintptrType} // Constraints will look something like: // "={rax},0,{rdi},{rsi},{rdx},{r10},{r8},{r9},~{rcx},~{r11}" constraints := "={rax},0" @@ -50,21 +50,21 @@ func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value, "{r12}", "{r13}", }[i] - llvmValue := c.getValue(frame, arg) + llvmValue := b.getValue(arg) args = append(args, llvmValue) argTypes = append(argTypes, llvmValue.Type()) } constraints += ",~{rcx},~{r11}" - fnType := llvm.FunctionType(c.uintptrType, argTypes, false) + fnType := llvm.FunctionType(b.uintptrType, argTypes, false) target := llvm.InlineAsm(fnType, "syscall", constraints, true, false, llvm.InlineAsmDialectIntel) - syscallResult = c.builder.CreateCall(target, args, "") - case c.GOARCH() == "386" && c.GOOS() == "linux": + syscallResult = b.CreateCall(target, args, "") + case b.GOARCH() == "386" && b.GOOS() == "linux": // Sources: // syscall(2) man page // https://stackoverflow.com/a/2538212 // https://en.wikibooks.org/wiki/X86_Assembly/Interfacing_with_Linux#int_0x80 args := []llvm.Value{num} - argTypes := []llvm.Type{c.uintptrType} + argTypes := []llvm.Type{b.uintptrType} // Constraints will look something like: // "={eax},0,{ebx},{ecx},{edx},{esi},{edi},{ebp}" constraints := "={eax},0" @@ -77,14 +77,14 @@ func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value, "{edi}", "{ebp}", }[i] - llvmValue := c.getValue(frame, arg) + llvmValue := b.getValue(arg) args = append(args, llvmValue) argTypes = append(argTypes, llvmValue.Type()) } - fnType := llvm.FunctionType(c.uintptrType, argTypes, false) + fnType := llvm.FunctionType(b.uintptrType, argTypes, false) target := llvm.InlineAsm(fnType, "int 0x80", constraints, true, false, llvm.InlineAsmDialectIntel) - syscallResult = c.builder.CreateCall(target, args, "") - case c.GOARCH() == "arm" && c.GOOS() == "linux": + syscallResult = b.CreateCall(target, args, "") + case b.GOARCH() == "arm" && b.GOOS() == "linux": // Implement the EABI system call convention for Linux. // Source: syscall(2) man page. args := []llvm.Value{} @@ -102,21 +102,21 @@ func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value, "{r5}", "{r6}", }[i] - llvmValue := c.getValue(frame, arg) + llvmValue := b.getValue(arg) args = append(args, llvmValue) argTypes = append(argTypes, llvmValue.Type()) } args = append(args, num) - argTypes = append(argTypes, c.uintptrType) + argTypes = append(argTypes, b.uintptrType) constraints += ",{r7}" // syscall number for i := len(call.Args) - 1; i < 4; i++ { // r0-r3 get clobbered after the syscall returns constraints += ",~{r" + strconv.Itoa(i) + "}" } - fnType := llvm.FunctionType(c.uintptrType, argTypes, false) + fnType := llvm.FunctionType(b.uintptrType, argTypes, false) target := llvm.InlineAsm(fnType, "svc #0", constraints, true, false, 0) - syscallResult = c.builder.CreateCall(target, args, "") - case c.GOARCH() == "arm64" && c.GOOS() == "linux": + syscallResult = b.CreateCall(target, args, "") + case b.GOARCH() == "arm64" && b.GOOS() == "linux": // Source: syscall(2) man page. args := []llvm.Value{} argTypes := []llvm.Type{} @@ -132,12 +132,12 @@ func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value, "{x4}", "{x5}", }[i] - llvmValue := c.getValue(frame, arg) + llvmValue := b.getValue(arg) args = append(args, llvmValue) argTypes = append(argTypes, llvmValue.Type()) } args = append(args, num) - argTypes = append(argTypes, c.uintptrType) + argTypes = append(argTypes, b.uintptrType) constraints += ",{x8}" // syscall number for i := len(call.Args) - 1; i < 8; i++ { // x0-x7 may get clobbered during the syscall following the aarch64 @@ -145,13 +145,13 @@ func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value, constraints += ",~{x" + strconv.Itoa(i) + "}" } constraints += ",~{x16},~{x17}" // scratch registers - fnType := llvm.FunctionType(c.uintptrType, argTypes, false) + fnType := llvm.FunctionType(b.uintptrType, argTypes, false) target := llvm.InlineAsm(fnType, "svc #0", constraints, true, false, 0) - syscallResult = c.builder.CreateCall(target, args, "") + syscallResult = b.CreateCall(target, args, "") default: - return llvm.Value{}, c.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+c.GOOS()+"/"+c.GOARCH()) + return llvm.Value{}, b.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+b.GOOS()+"/"+b.GOARCH()) } - switch c.GOOS() { + switch b.GOOS() { case "linux", "freebsd": // Return values: r0, r1 uintptr, err Errno // Pseudocode: @@ -160,15 +160,15 @@ func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value, // err = -syscallResult // } // return syscallResult, 0, err - zero := llvm.ConstInt(c.uintptrType, 0, false) - inrange1 := c.builder.CreateICmp(llvm.IntSLT, syscallResult, llvm.ConstInt(c.uintptrType, 0, false), "") - inrange2 := c.builder.CreateICmp(llvm.IntSGT, syscallResult, llvm.ConstInt(c.uintptrType, 0xfffffffffffff000, true), "") // -4096 - hasError := c.builder.CreateAnd(inrange1, inrange2, "") - errResult := c.builder.CreateSelect(hasError, c.builder.CreateSub(zero, syscallResult, ""), zero, "syscallError") - retval := llvm.Undef(c.ctx.StructType([]llvm.Type{c.uintptrType, c.uintptrType, c.uintptrType}, false)) - retval = c.builder.CreateInsertValue(retval, syscallResult, 0, "") - retval = c.builder.CreateInsertValue(retval, zero, 1, "") - retval = c.builder.CreateInsertValue(retval, errResult, 2, "") + zero := llvm.ConstInt(b.uintptrType, 0, false) + inrange1 := b.CreateICmp(llvm.IntSLT, syscallResult, llvm.ConstInt(b.uintptrType, 0, false), "") + inrange2 := b.CreateICmp(llvm.IntSGT, syscallResult, llvm.ConstInt(b.uintptrType, 0xfffffffffffff000, true), "") // -4096 + hasError := b.CreateAnd(inrange1, inrange2, "") + errResult := b.CreateSelect(hasError, b.CreateSub(zero, syscallResult, ""), zero, "syscallError") + retval := llvm.Undef(b.ctx.StructType([]llvm.Type{b.uintptrType, b.uintptrType, b.uintptrType}, false)) + retval = b.CreateInsertValue(retval, syscallResult, 0, "") + retval = b.CreateInsertValue(retval, zero, 1, "") + retval = b.CreateInsertValue(retval, errResult, 2, "") return retval, nil case "darwin": // Return values: r0, r1 uintptr, err Errno @@ -178,15 +178,15 @@ func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value, // err = syscallResult // } // return syscallResult, 0, err - zero := llvm.ConstInt(c.uintptrType, 0, false) - hasError := c.builder.CreateICmp(llvm.IntNE, syscallResult, llvm.ConstInt(c.uintptrType, 0, false), "") - errResult := c.builder.CreateSelect(hasError, syscallResult, zero, "syscallError") - retval := llvm.Undef(c.ctx.StructType([]llvm.Type{c.uintptrType, c.uintptrType, c.uintptrType}, false)) - retval = c.builder.CreateInsertValue(retval, syscallResult, 0, "") - retval = c.builder.CreateInsertValue(retval, zero, 1, "") - retval = c.builder.CreateInsertValue(retval, errResult, 2, "") + zero := llvm.ConstInt(b.uintptrType, 0, false) + hasError := b.CreateICmp(llvm.IntNE, syscallResult, llvm.ConstInt(b.uintptrType, 0, false), "") + errResult := b.CreateSelect(hasError, syscallResult, zero, "syscallError") + retval := llvm.Undef(b.ctx.StructType([]llvm.Type{b.uintptrType, b.uintptrType, b.uintptrType}, false)) + retval = b.CreateInsertValue(retval, syscallResult, 0, "") + retval = b.CreateInsertValue(retval, zero, 1, "") + retval = b.CreateInsertValue(retval, errResult, 2, "") return retval, nil default: - return llvm.Value{}, c.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+c.GOOS()+"/"+c.GOARCH()) + return llvm.Value{}, b.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+b.GOOS()+"/"+b.GOARCH()) } } diff --git a/compiler/volatile.go b/compiler/volatile.go index 7ee5b168f1..d2e51c84da 100644 --- a/compiler/volatile.go +++ b/compiler/volatile.go @@ -8,19 +8,23 @@ import ( "tinygo.org/x/go-llvm" ) -func (c *Compiler) emitVolatileLoad(frame *Frame, instr *ssa.CallCommon) (llvm.Value, error) { - addr := c.getValue(frame, instr.Args[0]) - c.emitNilCheck(frame, addr, "deref") - val := c.builder.CreateLoad(addr, "") +// createVolatileLoad is the implementation of the intrinsic function +// runtime/volatile.LoadT(). +func (b *builder) createVolatileLoad(instr *ssa.CallCommon) (llvm.Value, error) { + addr := b.getValue(instr.Args[0]) + b.createNilCheck(addr, "deref") + val := b.CreateLoad(addr, "") val.SetVolatile(true) return val, nil } -func (c *Compiler) emitVolatileStore(frame *Frame, instr *ssa.CallCommon) (llvm.Value, error) { - addr := c.getValue(frame, instr.Args[0]) - val := c.getValue(frame, instr.Args[1]) - c.emitNilCheck(frame, addr, "deref") - store := c.builder.CreateStore(val, addr) +// createVolatileStore is the implementation of the intrinsic function +// runtime/volatile.StoreT(). +func (b *builder) createVolatileStore(instr *ssa.CallCommon) (llvm.Value, error) { + addr := b.getValue(instr.Args[0]) + val := b.getValue(instr.Args[1]) + b.createNilCheck(addr, "deref") + store := b.CreateStore(val, addr) store.SetVolatile(true) return llvm.Value{}, nil }