Index: llgo/trunk/cmd/gllgo/gllgo.go =================================================================== --- llgo/trunk/cmd/gllgo/gllgo.go +++ llgo/trunk/cmd/gllgo/gllgo.go @@ -71,6 +71,7 @@ DebugPrefixMaps: opts.debugPrefixMaps, DumpSSA: opts.dumpSSA, GccgoPath: opts.gccgoPath, + GccgoABI: opts.gccgoPath != "", ImportPaths: importPaths, SanitizerAttribute: opts.sanitizer.getAttribute(), } Index: llgo/trunk/irgen/compiler.go =================================================================== --- llgo/trunk/irgen/compiler.go +++ llgo/trunk/irgen/compiler.go @@ -75,6 +75,9 @@ // 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) 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 @@ 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 @@ 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: llgo/trunk/irgen/ssa.go =================================================================== --- llgo/trunk/irgen/ssa.go +++ llgo/trunk/irgen/ssa.go @@ -409,11 +409,6 @@ } } - // 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 @@ 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 @@ // 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 @@ 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(). Index: llgo/trunk/test/irgen/imports.go =================================================================== --- llgo/trunk/test/irgen/imports.go +++ llgo/trunk/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: ;