github.com/llvm-mirror/llgo@v0.0.0-20190322182713-bf6f0a60fce1/irgen/compiler.go (about)

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