irgen: modify the ABI to use init guards instead of priority
authorPeter Collingbourne <peter@pcc.me.uk>
Wed, 31 Dec 2014 00:25:39 +0000 (00:25 +0000)
committerPeter Collingbourne <peter@pcc.me.uk>
Wed, 31 Dec 2014 00:25:39 +0000 (00:25 +0000)
The new ABI is simpler for use cases such as dynamically loaded packages.

The calling convention for import functions is similar to what go/ssa would
produce if BareInits were cleared. However, simply clearing this flag causes
two additional issues:

 1) We would need to special case the 'init$guard' variable (see
    discussion in https://codereview.appspot.com/78780043/).

 2) The call to __go_register_gc_roots needs to appear in the right
    place, i.e. after the guard check. Making this check appear
    in the right place with non-bare inits seems unreliable at best.

So we keep BareInits set and generate the necessary code manually.

It is still possible to get the old ABI by specifying a path to a gccgo
installation.

Differential Revision: http://reviews.llvm.org/D6804

llvm-svn: 225030

llgo/cmd/gllgo/gllgo.go
llgo/irgen/compiler.go
llgo/irgen/ssa.go
llgo/test/irgen/imports.go [new file with mode: 0644]

index e58686e..5aee321 100644 (file)
@@ -71,6 +71,7 @@ func initCompiler(opts *driverOptions) (*irgen.Compiler, error) {
                DebugPrefixMaps:    opts.debugPrefixMaps,
                DumpSSA:            opts.dumpSSA,
                GccgoPath:          opts.gccgoPath,
+               GccgoABI:           opts.gccgoPath != "",
                ImportPaths:        importPaths,
                SanitizerAttribute: opts.sanitizer.getAttribute(),
        }
index 60bd62f..16c3988 100644 (file)
@@ -75,6 +75,9 @@ type CompilerOptions struct {
        // path in ImportPaths.
        GccgoPath string
 
+       // Whether to use the gccgo ABI.
+       GccgoABI bool
+
        // ImportPaths is the list of additional import paths
        ImportPaths []string
 
@@ -322,8 +325,6 @@ func (c *compiler) buildPackageInitData(mainPkg *ssa.Package) gccgoimporter.Init
 }
 
 func (c *compiler) createInitMainFunction(mainPkg *ssa.Package) {
-       initdata := c.buildPackageInitData(mainPkg)
-
        ftyp := llvm.FunctionType(llvm.VoidType(), nil, false)
        initMain := llvm.AddFunction(c.module.Module, "__go_init_main", ftyp)
        c.addCommonFunctionAttrs(initMain)
@@ -333,6 +334,17 @@ func (c *compiler) createInitMainFunction(mainPkg *ssa.Package) {
        defer builder.Dispose()
        builder.SetInsertPointAtEnd(entry)
 
+       if !c.GccgoABI {
+               initfn := c.module.Module.NamedFunction("main..import")
+               if !initfn.IsNil() {
+                       builder.CreateCall(initfn, nil, "")
+               }
+               builder.CreateRetVoid()
+               return
+       }
+
+       initdata := c.buildPackageInitData(mainPkg)
+
        for _, init := range initdata.Inits {
                initfn := c.module.Module.NamedFunction(init.InitFunc)
                if initfn.IsNil() {
@@ -348,8 +360,13 @@ func (c *compiler) buildExportData(mainPkg *ssa.Package) []byte {
        exportData := importer.ExportData(mainPkg.Object)
        b := bytes.NewBuffer(exportData)
 
+       b.WriteString("v1;\n")
+       if !c.GccgoABI {
+               return b.Bytes()
+       }
+
        initdata := c.buildPackageInitData(mainPkg)
-       b.WriteString("v1;\npriority ")
+       b.WriteString("priority ")
        b.WriteString(strconv.Itoa(initdata.Priority))
        b.WriteString(";\n")
 
index 706eb77..1b09b71 100644 (file)
@@ -409,11 +409,6 @@ func (u *unit) defineFunction(f *ssa.Function) {
                }
        }
 
-       // If this is the "init" function, enable init-specific optimizations.
-       if !isMethod && f.Name() == "init" {
-               fr.isInit = true
-       }
-
        // If the function contains any defers, we must first create
        // an unwind block. We can short-circuit the check for defers with
        // f.Recover != nil.
@@ -422,8 +417,20 @@ func (u *unit) defineFunction(f *ssa.Function) {
                fr.frameptr = fr.builder.CreateAlloca(llvm.Int8Type(), "")
        }
 
-       term := fr.builder.CreateBr(fr.blocks[0])
-       fr.allocaBuilder.SetInsertPointBefore(term)
+       // Keep track of the block into which we need to insert the call
+       // to __go_register_gc_roots. This needs to be inserted after the
+       // init guard check under the llgo ABI.
+       var registerGcBlock llvm.BasicBlock
+
+       // If this is the "init" function, emit the init guard check and
+       // enable init-specific optimizations.
+       if !isMethod && f.Name() == "init" {
+               registerGcBlock = fr.emitInitPrologue()
+               fr.isInit = true
+       }
+
+       fr.builder.CreateBr(fr.blocks[0])
+       fr.allocaBuilder.SetInsertPointBefore(prologueBlock.FirstInstruction())
 
        for _, block := range f.DomPreorder() {
                fr.translateBlock(block, fr.blocks[block.Index])
@@ -439,7 +446,7 @@ func (u *unit) defineFunction(f *ssa.Function) {
        // after generating code for it because allocations may have caused
        // additional GC roots to be created.
        if fr.isInit {
-               fr.builder.SetInsertPointBefore(prologueBlock.FirstInstruction())
+               fr.builder.SetInsertPointBefore(registerGcBlock.FirstInstruction())
                fr.registerGcRoots()
        }
 }
@@ -484,6 +491,42 @@ func (fr *frame) dispose() {
        fr.allocaBuilder.Dispose()
 }
 
+// emitInitPrologue emits the init-specific function prologue (guard check and
+// initialization of dependent packages under the llgo native ABI), and returns
+// the basic block into which the GC registration call should be emitted.
+func (fr *frame) emitInitPrologue() llvm.BasicBlock {
+       if fr.GccgoABI {
+               return fr.builder.GetInsertBlock()
+       }
+
+       initGuard := llvm.AddGlobal(fr.module.Module, llvm.Int1Type(), "init$guard")
+       initGuard.SetLinkage(llvm.InternalLinkage)
+       initGuard.SetInitializer(llvm.ConstNull(llvm.Int1Type()))
+
+       returnBlock := llvm.AddBasicBlock(fr.function, "")
+       initBlock := llvm.AddBasicBlock(fr.function, "")
+
+       initGuardVal := fr.builder.CreateLoad(initGuard, "")
+       fr.builder.CreateCondBr(initGuardVal, returnBlock, initBlock)
+
+       fr.builder.SetInsertPointAtEnd(returnBlock)
+       fr.builder.CreateRetVoid()
+
+       fr.builder.SetInsertPointAtEnd(initBlock)
+       fr.builder.CreateStore(llvm.ConstInt(llvm.Int1Type(), 1, false), initGuard)
+       ftyp := llvm.FunctionType(llvm.VoidType(), nil, false)
+       for _, pkg := range fr.pkg.Object.Imports() {
+               initname := ManglePackagePath(pkg.Path()) + "..import"
+               initfn := fr.module.Module.NamedFunction(initname)
+               if initfn.IsNil() {
+                       initfn = llvm.AddFunction(fr.module.Module, initname, ftyp)
+               }
+               fr.builder.CreateCall(initfn, nil, "")
+       }
+
+       return initBlock
+}
+
 // bridgeRecoverFunc creates a function that may call recover(), and creates
 // a call to it from the current frame. The created function will be called
 // with a boolean parameter that indicates whether it may call recover().
diff --git a/llgo/test/irgen/imports.go b/llgo/test/irgen/imports.go
new file mode 100644 (file)
index 0000000..125bd5f
--- /dev/null
@@ -0,0 +1,20 @@
+// RUN: llgo -S -emit-llvm -o - %s | FileCheck %s
+
+package foo
+
+import _ "fmt"
+
+var X interface{}
+
+// CHECK: @"init$guard" = internal global i1 false
+
+// CHECK: define void @foo..import()
+// CHECK-NEXT: :
+// CHECK-NEXT: %[[N:.*]] = load i1* @"init$guard"
+// CHECK-NEXT: br i1 %[[N]], label %{{.*}}, label %[[L:.*]]
+
+// CHECK: ; <label>:[[L]]
+// CHECK-NEXT: call void @__go_register_gc_roots
+// CHECK-NEXT: store i1 true, i1* @"init$guard"
+// CHECK-NEXT: call void @fmt..import()
+// CHECK-NEXT: br label