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