github.com/axw/llgo@v0.0.0-20160805011314-95b5fe4dca20/irgen/compiler.go (about)

     1  //===- compiler.go - IR generator entry point -----------------------------===//
     2  //
     3  //                     The LLVM Compiler Infrastructure
     4  //
     5  // This file is distributed under the University of Illinois Open Source
     6  // License. See LICENSE.TXT for details.
     7  //
     8  //===----------------------------------------------------------------------===//
     9  //
    10  // This file implements the main IR generator entry point, (*Compiler).Compile.
    11  //
    12  //===----------------------------------------------------------------------===//
    13  
    14  package irgen
    15  
    16  import (
    17  	"bytes"
    18  	"go/ast"
    19  	"go/token"
    20  	"log"
    21  	"sort"
    22  	"strconv"
    23  
    24  	llgobuild "llvm.org/llgo/build"
    25  	"llvm.org/llgo/debug"
    26  	"llvm.org/llvm/bindings/go/llvm"
    27  
    28  	"llvm.org/llgo/third_party/gotools/go/gccgoimporter"
    29  	"llvm.org/llgo/third_party/gotools/go/importer"
    30  	"llvm.org/llgo/third_party/gotools/go/loader"
    31  	"llvm.org/llgo/third_party/gotools/go/ssa"
    32  	"llvm.org/llgo/third_party/gotools/go/types"
    33  )
    34  
    35  type Module struct {
    36  	llvm.Module
    37  	Path       string
    38  	ExportData []byte
    39  	Package    *types.Package
    40  	disposed   bool
    41  }
    42  
    43  func (m *Module) Dispose() {
    44  	if m.disposed {
    45  		return
    46  	}
    47  	m.Module.Dispose()
    48  	m.disposed = true
    49  }
    50  
    51  ///////////////////////////////////////////////////////////////////////////////
    52  
    53  type CompilerOptions struct {
    54  	// TargetTriple is the LLVM triple for the target.
    55  	TargetTriple string
    56  
    57  	// GenerateDebug decides whether debug data is
    58  	// generated in the output module.
    59  	GenerateDebug bool
    60  
    61  	// DebugPrefixMaps is a list of mappings from source prefixes to
    62  	// replacement prefixes, to be applied in debug info.
    63  	DebugPrefixMaps []debug.PrefixMap
    64  
    65  	// Logger is a logger used for tracing compilation.
    66  	Logger *log.Logger
    67  
    68  	// DumpSSA is a debugging option that dumps each SSA function
    69  	// to stderr before generating code for it.
    70  	DumpSSA bool
    71  
    72  	// GccgoPath is the path to the gccgo binary whose libgo we read import
    73  	// data from. If blank, the caller is expected to supply an import
    74  	// path in ImportPaths.
    75  	GccgoPath string
    76  
    77  	// Whether to use the gccgo ABI.
    78  	GccgoABI bool
    79  
    80  	// ImportPaths is the list of additional import paths
    81  	ImportPaths []string
    82  
    83  	// SanitizerAttribute is an attribute to apply to functions to enable
    84  	// dynamic instrumentation using a sanitizer.
    85  	SanitizerAttribute llvm.Attribute
    86  
    87  	// Importer is the importer. If nil, the compiler will set this field
    88  	// automatically using MakeImporter().
    89  	Importer types.Importer
    90  
    91  	// InitMap is the init map used by Importer. If Importer is nil, the
    92  	// compiler will set this field automatically using MakeImporter().
    93  	// If Importer is non-nil, InitMap must be non-nil also.
    94  	InitMap map[*types.Package]gccgoimporter.InitData
    95  
    96  	// PackageCreated is a hook passed to the go/loader package via
    97  	// loader.Config, see the documentation for that package for more
    98  	// information.
    99  	PackageCreated func(*types.Package)
   100  
   101  	// DisableUnusedImportCheck disables the unused import check performed
   102  	// by go/types if set to true.
   103  	DisableUnusedImportCheck bool
   104  
   105  	// Packages is used by go/types as the imported package map if non-nil.
   106  	Packages map[string]*types.Package
   107  }
   108  
   109  type Compiler struct {
   110  	opts       CompilerOptions
   111  	dataLayout string
   112  }
   113  
   114  func NewCompiler(opts CompilerOptions) (*Compiler, error) {
   115  	compiler := &Compiler{opts: opts}
   116  	dataLayout, err := llvmDataLayout(compiler.opts.TargetTriple)
   117  	if err != nil {
   118  		return nil, err
   119  	}
   120  	compiler.dataLayout = dataLayout
   121  	return compiler, nil
   122  }
   123  
   124  func (c *Compiler) Compile(fset *token.FileSet, astFiles []*ast.File, importpath string) (m *Module, err error) {
   125  	target := llvm.NewTargetData(c.dataLayout)
   126  	compiler := &compiler{
   127  		CompilerOptions: c.opts,
   128  		dataLayout:      c.dataLayout,
   129  		target:          target,
   130  		llvmtypes:       NewLLVMTypeMap(llvm.GlobalContext(), target),
   131  	}
   132  	return compiler.compile(fset, astFiles, importpath)
   133  }
   134  
   135  type compiler struct {
   136  	CompilerOptions
   137  
   138  	module     *Module
   139  	dataLayout string
   140  	target     llvm.TargetData
   141  	fileset    *token.FileSet
   142  
   143  	runtime   *runtimeInterface
   144  	llvmtypes *llvmTypeMap
   145  	types     *TypeMap
   146  
   147  	debug *debug.DIBuilder
   148  }
   149  
   150  func (c *compiler) logf(format string, v ...interface{}) {
   151  	if c.Logger != nil {
   152  		c.Logger.Printf(format, v...)
   153  	}
   154  }
   155  
   156  func (c *compiler) addCommonFunctionAttrs(fn llvm.Value) {
   157  	fn.AddTargetDependentFunctionAttr("disable-tail-calls", "true")
   158  	fn.AddTargetDependentFunctionAttr("split-stack", "")
   159  	if attr := c.SanitizerAttribute; attr != 0 {
   160  		fn.AddFunctionAttr(attr)
   161  	}
   162  }
   163  
   164  // MakeImporter sets CompilerOptions.Importer to an appropriate importer
   165  // for the search paths given in CompilerOptions.ImportPaths, and sets
   166  // CompilerOptions.InitMap to an init map belonging to that importer.
   167  // If CompilerOptions.GccgoPath is non-empty, the importer will also use
   168  // the search paths for that gccgo installation.
   169  func (opts *CompilerOptions) MakeImporter() error {
   170  	opts.InitMap = make(map[*types.Package]gccgoimporter.InitData)
   171  	if opts.GccgoPath == "" {
   172  		paths := append(append([]string{}, opts.ImportPaths...), ".")
   173  		opts.Importer = gccgoimporter.GetImporter(paths, opts.InitMap)
   174  	} else {
   175  		var inst gccgoimporter.GccgoInstallation
   176  		err := inst.InitFromDriver(opts.GccgoPath)
   177  		if err != nil {
   178  			return err
   179  		}
   180  		opts.Importer = inst.GetImporter(opts.ImportPaths, opts.InitMap)
   181  	}
   182  	return nil
   183  }
   184  
   185  func (compiler *compiler) compile(fset *token.FileSet, astFiles []*ast.File, importpath string) (m *Module, err error) {
   186  	buildctx, err := llgobuild.ContextFromTriple(compiler.TargetTriple)
   187  	if err != nil {
   188  		return nil, err
   189  	}
   190  
   191  	if compiler.Importer == nil {
   192  		err = compiler.MakeImporter()
   193  		if err != nil {
   194  			return nil, err
   195  		}
   196  	}
   197  
   198  	impcfg := &loader.Config{
   199  		Fset: fset,
   200  		TypeChecker: types.Config{
   201  			Packages: compiler.Packages,
   202  			Import:   compiler.Importer,
   203  			Sizes:    compiler.llvmtypes,
   204  			DisableUnusedImportCheck: compiler.DisableUnusedImportCheck,
   205  		},
   206  		ImportFromBinary: true,
   207  		Build:            &buildctx.Context,
   208  		PackageCreated:   compiler.PackageCreated,
   209  	}
   210  	// If no import path is specified, then set the import
   211  	// path to be the same as the package's name.
   212  	if importpath == "" {
   213  		importpath = astFiles[0].Name.String()
   214  	}
   215  	impcfg.CreateFromFiles(importpath, astFiles...)
   216  	iprog, err := impcfg.Load()
   217  	if err != nil {
   218  		return nil, err
   219  	}
   220  	program := ssa.Create(iprog, ssa.BareInits)
   221  	mainPkginfo := iprog.InitialPackages()[0]
   222  	mainPkg := program.CreatePackage(mainPkginfo)
   223  
   224  	// Create a Module, which contains the LLVM module.
   225  	modulename := importpath
   226  	compiler.module = &Module{Module: llvm.NewModule(modulename), Path: modulename, Package: mainPkg.Object}
   227  	compiler.module.SetTarget(compiler.TargetTriple)
   228  	compiler.module.SetDataLayout(compiler.dataLayout)
   229  
   230  	// Create a new translation unit.
   231  	unit := newUnit(compiler, mainPkg)
   232  
   233  	// Create the runtime interface.
   234  	compiler.runtime, err = newRuntimeInterface(compiler.module.Module, compiler.llvmtypes)
   235  	if err != nil {
   236  		return nil, err
   237  	}
   238  
   239  	mainPkg.Build()
   240  
   241  	// Create a struct responsible for mapping static types to LLVM types,
   242  	// and to runtime/dynamic type values.
   243  	compiler.types = NewTypeMap(
   244  		mainPkg,
   245  		compiler.llvmtypes,
   246  		compiler.module.Module,
   247  		compiler.runtime,
   248  		MethodResolver(unit),
   249  	)
   250  
   251  	if compiler.GenerateDebug {
   252  		compiler.debug = debug.NewDIBuilder(
   253  			types.Sizes(compiler.llvmtypes),
   254  			compiler.module.Module,
   255  			impcfg.Fset,
   256  			compiler.DebugPrefixMaps,
   257  		)
   258  		defer compiler.debug.Destroy()
   259  		defer compiler.debug.Finalize()
   260  	}
   261  
   262  	unit.translatePackage(mainPkg)
   263  	compiler.processAnnotations(unit, mainPkginfo)
   264  
   265  	if importpath == "main" {
   266  		compiler.createInitMainFunction(mainPkg)
   267  	} else {
   268  		compiler.module.ExportData = compiler.buildExportData(mainPkg)
   269  	}
   270  
   271  	return compiler.module, nil
   272  }
   273  
   274  type byPriorityThenFunc []gccgoimporter.PackageInit
   275  
   276  func (a byPriorityThenFunc) Len() int      { return len(a) }
   277  func (a byPriorityThenFunc) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
   278  func (a byPriorityThenFunc) Less(i, j int) bool {
   279  	switch {
   280  	case a[i].Priority < a[j].Priority:
   281  		return true
   282  	case a[i].Priority > a[j].Priority:
   283  		return false
   284  	case a[i].InitFunc < a[j].InitFunc:
   285  		return true
   286  	default:
   287  		return false
   288  	}
   289  }
   290  
   291  func (c *compiler) buildPackageInitData(mainPkg *ssa.Package) gccgoimporter.InitData {
   292  	var inits []gccgoimporter.PackageInit
   293  	for _, imp := range mainPkg.Object.Imports() {
   294  		inits = append(inits, c.InitMap[imp].Inits...)
   295  	}
   296  	sort.Sort(byPriorityThenFunc(inits))
   297  
   298  	// Deduplicate init entries. We want to preserve the entry with the highest priority.
   299  	// Normally a package's priorities will be consistent among its dependencies, but it is
   300  	// possible for them to be different. For example, if a standard library test augments a
   301  	// package which is a dependency of 'regexp' (which is imported by every test main package)
   302  	// with additional dependencies, those dependencies may cause the package under test to
   303  	// receive a higher priority than indicated by its init clause in 'regexp'.
   304  	uniqinits := make([]gccgoimporter.PackageInit, len(inits))
   305  	uniqinitpos := len(inits)
   306  	uniqinitnames := make(map[string]struct{})
   307  	for i, _ := range inits {
   308  		init := inits[len(inits)-1-i]
   309  		if _, ok := uniqinitnames[init.InitFunc]; !ok {
   310  			uniqinitnames[init.InitFunc] = struct{}{}
   311  			uniqinitpos--
   312  			uniqinits[uniqinitpos] = init
   313  		}
   314  	}
   315  	uniqinits = uniqinits[uniqinitpos:]
   316  
   317  	ourprio := 1
   318  	if len(uniqinits) != 0 {
   319  		ourprio = uniqinits[len(uniqinits)-1].Priority + 1
   320  	}
   321  
   322  	if imp := mainPkg.Func("init"); imp != nil {
   323  		impname := c.types.mc.mangleFunctionName(imp)
   324  		uniqinits = append(uniqinits, gccgoimporter.PackageInit{mainPkg.Object.Name(), impname, ourprio})
   325  	}
   326  
   327  	return gccgoimporter.InitData{ourprio, uniqinits}
   328  }
   329  
   330  func (c *compiler) createInitMainFunction(mainPkg *ssa.Package) {
   331  	int8ptr := llvm.PointerType(c.types.ctx.Int8Type(), 0)
   332  	ftyp := llvm.FunctionType(llvm.VoidType(), []llvm.Type{int8ptr}, false)
   333  	initMain := llvm.AddFunction(c.module.Module, "__go_init_main", ftyp)
   334  	c.addCommonFunctionAttrs(initMain)
   335  	entry := llvm.AddBasicBlock(initMain, "entry")
   336  
   337  	builder := llvm.GlobalContext().NewBuilder()
   338  	defer builder.Dispose()
   339  	builder.SetInsertPointAtEnd(entry)
   340  
   341  	args := []llvm.Value{llvm.Undef(int8ptr)}
   342  
   343  	if !c.GccgoABI {
   344  		initfn := c.module.Module.NamedFunction("main..import")
   345  		if !initfn.IsNil() {
   346  			builder.CreateCall(initfn, args, "")
   347  		}
   348  		builder.CreateRetVoid()
   349  		return
   350  	}
   351  
   352  	initdata := c.buildPackageInitData(mainPkg)
   353  
   354  	for _, init := range initdata.Inits {
   355  		initfn := c.module.Module.NamedFunction(init.InitFunc)
   356  		if initfn.IsNil() {
   357  			initfn = llvm.AddFunction(c.module.Module, init.InitFunc, ftyp)
   358  		}
   359  		builder.CreateCall(initfn, args, "")
   360  	}
   361  
   362  	builder.CreateRetVoid()
   363  }
   364  
   365  func (c *compiler) buildExportData(mainPkg *ssa.Package) []byte {
   366  	exportData := importer.ExportData(mainPkg.Object)
   367  	b := bytes.NewBuffer(exportData)
   368  
   369  	b.WriteString("v1;\n")
   370  	if !c.GccgoABI {
   371  		return b.Bytes()
   372  	}
   373  
   374  	initdata := c.buildPackageInitData(mainPkg)
   375  	b.WriteString("priority ")
   376  	b.WriteString(strconv.Itoa(initdata.Priority))
   377  	b.WriteString(";\n")
   378  
   379  	if len(initdata.Inits) != 0 {
   380  		b.WriteString("init")
   381  		for _, init := range initdata.Inits {
   382  			b.WriteRune(' ')
   383  			b.WriteString(init.Name)
   384  			b.WriteRune(' ')
   385  			b.WriteString(init.InitFunc)
   386  			b.WriteRune(' ')
   387  			b.WriteString(strconv.Itoa(init.Priority))
   388  		}
   389  		b.WriteString(";\n")
   390  	}
   391  
   392  	return b.Bytes()
   393  }
   394  
   395  // vim: set ft=go :