func (c *compiler) VisitAssignStmt(stmt *ast.AssignStmt) { // x (add_op|mul_op)= y if stmt.Tok != token.DEFINE && stmt.Tok != token.ASSIGN { op := nonAssignmentToken(stmt.Tok) lhs := c.VisitExpr(stmt.Lhs[0]) rhsValue := c.VisitExpr(stmt.Rhs[0]) newValue := lhs.BinaryOp(op, rhsValue).(*LLVMValue).LLVMValue() c.builder.CreateStore(newValue, lhs.(*LLVMValue).pointer.LLVMValue()) return } // a, b, ... [:]= x, y, ... values := make([]Value, len(stmt.Lhs)) if len(stmt.Rhs) == 1 && len(stmt.Lhs) > 1 { value := c.VisitExpr(stmt.Rhs[0]) ptr := value.LLVMValue() struct_type := value.Type().(*types.Struct) for i := 0; i < len(struct_type.Fields); i++ { t := c.ObjGetType(struct_type.Fields[i]) value_ := c.builder.CreateExtractValue(ptr, i, "") values[i] = c.NewLLVMValue(value_, t) } } else { for i, expr := range stmt.Rhs { values[i] = c.VisitExpr(expr) } } for i, expr := range stmt.Lhs { value := values[i] switch x := expr.(type) { case *ast.Ident: if x.Name != "_" { obj := x.Obj if stmt.Tok == token.DEFINE { value_type := value.LLVMValue().Type() ptr := c.builder.CreateAlloca(value_type, x.Name) c.builder.CreateStore(value.LLVMValue(), ptr) llvm_value := c.NewLLVMValue( ptr, &types.Pointer{Base: value.Type()}) obj.Data = llvm_value.makePointee() } else { ptr := (obj.Data).(*LLVMValue).pointer value = value.Convert(types.Deref(ptr.Type())) c.builder.CreateStore(value.LLVMValue(), ptr.LLVMValue()) } } default: ptr := c.VisitExpr(expr).(*LLVMValue).pointer value = value.Convert(types.Deref(ptr.Type())) c.builder.CreateStore(value.LLVMValue(), ptr.LLVMValue()) } } }
func (c *compiler) VisitSelectorExpr(expr *ast.SelectorExpr) Value { lhs := c.VisitExpr(expr.X) if lhs == nil { // The only time we should get a nil result is if the object is // a package. obj := expr.Sel.Obj if obj.Kind == ast.Typ { return TypeValue{obj.Type.(types.Type)} } return c.Resolve(obj) } // Method expression. Returns an unbound function pointer. // FIXME this is just the most basic case. It's also possible to // create a pointer-receiver function from a method that has a // value receiver (see Method Expressions in spec). name := expr.Sel.Name if _, ok := lhs.(TypeValue); ok { methodobj := expr.Sel.Obj value := c.Resolve(methodobj).(*LLVMValue) ftyp := value.typ.(*types.Func) methodParams := make(types.ObjList, len(ftyp.Params)+1) methodParams[0] = ftyp.Recv copy(methodParams[1:], ftyp.Params) ftyp = &types.Func{ Recv: nil, Params: methodParams, Results: ftyp.Results, IsVariadic: ftyp.IsVariadic, } return c.NewLLVMValue(value.value, ftyp) } // TODO(?) record path to field/method during typechecking, so we don't // have to search again here. if iface, ok := types.Underlying(lhs.Type()).(*types.Interface); ok { i := sort.Search(len(iface.Methods), func(i int) bool { return iface.Methods[i].Name >= name }) structValue := lhs.LLVMValue() receiver := c.builder.CreateExtractValue(structValue, 1, "") f := c.builder.CreateExtractValue(structValue, i+2, "") i8ptr := &types.Pointer{Base: types.Int8} ftype := iface.Methods[i].Type.(*types.Func) ftype.Recv = ast.NewObj(ast.Var, "") ftype.Recv.Type = i8ptr f = c.builder.CreateBitCast(f, c.types.ToLLVM(ftype), "") ftype.Recv = nil method := c.NewLLVMValue(f, ftype) method.receiver = c.NewLLVMValue(receiver, i8ptr) return method } // Search through embedded types for field/method. var result selectorCandidate curr := []selectorCandidate{{nil, lhs.Type()}} for result.Type == nil && len(curr) > 0 { var next []selectorCandidate for _, candidate := range curr { indices := candidate.Indices[0:] t := candidate.Type if p, ok := types.Underlying(t).(*types.Pointer); ok { if _, ok := types.Underlying(p.Base).(*types.Struct); ok { t = p.Base } } if n, ok := t.(*types.Name); ok { i := sort.Search(len(n.Methods), func(i int) bool { return n.Methods[i].Name >= name }) if i < len(n.Methods) && n.Methods[i].Name == name { result.Indices = indices result.Type = t } } if t, ok := types.Underlying(t).(*types.Struct); ok { if i, ok := t.FieldIndices[name]; ok { result.Indices = append(indices, int(i)) result.Type = t } else { // Add embedded types to the next set of types // to check. for i, field := range t.Fields { if field.Name == "" { indices = append(indices[0:], i) t := field.Type.(types.Type) candidate := selectorCandidate{indices, t} next = append(next, candidate) } } } } } curr = next } // Get a pointer to the field/receiver. recvValue := lhs.(*LLVMValue) if len(result.Indices) > 0 { if _, ok := types.Underlying(lhs.Type()).(*types.Pointer); !ok { recvValue = recvValue.pointer //recvValue = c.NewLLVMValue(recvValue.LLVMValue(), recvValue.Type()) } for _, v := range result.Indices { ptr := recvValue.LLVMValue() field := types.Underlying(types.Deref(recvValue.typ)).(*types.Struct).Fields[v] fieldPtr := c.builder.CreateStructGEP(ptr, v, "") fieldPtrTyp := &types.Pointer{Base: field.Type.(types.Type)} recvValue = c.NewLLVMValue(fieldPtr, fieldPtrTyp) // GEP returns a pointer; if the field is a pointer, // we must load our pointer-to-a-pointer. if _, ok := field.Type.(*types.Pointer); ok { recvValue = recvValue.makePointee() } } } // Method? if expr.Sel.Obj.Kind == ast.Fun { method := c.Resolve(expr.Sel.Obj).(*LLVMValue) methodType := expr.Sel.Obj.Type.(*types.Func) receiverType := methodType.Recv.Type.(types.Type) if types.Identical(recvValue.Type(), receiverType) { method.receiver = recvValue } else if types.Identical(&types.Pointer{Base: recvValue.Type()}, receiverType) { method.receiver = recvValue.pointer } else { method.receiver = recvValue.makePointee() } return method } else { resultType := expr.Sel.Obj.Type.(types.Type) if types.Identical(recvValue.Type(), resultType) { // no-op } else if types.Identical(&types.Pointer{Base: recvValue.Type()}, resultType) { recvValue = recvValue.pointer } else { recvValue = recvValue.makePointee() } return recvValue } panic("unreachable") }
func (c *compiler) VisitGoStmt(stmt *ast.GoStmt) { //stmt.Call *ast.CallExpr // TODO var fn *LLVMValue switch x := (stmt.Call.Fun).(type) { case *ast.Ident: fn = c.Resolve(x.Obj).(*LLVMValue) if fn == nil { panic(fmt.Sprintf( "No function found with name '%s'", x.String())) } default: fn = c.VisitExpr(stmt.Call.Fun).(*LLVMValue) } // Evaluate arguments, store in a structure on the stack. var args_struct_type llvm.Type var args_mem llvm.Value var args_size llvm.Value if stmt.Call.Args != nil { param_types := make([]llvm.Type, 0) fn_type := types.Deref(fn.Type()).(*types.Func) for _, param := range fn_type.Params { typ := param.Type.(types.Type) param_types = append(param_types, c.types.ToLLVM(typ)) } args_struct_type = llvm.StructType(param_types, false) args_mem = c.builder.CreateAlloca(args_struct_type, "") for i, expr := range stmt.Call.Args { value_i := c.VisitExpr(expr) value_i = value_i.Convert(fn_type.Params[i].Type.(types.Type)) arg_i := c.builder.CreateGEP(args_mem, []llvm.Value{ llvm.ConstInt(llvm.Int32Type(), 0, false), llvm.ConstInt(llvm.Int32Type(), uint64(i), false)}, "") c.builder.CreateStore(value_i.LLVMValue(), arg_i) } args_size = llvm.SizeOf(args_struct_type) args_size = llvm.ConstTrunc(args_size, llvm.Int32Type()) } else { args_struct_type = llvm.VoidType() args_mem = llvm.ConstNull(llvm.PointerType(args_struct_type, 0)) args_size = llvm.ConstInt(llvm.Int32Type(), 0, false) } // When done, return to where we were. defer c.builder.SetInsertPointAtEnd(c.builder.GetInsertBlock()) // Create a function that will take a pointer to a structure of the type // defined above, or no parameters if there are none to pass. indirect_fn_type := llvm.FunctionType( llvm.VoidType(), []llvm.Type{llvm.PointerType(args_struct_type, 0)}, false) indirect_fn := llvm.AddFunction(c.module.Module, "", indirect_fn_type) indirect_fn.SetFunctionCallConv(llvm.CCallConv) // Call "newgoroutine" with the indirect function and stored args. newgoroutine := getnewgoroutine(c.module.Module) ngr_param_types := newgoroutine.Type().ElementType().ParamTypes() fn_arg := c.builder.CreateBitCast(indirect_fn, ngr_param_types[0], "") args_arg := c.builder.CreateBitCast(args_mem, llvm.PointerType(llvm.Int8Type(), 0), "") c.builder.CreateCall(newgoroutine, []llvm.Value{fn_arg, args_arg, args_size}, "") entry := llvm.AddBasicBlock(indirect_fn, "entry") c.builder.SetInsertPointAtEnd(entry) var args []llvm.Value if stmt.Call.Args != nil { args_mem = indirect_fn.Param(0) args = make([]llvm.Value, len(stmt.Call.Args)) for i := range stmt.Call.Args { arg_i := c.builder.CreateGEP(args_mem, []llvm.Value{ llvm.ConstInt(llvm.Int32Type(), 0, false), llvm.ConstInt(llvm.Int32Type(), uint64(i), false)}, "") args[i] = c.builder.CreateLoad(arg_i, "") } } c.builder.CreateCall(fn.LLVMValue(), args, "") c.builder.CreateRetVoid() }
func (v *LLVMValue) makePointee() *LLVMValue { t := v.compiler.NewLLVMValue(llvm.Value{}, types.Deref(v.typ)) t.pointer = v return t }
func (c *compiler) VisitSelectorExpr(expr *ast.SelectorExpr) Value { lhs := c.VisitExpr(expr.X) if lhs == nil { // The only time we should get a nil result is if the object is // a package. obj := expr.Sel.Obj if obj.Kind == ast.Typ { return TypeValue{obj.Type.(types.Type)} } return c.Resolve(obj) } // TODO(?) record path to field/method during typechecking, so we don't // have to search again here. name := expr.Sel.Name if iface, ok := types.Underlying(lhs.Type()).(*types.Interface); ok { i := sort.Search(len(iface.Methods), func(i int) bool { return iface.Methods[i].Name >= name }) structValue := lhs.LLVMValue() receiver := c.builder.CreateExtractValue(structValue, 0, "") f := c.builder.CreateExtractValue(structValue, i+2, "") ftype := c.ObjGetType(iface.Methods[i]).(*types.Func) method := c.NewLLVMValue(c.builder.CreateBitCast(f, c.types.ToLLVM(ftype), ""), ftype) method.receiver = c.NewLLVMValue(receiver, ftype.Recv.Type.(types.Type)) return method } // Search through embedded types for field/method. var result selectorCandidate curr := []selectorCandidate{{nil, lhs.Type()}} for result.Type == nil && len(curr) > 0 { var next []selectorCandidate for _, candidate := range curr { indices := candidate.Indices[0:] t := candidate.Type if p, ok := types.Underlying(t).(*types.Pointer); ok { if _, ok := types.Underlying(p.Base).(*types.Struct); ok { t = p.Base } } if n, ok := t.(*types.Name); ok { i := sort.Search(len(n.Methods), func(i int) bool { return n.Methods[i].Name >= name }) if i < len(n.Methods) && n.Methods[i].Name == name { result.Indices = indices result.Type = t } } if t, ok := types.Underlying(t).(*types.Struct); ok { if i, ok := t.FieldIndices[name]; ok { result.Indices = append(indices, int(i)) result.Type = t } else { // Add embedded types to the next set of types // to check. for i, field := range t.Fields { if field.Name == "" { indices = append(indices[0:], i) t := field.Type.(types.Type) candidate := selectorCandidate{indices, t} next = append(next, candidate) } } } } } curr = next } // Get a pointer to the field/receiver. recvValue := lhs.(*LLVMValue) if _, ok := types.Underlying(lhs.Type()).(*types.Pointer); !ok { recvValue = recvValue.pointer } recvValue = c.NewLLVMValue(recvValue.LLVMValue(), recvValue.Type()) if len(result.Indices) > 0 { for _, v := range result.Indices { ptr := recvValue.LLVMValue() field := types.Underlying(types.Deref(recvValue.typ)).(*types.Struct).Fields[v] fieldPtr := c.builder.CreateStructGEP(ptr, v, "") fieldPtrTyp := &types.Pointer{Base: field.Type.(types.Type)} recvValue = c.NewLLVMValue(fieldPtr, fieldPtrTyp) // GEP returns a pointer; if the field is a pointer, // we must load our pointer-to-a-pointer. if _, ok := field.Type.(*types.Pointer); ok { recvValue = recvValue.makePointee() } } } if !types.Identical(recvValue.typ, expr.Sel.Obj.Type.(types.Type)) { recvValue = recvValue.makePointee() } // Method? if expr.Sel.Obj.Kind == ast.Fun { method := c.Resolve(expr.Sel.Obj).(*LLVMValue) methodType := expr.Sel.Obj.Type.(*types.Func) receiverType := methodType.Recv.Type.(types.Type) if types.Identical(recvValue.Type(), receiverType) { method.receiver = recvValue } else if types.Identical(&types.Pointer{Base: recvValue.Type()}, receiverType) { method.receiver = recvValue.pointer } else { method.receiver = recvValue.makePointee() } return method } else { return recvValue } panic("unreachable") }
func (c *compiler) VisitAssignStmt(stmt *ast.AssignStmt) { // x (add_op|mul_op)= y if stmt.Tok != token.DEFINE && stmt.Tok != token.ASSIGN { // TODO handle assignment to map element. op := nonAssignmentToken(stmt.Tok) lhs := c.VisitExpr(stmt.Lhs[0]) rhsValue := c.VisitExpr(stmt.Rhs[0]) rhsValue = rhsValue.Convert(lhs.Type()) newValue := lhs.BinaryOp(op, rhsValue).(*LLVMValue).LLVMValue() c.builder.CreateStore(newValue, lhs.(*LLVMValue).pointer.LLVMValue()) return } // a, b, ... [:]= x, y, ... var values []Value if len(stmt.Rhs) == 1 && len(stmt.Lhs) > 1 { values = c.destructureExpr(stmt.Rhs[0]) } else { values = make([]Value, len(stmt.Lhs)) for i, expr := range stmt.Rhs { values[i] = c.VisitExpr(expr) } } for i, expr := range stmt.Lhs { value := values[i] switch x := expr.(type) { case *ast.Ident: if x.Name == "_" { continue } obj := x.Obj if stmt.Tok == token.DEFINE { value_type := value.LLVMValue().Type() ptr := c.builder.CreateAlloca(value_type, x.Name) c.builder.CreateStore(value.LLVMValue(), ptr) llvm_value := c.NewLLVMValue(ptr, &types.Pointer{Base: value.Type()}) obj.Data = llvm_value.makePointee() continue } if obj.Data == nil { // FIXME this is crap, going to need to revisit // how decl's are visited (should be in data // dependent order.) functions := c.functions c.functions = nil c.VisitValueSpec(obj.Decl.(*ast.ValueSpec), false) c.functions = functions } case *ast.IndexExpr: if t, ok := c.types.expr[x.X]; ok { if _, ok := t.(*types.Map); ok { m := c.VisitExpr(x.X).(*LLVMValue) index := c.VisitExpr(x.Index) elem, _ := c.mapLookup(m, index, true) ptr := elem.pointer value = value.Convert(types.Deref(ptr.Type())) c.builder.CreateStore(value.LLVMValue(), ptr.LLVMValue()) continue } } } // default (since we can't fallthrough in non-map index exprs) ptr := c.VisitExpr(expr).(*LLVMValue).pointer value = value.Convert(types.Deref(ptr.Type())) c.builder.CreateStore(value.LLVMValue(), ptr.LLVMValue()) } }