summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Collingbourne <peter@pcc.me.uk>2014-12-31 00:25:39 +0000
committerPeter Collingbourne <peter@pcc.me.uk>2014-12-31 00:25:39 +0000
commitd34d92fb9c316a9ccd82989cd89b67143cf3fb8d (patch)
treebec6cddb914b62d626e359eb2d79376eb58df754
parenta0a53085e1909c18af8e77c4eb02d8448c7314ca (diff)
downloadllvm-d34d92fb9c316a9ccd82989cd89b67143cf3fb8d.tar.gz
irgen: modify the ABI to use init guards instead of priority
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
-rw-r--r--llgo/cmd/gllgo/gllgo.go1
-rw-r--r--llgo/irgen/compiler.go23
-rw-r--r--llgo/irgen/ssa.go59
-rw-r--r--llgo/test/irgen/imports.go20
4 files changed, 92 insertions, 11 deletions
diff --git a/llgo/cmd/gllgo/gllgo.go b/llgo/cmd/gllgo/gllgo.go
index e58686e57dc0..5aee3211b24a 100644
--- a/llgo/cmd/gllgo/gllgo.go
+++ b/llgo/cmd/gllgo/gllgo.go
@@ -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(),
}
diff --git a/llgo/irgen/compiler.go b/llgo/irgen/compiler.go
index 60bd62f71969..16c3988ba7be 100644
--- a/llgo/irgen/compiler.go
+++ b/llgo/irgen/compiler.go
@@ -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")
diff --git a/llgo/irgen/ssa.go b/llgo/irgen/ssa.go
index 706eb7790e75..1b09b71a7d9c 100644
--- a/llgo/irgen/ssa.go
+++ b/llgo/irgen/ssa.go
@@ -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
index 000000000000..125bd5ff251f
--- /dev/null
+++ b/llgo/test/irgen/imports.go
@@ -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