Various changes
* Don't skip init function * Add global variables and constants * Add unary operations * Use import path instead of package name (except for main) * ...more
Этот коммит содержится в:
		
							родитель
							
								
									5dfcb5f085
								
							
						
					
					
						коммит
						de0ff3b3af
					
				
					 1 изменённых файлов: 174 добавлений и 99 удалений
				
			
		
							
								
								
									
										273
									
								
								tgo.go
									
										
									
									
									
								
							
							
						
						
									
										273
									
								
								tgo.go
									
										
									
									
									
								
							|  | @ -5,6 +5,8 @@ import ( | |||
| 	"errors" | ||||
| 	"flag" | ||||
| 	"fmt" | ||||
| 	"go/ast" | ||||
| 	"go/build" | ||||
| 	"go/constant" | ||||
| 	"go/token" | ||||
| 	"go/types" | ||||
|  | @ -27,6 +29,7 @@ func init() { | |||
| } | ||||
| 
 | ||||
| type Compiler struct { | ||||
| 	triple          string | ||||
| 	mod             llvm.Module | ||||
| 	ctx             llvm.Context | ||||
| 	builder         llvm.Builder | ||||
|  | @ -41,12 +44,12 @@ type Compiler struct { | |||
| } | ||||
| 
 | ||||
| type Frame struct { | ||||
| 	pkgName string | ||||
| 	name    string                   // full name, including package | ||||
| 	params  map[*ssa.Parameter]int   // arguments to the function | ||||
| 	locals  map[ssa.Value]llvm.Value // local variables | ||||
| 	blocks  map[*ssa.BasicBlock]llvm.BasicBlock | ||||
| 	phis    []Phi | ||||
| 	pkgPrefix string | ||||
| 	name      string                   // full name, including package | ||||
| 	params    map[*ssa.Parameter]int   // arguments to the function | ||||
| 	locals    map[ssa.Value]llvm.Value // local variables | ||||
| 	blocks    map[*ssa.BasicBlock]llvm.BasicBlock | ||||
| 	phis      []Phi | ||||
| } | ||||
| 
 | ||||
| type Phi struct { | ||||
|  | @ -54,8 +57,10 @@ type Phi struct { | |||
| 	llvm llvm.Value | ||||
| } | ||||
| 
 | ||||
| func NewCompiler(path, triple string) (*Compiler, error) { | ||||
| 	c := &Compiler{} | ||||
| func NewCompiler(pkgName, triple string) (*Compiler, error) { | ||||
| 	c := &Compiler{ | ||||
| 		triple: triple, | ||||
| 	} | ||||
| 
 | ||||
| 	target, err := llvm.GetTargetFromTriple(triple) | ||||
| 	if err != nil { | ||||
|  | @ -63,7 +68,7 @@ func NewCompiler(path, triple string) (*Compiler, error) { | |||
| 	} | ||||
| 	c.machine = target.CreateTargetMachine(triple, "", "", llvm.CodeGenLevelDefault, llvm.RelocDefault, llvm.CodeModelDefault) | ||||
| 
 | ||||
| 	c.mod = llvm.NewModule(path) | ||||
| 	c.mod = llvm.NewModule(pkgName) | ||||
| 	c.ctx = c.mod.Context() | ||||
| 	c.builder = c.ctx.NewBuilder() | ||||
| 
 | ||||
|  | @ -86,21 +91,41 @@ func NewCompiler(path, triple string) (*Compiler, error) { | |||
| 	return c, nil | ||||
| } | ||||
| 
 | ||||
| func (c *Compiler) Parse(path string) error { | ||||
| func (c *Compiler) Parse(pkgName string) error { | ||||
| 	tripleSplit := strings.Split(c.triple, "-") | ||||
| 
 | ||||
| 	config := loader.Config { | ||||
| 		// TODO: TypeChecker.Sizes | ||||
| 		// TODO: Build (build.Context) - GOOS, GOARCH, GOPATH, etc | ||||
| 		Build: &build.Context { | ||||
| 			GOARCH:      tripleSplit[0], | ||||
| 			GOOS:        tripleSplit[2], | ||||
| 			GOROOT:      ".", | ||||
| 			CgoEnabled:  true, | ||||
| 			UseAllFiles: false, | ||||
| 			Compiler:    "gc", // must be one of the recognized compilers | ||||
| 			BuildTags:   []string{"tgo"}, | ||||
| 		}, | ||||
| 		AllowErrors: true, | ||||
| 	} | ||||
| 	config.CreateFromFilenames("main", path) | ||||
| 	config.Import(pkgName) | ||||
| 	lprogram, err := config.Load() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	program := ssautil.CreateProgram(lprogram, ssa.SanityCheckFunctions) | ||||
| 	// TODO: pick the error of the first package, not a random package | ||||
| 	for _, pkgInfo := range lprogram.AllPackages { | ||||
| 		fmt.Println("package:", pkgInfo.Pkg.Name()) | ||||
| 		if len(pkgInfo.Errors) != 0 { | ||||
| 			return pkgInfo.Errors[0] | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	program := ssautil.CreateProgram(lprogram, ssa.SanityCheckFunctions | ssa.BareInits) | ||||
| 	program.Build() | ||||
| 	// TODO: order of packages is random | ||||
| 	for _, pkg := range program.AllPackages() { | ||||
| 		fmt.Println("package:", pkg.Pkg.Name()) | ||||
| 		fmt.Println("package:", pkg.Pkg.Path()) | ||||
| 
 | ||||
| 		// Make sure we're walking through all members in a constant order every | ||||
| 		// run. | ||||
|  | @ -115,12 +140,41 @@ func (c *Compiler) Parse(path string) error { | |||
| 		// First, build all function declarations. | ||||
| 		for _, name := range memberNames { | ||||
| 			member := pkg.Members[name] | ||||
| 			if member, ok := member.(*ssa.Function); ok { | ||||
| 				frame, err := c.parseFuncDecl(pkg.Pkg.Name(), member) | ||||
| 
 | ||||
| 			pkgPrefix := pkg.Pkg.Path() | ||||
| 			if pkg.Pkg.Name() == "main" { | ||||
| 				pkgPrefix = "main" | ||||
| 			} | ||||
| 
 | ||||
| 			switch member := member.(type) { | ||||
| 			case *ssa.Function: | ||||
| 				frame, err := c.parseFuncDecl(pkgPrefix, member) | ||||
| 				if err != nil { | ||||
| 					return err | ||||
| 				} | ||||
| 				frames[member] = frame | ||||
| 			case *ssa.NamedConst: | ||||
| 				val, err := c.parseConst(member.Value) | ||||
| 				if err != nil { | ||||
| 					return err | ||||
| 				} | ||||
| 				global := llvm.AddGlobal(c.mod, val.Type(), pkgPrefix + "." +  member.Name()) | ||||
| 				global.SetInitializer(val) | ||||
| 				global.SetGlobalConstant(true) | ||||
| 				if ast.IsExported(member.Name()) { | ||||
| 					global.SetLinkage(llvm.PrivateLinkage) | ||||
| 				} | ||||
| 			case *ssa.Global: | ||||
| 				typ, err := c.getLLVMType(member.Type()) | ||||
| 				if err != nil { | ||||
| 					return err | ||||
| 				} | ||||
| 				global := llvm.AddGlobal(c.mod, typ, pkgPrefix + "." +  member.Name()) | ||||
| 				if ast.IsExported(member.Name()) { | ||||
| 					global.SetLinkage(llvm.PrivateLinkage) | ||||
| 				} | ||||
| 			default: | ||||
| 				return errors.New("todo: member: " + fmt.Sprintf("%#v", member)) | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
|  | @ -128,18 +182,15 @@ func (c *Compiler) Parse(path string) error { | |||
| 		for _, name := range memberNames { | ||||
| 			member := pkg.Members[name] | ||||
| 			fmt.Println("member:", member.Token(), member) | ||||
| 			if name == "init" { | ||||
| 				continue | ||||
| 			} | ||||
| 			switch member := member.(type) { | ||||
| 			case *ssa.Function: | ||||
| 
 | ||||
| 			if member, ok := member.(*ssa.Function); ok { | ||||
| 				if member.Blocks == nil { | ||||
| 					continue // external function | ||||
| 				} | ||||
| 				err := c.parseFunc(frames[member], member) | ||||
| 				if err != nil { | ||||
| 					return err | ||||
| 				} | ||||
| 
 | ||||
| 			default: | ||||
| 				fmt.Println("  TODO") | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | @ -151,26 +202,43 @@ func (c *Compiler) getLLVMType(goType types.Type) (llvm.Type, error) { | |||
| 	switch typ := goType.(type) { | ||||
| 	case *types.Basic: | ||||
| 		switch typ.Kind() { | ||||
| 		case types.Bool: | ||||
| 			return llvm.Int1Type(), nil | ||||
| 		case types.Int: | ||||
| 			return c.intType, nil | ||||
| 		case types.Int32: | ||||
| 			return llvm.Int32Type(), nil | ||||
| 		case types.UnsafePointer: | ||||
| 			return llvm.PointerType(llvm.Int8Type(), 0), nil | ||||
| 		default: | ||||
| 			return llvm.Type{}, errors.New("todo: unknown basic type") | ||||
| 			return llvm.Type{}, errors.New("todo: unknown basic type: " + fmt.Sprintf("%#v", typ)) | ||||
| 		} | ||||
| 	case *types.Pointer: | ||||
| 		ptrTo, err := c.getLLVMType(typ.Elem()) | ||||
| 		if err != nil { | ||||
| 			return llvm.Type{}, err | ||||
| 		} | ||||
| 		return llvm.PointerType(ptrTo, 0), nil | ||||
| 	default: | ||||
| 		return llvm.Type{}, errors.New("todo: unknown type") | ||||
| 		return llvm.Type{}, errors.New("todo: unknown type: " + fmt.Sprintf("%#v", goType)) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (c *Compiler) parseFuncDecl(pkgName string, f *ssa.Function) (*Frame, error) { | ||||
| 	name := pkgName + "." + f.Name() | ||||
| func (c *Compiler) getPackageRelativeName(frame *Frame, name string) string { | ||||
| 	if strings.IndexByte(name, '.') == -1 { | ||||
| 		name = frame.pkgPrefix + "." + name | ||||
| 	} | ||||
| 	return name | ||||
| } | ||||
| 
 | ||||
| func (c *Compiler) parseFuncDecl(pkgPrefix string, f *ssa.Function) (*Frame, error) { | ||||
| 	name := pkgPrefix + "." + f.Name() | ||||
| 	frame := &Frame{ | ||||
| 		pkgName: pkgName, | ||||
| 		name:    name, | ||||
| 		params:  make(map[*ssa.Parameter]int), | ||||
| 		locals:  make(map[ssa.Value]llvm.Value), | ||||
| 		blocks:  make(map[*ssa.BasicBlock]llvm.BasicBlock), | ||||
| 		pkgPrefix: pkgPrefix, | ||||
| 		name:      name, | ||||
| 		params:    make(map[*ssa.Parameter]int), | ||||
| 		locals:    make(map[ssa.Value]llvm.Value), | ||||
| 		blocks:    make(map[*ssa.BasicBlock]llvm.BasicBlock), | ||||
| 	} | ||||
| 
 | ||||
| 	var retType llvm.Type | ||||
|  | @ -188,22 +256,12 @@ func (c *Compiler) parseFuncDecl(pkgName string, f *ssa.Function) (*Frame, error | |||
| 
 | ||||
| 	var paramTypes []llvm.Type | ||||
| 	for i, param := range f.Params { | ||||
| 		switch typ := param.Type().(type) { | ||||
| 		case *types.Basic: | ||||
| 			var paramType llvm.Type | ||||
| 			switch typ.Kind() { | ||||
| 			case types.Int: | ||||
| 				paramType = c.intType | ||||
| 			case types.Int32: | ||||
| 				paramType = llvm.Int32Type() | ||||
| 			default: | ||||
| 				return nil, errors.New("todo: unknown basic param type") | ||||
| 			} | ||||
| 			paramTypes = append(paramTypes, paramType) | ||||
| 			frame.params[param] = i | ||||
| 		default: | ||||
| 			return nil, errors.New("todo: unknown param type") | ||||
| 		paramType, err := c.getLLVMType(param.Type()) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		paramTypes = append(paramTypes, paramType) | ||||
| 		frame.params[param] = i | ||||
| 	} | ||||
| 
 | ||||
| 	fnType := llvm.FunctionType(retType, paramTypes, false) | ||||
|  | @ -212,8 +270,6 @@ func (c *Compiler) parseFuncDecl(pkgName string, f *ssa.Function) (*Frame, error | |||
| } | ||||
| 
 | ||||
| func (c *Compiler) parseFunc(frame *Frame, f *ssa.Function) error { | ||||
| 	// TODO: external functions | ||||
| 
 | ||||
| 	// Pre-create all basic blocks in the function. | ||||
| 	llvmFn := c.mod.NamedFunction(frame.name) | ||||
| 	for _, block := range f.DomPreorder() { | ||||
|  | @ -289,7 +345,7 @@ func (c *Compiler) parseInstr(frame *Frame, instr ssa.Instruction) error { | |||
| } | ||||
| 
 | ||||
| func (c *Compiler) parseBuiltin(frame *Frame, instr *ssa.CallCommon, call *ssa.Builtin) (llvm.Value, error) { | ||||
| 	fmt.Printf("    builtin: %#v\n", call) | ||||
| 	fmt.Printf("    builtin: %v\n", call) | ||||
| 	name := call.Name() | ||||
| 
 | ||||
| 	switch name { | ||||
|  | @ -324,11 +380,7 @@ func (c *Compiler) parseBuiltin(frame *Frame, instr *ssa.CallCommon, call *ssa.B | |||
| func (c *Compiler) parseFunctionCall(frame *Frame, call *ssa.CallCommon, fn *ssa.Function) (llvm.Value, error) { | ||||
| 	fmt.Printf("    function: %s\n", fn) | ||||
| 
 | ||||
| 	name := fn.Name() | ||||
| 	if strings.IndexByte(name, '.') == -1 { | ||||
| 		// TODO: import path instead of pkgName | ||||
| 		name = frame.pkgName + "." + name | ||||
| 	} | ||||
| 	name := c.getPackageRelativeName(frame, fn.Name()) | ||||
| 	target := c.mod.NamedFunction(name) | ||||
| 	if target.IsNil() { | ||||
| 		return llvm.Value{}, errors.New("undefined function: " + name) | ||||
|  | @ -359,6 +411,44 @@ func (c *Compiler) parseCall(frame *Frame, instr *ssa.Call) (llvm.Value, error) | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) { | ||||
| 	fmt.Printf("      expr: %v\n", expr) | ||||
| 
 | ||||
| 	if frame != nil { | ||||
| 		if value, ok := frame.locals[expr]; ok { | ||||
| 			// Value is a local variable that has already been computed. | ||||
| 			fmt.Println("        from local var") | ||||
| 			return value, nil | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	switch expr := expr.(type) { | ||||
| 	case *ssa.Const: | ||||
| 		return c.parseConst(expr) | ||||
| 	case *ssa.BinOp: | ||||
| 		return c.parseBinOp(frame, expr) | ||||
| 	case *ssa.Call: | ||||
| 		return c.parseCall(frame, expr) | ||||
| 	case *ssa.Global: | ||||
| 		return c.mod.NamedGlobal(c.getPackageRelativeName(frame, expr.Name())), nil | ||||
| 	case *ssa.Parameter: | ||||
| 		llvmFn := c.mod.NamedFunction(frame.name) | ||||
| 		return llvmFn.Param(frame.params[expr]), nil | ||||
| 	case *ssa.Phi: | ||||
| 		t, err := c.getLLVMType(expr.Type()) | ||||
| 		if err != nil { | ||||
| 			return llvm.Value{}, err | ||||
| 		} | ||||
| 		phi := c.builder.CreatePHI(t, "") | ||||
| 		frame.phis = append(frame.phis, Phi{expr, phi}) | ||||
| 		return phi, nil | ||||
| 	case *ssa.UnOp: | ||||
| 		return c.parseUnOp(frame, expr) | ||||
| 	default: | ||||
| 		return llvm.Value{}, errors.New("todo: unknown expression: " + fmt.Sprintf("%#v", expr)) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (c *Compiler) parseBinOp(frame *Frame, binop *ssa.BinOp) (llvm.Value, error) { | ||||
| 	x, err := c.parseExpr(frame, binop.X) | ||||
| 	if err != nil { | ||||
|  | @ -410,47 +500,32 @@ func (c *Compiler) parseBinOp(frame *Frame, binop *ssa.BinOp) (llvm.Value, error | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) { | ||||
| 	fmt.Printf("      expr: %v\n", expr) | ||||
| 
 | ||||
| 	if value, ok := frame.locals[expr]; ok { | ||||
| 		// Value is a local variable that has already been computed. | ||||
| 		fmt.Println("        from local var") | ||||
| 		return value, nil | ||||
| 	} | ||||
| 
 | ||||
| 	switch expr := expr.(type) { | ||||
| 	case *ssa.Const: | ||||
| 		switch expr.Value.Kind() { | ||||
| 		case constant.String: | ||||
| 			str := constant.StringVal(expr.Value) | ||||
| 			strLen := llvm.ConstInt(c.stringLenType, uint64(len(str)), false) | ||||
| 			strPtr := c.builder.CreateGlobalStringPtr(str, ".str") | ||||
| 			strObj := llvm.ConstStruct([]llvm.Value{strLen, strPtr}, false) | ||||
| 			return strObj, nil | ||||
| 		case constant.Int: | ||||
| 			n, _ := constant.Int64Val(expr.Value) // TODO: do something with the 'exact' return value? | ||||
| 			return llvm.ConstInt(c.intType, uint64(n), true), nil | ||||
| 		default: | ||||
| 			return llvm.Value{}, errors.New("todo: unknown constant") | ||||
| 		} | ||||
| 	case *ssa.BinOp: | ||||
| 		return c.parseBinOp(frame, expr) | ||||
| 	case *ssa.Call: | ||||
| 		return c.parseCall(frame, expr) | ||||
| 	case *ssa.Parameter: | ||||
| 		llvmFn := c.mod.NamedFunction(frame.name) | ||||
| 		return llvmFn.Param(frame.params[expr]), nil | ||||
| 	case *ssa.Phi: | ||||
| 		t, err := c.getLLVMType(expr.Type()) | ||||
| 		if err != nil { | ||||
| 			return llvm.Value{}, err | ||||
| 		} | ||||
| 		phi := c.builder.CreatePHI(t, "") | ||||
| 		frame.phis = append(frame.phis, Phi{expr, phi}) | ||||
| 		return phi, nil | ||||
| func (c *Compiler) parseConst(expr *ssa.Const) (llvm.Value, error) { | ||||
| 	switch expr.Value.Kind() { | ||||
| 	case constant.String: | ||||
| 		str := constant.StringVal(expr.Value) | ||||
| 		strLen := llvm.ConstInt(c.stringLenType, uint64(len(str)), false) | ||||
| 		strPtr := c.builder.CreateGlobalStringPtr(str, ".str") | ||||
| 		strObj := llvm.ConstStruct([]llvm.Value{strLen, strPtr}, false) | ||||
| 		return strObj, nil | ||||
| 	case constant.Int: | ||||
| 		n, _ := constant.Int64Val(expr.Value) // TODO: do something with the 'exact' return value? | ||||
| 		return llvm.ConstInt(c.intType, uint64(n), true), nil | ||||
| 	default: | ||||
| 		return llvm.Value{}, errors.New("todo: unknown expression: " + fmt.Sprintf("%#v", expr)) | ||||
| 		return llvm.Value{}, errors.New("todo: unknown constant") | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (c *Compiler) parseUnOp(frame *Frame, unop *ssa.UnOp) (llvm.Value, error) { | ||||
| 	x, err := c.parseExpr(frame, unop.X) | ||||
| 	if err != nil { | ||||
| 		return llvm.Value{}, err | ||||
| 	} | ||||
| 	switch unop.Op { | ||||
| 	case token.NOT: | ||||
| 		return c.builder.CreateNot(x, ""), nil | ||||
| 	default: | ||||
| 		return llvm.Value{}, errors.New("todo: unknown unop") | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | @ -495,13 +570,13 @@ func (c *Compiler) EmitObject(path string) error { | |||
| } | ||||
| 
 | ||||
| // Helper function for Compiler object. | ||||
| func Compile(inpath, outpath, target string, printIR bool) error { | ||||
| 	c, err := NewCompiler(inpath, target) | ||||
| func Compile(pkgName, outpath, target string, printIR bool) error { | ||||
| 	c, err := NewCompiler(pkgName, target) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	parseErr := c.Parse(inpath) | ||||
| 	parseErr := c.Parse(pkgName) | ||||
| 	if printIR { | ||||
| 		fmt.Println(c.IR()) | ||||
| 	} | ||||
|  |  | |||
		Загрузка…
	
	Создание таблицы
		
		Сослаться в новой задаче
	
	 Ayke van Laethem
						Ayke van Laethem