Exemple #1
0
func (c *compiler) createFunctionMetadata(f *ast.FuncDecl, fn *LLVMValue) llvm.DebugDescriptor {
	if len(c.debug_context) == 0 {
		return nil
	}

	file := c.fileset.File(f.Pos())
	fnptr := fn.value
	fun := fnptr.IsAFunction()
	if fun.IsNil() {
		fnptr = llvm.ConstExtractValue(fn.value, []uint32{0})
	}
	meta := &llvm.SubprogramDescriptor{
		Name:        fnptr.Name(),
		DisplayName: f.Name.Name,
		Path:        llvm.FileDescriptor(file.Name()),
		Line:        uint32(file.Line(f.Pos())),
		ScopeLine:   uint32(file.Line(f.Body.Pos())),
		Context:     &llvm.ContextDescriptor{llvm.FileDescriptor(file.Name())},
		Function:    fnptr}

	var result types.Type
	var metaparams []llvm.DebugDescriptor
	if ftyp, ok := fn.Type().(*types.Signature); ok {
		if recv := ftyp.Recv(); recv != nil {
			metaparams = append(metaparams, c.tollvmDebugDescriptor(recv.Type()))
		}
		if ftyp.Params() != nil {
			for i := 0; i < ftyp.Params().Len(); i++ {
				p := ftyp.Params().At(i)
				metaparams = append(metaparams, c.tollvmDebugDescriptor(p.Type()))
			}
		}
		if ftyp.Results() != nil {
			result = ftyp.Results().At(0).Type()
			// TODO: what to do with multiple returns?
			for i := 1; i < ftyp.Results().Len(); i++ {
				p := ftyp.Results().At(i)
				metaparams = append(metaparams, c.tollvmDebugDescriptor(p.Type()))
			}
		}
	}

	meta.Type = llvm.NewSubroutineCompositeType(
		c.tollvmDebugDescriptor(result),
		metaparams,
	)

	// compile unit is the first context object pushed
	compileUnit := c.debug_context[0].(*llvm.CompileUnitDescriptor)
	compileUnit.Subprograms = append(compileUnit.Subprograms, meta)
	return meta
}
Exemple #2
0
func (c *compiler) createBlockMetadata(stmt *ast.BlockStmt) llvm.DebugDescriptor {
	uniqueId++
	file := c.fileset.File(stmt.Pos())
	fd := llvm.FileDescriptor(file.Name())
	return &llvm.BlockDescriptor{
		File:    &fd,
		Line:    uint32(file.Line(stmt.Pos())),
		Context: c.currentDebugContext(),
		Id:      uniqueId,
	}
}
Exemple #3
0
func (c *compiler) newStackVarEx(argument int, stackf *LLVMValue, v types.Object, value llvm.Value, name string) (stackvalue llvm.Value, stackvar *LLVMValue) {
	typ := v.Type()

	// We need to put alloca instructions in the top block or the values
	// displayed when inspecting these variables in a debugger will be
	// completely messed up.
	curBlock := c.builder.GetInsertBlock()
	if p := curBlock.Parent(); !p.IsNil() {
		fb := p.FirstBasicBlock()
		fi := fb.FirstInstruction()
		if !fb.IsNil() && !fi.IsNil() {
			c.builder.SetInsertPointBefore(fi)
		}
	}
	old := c.builder.CurrentDebugLocation()
	c.builder.SetCurrentDebugLocation(llvm.Value{})
	stackvalue = c.builder.CreateAlloca(c.types.ToLLVM(typ), name)

	// For arguments we want to insert the store instruction
	// without debug information to ensure that they are executed
	// (and hence have proper values) before the debugger hits the
	// first line in a function.
	if argument == 0 {
		c.builder.SetCurrentDebugLocation(old)
		c.builder.SetInsertPointAtEnd(curBlock)
	}

	if !value.IsNil() {
		c.builder.CreateStore(value, stackvalue)
	}
	c.builder.SetCurrentDebugLocation(old)
	c.builder.SetInsertPointAtEnd(curBlock)

	ptrvalue := c.NewValue(stackvalue, types.NewPointer(typ))
	stackvar = ptrvalue.makePointee()
	stackvar.stack = stackf
	c.objectdata[v].Value = stackvar

	file := c.fileset.File(v.Pos())
	tag := llvm.DW_TAG_auto_variable
	if argument > 0 {
		tag = llvm.DW_TAG_arg_variable
	}
	ld := llvm.NewLocalVariableDescriptor(tag)
	ld.Argument = uint32(argument)
	ld.Line = uint32(file.Line(v.Pos()))
	ld.Name = name
	ld.File = &llvm.ContextDescriptor{llvm.FileDescriptor(file.Name())}
	ld.Type = c.tollvmDebugDescriptor(typ)
	ld.Context = c.currentDebugContext()
	c.builder.InsertDeclare(c.module.Module, llvm.MDNode([]llvm.Value{stackvalue}), c.debug_info.MDNode(ld))
	return stackvalue, stackvar
}
Exemple #4
0
// createLocalVariableMetadata creates and returns a debug descriptor for
// a local variable in a function.
//
// obj is the go/types object for the var.
// paramIndex is 0 for "auto" variables (non-parameter stack vars), and a
// 1-based index for parameter vars.
func (c *compiler) createLocalVariableMetadata(obj types.Object, paramIndex int) llvm.DebugDescriptor {
	ctx := c.currentDebugContext()
	if ctx == nil {
		return nil
	}
	tag := llvm.DW_TAG_auto_variable
	if paramIndex > 0 {
		tag = llvm.DW_TAG_arg_variable
	}
	position := c.fileset.Position(obj.Pos())
	ld := llvm.NewLocalVariableDescriptor(tag)
	ld.Argument = uint32(paramIndex)
	ld.Line = uint32(position.Line)
	ld.Name = obj.Name()
	ld.File = &llvm.ContextDescriptor{llvm.FileDescriptor(position.Filename)}
	ld.Type = c.tollvmDebugDescriptor(obj.Type())
	ld.Context = ctx
	return ld
}
Exemple #5
0
func (compiler *compiler) Compile(fset *token.FileSet, files []*ast.File, importpath string) (m *Module, err error) {
	// FIXME create a compilation state, rather than storing in 'compiler'.
	compiler.fileset = fset
	compiler.initfuncs = nil
	compiler.varinitfuncs = nil

	// If no import path is specified, or the package's
	// name (not path) is "main", then set the import
	// path to be the same as the package's name.
	if importpath == "" || files[0].Name.String() == "main" {
		importpath = files[0].Name.String()
	}

	// Type-check, and store object data.
	compiler.typeinfo.Types = make(map[ast.Expr]types.Type)
	compiler.typeinfo.Values = make(map[ast.Expr]exact.Value)
	compiler.typeinfo.Objects = make(map[*ast.Ident]types.Object)
	compiler.typeinfo.Implicits = make(map[ast.Node]types.Object)
	compiler.typeinfo.Selections = make(map[*ast.SelectorExpr]*types.Selection)
	compiler.objectdata = make(map[types.Object]*ObjectData)
	compiler.methodsets = make(map[types.Type]*methodset)
	compiler.exportedtypes = nil
	compiler.llvmtypes = NewLLVMTypeMap(compiler.target)
	pkg, err := compiler.typecheck(importpath, fset, files)
	if err != nil {
		return nil, err
	}
	compiler.pkg = pkg

	// Create a Module, which contains the LLVM bitcode. Dispose it on panic,
	// otherwise we'll set a finalizer at the end. The caller may invoke
	// Dispose manually, which will render the finalizer a no-op.
	modulename := importpath
	compiler.module = &Module{llvm.NewModule(modulename), modulename, false}
	compiler.module.SetTarget(compiler.TargetTriple)
	compiler.module.SetDataLayout(compiler.target.String())
	defer func() {
		if e := recover(); e != nil {
			compiler.module.Dispose()
			panic(e)
		}
	}()

	// Create a struct responsible for mapping static types to LLVM types,
	// and to runtime/dynamic type values.
	var resolver Resolver = compiler
	compiler.FunctionCache = NewFunctionCache(compiler)
	compiler.types = NewTypeMap(compiler.llvmtypes, compiler.module.Module, importpath, compiler.FunctionCache, resolver)

	// Create a Builder, for building LLVM instructions.
	compiler.builder = newBuilder(compiler.types)
	defer compiler.builder.Dispose()

	compiler.debug_info = &llvm.DebugInfo{}
	// Compile each file in the package.
	for _, file := range files {
		compiler.compile_unit = &llvm.CompileUnitDescriptor{
			Language: llvm.DW_LANG_Go,
			Path:     llvm.FileDescriptor(fset.File(file.Pos()).Name()),
			Producer: LLGOProducer,
			Runtime:  LLGORuntimeVersion,
		}
		compiler.pushDebugContext(&compiler.compile_unit.Path)

		for _, decl := range file.Decls {
			compiler.VisitDecl(decl)
		}
		compiler.popDebugContext()
		if len(compiler.debug_context) > 0 {
			log.Panicln(compiler.debug_context)
		}
		compiler.module.AddNamedMetadataOperand("llvm.dbg.cu", compiler.debug_info.MDNode(compiler.compile_unit))
	}

	// Export runtime type information.
	compiler.exportRuntimeTypes()

	// Wrap "main.main" in a call to runtime.main.
	if importpath == "main" {
		err = compiler.createMainFunction()
		if err != nil {
			return nil, err
		}
	} else {
		var e = exporter{compiler: compiler}
		if err := e.Export(pkg); err != nil {
			return nil, err
		}
	}

	// Create global constructors. The initfuncs/varinitfuncs
	// slices are in the order of visitation; we generate the
	// list of constructors in the reverse order.
	//
	// The llgo linker will link modules in the order of
	// package dependency, i.e. if A requires B, then llgo-link
	// will link the modules in the order A, B. The "runtime"
	// package is always last.
	//
	// At program initialisation, the runtime initialisation
	// function (runtime.main) will invoke the constructors
	// in reverse order.
	var initfuncs [][]llvm.Value
	if compiler.varinitfuncs != nil {
		initfuncs = append(initfuncs, compiler.varinitfuncs)
	}
	if compiler.initfuncs != nil {
		initfuncs = append(initfuncs, compiler.initfuncs)
	}
	if initfuncs != nil {
		ctortype := llvm.PointerType(llvm.Int8Type(), 0)
		var ctors []llvm.Value
		var index int = 0
		for _, initfuncs := range initfuncs {
			for _, fnptr := range initfuncs {
				name := fmt.Sprintf("__llgo.ctor.%s.%d", importpath, index)
				fnptr.SetName(name)
				fnptr = llvm.ConstBitCast(fnptr, ctortype)
				ctors = append(ctors, fnptr)
				index++
			}
		}
		for i, n := 0, len(ctors); i < n/2; i++ {
			ctors[i], ctors[n-i-1] = ctors[n-i-1], ctors[i]
		}
		ctorsInit := llvm.ConstArray(ctortype, ctors)
		ctorsVar := llvm.AddGlobal(compiler.module.Module, ctorsInit.Type(), "runtime.ctors")
		ctorsVar.SetInitializer(ctorsInit)
		ctorsVar.SetLinkage(llvm.AppendingLinkage)
	}

	// Create debug metadata.
	//compiler.createMetadata()

	return compiler.module, nil
}