Skip to content
Snippets Groups Projects
reader.go 54.5 KiB
Newer Older
  • Learn to ignore specific revisions
  • 
    			// Quirkish. TODO(mdempsky): Document why.
    			if name.AutoTemp() {
    				name.SetEsc(ir.EscUnknown)
    
    				if base.Flag.GenDwarfInl != 0 {
    					name.SetInlLocal(true)
    				} else {
    					name.SetPos(r.inlCall.Pos())
    				}
    			}
    		}
    	}
    
    	body.Append(ir.NewLabelStmt(call.Pos(), r.retlabel))
    
    	res := ir.NewInlinedCallExpr(call.Pos(), body, append([]ir.Node(nil), r.retvars...))
    	res.SetInit(init)
    	res.SetType(call.Type())
    	res.SetTypecheck(1)
    
    	// Inlining shouldn't add any functions to todoBodies.
    	assert(len(todoBodies) == 0)
    
    	return res
    }
    
    // inlReturn returns a statement that can substitute for the given
    // return statement when inlining.
    func (r *reader) inlReturn(ret *ir.ReturnStmt) *ir.BlockStmt {
    	pos := r.inlCall.Pos()
    
    	block := ir.TakeInit(ret)
    
    	if results := ret.Results; len(results) != 0 {
    		assert(len(r.retvars) == len(results))
    
    		as2 := ir.NewAssignListStmt(pos, ir.OAS2, append([]ir.Node(nil), r.retvars...), ret.Results)
    
    		if r.delayResults {
    			for _, name := range r.retvars {
    				// TODO(mdempsky): Use inlined position of name.Pos() instead?
    				name := name.(*ir.Name)
    				block.Append(ir.NewDecl(pos, ir.ODCL, name))
    				name.Defn = as2
    			}
    		}
    
    		block.Append(as2)
    	}
    
    	block.Append(ir.NewBranchStmt(pos, ir.OGOTO, r.retlabel))
    	return ir.NewBlockStmt(pos, block)
    }
    
    // expandInline reads in an extra copy of IR to populate
    // fn.Inl.{Dcl,Body}.
    func expandInline(fn *ir.Func, pri pkgReaderIndex) {
    
    	// TODO(mdempsky): Remove this function. It's currently needed by
    	// dwarfgen/dwarf.go:preInliningDcls, which requires fn.Inl.Dcl to
    	// create abstract function DIEs. But we should be able to provide it
    
    	// with the same information some other way.
    
    	fndcls := len(fn.Dcl)
    	topdcls := len(typecheck.Target.Decls)
    
    	tmpfn := ir.NewFunc(fn.Pos())
    	tmpfn.Nname = ir.NewNameAt(fn.Nname.Pos(), fn.Sym())
    	tmpfn.ClosureVars = fn.ClosureVars
    
    	{
    		r := pri.asReader(relocBody, syncFuncBody)
    
    		setType(tmpfn.Nname, fn.Type())
    
    
    		// Don't change parameter's Sym/Nname fields.
    		r.funarghack = true
    
    		r.funcBody(tmpfn)
    
    
    		ir.WithFunc(tmpfn, func() {
    			deadcode.Func(tmpfn)
    		})
    	}
    
    
    	used := usedLocals(tmpfn.Body)
    
    	for _, name := range tmpfn.Dcl {
    		if name.Class != ir.PAUTO || used.Has(name) {
    			name.Curfn = fn
    			fn.Inl.Dcl = append(fn.Inl.Dcl, name)
    		}
    	}
    	fn.Inl.Body = tmpfn.Body
    
    	// Double check that we didn't change fn.Dcl by accident.
    	assert(fndcls == len(fn.Dcl))
    
    	// typecheck.Stmts may have added function literals to
    	// typecheck.Target.Decls. Remove them again so we don't risk trying
    	// to compile them multiple times.
    	typecheck.Target.Decls = typecheck.Target.Decls[:topdcls]
    }
    
    // usedLocals returns a set of local variables that are used within body.
    func usedLocals(body []ir.Node) ir.NameSet {
    	var used ir.NameSet
    	ir.VisitList(body, func(n ir.Node) {
    		if n, ok := n.(*ir.Name); ok && n.Op() == ir.ONAME && n.Class == ir.PAUTO {
    			used.Add(n)
    		}
    	})
    	return used
    }
    
    
    // @@@ Method wrappers
    
    // needWrapperTypes lists types for which we may need to generate
    // method wrappers.
    var needWrapperTypes []*types.Type
    
    
    // haveWrapperTypes lists types for which we know we already have
    // method wrappers, because we found the type in an imported package.
    var haveWrapperTypes []*types.Type
    
    // needMethodValueWrappers lists methods for which we may need to
    // generate method value wrappers.
    var needMethodValueWrappers []methodValueWrapper
    
    // haveMethodValueWrappers lists methods for which we know we already
    // have method value wrappers, because we found it in an imported
    // package.
    var haveMethodValueWrappers []methodValueWrapper
    
    type methodValueWrapper struct {
    	rcvr   *types.Type
    	method *types.Field
    }
    
    func (r *reader) needWrapper(typ *types.Type) {
    
    		return
    	}
    
    	// If a type was found in an imported package, then we can assume
    	// that package (or one of its transitive dependencies) already
    	// generated method wrappers for it.
    	if r.importedDef() {
    		haveWrapperTypes = append(haveWrapperTypes, typ)
    	} else {
    		needWrapperTypes = append(needWrapperTypes, typ)
    
    func (r *reader) importedDef() bool {
    
    	// If a type was found in an imported package, then we can assume
    	// that package (or one of its transitive dependencies) already
    	// generated method wrappers for it.
    	//
    	// Exception: If we're instantiating an imported generic type or
    	// function, we might be instantiating it with type arguments not
    	// previously seen before.
    	//
    	// TODO(mdempsky): Distinguish when a generic function or type was
    	// instantiated in an imported package so that we can add types to
    	// haveWrapperTypes instead.
    
    	return r.p != localPkgReader && !r.hasTypeParams()
    
    func MakeWrappers(target *ir.Package) {
    
    	// Only unified IR emits its own wrappers.
    	if base.Debug.Unified == 0 {
    
    	// always generate a wrapper for error.Error (#29304)
    
    	needWrapperTypes = append(needWrapperTypes, types.ErrorType)
    
    		wrapType(typ, target, seen, false)
    
    	}
    	haveWrapperTypes = nil
    
    	for _, typ := range needWrapperTypes {
    
    		wrapType(typ, target, seen, true)
    
    
    	for _, wrapper := range haveMethodValueWrappers {
    
    		wrapMethodValue(wrapper.rcvr, wrapper.method, target, false)
    
    	}
    	haveMethodValueWrappers = nil
    
    	for _, wrapper := range needMethodValueWrappers {
    
    		wrapMethodValue(wrapper.rcvr, wrapper.method, target, true)
    
    func wrapType(typ *types.Type, target *ir.Package, seen map[string]*types.Type, needed bool) {
    
    	key := typ.LinkString()
    	if prev := seen[key]; prev != nil {
    		if !types.Identical(typ, prev) {
    			base.Fatalf("collision: types %v and %v have link string %q", typ, prev, key)
    		}
    		return
    	}
    	seen[key] = typ
    
    	if !needed {
    		// Only called to add to 'seen'.
    		return
    	}
    
    
    	if !typ.IsInterface() {
    		typecheck.CalcMethods(typ)
    	}
    	for _, meth := range typ.AllMethods().Slice() {
    		if meth.Sym.IsBlank() || !meth.IsMethod() {
    			base.FatalfAt(meth.Pos, "invalid method: %v", meth)
    		}
    
    
    		methodWrapper(0, typ, meth, target)
    
    
    		// For non-interface types, we also want *T wrappers.
    		if !typ.IsInterface() {
    
    			methodWrapper(1, typ, meth, target)
    
    
    			// For not-in-heap types, *T is a scalar, not pointer shaped,
    			// so the interface wrappers use **T.
    			if typ.NotInHeap() {
    
    				methodWrapper(2, typ, meth, target)
    
    func methodWrapper(derefs int, tbase *types.Type, method *types.Field, target *ir.Package) {
    
    	wrapper := tbase
    	for i := 0; i < derefs; i++ {
    		wrapper = types.NewPtr(wrapper)
    	}
    
    	sym := ir.MethodSym(wrapper, method.Sym)
    
    	base.Assertf(!sym.Siggen(), "already generated wrapper %v", sym)
    
    	sym.SetSiggen(true)
    
    	wrappee := method.Type.Recv().Type
    	if types.Identical(wrapper, wrappee) ||
    		!types.IsMethodApplicable(wrapper, method) ||
    		!reflectdata.NeedEmit(tbase) {
    		return
    	}
    
    	// TODO(mdempsky): Use method.Pos instead?
    	pos := base.AutogeneratedPos
    
    
    	fn := newWrapperFunc(pos, sym, wrapper, method)
    
    
    	var recv ir.Node = fn.Nname.Type().Recv().Nname.(*ir.Name)
    
    	// For simple *T wrappers around T methods, panicwrap produces a
    	// nicer panic message.
    	if wrapper.IsPtr() && types.Identical(wrapper.Elem(), wrappee) {
    		cond := ir.NewBinaryExpr(pos, ir.OEQ, recv, types.BuiltinPkg.Lookup("nil").Def.(ir.Node))
    		then := []ir.Node{ir.NewCallExpr(pos, ir.OCALL, typecheck.LookupRuntime("panicwrap"), nil)}
    		fn.Body.Append(ir.NewIfStmt(pos, cond, then, nil))
    	}
    
    	// typecheck will add one implicit deref, if necessary,
    	// but not-in-heap types require more for their **T wrappers.
    	for i := 1; i < derefs; i++ {
    		recv = Implicit(ir.NewStarExpr(pos, recv))
    	}
    
    	addTailCall(pos, fn, recv, method)
    
    	finishWrapperFunc(fn, target)
    
    func wrapMethodValue(recvType *types.Type, method *types.Field, target *ir.Package, needed bool) {
    
    	sym := ir.MethodSymSuffix(recvType, method.Sym, "-fm")
    
    	sym.SetUniq(true)
    
    	// TODO(mdempsky): Use method.Pos instead?
    	pos := base.AutogeneratedPos
    
    
    	fn := newWrapperFunc(pos, sym, nil, method)
    
    
    	// Declare and initialize variable holding receiver.
    
    	recv := ir.NewHiddenParam(pos, fn, typecheck.Lookup(".this"), recvType)
    
    	finishWrapperFunc(fn, target)
    
    func newWrapperFunc(pos src.XPos, sym *types.Sym, wrapper *types.Type, method *types.Field) *ir.Func {
    
    	fn.SetDupok(true) // TODO(mdempsky): Leave unset for local, non-generic wrappers?
    
    	name := ir.NewNameAt(pos, sym)
    	ir.MarkFunc(name)
    	name.Func = fn
    	name.Defn = fn
    	fn.Nname = name
    
    	sig := newWrapperType(wrapper, method)
    
    
    	// TODO(mdempsky): De-duplicate with similar logic in funcargs.
    
    	defParams := func(class ir.Class, params *types.Type) {
    		for _, param := range params.FieldSlice() {
    
    			name := ir.NewNameAt(param.Pos, param.Sym)
    			name.Class = class
    
    			setType(name, param.Type)
    
    
    			name.Curfn = fn
    			fn.Dcl = append(fn.Dcl, name)
    
    			param.Nname = name
    		}
    	}
    
    
    	defParams(ir.PPARAM, sig.Recvs())
    	defParams(ir.PPARAM, sig.Params())
    	defParams(ir.PPARAMOUT, sig.Results())
    
    func finishWrapperFunc(fn *ir.Func, target *ir.Package) {
    
    	typecheck.Func(fn)
    
    	ir.WithFunc(fn, func() {
    		typecheck.Stmts(fn.Body)
    	})
    
    
    	// We generate wrappers after the global inlining pass,
    	// so we're responsible for applying inlining ourselves here.
    	inline.InlineCalls(fn)
    
    
    // newWrapperType returns a copy of the given signature type, but with
    
    // the receiver parameter type substituted with recvType.
    // If recvType is nil, newWrapperType returns a signature
    // without a receiver parameter.
    func newWrapperType(recvType *types.Type, method *types.Field) *types.Type {
    
    	clone := func(params []*types.Field) []*types.Field {
    		res := make([]*types.Field, len(params))
    		for i, param := range params {
    			sym := param.Sym
    			if sym == nil || sym.Name == "_" {
    				sym = typecheck.LookupNum(".anon", i)
    			}
    			res[i] = types.NewField(param.Pos, sym, param.Type)
    			res[i].SetIsDDD(param.IsDDD())
    		}
    		return res
    	}
    
    
    	sig := method.Type
    
    	var recv *types.Field
    	if recvType != nil {
    		recv = types.NewField(sig.Recv().Pos, typecheck.Lookup(".this"), recvType)
    	}
    
    	params := clone(sig.Params().FieldSlice())
    	results := clone(sig.Results().FieldSlice())
    
    	return types.NewSignature(types.NoPkg, recv, nil, params, results)
    }
    
    
    func addTailCall(pos src.XPos, fn *ir.Func, recv ir.Node, method *types.Field) {
    	sig := fn.Nname.Type()
    	args := make([]ir.Node, sig.NumParams())
    	for i, param := range sig.Params().FieldSlice() {
    		args[i] = param.Nname.(*ir.Name)
    	}
    
    
    	// TODO(mdempsky): Support creating OTAILCALL, when possible. See reflectdata.methodWrapper.
    	// Not urgent though, because tail calls are currently incompatible with regabi anyway.
    
    
    	fn.SetWrapper(true) // TODO(mdempsky): Leave unset for tail calls?
    
    
    	dot := ir.NewSelectorExpr(pos, ir.OXDOT, recv, method.Sym)
    	call := typecheck.Call(pos, dot, args, method.Type.IsVariadic()).(*ir.CallExpr)
    
    	}
    
    	ret := ir.NewReturnStmt(pos, nil)
    	ret.Results = []ir.Node{call}