/* * Copyright (c) 2023 The GoPlus Authors (goplus.org). All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package cl import ( "go/types" "sort" "github.com/goplus/llgo/loader" "github.com/qiniu/x/errors" "golang.org/x/tools/go/ssa" llvm "tinygo.org/x/go-llvm" ) type Config struct { Triple string Target llvm.TargetMachine } type context struct { mod llvm.Module ctx llvm.Context embedGlobals map[string][]*loader.EmbedFile errs errors.List } func newContext(moduleName string, conf *Config) *context { machine := conf.Target targetData := machine.CreateTargetData() ctx := llvm.NewContext() mod := ctx.NewModule(moduleName) mod.SetTarget(conf.Triple) mod.SetDataLayout(targetData.String()) return &context{mod: mod, ctx: ctx} } func (c *context) dispose() { panic("todo") } // createPackage builds the LLVM IR for all types, methods, and global variables // in the given package. func (c *context) createPackage(irbuilder llvm.Builder, pkg *ssa.Package) { type namedMember struct { name string val ssa.Member } // Sort by position, so that the order of the functions in the IR matches // the order of functions in the source file. This is useful for testing, // for example. var members []*namedMember for name, v := range pkg.Members { members = append(members, &namedMember{name, v}) } sort.Slice(members, func(i, j int) bool { iPos := members[i].val.Pos() jPos := members[j].val.Pos() return iPos < jPos }) // Define all functions. for _, m := range members { member := m.val switch member := member.(type) { case *ssa.Function: if member.TypeParams() != nil { // Do not try to build generic (non-instantiated) functions. continue } // Create the function definition. b := newBuilder(c, irbuilder, member) if _, ok := mathToLLVMMapping[member.RelString(nil)]; ok { // The body of this function (if there is one) is ignored and // replaced with a LLVM intrinsic call. b.defineMathOp() continue } if ok := b.defineMathBitsIntrinsic(); ok { // Like a math intrinsic, the body of this function was replaced // with a LLVM intrinsic. continue } if member.Blocks == nil { // Try to define this as an intrinsic function. b.defineIntrinsicFunction() // It might not be an intrinsic function but simply an external // function (defined via //go:linkname). Leave it undefined in // that case. continue } b.createFunction() case *ssa.Type: if types.IsInterface(member.Type()) { // Interfaces don't have concrete methods. continue } // Named type. We should make sure all methods are created. // This includes both functions with pointer receivers and those // without. methods := getAllMethods(pkg.Prog, member.Type()) methods = append(methods, getAllMethods(pkg.Prog, types.NewPointer(member.Type()))...) for _, method := range methods { // Parse this method. fn := pkg.Prog.MethodValue(method) if fn == nil { continue // probably a generic method } if member.Type().String() != member.String() { // This is a member on a type alias. Do not build such a // function. continue } if fn.Blocks == nil { continue // external function } if fn.Synthetic != "" && fn.Synthetic != "package initializer" { // This function is a kind of wrapper function (created by // the ssa package, not appearing in the source code) that // is created by the getFunction method as needed. // Therefore, don't build it here to avoid "function // redeclared" errors. continue } // Create the function definition. b := newBuilder(c, irbuilder, fn) b.createFunction() } case *ssa.Global: // Global variable. info := c.getGlobalInfo(member) global := c.getGlobal(member) if files, ok := c.embedGlobals[member.Name()]; ok { c.createEmbedGlobal(member, global, files) } else if !info.extern { global.SetInitializer(llvm.ConstNull(global.GlobalValueType())) global.SetVisibility(llvm.HiddenVisibility) if info.section != "" { global.SetSection(info.section) } } } } // Add forwarding functions for functions that would otherwise be // implemented in assembly. for _, m := range members { member := m.val switch member := member.(type) { case *ssa.Function: if member.Blocks != nil { continue // external function } info := c.getFunctionInfo(member) if aliasName, ok := stdlibAliases[info.linkName]; ok { alias := c.mod.NamedFunction(aliasName) if alias.IsNil() { // Shouldn't happen, but perhaps best to just ignore. // The error will be a link error, if there is an error. continue } b := newBuilder(c, irbuilder, member) b.createAlias(alias) } } } } // createEmbedGlobal creates an initializer for a //go:embed global variable. func (c *context) createEmbedGlobal(member *ssa.Global, global llvm.Value, files []*loader.EmbedFile) { panic("todo") } func NewPackage(moduleName string, pkg loader.Package, conf *Config) (ret llvm.Module, err error) { ssaPkg := pkg.SSA ssaPkg.Build() c := newContext(moduleName, conf) defer c.dispose() // Load comments such as //go:extern on globals. c.loadASTComments(pkg) /* TODO: gc related // Predeclare the runtime.alloc function, which is used by the wordpack // functionality. c.getFunction(c.program.ImportedPackage("runtime").Members["alloc"].(*ssa.Function)) if c.NeedsStackObjects { // Predeclare trackPointer, which is used everywhere we use runtime.alloc. c.getFunction(c.program.ImportedPackage("runtime").Members["trackPointer"].(*ssa.Function)) } */ // Compile all functions, methods, and global variables in this package. irbuilder := c.ctx.NewBuilder() defer irbuilder.Dispose() c.createPackage(irbuilder, ssaPkg) /* TODO: risc-v // Add the "target-abi" flag, which is necessary on RISC-V otherwise it will // pick one that doesn't match the -mabi Clang flag. if c.ABI != "" { c.mod.AddNamedMetadataOperand("llvm.module.flags", c.ctx.MDNode([]llvm.Metadata{ llvm.ConstInt(c.ctx.Int32Type(), 1, false).ConstantAsMetadata(), // Error on mismatch c.ctx.MDString("target-abi"), c.ctx.MDString(c.ABI), }), ) } */ return c.mod, c.errs.ToError() }