in tool/instrument/inst_func.go [128:262]
func (rp *RuleProcessor) insertTJump(t *resource.InstFuncRule,
funcDecl *dst.FuncDecl) error {
util.Assert(t.OnEnter != "" || t.OnExit != "", "sanity check")
var retVals []dst.Expr // nil by default
if retList := funcDecl.Type.Results; retList != nil {
retVals = make([]dst.Expr, 0)
// If return values are named, collect their names, otherwise we try to
// name them manually for further use
for _, field := range retList.List {
if field.Names != nil {
for _, name := range field.Names {
retVals = append(retVals, dst.NewIdent(name.Name))
}
} else {
retValIdent := dst.NewIdent("retVal" + util.RandomString(5))
field.Names = []*dst.Ident{retValIdent}
retVals = append(retVals, dst.Clone(retValIdent).(*dst.Ident))
}
}
}
// Arguments for onEnter trampoline
args := make([]dst.Expr, 0)
// Receiver as argument for trampoline func, if any
if util.HasReceiver(funcDecl) {
if recv := funcDecl.Recv.List; recv != nil {
receiver := recv[0].Names[0].Name
args = append(args, util.AddressOf(util.Ident(receiver)))
} else {
util.Unimplemented()
}
}
// Original function arguments as arguments for trampoline func
for _, field := range funcDecl.Type.Params.List {
for _, name := range field.Names {
args = append(args, util.AddressOf(util.Ident(name.Name)))
}
}
varSuffix := util.RandomString(5)
rp.rule2Suffix[t] = varSuffix
// Generate the trampoline-jump-if. N.B. Note that future optimization pass
// heavily depends on the structure of trampoline-jump-if. Any change in it
// should be carefully examined.
onEnterCall := util.CallTo(rp.makeName(t, rp.rawFunc, true), args)
onExitCall := util.CallTo(rp.makeName(t, rp.rawFunc, false), func() []dst.Expr {
// NB. DST framework disallows duplicated node in the
// AST tree, we need to replicate the return values
// as they are already used in return statement above
clone := make([]dst.Expr, len(retVals)+1)
clone[0] = util.Ident(TrampolineCallContextName + varSuffix)
for i := 1; i < len(clone); i++ {
clone[i] = util.AddressOf(retVals[i-1])
}
return clone
}())
tjumpInit := util.DefineStmts(
util.Exprs(
util.Ident(TrampolineCallContextName+varSuffix),
util.Ident(TrampolineSkipName+varSuffix),
),
util.Exprs(onEnterCall),
)
tjumpCond := util.Ident(TrampolineSkipName + varSuffix)
tjumpBody := util.BlockStmts(
util.ExprStmt(onExitCall),
util.ReturnStmt(retVals),
)
tjumpElse := util.Block(util.DeferStmt(onExitCall))
tjump := util.IfStmt(tjumpInit, tjumpCond, tjumpBody, tjumpElse)
// Add this trampoline-jump-if as optimization candidates
rp.trampolineJumps = append(rp.trampolineJumps, &TJump{
target: funcDecl,
ifStmt: tjump,
rule: t,
})
// Add label for trampoline-jump-if. Note that the label will be cleared
// during optimization pass, to make it pretty in the generated code
tjump.Decs.If.Append(TJumpLabel)
// Find if there is already a trampoline-jump-if, insert new tjump if so,
// otherwise prepend to block body
found := false
if len(funcDecl.Body.List) > 0 {
firstStmt := funcDecl.Body.List[0]
if ifStmt, ok := firstStmt.(*dst.IfStmt); ok {
point := findJumpPoint(ifStmt)
if point != nil {
point.List = append(point.List, util.EmptyStmt())
point.List = append(point.List, tjump)
found = true
}
}
}
if !found {
// Tag the trampoline-jump-if with a special line directive so that
// debugger can show the correct line number
tjump.Decs.Before = dst.NewLine
tjump.Decs.Start.Append("//line <generated>:1")
pos := rp.parser.FindPosition(funcDecl.Body)
if len(funcDecl.Body.List) > 0 {
// It does happens because we may insert raw code snippets at the
// function entry. These dynamically generated nodes do not have
// corresponding node positions. We need to keep looking downward
// until we find a node that contains position information, and then
// annotate it with a line directive.
for i := 0; i < len(funcDecl.Body.List); i++ {
stmt := funcDecl.Body.List[i]
pos = rp.parser.FindPosition(stmt)
if !pos.IsValid() {
continue
}
tag := fmt.Sprintf("//line %s", pos.String())
stmt.Decorations().Before = dst.NewLine
stmt.Decorations().Start.Append(tag)
}
} else {
pos = rp.parser.FindPosition(funcDecl.Body)
tag := fmt.Sprintf("//line %s", pos.String())
empty := util.EmptyStmt()
empty.Decs.Before = dst.NewLine
empty.Decs.Start.Append(tag)
funcDecl.Body.List = append(funcDecl.Body.List, empty)
}
funcDecl.Body.List = append([]dst.Stmt{tjump}, funcDecl.Body.List...)
}
// Generate corresponding trampoline code
err := rp.generateTrampoline(t)
if err != nil {
return err
}
return nil
}