diff options
-rw-r--r-- | llgo/cmd/gllgo/gllgo.go | 1 | ||||
-rw-r--r-- | llgo/irgen/compiler.go | 23 | ||||
-rw-r--r-- | llgo/irgen/ssa.go | 59 | ||||
-rw-r--r-- | llgo/test/irgen/imports.go | 20 |
4 files changed, 92 insertions, 11 deletions
diff --git a/llgo/cmd/gllgo/gllgo.go b/llgo/cmd/gllgo/gllgo.go index e58686e57dc..5aee3211b24 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 60bd62f7196..16c3988ba7b 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 706eb7790e7..1b09b71a7d9 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 00000000000..125bd5ff251 --- /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 |