From 88f143f3e6ff94fce3e4f3944561ff0bfd5ddcff Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Thu, 3 May 2018 22:38:04 +0200 Subject: [PATCH] compiler: Implement initializers for global structs --- tgo.go | 83 +++++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 68 insertions(+), 15 deletions(-) diff --git a/tgo.go b/tgo.go index 5a6aa8cc..40835ede 100644 --- a/tgo.go +++ b/tgo.go @@ -19,9 +19,6 @@ import ( "llvm.org/llvm/bindings/go/llvm" ) -// Silently ignore this instruction. -var ErrCGoIgnore = errors.New("cgo: ignore") - func init() { llvm.InitializeAllTargets() llvm.InitializeAllTargetMCs() @@ -395,7 +392,12 @@ func (c *Compiler) parsePackage(program *ssa.Program, pkg *ssa.Package) error { if member.Blocks == nil { continue // external function } - err := c.parseFunc(frames[member], member) + var err error + if member.Synthetic == "package initializer" { + err = c.parseInitFunc(frames[member], member) + } else { + err = c.parseFunc(frames[member], member) + } if err != nil { return err } @@ -456,6 +458,67 @@ func (c *Compiler) parseFuncDecl(f *ssa.Function) (*Frame, error) { return frame, nil } +// Special function parser for generated package initializers (which also +// initializes global variables). +func (c *Compiler) parseInitFunc(frame *Frame, f *ssa.Function) error { + llvmBlock := c.ctx.AddBasicBlock(frame.llvmFn, "entry") + c.builder.SetInsertPointAtEnd(llvmBlock) + + for _, block := range f.DomPreorder() { + for _, instr := range block.Instrs { + var err error + switch instr := instr.(type) { + case *ssa.Convert: + // ignore: CGo pointer conversion + case *ssa.Return: + err = c.parseInstr(frame, instr) + case *ssa.FieldAddr: + // ignore + case *ssa.Store: + var llvmAddr llvm.Value + switch addr := instr.Addr.(type) { + case *ssa.Global: + // Regular store, like a global int variable. + if strings.HasPrefix(addr.Name(), "__cgofn__cgo_") || strings.HasPrefix(addr.Name(), "_cgo_") { + // Ignore CGo global variables which we don't use. + continue + } + val, err := c.parseExpr(frame, instr.Val) + if err != nil { + return err + } + fullName := pkgPrefix(addr.Pkg) + "." + addr.Name() + llvmAddr = c.mod.NamedGlobal(fullName) + llvmAddr.SetInitializer(val) + case *ssa.FieldAddr: + // Initialize field of a global struct. + // LLVM does not allow setting an initializer on part of a + // global variable. So we take the current initializer, add + // the field, and replace the initializer with the new + // initializer. + val, err := c.parseExpr(frame, instr.Val) + if err != nil { + return err + } + global := addr.X.(*ssa.Global) + llvmAddr := c.mod.NamedGlobal(pkgPrefix(global.Pkg) + "." + global.Name()) + llvmValue := llvmAddr.Initializer() + llvmValue = c.builder.CreateInsertValue(llvmValue, val, addr.Field, "") + llvmAddr.SetInitializer(llvmValue) + default: + return errors.New("unknown init store: " + fmt.Sprintf("%#v", addr)) + } + default: + return errors.New("unknown init instruction: " + fmt.Sprintf("%#v", instr)) + } + if err != nil { + return err + } + } + } + return nil +} + func (c *Compiler) parseFunc(frame *Frame, f *ssa.Function) error { if frame.llvmFn.Name() != "main.main" { // This function is only used from within Go. @@ -480,9 +543,6 @@ func (c *Compiler) parseFunc(frame *Frame, f *ssa.Function) error { for _, instr := range block.Instrs { fmt.Printf(" instr: %v\n", instr) err := c.parseInstr(frame, instr) - if err == ErrCGoIgnore { - continue // ignore irrelevant CGo instruction - } if err != nil { return err } @@ -556,11 +616,7 @@ func (c *Compiler) parseInstr(frame *Frame, instr ssa.Instruction) error { if err != nil { return err } - if instr.Parent().Synthetic == "package initializer" { - addr.SetInitializer(val) - } else { - c.builder.CreateStore(val, addr) - } + c.builder.CreateStore(val, addr) return nil default: return errors.New("unknown instruction: " + fmt.Sprintf("%#v", instr)) @@ -730,9 +786,6 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) { } return c.builder.CreateGEP(val, indices, ""), nil case *ssa.Global: - if strings.HasPrefix(expr.Name(), "__cgofn__cgo_") || strings.HasPrefix(expr.Name(), "_cgo_") { - return llvm.Value{}, ErrCGoIgnore - } fullName := pkgPrefix(expr.Pkg) + "." + expr.Name() value := c.mod.NamedGlobal(fullName) if value.IsNil() {