github.com/tardisgo/tardisgo@v0.0.0-20161119180838-e0dd9a7e46b5/pogo/instructions.go (about)

     1  // Copyright 2014 Elliott Stoneham and The TARDIS Go Authors
     2  // Use of this source code is governed by an MIT-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package pogo
     6  
     7  import (
     8  	"fmt"
     9  	"go/ast"
    10  	"go/types"
    11  	"reflect"
    12  
    13  	"golang.org/x/tools/go/ssa"
    14  
    15  	"github.com/tardisgo/tardisgo/tgoutil"
    16  )
    17  
    18  // RegisterName returns the name of an ssa.Value, a utility function in case it needs to be altered.
    19  func (comp *Compilation) RegisterName(val ssa.Value) string {
    20  	//NOTE the SSA code says that name() should not be relied on, so this code may need to alter
    21  	return LanguageList[comp.TargetLang].RegisterName(val)
    22  }
    23  
    24  // Handle an individual instruction.
    25  func (comp *Compilation) emitInstruction(instruction interface{}, operands []*ssa.Value) (emitPhiFlag bool) {
    26  	l := comp.TargetLang
    27  	emitPhiFlag = true
    28  	errorInfo := ""
    29  	_, isDebug := instruction.(*ssa.DebugRef)
    30  	if !isDebug { // Don't update the code position for debug refs
    31  		prev := comp.LatestValidPosHash
    32  		comp.MakePosHash(instruction.(ssa.Instruction).Pos()) // this so that we log the nearby position info
    33  		if prev != comp.LatestValidPosHash {                  // new info, so put out an update
    34  			if comp.DebugFlag { // but only in Debug mode
    35  				fmt.Fprintln(&LanguageList[l].buffer,
    36  					LanguageList[l].SetPosHash())
    37  			}
    38  		}
    39  		errorInfo = comp.CodePosition(instruction.(ssa.Instruction).Pos())
    40  	}
    41  	if errorInfo == "" {
    42  		errorInfo = comp.previousErrorInfo
    43  	} else {
    44  		comp.previousErrorInfo = "near " + errorInfo
    45  		errorInfo = "@ " + errorInfo
    46  	}
    47  	errorInfo = reflect.TypeOf(instruction).String() + " " + errorInfo //TODO consider removing as for DEBUG only
    48  	instrVal, hasVal := instruction.(ssa.Value)
    49  	register := ""
    50  	comment := ""
    51  	if hasVal {
    52  		register = comp.RegisterName(instrVal)
    53  		comment = fmt.Sprintf("%s = %+v %s", register, instruction, errorInfo)
    54  		//emitComment(comment)
    55  		switch len(*instruction.(ssa.Value).Referrers()) {
    56  		case 0: // no other instruction uses the result of this one
    57  			comment += " [REGISTER VALUE UN-USED]"
    58  			register = ""
    59  		case 1: // only 1 other use of the register
    60  			// TODO register optimisation currently disabled, consider reimplimentation
    61  			user := (*instruction.(ssa.Value).Referrers())[0]
    62  			if user.Block() == instruction.(ssa.Instruction).Block() {
    63  				comment += " [REGISTER MAY BE OPTIMIZABLE]"
    64  			}
    65  		default: //multiple usage of the register
    66  		}
    67  		if len(register) > 0 {
    68  			if LanguageList[comp.TargetLang].LangType(instruction.(ssa.Value).Type(), false, errorInfo) == "" { // NOTE an empty type def makes a register useless too
    69  				register = ""
    70  			}
    71  		}
    72  	} else {
    73  		comment = fmt.Sprintf("%+v %s", instruction, errorInfo)
    74  		//emitComment(comment)
    75  	}
    76  	switch instruction.(type) {
    77  	case *ssa.Jump:
    78  		fmt.Fprintln(&LanguageList[l].buffer,
    79  			LanguageList[l].Jump(instruction.(*ssa.Jump).Block().Succs[0].Index,
    80  				instruction.(*ssa.Jump).Block().Index,
    81  				LanguageList[l].PhiCode(false, instruction.(*ssa.Jump).Block().Index,
    82  					instruction.(*ssa.Jump).Block().Succs[0].Instrs,
    83  					errorInfo))+
    84  				LanguageList[l].Comment(comment))
    85  
    86  	case *ssa.If:
    87  		fmt.Fprintln(&LanguageList[l].buffer,
    88  			LanguageList[l].If(*operands[0],
    89  				instruction.(*ssa.If).Block().Succs[0].Index,
    90  				instruction.(*ssa.If).Block().Succs[1].Index,
    91  				instruction.(*ssa.If).Block().Index,
    92  				LanguageList[l].PhiCode(false, instruction.(*ssa.If).Block().Index,
    93  					instruction.(*ssa.If).Block().Succs[0].Instrs, errorInfo),
    94  				LanguageList[l].PhiCode(false, instruction.(*ssa.If).Block().Index,
    95  					instruction.(*ssa.If).Block().Succs[1].Instrs, errorInfo),
    96  				errorInfo)+LanguageList[l].Comment(comment))
    97  
    98  	case *ssa.Phi:
    99  		text := ""
   100  		if len(*instruction.(*ssa.Phi).Referrers()) > 0 {
   101  			phiEntries := make([]int, len(operands))
   102  			valEntries := make([]interface{}, len(operands))
   103  			for o := range operands {
   104  				phiEntries[o] = instruction.(*ssa.Phi).Block().Preds[o].Index
   105  				valEntries[o] = *operands[o]
   106  			}
   107  			text = LanguageList[l].Phi(register, phiEntries, valEntries,
   108  				LanguageList[l].LangType(instrVal.Type(), true, errorInfo), errorInfo)
   109  		}
   110  		fmt.Fprintln(&LanguageList[l].buffer, text+LanguageList[l].Comment(comment))
   111  
   112  	case *ssa.Call:
   113  		if instruction.(*ssa.Call).Call.IsInvoke() {
   114  			fmt.Fprintln(&LanguageList[l].buffer,
   115  				LanguageList[l].EmitInvoke(register, getFnPath(instruction.(*ssa.Call).Parent()),
   116  					false, false, comp.grMap[instruction.(*ssa.Call).Parent()],
   117  					instruction.(*ssa.Call).Call, errorInfo)+LanguageList[l].Comment(comment))
   118  		} else {
   119  			switch instruction.(*ssa.Call).Call.Value.(type) {
   120  			case *ssa.Builtin:
   121  				comp.emitCall(true, false, false, comp.grMap[instruction.(*ssa.Call).Parent()],
   122  					register, instruction.(*ssa.Call).Call, errorInfo, comment)
   123  			default:
   124  				comp.emitCall(false, false, false, comp.grMap[instruction.(*ssa.Call).Parent()],
   125  					register, instruction.(*ssa.Call).Call, errorInfo, comment)
   126  			}
   127  		}
   128  
   129  	case *ssa.Go:
   130  		if instruction.(*ssa.Go).Call.IsInvoke() {
   131  			if comp.grMap[instruction.(*ssa.Go).Parent()] != true {
   132  				panic("attempt to Go a method, from a function that does not use goroutines at " + errorInfo)
   133  			}
   134  			fmt.Fprintln(&LanguageList[l].buffer,
   135  				LanguageList[l].EmitInvoke(register, getFnPath(instruction.(*ssa.Go).Parent()),
   136  					true, false, true, instruction.(*ssa.Go).Call, errorInfo)+
   137  					LanguageList[l].Comment(comment))
   138  		} else {
   139  			switch instruction.(*ssa.Go).Call.Value.(type) {
   140  			case *ssa.Builtin: // no builtin functions can be go'ed
   141  				comp.LogError(errorInfo, "pogo", fmt.Errorf("builtin functions cannot be go'ed"))
   142  			default:
   143  				if comp.grMap[instruction.(*ssa.Go).Parent()] != true {
   144  					panic("attempt to Go a function, from a function does not use goroutines at " + errorInfo)
   145  				}
   146  				comp.emitCall(false, true, false, true,
   147  					register, instruction.(*ssa.Go).Call, errorInfo, comment)
   148  			}
   149  		}
   150  
   151  	case *ssa.Defer:
   152  		if instruction.(*ssa.Defer).Call.IsInvoke() {
   153  			fmt.Fprintln(&LanguageList[l].buffer,
   154  				LanguageList[l].EmitInvoke(register,
   155  					getFnPath(instruction.(*ssa.Defer).Parent()),
   156  					false, true, comp.grMap[instruction.(*ssa.Defer).Parent()],
   157  					instruction.(*ssa.Defer).Call, errorInfo)+
   158  					LanguageList[l].Comment(comment))
   159  		} else {
   160  			switch instruction.(*ssa.Defer).Call.Value.(type) {
   161  			case *ssa.Builtin: // no builtin functions can be defer'ed - TODO: the spec does allow this in some circumstances
   162  				switch instruction.(*ssa.Defer).Call.Value.(*ssa.Builtin).Name() {
   163  				case "close":
   164  					//LogError(errorInfo, "pogo", fmt.Errorf("builtin function close() cannot be defer'ed"))
   165  					comp.emitCall(true, false, true, comp.grMap[instruction.(*ssa.Defer).Parent()],
   166  						register, instruction.(*ssa.Defer).Call, errorInfo, comment)
   167  				default:
   168  					comp.LogError(errorInfo, "pogo", fmt.Errorf("builtin functions cannot be defer'ed"))
   169  				}
   170  			default:
   171  				comp.emitCall(false, false, true, comp.grMap[instruction.(*ssa.Defer).Parent()],
   172  					register, instruction.(*ssa.Defer).Call, errorInfo, comment)
   173  			}
   174  		}
   175  
   176  	case *ssa.Return:
   177  		emitPhiFlag = false
   178  		r := LanguageList[l].Ret(operands, errorInfo)
   179  		fmt.Fprintln(&LanguageList[l].buffer, r+LanguageList[l].Comment(comment))
   180  
   181  	case *ssa.Panic:
   182  		emitPhiFlag = false
   183  		fmt.Fprintln(&LanguageList[l].buffer,
   184  			LanguageList[l].Panic(*operands[0], errorInfo,
   185  				comp.grMap[instruction.(*ssa.Panic).Parent()])+LanguageList[l].Comment(comment))
   186  
   187  	case *ssa.UnOp:
   188  		if register == "" && instruction.(*ssa.UnOp).Op.String() != "<-" {
   189  			comp.emitComment(comment)
   190  		} else {
   191  			fmt.Fprintln(&LanguageList[l].buffer,
   192  				LanguageList[l].UnOp(register, instrVal.Type(), instruction.(*ssa.UnOp).Op.String(), *operands[0],
   193  					instruction.(*ssa.UnOp).CommaOk, errorInfo)+
   194  					LanguageList[l].Comment(comment))
   195  		}
   196  
   197  	case *ssa.BinOp:
   198  		if register == "" {
   199  			comp.emitComment(comment)
   200  		} else {
   201  			op := instruction.(*ssa.BinOp).Op.String()
   202  			fmt.Fprintln(&LanguageList[l].buffer,
   203  				LanguageList[l].BinOp(register, instrVal.Type(), op, *operands[0], *operands[1], errorInfo)+
   204  					LanguageList[l].Comment(comment))
   205  		}
   206  
   207  	case *ssa.Store:
   208  		fmt.Fprintln(&LanguageList[l].buffer,
   209  			LanguageList[l].Store(*operands[0], *operands[1], errorInfo)+LanguageList[l].Comment(comment))
   210  
   211  	case *ssa.Send:
   212  		fmt.Fprintln(&LanguageList[l].buffer,
   213  			LanguageList[l].Send(*operands[0], *operands[1], errorInfo)+LanguageList[l].Comment(comment))
   214  
   215  	case *ssa.Convert:
   216  		fmt.Fprintln(&LanguageList[l].buffer,
   217  			LanguageList[l].Convert(register, LanguageList[l].LangType(instrVal.Type(), false, errorInfo), instrVal.Type(), *operands[0], errorInfo)+
   218  				LanguageList[l].Comment(comment))
   219  
   220  	case *ssa.ChangeType:
   221  		fmt.Fprintln(&LanguageList[l].buffer,
   222  			LanguageList[l].ChangeType(register, instruction.(ssa.Value).Type(), *operands[0], errorInfo)+
   223  				LanguageList[l].Comment(comment))
   224  
   225  	case *ssa.MakeInterface:
   226  		fmt.Fprintln(&LanguageList[l].buffer,
   227  			LanguageList[l].MakeInterface(register, instruction.(ssa.Value).Type(), *operands[0], errorInfo)+
   228  				LanguageList[l].Comment(comment))
   229  
   230  	case *ssa.ChangeInterface:
   231  		fmt.Fprintln(&LanguageList[l].buffer,
   232  			LanguageList[l].ChangeInterface(register, instruction.(ssa.Value).Type(), *operands[0], errorInfo)+
   233  				LanguageList[l].Comment(comment))
   234  
   235  	case *ssa.TypeAssert:
   236  		fmt.Fprintln(&LanguageList[l].buffer,
   237  			LanguageList[l].TypeAssert(register, instruction.(*ssa.TypeAssert).X,
   238  				instruction.(*ssa.TypeAssert).AssertedType, instruction.(*ssa.TypeAssert).CommaOk, errorInfo)+
   239  				LanguageList[l].Comment(comment))
   240  
   241  	case *ssa.RunDefers:
   242  		fmt.Fprintln(&LanguageList[l].buffer,
   243  			LanguageList[l].RunDefers(comp.grMap[instruction.(*ssa.RunDefers).Parent()])+
   244  				LanguageList[l].Comment(comment))
   245  
   246  	case *ssa.Alloc:
   247  		fmt.Fprintln(&LanguageList[l].buffer,
   248  			LanguageList[l].Alloc(register, instruction.(*ssa.Alloc).Heap,
   249  				instruction.(*ssa.Alloc).Type(), errorInfo)+
   250  				LanguageList[l].Comment(instruction.(*ssa.Alloc).Comment+" "+comment))
   251  
   252  	case *ssa.MakeClosure:
   253  		fmt.Fprintln(&LanguageList[l].buffer,
   254  			LanguageList[l].MakeClosure(register,
   255  				instruction,
   256  				errorInfo)+
   257  				LanguageList[l].Comment(comment))
   258  
   259  	case *ssa.MakeSlice:
   260  		fmt.Fprintln(&LanguageList[l].buffer,
   261  			LanguageList[l].MakeSlice(register,
   262  				instruction,
   263  				errorInfo)+
   264  				LanguageList[l].Comment(comment))
   265  
   266  	case *ssa.MakeChan:
   267  		fmt.Fprintln(&LanguageList[l].buffer,
   268  			LanguageList[l].MakeChan(register,
   269  				instruction,
   270  				errorInfo)+
   271  				LanguageList[l].Comment(comment))
   272  
   273  	case *ssa.MakeMap:
   274  		fmt.Fprintln(&LanguageList[l].buffer,
   275  			LanguageList[l].MakeMap(register,
   276  				instruction,
   277  				errorInfo)+
   278  				LanguageList[l].Comment(comment))
   279  
   280  	case *ssa.MapUpdate:
   281  		fmt.Fprintln(&LanguageList[l].buffer,
   282  			LanguageList[l].MapUpdate(*operands[0], *operands[1], *operands[2], errorInfo)+LanguageList[l].Comment(comment))
   283  
   284  	case *ssa.Range:
   285  		fmt.Fprintln(&LanguageList[l].buffer,
   286  			LanguageList[l].Range(register, *operands[0], errorInfo)+LanguageList[l].Comment(comment))
   287  
   288  	case *ssa.Next:
   289  		fmt.Fprintln(&LanguageList[l].buffer,
   290  			LanguageList[l].Next(register, *operands[0], instruction.(*ssa.Next).IsString,
   291  				errorInfo)+LanguageList[l].Comment(comment))
   292  
   293  	case *ssa.Lookup:
   294  		fmt.Fprintln(&LanguageList[l].buffer,
   295  			LanguageList[l].Lookup(register, *operands[0], *operands[1], instruction.(*ssa.Lookup).CommaOk, errorInfo)+
   296  				LanguageList[l].Comment(comment))
   297  
   298  	case *ssa.Extract:
   299  		if register == "" { // rquired here because of a "feature" in the generated SSA form
   300  			comp.emitComment(comment)
   301  		} else {
   302  			fmt.Fprintln(&LanguageList[l].buffer,
   303  				LanguageList[l].Extract(register, *operands[0], instruction.(*ssa.Extract).Index, errorInfo)+
   304  					LanguageList[l].Comment(comment))
   305  		}
   306  
   307  	case *ssa.Slice:
   308  		// TODO see http://tip.golang.org/doc/go1.2#three_index
   309  		// TODO add third parameter when SSA code provides it to enable slice instructions to specify a capacity
   310  		if register == "" {
   311  			comp.emitComment(comment)
   312  		} else {
   313  			fmt.Fprintln(&LanguageList[l].buffer,
   314  				LanguageList[l].Slice(register, instruction.(*ssa.Slice).X,
   315  					instruction.(*ssa.Slice).Low, instruction.(*ssa.Slice).High, errorInfo)+
   316  					LanguageList[l].Comment(comment))
   317  
   318  		}
   319  
   320  	case *ssa.Index:
   321  		if register == "" {
   322  			comp.emitComment(comment)
   323  		} else {
   324  			doRangeCheck := true
   325  			aLen := 0
   326  			switch instruction.(*ssa.Index).X.Type().(type) {
   327  			case *types.Array:
   328  				aLen = int(instruction.(*ssa.Index).X.Type().(*types.Array).Len())
   329  			case *types.Pointer:
   330  				switch instruction.(*ssa.Index).X.Type().(*types.Pointer).Elem().(type) {
   331  				case *types.Array:
   332  					aLen = int(instruction.(*ssa.Index).X.Type().(*types.Pointer).Elem().(*types.Array).Len())
   333  				}
   334  			}
   335  			if aLen > 0 {
   336  				_, indexIsConst := instruction.(*ssa.Index).Index.(*ssa.Const)
   337  				if indexIsConst {
   338  					// this error handling is defensive, as the Go SSA code catches this error
   339  					index := instruction.(*ssa.Index).Index.(*ssa.Const).Int64()
   340  					if (index < 0) || (index >= int64(aLen)) {
   341  						comp.LogError(errorInfo, "pogo", fmt.Errorf("index [%d] out of range: 0 to %d", index, aLen-1))
   342  					}
   343  					doRangeCheck = false
   344  				}
   345  			}
   346  			if doRangeCheck {
   347  				fmt.Fprintln(&LanguageList[l].buffer,
   348  					LanguageList[l].RangeCheck(instruction.(*ssa.Index).X, instruction.(*ssa.Index).Index, aLen, errorInfo))
   349  			}
   350  			fmt.Fprintln(&LanguageList[l].buffer,
   351  				LanguageList[l].Index(register, *operands[0], *operands[1], errorInfo)+
   352  					LanguageList[l].Comment(comment))
   353  		}
   354  
   355  	case *ssa.IndexAddr:
   356  		if register == "" {
   357  			comp.emitComment(comment)
   358  		} else {
   359  			doRangeCheck := true
   360  			aLen := 0
   361  			switch instruction.(*ssa.IndexAddr).X.Type().(type) {
   362  			case *types.Array:
   363  				aLen = int(instruction.(*ssa.IndexAddr).X.Type().(*types.Array).Len())
   364  			case *types.Pointer:
   365  				switch instruction.(*ssa.IndexAddr).X.Type().(*types.Pointer).Elem().(type) {
   366  				case *types.Array:
   367  					aLen = int(instruction.(*ssa.IndexAddr).X.Type().(*types.Pointer).Elem().(*types.Array).Len())
   368  				}
   369  			}
   370  			if aLen > 0 {
   371  				_, indexIsConst := instruction.(*ssa.IndexAddr).Index.(*ssa.Const)
   372  				if indexIsConst {
   373  					index := instruction.(*ssa.IndexAddr).Index.(*ssa.Const).Int64()
   374  					if (index < 0) || (index >= int64(aLen)) {
   375  						comp.LogError(errorInfo, "pogo", fmt.Errorf("index [%d] out of range: 0 to %d", index, aLen-1))
   376  					}
   377  					doRangeCheck = false
   378  				}
   379  			}
   380  			if doRangeCheck {
   381  				fmt.Fprintln(&LanguageList[l].buffer,
   382  					LanguageList[l].RangeCheck(instruction.(*ssa.IndexAddr).X, instruction.(*ssa.IndexAddr).Index, aLen, errorInfo)+
   383  						LanguageList[l].Comment(comment+" [POINTER]"))
   384  			}
   385  			fmt.Fprintln(&LanguageList[l].buffer, LanguageList[l].IndexAddr(register, instruction, errorInfo),
   386  				LanguageList[l].Comment(comment+" [POINTER]"))
   387  
   388  		}
   389  
   390  	case *ssa.FieldAddr:
   391  		fmt.Fprintln(&LanguageList[l].buffer, LanguageList[l].FieldAddr(register, instruction, errorInfo),
   392  			LanguageList[l].Comment(comment+" [POINTER]"))
   393  
   394  	case *ssa.Field:
   395  		if register == "" {
   396  			comp.emitComment(comment)
   397  		} else {
   398  			st := instruction.(*ssa.Field).X.Type().Underlying().(*types.Struct)
   399  			fName := tgoutil.MakeID(st.Field(instruction.(*ssa.Field).Field).Name())
   400  			l := comp.TargetLang
   401  			fmt.Fprintln(&LanguageList[l].buffer,
   402  				LanguageList[l].Field(register, instruction.(*ssa.Field).X,
   403  					instruction.(*ssa.Field).Field, fName, errorInfo, false)+
   404  					LanguageList[l].Comment(comment))
   405  		}
   406  
   407  	case *ssa.DebugRef: // TODO the comment could include the actual Go code
   408  		debugCode := ""
   409  		ident, ok := instruction.(*ssa.DebugRef).Expr.(*ast.Ident)
   410  		if ok {
   411  			if ident.Obj != nil {
   412  				if ident.Obj.Kind == ast.Var {
   413  					//fmt.Printf("DEBUGref %s (%s) => %s %+v %+v %+v\n", instruction.(*ssa.DebugRef).X.Name(),
   414  					//	instruction.(*ssa.DebugRef).X.Type().String(),
   415  					//	ident.Name, ident.Obj.Decl, ident.Obj.Data, ident.Obj.Type)
   416  					name := ident.Name
   417  					glob, isGlob := instruction.(*ssa.DebugRef).X.(*ssa.Global)
   418  					if isGlob {
   419  						name = glob.Pkg.String()[len("package "):] + "." + name
   420  					}
   421  					debugCode = LanguageList[l].DebugRef(name, instruction.(*ssa.DebugRef).X, errorInfo)
   422  				}
   423  			}
   424  		}
   425  		fmt.Fprintln(&LanguageList[l].buffer, debugCode+LanguageList[l].Comment(comment))
   426  
   427  	case *ssa.Select:
   428  		text := LanguageList[l].Select(true, register, instruction, false, errorInfo)
   429  		fmt.Fprintln(&LanguageList[l].buffer, text+LanguageList[l].Comment(comment))
   430  
   431  	default:
   432  		comp.emitComment(comment + " [NO CODE GENERATED]")
   433  		comp.LogError(errorInfo, "pogo", fmt.Errorf("SSA instruction not implemented: %v", reflect.TypeOf(instruction)))
   434  	}
   435  	if false { //TODO add instruction detail DEBUG FLAG
   436  		for o := range operands { // this loop for the creation of comments to show what is in the instructions
   437  			val := *operands[o]
   438  			vip := valIsPointer(val)
   439  			if vip {
   440  				vipOut := showIndirectValue(val)
   441  				comp.emitComment(fmt.Sprintf("Op[%d].VIP: %+v", o, vipOut))
   442  			} else {
   443  				var ic interface{} = *operands[o]
   444  				constVal, isConst := ic.(*ssa.Const)
   445  				if isConst {
   446  					comp.emitComment(fmt.Sprintf("Op[%d]: Constant= %+v", o, constVal))
   447  				} else {
   448  					comp.emitComment(fmt.Sprintf("Op[%d]: %v = %+v", o, (*operands[o]), val))
   449  				}
   450  			}
   451  			// l := TargetLang
   452  			// fmt.Fprintln(&LanguageList[l].buffer, LanguageList[l].Value(*operands[o], "TEST"))
   453  		}
   454  	}
   455  	return // return value is named and set in the code above
   456  }
   457  
   458  func getFnPath(fn *ssa.Function) string {
   459  	if fn == nil {
   460  		//println("DEBUG getFnPath nil function")
   461  		return ""
   462  	}
   463  	ob := fn.Object()
   464  	if ob == nil {
   465  		//println("DEBUG getFnPath nil object: name,synthetic=", fn.Name(), ",", fn.Synthetic)
   466  		return ""
   467  	}
   468  	pk := ob.Pkg()
   469  	if pk == nil {
   470  		//println("DEBUG getFnPath nil package: name,synthetic=", fn.Name(), ",", fn.Synthetic)
   471  		return ""
   472  	}
   473  	return pk.Path()
   474  }