mirror of
https://github.com/golang/go
synced 2024-11-18 18:14:43 -07:00
cmd/compile/internal/inline: extend flag calculation via export data
Extend the code that computes various properties and parameter flags to incorporate information from export data in addition to things we can get from the current package. Specifically: - when deciding whether the current function always calls panic/exit, check to see whether it has an unconditional call to some other function that has that flag. - when computing "parameter feeds" properties, look not just for cases where a parameter feeds an interesting construct (if/switch, indirect/interface call, etc) but where it feeds a call whose corresponding param has that flag. - when computing return properties, if a given return is always the results of a call to X, then set the return properties to those of X. Updates #61502. Change-Id: I6472fe98759cccad05b8eed58e4fc568201d88ad Reviewed-on: https://go-review.googlesource.com/c/go/+/511563 Reviewed-by: Matthew Dempsky <mdempsky@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
This commit is contained in:
parent
323cf73091
commit
d2024a091d
@ -104,6 +104,17 @@ func runAnalyzersOnFunction(fn *ir.Func, analyzers []propAnalyzer) {
|
||||
doNode(fn)
|
||||
}
|
||||
|
||||
func propsForFunc(fn *ir.Func) *FuncProps {
|
||||
if fih, ok := fpmap[fn]; ok {
|
||||
return fih.props
|
||||
} else if fn.Inl != nil && fn.Inl.Properties != "" {
|
||||
// FIXME: considering adding some sort of cache or table
|
||||
// for deserialized properties of imported functions.
|
||||
return DeserializeFromString(fn.Inl.Properties)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func fnFileLine(fn *ir.Func) (string, uint) {
|
||||
p := base.Ctxt.InnermostPos(fn.Pos())
|
||||
return filepath.Base(p.Filename()), p.Line()
|
||||
@ -176,6 +187,9 @@ func captureFuncDumpEntry(fn *ir.Func, canInline func(*ir.Func)) {
|
||||
} else {
|
||||
AnalyzeFunc(fn, canInline)
|
||||
fih = fpmap[fn]
|
||||
if fn.Inl != nil && fn.Inl.Properties == "" {
|
||||
fn.Inl.Properties = fih.props.SerializeToString()
|
||||
}
|
||||
}
|
||||
if dumpBuffer == nil {
|
||||
dumpBuffer = make(map[*ir.Func]fnInlHeur)
|
||||
|
@ -175,9 +175,11 @@ func isExitCall(n ir.Node) bool {
|
||||
isWellKnownFunc(s, "runtime", "throw") {
|
||||
return true
|
||||
}
|
||||
// FIXME: consult results of flags computation for
|
||||
// previously analyzed Go functions, including props
|
||||
// read from export data for functions in other packages.
|
||||
if fp := propsForFunc(name.Func); fp != nil {
|
||||
if fp.Flags&FuncPropNeverReturns != 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
|
@ -106,21 +106,35 @@ func (pa *paramsAnalyzer) setResults(fp *FuncProps) {
|
||||
fp.ParamFlags = pa.values
|
||||
}
|
||||
|
||||
func (pa *paramsAnalyzer) findParamIdx(n *ir.Name) int {
|
||||
if n == nil {
|
||||
panic("bad")
|
||||
}
|
||||
for i := range pa.params {
|
||||
if pa.params[i] == n {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
type testfType func(x ir.Node, param *ir.Name, idx int) (bool, bool)
|
||||
|
||||
// paramsAnalyzer invokes function 'testf' on the specified expression
|
||||
// 'x' for each parameter, and if the result is TRUE, or's 'flag' into
|
||||
// the flags for that param.
|
||||
func (pa *paramsAnalyzer) checkParams(x ir.Node, flag ParamPropBits, mayflag ParamPropBits, testf func(x ir.Node, param *ir.Name) bool) {
|
||||
func (pa *paramsAnalyzer) checkParams(x ir.Node, flag ParamPropBits, mayflag ParamPropBits, testf testfType) {
|
||||
for idx, p := range pa.params {
|
||||
if !pa.top[idx] && pa.values[idx] == ParamNoInfo {
|
||||
continue
|
||||
}
|
||||
result := testf(x, p)
|
||||
result, may := testf(x, p, idx)
|
||||
if debugTrace&debugTraceParams != 0 {
|
||||
fmt.Fprintf(os.Stderr, "=-= test expr %v param %s result=%v flag=%s\n", x, p.Sym().Name, result, flag.String())
|
||||
}
|
||||
if result {
|
||||
v := flag
|
||||
if pa.condLevel != 0 {
|
||||
if pa.condLevel != 0 || may {
|
||||
v = mayflag
|
||||
}
|
||||
pa.values[idx] |= v
|
||||
@ -134,8 +148,8 @@ func (pa *paramsAnalyzer) checkParams(x ir.Node, flag ParamPropBits, mayflag Par
|
||||
// specific parameter had a constant value.
|
||||
func (pa *paramsAnalyzer) foldCheckParams(x ir.Node) {
|
||||
pa.checkParams(x, ParamFeedsIfOrSwitch, ParamMayFeedIfOrSwitch,
|
||||
func(x ir.Node, p *ir.Name) bool {
|
||||
return ShouldFoldIfNameConstant(x, []*ir.Name{p})
|
||||
func(x ir.Node, p *ir.Name, idx int) (bool, bool) {
|
||||
return ShouldFoldIfNameConstant(x, []*ir.Name{p}), false
|
||||
})
|
||||
}
|
||||
|
||||
@ -158,9 +172,9 @@ func (pa *paramsAnalyzer) callCheckParams(ce *ir.CallExpr) {
|
||||
}
|
||||
pa.checkParams(r, ParamFeedsInterfaceMethodCall,
|
||||
ParamMayFeedInterfaceMethodCall,
|
||||
func(x ir.Node, p *ir.Name) bool {
|
||||
func(x ir.Node, p *ir.Name, idx int) (bool, bool) {
|
||||
name := x.(*ir.Name)
|
||||
return name == p
|
||||
return name == p, false
|
||||
})
|
||||
case ir.OCALLFUNC:
|
||||
if ce.X.Op() != ir.ONAME {
|
||||
@ -171,15 +185,89 @@ func (pa *paramsAnalyzer) callCheckParams(ce *ir.CallExpr) {
|
||||
return
|
||||
}
|
||||
name := called.(*ir.Name)
|
||||
if name.Class == ir.PPARAM {
|
||||
pa.checkParams(called, ParamFeedsIndirectCall,
|
||||
ParamMayFeedIndirectCall,
|
||||
func(x ir.Node, p *ir.Name, idx int) (bool, bool) {
|
||||
name := x.(*ir.Name)
|
||||
return name == p, false
|
||||
})
|
||||
} else {
|
||||
cname, isFunc, _ := isFuncName(called)
|
||||
if isFunc {
|
||||
pa.deriveFlagsFromCallee(ce, cname.Func)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// deriveFlagsFromCallee tries to derive flags for the current
|
||||
// function based on a call this function makes to some other
|
||||
// function. Example:
|
||||
//
|
||||
// /* Simple */ /* Derived from callee */
|
||||
// func foo(f func(int)) { func foo(f func(int)) {
|
||||
// f(2) bar(32, f)
|
||||
// } }
|
||||
// func bar(x int, f func()) {
|
||||
// f(x)
|
||||
// }
|
||||
//
|
||||
// Here we can set the "param feeds indirect call" flag for
|
||||
// foo's param 'f' since we know that bar has that flag set for
|
||||
// its second param, and we're passing that param a function.
|
||||
func (pa *paramsAnalyzer) deriveFlagsFromCallee(ce *ir.CallExpr, callee *ir.Func) {
|
||||
calleeProps := propsForFunc(callee)
|
||||
if calleeProps == nil {
|
||||
return
|
||||
}
|
||||
if debugTrace&debugTraceParams != 0 {
|
||||
fmt.Fprintf(os.Stderr, "=-= callee props for %v:\n%s",
|
||||
callee.Sym().Name, calleeProps.String())
|
||||
}
|
||||
|
||||
must := []ParamPropBits{ParamFeedsInterfaceMethodCall, ParamFeedsIndirectCall, ParamFeedsIfOrSwitch}
|
||||
may := []ParamPropBits{ParamMayFeedInterfaceMethodCall, ParamMayFeedIndirectCall, ParamMayFeedIfOrSwitch}
|
||||
|
||||
for pidx, arg := range ce.Args {
|
||||
// Does the callee param have any interesting properties?
|
||||
// If not we can skip this one.
|
||||
pflag := calleeProps.ParamFlags[pidx]
|
||||
if pflag == 0 {
|
||||
continue
|
||||
}
|
||||
// See if one of the caller's parameters is flowing unmodified
|
||||
// into this actual expression.
|
||||
r := ir.StaticValue(arg)
|
||||
if r.Op() != ir.ONAME {
|
||||
return
|
||||
}
|
||||
name := r.(*ir.Name)
|
||||
if name.Class != ir.PPARAM {
|
||||
return
|
||||
}
|
||||
pa.checkParams(called, ParamFeedsIndirectCall,
|
||||
ParamMayFeedIndirectCall,
|
||||
func(x ir.Node, p *ir.Name) bool {
|
||||
name := x.(*ir.Name)
|
||||
return name == p
|
||||
})
|
||||
callerParamIdx := pa.findParamIdx(name)
|
||||
if callerParamIdx == -1 || pa.params[callerParamIdx] == nil {
|
||||
panic("something went wrong")
|
||||
}
|
||||
if !pa.top[callerParamIdx] &&
|
||||
pa.values[callerParamIdx] == ParamNoInfo {
|
||||
continue
|
||||
}
|
||||
if debugTrace&debugTraceParams != 0 {
|
||||
fmt.Fprintf(os.Stderr, "=-= pflag for arg %d is %s\n",
|
||||
pidx, pflag.String())
|
||||
}
|
||||
for i := range must {
|
||||
mayv := may[i]
|
||||
mustv := must[i]
|
||||
if pflag&mustv != 0 && pa.condLevel == 0 {
|
||||
pa.values[callerParamIdx] |= mustv
|
||||
} else if pflag&(mustv|mayv) != 0 {
|
||||
pa.values[callerParamIdx] |= mayv
|
||||
}
|
||||
}
|
||||
pa.top[callerParamIdx] = false
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -28,10 +28,11 @@ type returnsAnalyzer struct {
|
||||
// the same function, etc. This container stores info on a the specific
|
||||
// scenarios we're looking for.
|
||||
type resultVal struct {
|
||||
lit constant.Value
|
||||
fn *ir.Name
|
||||
fnClo bool
|
||||
top bool
|
||||
lit constant.Value
|
||||
fn *ir.Name
|
||||
fnClo bool
|
||||
top bool
|
||||
derived bool // see deriveReturnFlagsFromCallee below
|
||||
}
|
||||
|
||||
func makeResultsAnalyzer(fn *ir.Func, canInline func(*ir.Func)) *returnsAnalyzer {
|
||||
@ -62,7 +63,7 @@ func makeResultsAnalyzer(fn *ir.Func, canInline func(*ir.Func)) *returnsAnalyzer
|
||||
func (ra *returnsAnalyzer) setResults(fp *FuncProps) {
|
||||
// Promote ResultAlwaysSameFunc to ResultAlwaysSameInlinableFunc
|
||||
for i := range ra.values {
|
||||
if ra.props[i] == ResultAlwaysSameFunc {
|
||||
if ra.props[i] == ResultAlwaysSameFunc && !ra.values[i].derived {
|
||||
f := ra.values[i].fn.Func
|
||||
// If the function being returns is a closure that hasn't
|
||||
// yet been checked by CanInline, invoke it now. NB: this
|
||||
@ -149,6 +150,7 @@ func (ra *returnsAnalyzer) analyzeResult(ii int, n ir.Node) {
|
||||
lit, isConst := isLiteral(n)
|
||||
rfunc, isFunc, isClo := isFuncName(n)
|
||||
curp := ra.props[ii]
|
||||
dprops, isDerivedFromCall := deriveReturnFlagsFromCallee(n)
|
||||
newp := ResultNoInfo
|
||||
var newlit constant.Value
|
||||
var newfunc *ir.Name
|
||||
@ -172,30 +174,35 @@ func (ra *returnsAnalyzer) analyzeResult(ii int, n ir.Node) {
|
||||
case isConst:
|
||||
newp = ResultAlwaysSameConstant
|
||||
newlit = lit
|
||||
case isDerivedFromCall:
|
||||
newp = dprops
|
||||
ra.values[ii].derived = true
|
||||
}
|
||||
} else {
|
||||
// this is not the first return we've seen; apply
|
||||
// what amounts of a "meet" operator to combine
|
||||
// the properties we see here with what we saw on
|
||||
// the previous returns.
|
||||
switch curp {
|
||||
case ResultIsAllocatedMem:
|
||||
if isAllocatedMem(n) {
|
||||
newp = ResultIsAllocatedMem
|
||||
}
|
||||
case ResultIsConcreteTypeConvertedToInterface:
|
||||
if isConcreteConvIface(n) {
|
||||
newp = ResultIsConcreteTypeConvertedToInterface
|
||||
}
|
||||
case ResultAlwaysSameConstant:
|
||||
if isConst && isSameLiteral(lit, ra.values[ii].lit) {
|
||||
newp = ResultAlwaysSameConstant
|
||||
newlit = lit
|
||||
}
|
||||
case ResultAlwaysSameFunc:
|
||||
if isFunc && isSameFuncName(rfunc, ra.values[ii].fn) {
|
||||
newp = ResultAlwaysSameFunc
|
||||
newfunc = rfunc
|
||||
if !ra.values[ii].derived {
|
||||
// this is not the first return we've seen; apply
|
||||
// what amounts of a "meet" operator to combine
|
||||
// the properties we see here with what we saw on
|
||||
// the previous returns.
|
||||
switch curp {
|
||||
case ResultIsAllocatedMem:
|
||||
if isAllocatedMem(n) {
|
||||
newp = ResultIsAllocatedMem
|
||||
}
|
||||
case ResultIsConcreteTypeConvertedToInterface:
|
||||
if isConcreteConvIface(n) {
|
||||
newp = ResultIsConcreteTypeConvertedToInterface
|
||||
}
|
||||
case ResultAlwaysSameConstant:
|
||||
if isConst && isSameLiteral(lit, ra.values[ii].lit) {
|
||||
newp = ResultAlwaysSameConstant
|
||||
newlit = lit
|
||||
}
|
||||
case ResultAlwaysSameFunc:
|
||||
if isFunc && isSameFuncName(rfunc, ra.values[ii].fn) {
|
||||
newp = ResultAlwaysSameFunc
|
||||
newfunc = rfunc
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -208,7 +215,6 @@ func (ra *returnsAnalyzer) analyzeResult(ii int, n ir.Node) {
|
||||
fmt.Fprintf(os.Stderr, "=-= %v: analyzeResult newp=%s\n",
|
||||
ir.Line(n), newp)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func isAllocatedMem(n ir.Node) bool {
|
||||
@ -220,6 +226,48 @@ func isAllocatedMem(n ir.Node) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// deriveReturnFlagsFromCallee tries to set properties for a given
|
||||
// return result where we're returning call expression; return value
|
||||
// is a return property value and a boolean indicating whether the
|
||||
// prop is valid. Examples:
|
||||
//
|
||||
// func foo() int { return bar() }
|
||||
// func bar() int { return 42 }
|
||||
// func blix() int { return 43 }
|
||||
// func two(y int) int {
|
||||
// if y < 0 { return bar() } else { return blix() }
|
||||
// }
|
||||
//
|
||||
// Since "foo" always returns the result of a call to "bar", we can
|
||||
// set foo's return property to that of bar. In the case of "two", however,
|
||||
// even though each return path returns a constant, we don't know
|
||||
// whether the constants are identical, hence we need to be conservative.
|
||||
func deriveReturnFlagsFromCallee(n ir.Node) (ResultPropBits, bool) {
|
||||
if n.Op() != ir.OCALLFUNC {
|
||||
return 0, false
|
||||
}
|
||||
ce := n.(*ir.CallExpr)
|
||||
if ce.X.Op() != ir.ONAME {
|
||||
return 0, false
|
||||
}
|
||||
called := ir.StaticValue(ce.X)
|
||||
if called.Op() != ir.ONAME {
|
||||
return 0, false
|
||||
}
|
||||
cname, isFunc, _ := isFuncName(called)
|
||||
if !isFunc {
|
||||
return 0, false
|
||||
}
|
||||
calleeProps := propsForFunc(cname.Func)
|
||||
if calleeProps == nil {
|
||||
return 0, false
|
||||
}
|
||||
if len(calleeProps.ResultFlags) != 1 {
|
||||
return 0, false
|
||||
}
|
||||
return calleeProps.ResultFlags[0], true
|
||||
}
|
||||
|
||||
func isLiteral(n ir.Node) (constant.Value, bool) {
|
||||
sv := ir.StaticValue(n)
|
||||
switch sv.Op() {
|
||||
|
@ -22,9 +22,9 @@ var remasterflag = flag.Bool("update-expected", false, "if true, generate update
|
||||
|
||||
func TestFuncProperties(t *testing.T) {
|
||||
td := t.TempDir()
|
||||
//td = "/tmp/qqq"
|
||||
//os.RemoveAll(td)
|
||||
//os.Mkdir(td, 0777)
|
||||
// td = "/tmp/qqq"
|
||||
// os.RemoveAll(td)
|
||||
// os.Mkdir(td, 0777)
|
||||
testenv.MustHaveGoBuild(t)
|
||||
|
||||
// NOTE: this testpoint has the unfortunate characteristic that it
|
||||
@ -35,7 +35,7 @@ func TestFuncProperties(t *testing.T) {
|
||||
// to building a fresh compiler on the fly, or using some other
|
||||
// scheme.
|
||||
|
||||
testcases := []string{"funcflags", "returns", "params"}
|
||||
testcases := []string{"funcflags", "returns", "params", "acrosscall"}
|
||||
|
||||
for _, tc := range testcases {
|
||||
dumpfile, err := gatherPropsDumpForFile(t, tc, td)
|
||||
|
189
src/cmd/compile/internal/inline/inlheur/testdata/props/acrosscall.go
vendored
Normal file
189
src/cmd/compile/internal/inline/inlheur/testdata/props/acrosscall.go
vendored
Normal file
@ -0,0 +1,189 @@
|
||||
// Copyright 2023 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// DO NOT EDIT (use 'go test -v -update-expected' instead.)
|
||||
// See cmd/compile/internal/inline/inlheur/testdata/props/README.txt
|
||||
// for more information on the format of this file.
|
||||
// <endfilepreamble>
|
||||
package params
|
||||
|
||||
// acrosscall.go T_feeds_indirect_call_via_call_toplevel 17 0 1
|
||||
// ParamFlags
|
||||
// 0 ParamFeedsIndirectCall
|
||||
// <endpropsdump>
|
||||
// {"Flags":0,"ParamFlags":[8],"ResultFlags":[]}
|
||||
// <endfuncpreamble>
|
||||
func T_feeds_indirect_call_via_call_toplevel(f func(int)) {
|
||||
callsparam(f)
|
||||
}
|
||||
|
||||
// acrosscall.go T_feeds_indirect_call_via_call_conditional 27 0 1
|
||||
// ParamFlags
|
||||
// 0 ParamMayFeedIndirectCall
|
||||
// <endpropsdump>
|
||||
// {"Flags":0,"ParamFlags":[16],"ResultFlags":[]}
|
||||
// <endfuncpreamble>
|
||||
func T_feeds_indirect_call_via_call_conditional(f func(int)) {
|
||||
if G != 101 {
|
||||
callsparam(f)
|
||||
}
|
||||
}
|
||||
|
||||
// acrosscall.go T_feeds_conditional_indirect_call_via_call_toplevel 39 0 1
|
||||
// ParamFlags
|
||||
// 0 ParamMayFeedIndirectCall
|
||||
// <endpropsdump>
|
||||
// {"Flags":0,"ParamFlags":[16],"ResultFlags":[]}
|
||||
// <endfuncpreamble>
|
||||
func T_feeds_conditional_indirect_call_via_call_toplevel(f func(int)) {
|
||||
callsparamconditional(f)
|
||||
}
|
||||
|
||||
// acrosscall.go T_feeds_if_via_call 49 0 1
|
||||
// ParamFlags
|
||||
// 0 ParamFeedsIfOrSwitch
|
||||
// <endpropsdump>
|
||||
// {"Flags":0,"ParamFlags":[32],"ResultFlags":[]}
|
||||
// <endfuncpreamble>
|
||||
func T_feeds_if_via_call(x int) {
|
||||
feedsif(x)
|
||||
}
|
||||
|
||||
// acrosscall.go T_feeds_if_via_call_conditional 59 0 1
|
||||
// ParamFlags
|
||||
// 0 ParamMayFeedIfOrSwitch
|
||||
// <endpropsdump>
|
||||
// {"Flags":0,"ParamFlags":[64],"ResultFlags":[]}
|
||||
// <endfuncpreamble>
|
||||
func T_feeds_if_via_call_conditional(x int) {
|
||||
if G != 101 {
|
||||
feedsif(x)
|
||||
}
|
||||
}
|
||||
|
||||
// acrosscall.go T_feeds_conditional_if_via_call 71 0 1
|
||||
// ParamFlags
|
||||
// 0 ParamMayFeedIfOrSwitch
|
||||
// <endpropsdump>
|
||||
// {"Flags":0,"ParamFlags":[64],"ResultFlags":[]}
|
||||
// <endfuncpreamble>
|
||||
func T_feeds_conditional_if_via_call(x int) {
|
||||
feedsifconditional(x)
|
||||
}
|
||||
|
||||
// acrosscall.go T_multifeeds 82 0 1
|
||||
// ParamFlags
|
||||
// 0 ParamFeedsIndirectCall|ParamMayFeedIndirectCall
|
||||
// 1 ParamFeedsIndirectCall
|
||||
// <endpropsdump>
|
||||
// {"Flags":0,"ParamFlags":[24,8],"ResultFlags":[]}
|
||||
// <endfuncpreamble>
|
||||
func T_multifeeds(f1, f2 func(int)) {
|
||||
callsparam(f1)
|
||||
callsparamconditional(f1)
|
||||
callsparam(f2)
|
||||
}
|
||||
|
||||
// acrosscall.go T_acrosscall_returnsconstant 94 0 1
|
||||
// ResultFlags
|
||||
// 0 ResultAlwaysSameConstant
|
||||
// <endpropsdump>
|
||||
// {"Flags":0,"ParamFlags":[],"ResultFlags":[8]}
|
||||
// <endfuncpreamble>
|
||||
func T_acrosscall_returnsconstant() int {
|
||||
return returnsconstant()
|
||||
}
|
||||
|
||||
// acrosscall.go T_acrosscall_returnsmem 104 0 1
|
||||
// ResultFlags
|
||||
// 0 ResultIsAllocatedMem
|
||||
// <endpropsdump>
|
||||
// {"Flags":0,"ParamFlags":[],"ResultFlags":[2]}
|
||||
// <endfuncpreamble>
|
||||
func T_acrosscall_returnsmem() *int {
|
||||
return returnsmem()
|
||||
}
|
||||
|
||||
// acrosscall.go T_acrosscall_returnscci 114 0 1
|
||||
// ResultFlags
|
||||
// 0 ResultIsConcreteTypeConvertedToInterface
|
||||
// <endpropsdump>
|
||||
// {"Flags":0,"ParamFlags":[],"ResultFlags":[4]}
|
||||
// <endfuncpreamble>
|
||||
func T_acrosscall_returnscci() I {
|
||||
return returnscci()
|
||||
}
|
||||
|
||||
// acrosscall.go T_acrosscall_multiret 122 0 1
|
||||
// <endpropsdump>
|
||||
// {"Flags":0,"ParamFlags":[0],"ResultFlags":[0]}
|
||||
// <endfuncpreamble>
|
||||
func T_acrosscall_multiret(q int) int {
|
||||
if q != G {
|
||||
return returnsconstant()
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// acrosscall.go T_acrosscall_multiret2 133 0 1
|
||||
// <endpropsdump>
|
||||
// {"Flags":0,"ParamFlags":[0],"ResultFlags":[0]}
|
||||
// <endfuncpreamble>
|
||||
func T_acrosscall_multiret2(q int) int {
|
||||
if q == G {
|
||||
return returnsconstant()
|
||||
} else {
|
||||
return returnsconstant()
|
||||
}
|
||||
}
|
||||
|
||||
func callsparam(f func(int)) {
|
||||
f(2)
|
||||
}
|
||||
|
||||
func callsparamconditional(f func(int)) {
|
||||
if G != 101 {
|
||||
f(2)
|
||||
}
|
||||
}
|
||||
|
||||
func feedsif(x int) int {
|
||||
if x != 101 {
|
||||
return 42
|
||||
}
|
||||
return 43
|
||||
}
|
||||
|
||||
func feedsifconditional(x int) int {
|
||||
if G != 101 {
|
||||
if x != 101 {
|
||||
return 42
|
||||
}
|
||||
}
|
||||
return 43
|
||||
}
|
||||
|
||||
func returnsconstant() int {
|
||||
return 42
|
||||
}
|
||||
|
||||
func returnsmem() *int {
|
||||
return new(int)
|
||||
}
|
||||
|
||||
func returnscci() I {
|
||||
var q Q
|
||||
return q
|
||||
}
|
||||
|
||||
type I interface {
|
||||
Foo()
|
||||
}
|
||||
|
||||
type Q int
|
||||
|
||||
func (q Q) Foo() {
|
||||
}
|
||||
|
||||
var G int
|
@ -304,6 +304,15 @@ func T_select_mayreturn(chi chan int, chf chan float32, p *int) int {
|
||||
panic("bad")
|
||||
}
|
||||
|
||||
// funcflags.go T_calls_callsexit 291 0 1
|
||||
// Flags FuncPropNeverReturns
|
||||
// <endpropsdump>
|
||||
// {"Flags":1,"ParamFlags":[0],"ResultFlags":[]}
|
||||
// <endfuncpreamble>
|
||||
func T_calls_callsexit(x int) {
|
||||
exprcallsexit(x)
|
||||
}
|
||||
|
||||
func exprcallsexit(x int) int {
|
||||
os.Exit(x)
|
||||
return x
|
||||
|
Loading…
Reference in New Issue
Block a user