go/ssa: lift structs, arrays and typeparams
Lift locals with underlying types of struct, array and typeparam.
Changes to vta tests are due to the tests being sensitive to register names.
Change-Id: I558c0e6897b62f93ae1e700307e6e1e4dea008d0
Reviewed-on: https://go-review.googlesource.com/c/tools/+/451996
Run-TryBot: Tim King <taking@google.com>
Commit-Queue: Tim King <taking@google.com>
gopls-CI: kokoro <noreply+kokoro@google.com>
Reviewed-by: Alan Donovan <adonovan@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Zvonimir Pavlinovic <zpavlinovic@google.com>
diff --git a/go/callgraph/vta/graph_test.go b/go/callgraph/vta/graph_test.go
index 8b8c697..da574d7 100644
--- a/go/callgraph/vta/graph_test.go
+++ b/go/callgraph/vta/graph_test.go
@@ -152,21 +152,21 @@
return vgs
}
-// subGraph checks if a graph `g1` is a subgraph of graph `g2`.
-// Assumes that each element in `g1` and `g2` is an edge set
-// for a particular node in a fixed yet arbitrary format.
-func subGraph(g1, g2 []string) bool {
- m := make(map[string]bool)
- for _, s := range g2 {
- m[s] = true
+// setdiff returns the set difference of `X-Y` or {s | s ∈ X, s ∉ Y }.
+func setdiff(X, Y []string) []string {
+ y := make(map[string]bool)
+ var delta []string
+ for _, s := range Y {
+ y[s] = true
}
- for _, s := range g1 {
- if _, ok := m[s]; !ok {
- return false
+ for _, s := range X {
+ if _, ok := y[s]; !ok {
+ delta = append(delta, s)
}
}
- return true
+ sort.Strings(delta)
+ return delta
}
func TestVTAGraphConstruction(t *testing.T) {
@@ -201,8 +201,9 @@
}
g, _ := typePropGraph(ssautil.AllFunctions(prog), cha.CallGraph(prog))
- if gs := vtaGraphStr(g); !subGraph(want, gs) {
- t.Errorf("`%s`: want superset of %v;\n got %v", file, want, gs)
+ got := vtaGraphStr(g)
+ if diff := setdiff(want, got); len(diff) > 0 {
+ t.Errorf("`%s`: want superset of %v;\n got %v\ndiff: %v", file, want, got, diff)
}
})
}
diff --git a/go/callgraph/vta/testdata/src/callgraph_collections.go b/go/callgraph/vta/testdata/src/callgraph_collections.go
index bc418e3..6177a32 100644
--- a/go/callgraph/vta/testdata/src/callgraph_collections.go
+++ b/go/callgraph/vta/testdata/src/callgraph_collections.go
@@ -37,31 +37,5 @@
x[len(x)-1].Foo()
}
-// Relevant SSA:
-// func Baz(a A, b B):
-// ...
-// t4 = Do(t2, t3)
-// t5 = range t4
-// jump 1
-// 1:
-// t6 = phi [0: nil:[]I, 2: t16] #x
-// t7 = next t5
-// t8 = extract t7 #0
-// if t8 goto 2 else 3
-// 2:
-// t9 = extract t7 #1
-// t10 = extract t7 #2
-// t11 = invoke t9.Foo()
-// t12 = invoke t10.Foo()
-// ...
-// jump 1
-// 3:
-// t17 = len(t6)
-// t18 = t17 - 1:int
-// t19 = &t6[t18]
-// t20 = *t19
-// t21 = invoke t20.Foo()
-// return
-
// WANT:
-// Baz: Do(t2, t3) -> Do; invoke t10.Foo() -> B.Foo; invoke t20.Foo() -> A.Foo, B.Foo; invoke t9.Foo() -> A.Foo, B.Foo
+// Baz: Do(a, b) -> Do; invoke t16.Foo() -> A.Foo, B.Foo; invoke t5.Foo() -> A.Foo, B.Foo; invoke t6.Foo() -> B.Foo
diff --git a/go/callgraph/vta/testdata/src/callgraph_fields.go b/go/callgraph/vta/testdata/src/callgraph_fields.go
index 00aa649..9149bdd 100644
--- a/go/callgraph/vta/testdata/src/callgraph_fields.go
+++ b/go/callgraph/vta/testdata/src/callgraph_fields.go
@@ -31,61 +31,6 @@
a.Do()
}
-// Relevant SSA:
-// func Baz(b B):
-// t0 = local B (b)
-// *t0 = b
-// t1 = *t0
-// t2 = NewA(t1)
-// t3 = (*A).Do(t2)
-// return
-//
-// func (a *A) Do():
-// t0 = &a.I [#0]
-// t1 = *t0
-// t2 = invoke t1.Foo()
-// return
-//
-// Name: (testdata.A).Foo
-// Synthetic: wrapper for func (testdata.I).Foo()
-// Location: testdata/callgraph_fields.go:10:2
-// func (arg0 testdata.A) Foo():
-// t0 = local testdata.A ()
-// *t0 = arg0
-// t1 = &t0.I [#0]
-// t2 = *t1
-// t3 = invoke t2.Foo()
-// return
-//
-// Name: (*testdata.A).Foo
-// Synthetic: wrapper for func (testdata.I).Foo()
-// Location: testdata/callgraph_fields.go:10:2
-// func (arg0 *testdata.A) Foo():
-// t0 = &arg0.I [#0]
-// t1 = *t0
-// t2 = invoke t1.Foo()
-// return
-//
-// func (b B) Foo():
-// t0 = local B (b)
-// *t0 = b
-// return
-//
-// func (b *testdata.B) Foo():
-// t0 = ssa:wrapnilchk(b, "testdata.B":string, "Foo":string)
-// t1 = *t0
-// t2 = (testdata.B).Foo(t1)
-// return
-//
-// func NewA(b B) *A:
-// t0 = new B (b)
-// *t0 = b
-// t1 = new A (complit)
-// t2 = &t1.I [#0]
-// t3 = make I <- *B (t0)
-// *t2 = t3
-// return t1
-
// WANT:
-// Baz: (*A).Do(t2) -> A.Do; NewA(t1) -> NewA
+// Baz: (*A).Do(t0) -> A.Do; NewA(b) -> NewA
// A.Do: invoke t1.Foo() -> B.Foo
diff --git a/go/callgraph/vta/testdata/src/callgraph_generics.go b/go/callgraph/vta/testdata/src/callgraph_generics.go
index da3dca5..a0ac8fd 100644
--- a/go/callgraph/vta/testdata/src/callgraph_generics.go
+++ b/go/callgraph/vta/testdata/src/callgraph_generics.go
@@ -39,33 +39,24 @@
// Relevant SSA:
//func Foo(a A, b B):
-// t0 = local A (a)
-// *t0 = a
-// t1 = local B (b)
-// *t1 = b
-// t2 = new bool (x)
-// *t2 = true:bool
-// t3 = instantiated[bool](t2)
-// t4 = new int (y)
-// *t4 = 1:int
-// t5 = instantiated[int](t4)
-// t6 = *t0
-// t7 = interfaceInstantiated[testdata.A](t6)
-// t8 = *t1
-// t9 = interfaceInstantiated[testdata.B](t8)
+// t0 = new bool (x)
+// *t0 = true:bool
+// t1 = instantiated[bool](t2)
+// t1 = new int (y)
+// *t2 = 1:int
+// t3 = instantiated[[int]](t4)
+// t4 = interfaceInstantiated[testdata.A](a)
+// t5 = interfaceInstantiated[testdata.B](b)
// return
//
-//func interfaceInstantiated[testdata.B](x B):
-// t0 = local B (x)
-// *t0 = x
-// t1 = *t0
-// t2 = (B).Bar(t1)
+//func interfaceInstantiated[[testdata.B]](x B):
+// t0 = (B).Bar(b)
// return
//
//func interfaceInstantiated[X I](x X):
// (external)
// WANT:
-// Foo: instantiated[bool](t2) -> instantiated[bool]; instantiated[int](t4) -> instantiated[int]; interfaceInstantiated[testdata.A](t6) -> interfaceInstantiated[testdata.A]; interfaceInstantiated[testdata.B](t8) -> interfaceInstantiated[testdata.B]
-// interfaceInstantiated[testdata.B]: (B).Bar(t1) -> B.Bar
-// interfaceInstantiated[testdata.A]: (A).Bar(t1) -> A.Bar
+// Foo: instantiated[bool](t0) -> instantiated[bool]; instantiated[int](t2) -> instantiated[int]; interfaceInstantiated[testdata.A](a) -> interfaceInstantiated[testdata.A]; interfaceInstantiated[testdata.B](b) -> interfaceInstantiated[testdata.B]
+// interfaceInstantiated[testdata.B]: (B).Bar(x) -> B.Bar
+// interfaceInstantiated[testdata.A]: (A).Bar(x) -> A.Bar
diff --git a/go/callgraph/vta/testdata/src/callgraph_interfaces.go b/go/callgraph/vta/testdata/src/callgraph_interfaces.go
index 123468c..c272ff0 100644
--- a/go/callgraph/vta/testdata/src/callgraph_interfaces.go
+++ b/go/callgraph/vta/testdata/src/callgraph_interfaces.go
@@ -49,13 +49,11 @@
// func Do(b bool) I:
// ...
-// t3 = local C (c)
-// t4 = *t3
-// t5 = (C).Foo(t4)
-// t6 = NewB()
-// t7 = make I <- B (t6)
-// return t7
+// t1 = (C).Foo(struct{}{}:C)
+// t2 = NewB()
+// t3 = make I <- B (t2)
+// return t3
// WANT:
// Baz: Do(b) -> Do; invoke t0.Foo() -> A.Foo, B.Foo
-// Do: (C).Foo(t4) -> C.Foo; NewB() -> NewB
+// Do: (C).Foo(struct{}{}:C) -> C.Foo; NewB() -> NewB
diff --git a/go/callgraph/vta/testdata/src/callgraph_pointers.go b/go/callgraph/vta/testdata/src/callgraph_pointers.go
index e07f969..ae8fe43 100644
--- a/go/callgraph/vta/testdata/src/callgraph_pointers.go
+++ b/go/callgraph/vta/testdata/src/callgraph_pointers.go
@@ -35,37 +35,9 @@
(*x).Foo()
}
-// Relevant SSA:
-// func Baz(a A, b B, c bool):
-// t0 = local A (a)
-// ...
-// t5 = Do(t2, t4, c)
-// t6 = *t5
-// t7 = invoke t6.Foo()
-// return
-
-// func Do(a A, i I, c bool) *I:
-// t0 = local A (a)
-// *t0 = a
-// ...
-// if c goto 1 else 3
-// 1:
-// t2 = &t0.f [#0]
-// ...
-// jump 2
-// 2:
-// t6 = &t0.f [#0]
-// ...
-// t9 = invoke t8.Foo()
-// return t1
-// 3:
-// t10 = &t0.f [#0] alias between A.f and t10
-// *t10 = t1 alias between t10 and t1
-// jump 2
-
// The command a.f = &i introduces aliasing that results in
// A and B reaching both *A.f and return value of Do(a, b, c).
// WANT:
-// Baz: Do(t2, t4, c) -> Do; invoke t6.Foo() -> A.Foo, B.Foo
+// Baz: Do(a, t0, c) -> Do; invoke t2.Foo() -> A.Foo, B.Foo
// Do: invoke t8.Foo() -> A.Foo, B.Foo
diff --git a/go/callgraph/vta/testdata/src/callgraph_static.go b/go/callgraph/vta/testdata/src/callgraph_static.go
index 1ed904f..147ed46 100644
--- a/go/callgraph/vta/testdata/src/callgraph_static.go
+++ b/go/callgraph/vta/testdata/src/callgraph_static.go
@@ -20,11 +20,9 @@
// Relevant SSA:
// func Baz(a A):
-// ...
-// t2 = (A).foo(t1)
-// t3 = Bar()
-// ...
-// t6 = Baz(t5)
+// t0 = (A).foo(a)
+// t1 = Bar()
+// t2 = Baz(struct{}{}:A)
// WANT:
-// Baz: (A).foo(t1) -> A.foo; Bar() -> Bar; Baz(t5) -> Baz
+// Baz: (A).foo(a) -> A.foo; Bar() -> Bar; Baz(struct{}{}:A) -> Baz
diff --git a/go/callgraph/vta/testdata/src/dynamic_calls.go b/go/callgraph/vta/testdata/src/dynamic_calls.go
index b8c14b2..f8f8898 100644
--- a/go/callgraph/vta/testdata/src/dynamic_calls.go
+++ b/go/callgraph/vta/testdata/src/dynamic_calls.go
@@ -27,23 +27,22 @@
return h()
}
+var g *B = &B{} // ensure *B.foo is created.
+
// Relevant SSA:
// func Baz(x B, h func() I, i I) I:
-// t0 = local B (x)
-// *t0 = x
-// t1 = *t0
-// t2 = make I <- B (t1)
-// t3 = invoke i.foo(t2)
-// t4 = h()
-// return t4
+// t0 = make I <- B (x)
+// t1 = invoke i.foo(t0)
+// t2 = h()
+// return t2
-// Local(t2) has seemingly duplicates of successors. This
+// Local(t0) has seemingly duplicates of successors. This
// happens in stringification of type propagation graph.
// Due to CHA, we analyze A.foo and *A.foo as well as B.foo
// and *B.foo, which have similar bodies and hence similar
// type flow that gets merged together during stringification.
// WANT:
-// Local(t2) -> Local(ai), Local(ai), Local(bi), Local(bi)
-// Constant(testdata.I) -> Local(t4)
-// Local(t1) -> Local(t2)
+// Local(t0) -> Local(ai), Local(ai), Local(bi), Local(bi)
+// Constant(testdata.I) -> Local(t2)
+// Local(x) -> Local(t0)
diff --git a/go/callgraph/vta/testdata/src/maps.go b/go/callgraph/vta/testdata/src/maps.go
index b7354dc..f5f51a3 100644
--- a/go/callgraph/vta/testdata/src/maps.go
+++ b/go/callgraph/vta/testdata/src/maps.go
@@ -30,23 +30,16 @@
// Relevant SSA:
// func Baz(m map[I]I, b1 B, b2 B, n map[string]*J) *J:
-// t0 = local B (b1)
-// *t0 = b1
-// t1 = local B (b2)
-// *t1 = b2
-// t2 = *t0
-// t3 = make I <- B (t2)
-// t4 = *t1
-// t5 = make I <- B (t4)
-// m[t3] = t5
-// t6 = *t0
-// t7 = (B).Foo(t6)
-// t8 = n[t7]
-// return t8
+// t0 = make I <- B (b1)
+// t1 = make I <- B (b2)
+// m[t0] = t1
+// t2 = (B).Foo(b1)
+// t3 = n[t2]
+// return t3
// WANT:
-// Local(t4) -> Local(t5)
-// Local(t5) -> MapValue(testdata.I)
-// Local(t3) -> MapKey(testdata.I)
-// Local(t8) -> MapValue(*testdata.J)
-// MapValue(*testdata.J) -> Local(t8)
+// Local(b2) -> Local(t1)
+// Local(t1) -> MapValue(testdata.I)
+// Local(t0) -> MapKey(testdata.I)
+// Local(t3) -> MapValue(*testdata.J)
+// MapValue(*testdata.J) -> Local(t3)
diff --git a/go/callgraph/vta/testdata/src/panic.go b/go/callgraph/vta/testdata/src/panic.go
index 5ef3548..1a987d0 100644
--- a/go/callgraph/vta/testdata/src/panic.go
+++ b/go/callgraph/vta/testdata/src/panic.go
@@ -50,18 +50,14 @@
// return
//
// func Baz(i I):
-// t0 = local A (a)
-// *t0 = a
// defer recover1()
-// defer recover()
-// t1 = *t0
-// t2 = make interface{} <- A (t1)
+// t0 = make interface{} <- A (a)
// panic t2
-// t2 argument to panic in Baz gets ultimately connected to recover
+// t0 argument to panic in Baz gets ultimately connected to recover
// registers t1 in recover1() and t0 in recover2().
// WANT:
// Panic -> Recover
-// Local(t2) -> Panic
+// Local(t0) -> Panic
// Recover -> Local(t0), Local(t1)
diff --git a/go/callgraph/vta/testdata/src/phi.go b/go/callgraph/vta/testdata/src/phi.go
index 2144a2c..a4a3efd 100644
--- a/go/callgraph/vta/testdata/src/phi.go
+++ b/go/callgraph/vta/testdata/src/phi.go
@@ -28,28 +28,23 @@
// Relevant SSA:
// func Baz(b B, c bool):
// 0:
-// t0 = local B (b)
-// *t0 = b
// if c goto 1 else 3
//
// 1:
-// t1 = *t0
-// t2 = make I <- B (t1)
+// t0 = make I <- B (b)
// jump 2
//
// 2:
-// t3 = phi [1: t2, 3: t7] #i
-// t4 = invoke t3.foo()
+// t1 = phi [1: t0, 3: t3] #i
+// t2 = invoke t1.foo()
// return
//
// 3:
-// t5 = local A (a)
-// t6 = *t5
-// t7 = make I <- A (t6)
+// t3 = make I <- A (struct{}{}:A)
// jump 2
// WANT:
-// Local(t1) -> Local(t2)
-// Local(t2) -> Local(t3)
-// Local(t7) -> Local(t3)
-// Local(t6) -> Local(t7)
+// Local(b) -> Local(t0)
+// Local(t0) -> Local(t1)
+// Local(t3) -> Local(t1)
+// Constant(testdata.A) -> Local(t3)
diff --git a/go/callgraph/vta/testdata/src/select.go b/go/callgraph/vta/testdata/src/select.go
index 50586d3..8774aec 100644
--- a/go/callgraph/vta/testdata/src/select.go
+++ b/go/callgraph/vta/testdata/src/select.go
@@ -39,22 +39,20 @@
// Relevant SSA:
// func Baz(b1 B, b2 B, c1 chan I, c2 chan J):
// ...
-// t2 = *t0
-// t3 = make I <- B (t2)
-// t4 = *t1
-// t5 = make J <- B (t4)
-// t6 = select blocking [c1<-t3, c2<-t5, <-c1, <-c2] (index int, ok bool, I, J)
-// t7 = extract t6 #0
-// t8 = t7 == 0:int
-// if t8 goto 2 else 3
+// t0 = make I <- B (b1)
+// t1 = make J <- B (b2)
+// t2 = select blocking [c1<-t0, c2<-t1, <-c1, <-c2] (index int, ok bool, I, J)
+// t3 = extract t2 #0
+// t4 = t73== 0:int
+// if t4 goto 2 else 3
// ...
// 8:
-// t15 = extract t6 #3
-// t16 = invoke t15.Foo()
-// t17 = print(t18)
+// t12 = extract t2 #3
+// t13 = invoke t12.Foo()
+// t14 = print(t15)
// WANT:
-// Local(t3) -> Channel(chan testdata.I)
-// Local(t5) -> Channel(chan testdata.J)
-// Channel(chan testdata.I) -> Local(t6[2])
-// Channel(chan testdata.J) -> Local(t6[3])
+// Local(t0) -> Channel(chan testdata.I)
+// Local(t1) -> Channel(chan testdata.J)
+// Channel(chan testdata.I) -> Local(t2[2])
+// Channel(chan testdata.J) -> Local(t2[3])
diff --git a/go/callgraph/vta/testdata/src/store.go b/go/callgraph/vta/testdata/src/store.go
index 8a36d4e..322e9e5 100644
--- a/go/callgraph/vta/testdata/src/store.go
+++ b/go/callgraph/vta/testdata/src/store.go
@@ -26,16 +26,14 @@
}
// Relevant SSA:
-// t0 = local A (a)
-// t1 = new I (i)
-// t2 = *t0 no interesting flow: concrete types
-// t3 = make I <- A (t2) t2 -> t3
-// *t1 = t3 t3 -> t1
-// t4 = *t1 t1 -> t4
-// t5 = invoke t4.foo()
+// t0 = new I (i)
+// t1 = make I <- A (struct{}{}:A) A -> t1
+// *t0 = t1 t1 -> t0
+// t2 = *t0 t0 -> t2
+// t3 = invoke t2.foo()
// return
// WANT:
-// Local(t2) -> Local(t3)
-// Local(t3) -> Local(t1)
-// Local(t1) -> Local(t4)
+// Constant(testdata.A) -> Local(t1)
+// Local(t1) -> Local(t0)
+// Local(t0) -> Local(t2)
diff --git a/go/callgraph/vta/testdata/src/store_load_alias.go b/go/callgraph/vta/testdata/src/store_load_alias.go
index b9814f0..71f843b 100644
--- a/go/callgraph/vta/testdata/src/store_load_alias.go
+++ b/go/callgraph/vta/testdata/src/store_load_alias.go
@@ -27,26 +27,24 @@
// t1 = new *I (j)
// *t1 = t0
// t2 = *t1
-// t3 = local A (complit)
-// t4 = *t3
-// t5 = make I <- A (t4)
-// *t2 = t5
-// t6 = *t0
-// t7 = invoke t6.foo()
-// t8 = *t1
-// t9 = *t8
-// t10 = invoke t9.foo()
+// t3 = make I <- A (struct{}{}:A) I
+// *t2 = t3
+// t4 = *t0
+// t5 = invoke t4.foo()
+// t6 = *t1
+// t7 = *t6
+// t8 = invoke t7.foo()
// Flow chain showing that A reaches i.foo():
-// t4 (A) -> t5 -> t2 <-> PtrInterface(I) <-> t0 -> t6
+// Constant(A) -> t3 -> t2 <-> PtrInterface(I) <-> t0 -> t4
// Flow chain showing that A reaches (**k).foo():
-// t4 (A) -> t5 -> t2 <-> PtrInterface(I) <-> t8 -> t9
+// Constant(A) -> t3 -> t2 <-> PtrInterface(I) <-> t6 -> t7
// WANT:
// Local(i) -> Local(t0)
-// Local(t0) -> Local(t6), PtrInterface(testdata.I)
-// PtrInterface(testdata.I) -> Local(t0), Local(t2), Local(t8)
+// Local(t0) -> Local(t4), PtrInterface(testdata.I)
+// PtrInterface(testdata.I) -> Local(t0), Local(t2), Local(t6)
// Local(t2) -> PtrInterface(testdata.I)
-// Local(t4) -> Local(t5)
-// Local(t5) -> Local(t2)
-// Local(t8) -> Local(t9), PtrInterface(testdata.I)
+// Constant(testdata.A) -> Local(t3)
+// Local(t3) -> Local(t2)
+// Local(t6) -> Local(t7), PtrInterface(testdata.I)
diff --git a/go/callgraph/vta/testdata/src/type_conversions.go b/go/callgraph/vta/testdata/src/type_conversions.go
index e18077f..3dbfb0c 100644
--- a/go/callgraph/vta/testdata/src/type_conversions.go
+++ b/go/callgraph/vta/testdata/src/type_conversions.go
@@ -60,26 +60,22 @@
// t5 = change interface X <- Y (t4)
// t6 = invoke t5.Foo()
//
-// t7 = local A (complit)
-// t8 = *t7
-// t9 = make Y <- A (t8)
-// *t0 = t9
-// t10 = changetype *W <- *Y (t0)
-// t11 = local B (complit)
-// t12 = *t11
-// t13 = make W <- B (t12)
-// *t10 = t13
-// t14 = *t0
-// t15 = invoke t14.Foo()
-// t16 = *t10
-// t17 = invoke t16.Foo()
+// t7 = make Y <- A (struct{}{}:A)
+// *t0 = t7
+// t8 = changetype *W <- *Y (t0)
+// t9 = make W <- B (struct{}{}:B)
+// *t8 = t9
+// t10 = *t0
+// t11 = invoke t10.Foo()
+// t12 = *t8
+// t13 = invoke t12.Foo()
// return
// WANT:
// Local(t1) -> Local(t2)
// Local(t4) -> Local(t5)
-// Local(t0) -> Local(t1), Local(t10), Local(t14), Local(t4)
+// Local(t0) -> Local(t1), Local(t10), Local(t4), Local(t8)
// Local(y) -> Local(t0)
-// Local(t8) -> Local(t9)
-// Local(t9) -> Local(t0)
-// Local(t13) -> Local(t10)
+// Constant(testdata.A) -> Local(t7)
+// Local(t7) -> Local(t0)
+// Local(t9) -> Local(t8)
diff --git a/go/callgraph/vta/vta_go117_test.go b/go/callgraph/vta/vta_go117_test.go
index 04f6980..6a5af2c 100644
--- a/go/callgraph/vta/vta_go117_test.go
+++ b/go/callgraph/vta/vta_go117_test.go
@@ -26,7 +26,8 @@
}
g, _ := typePropGraph(ssautil.AllFunctions(prog), cha.CallGraph(prog))
- if gs := vtaGraphStr(g); !subGraph(want, gs) {
- t.Errorf("`%s`: want superset of %v;\n got %v", file, want, gs)
+ got := vtaGraphStr(g)
+ if diff := setdiff(want, got); len(diff) != 0 {
+ t.Errorf("`%s`: want superset of %v;\n got %v", file, want, got)
}
}
diff --git a/go/callgraph/vta/vta_test.go b/go/callgraph/vta/vta_test.go
index 549c4af..75a8ceb 100644
--- a/go/callgraph/vta/vta_test.go
+++ b/go/callgraph/vta/vta_test.go
@@ -38,8 +38,9 @@
}
g := CallGraph(ssautil.AllFunctions(prog), cha.CallGraph(prog))
- if got := callGraphStr(g); !subGraph(want, got) {
- t.Errorf("computed callgraph %v should contain %v", got, want)
+ got := callGraphStr(g)
+ if diff := setdiff(want, got); len(diff) > 0 {
+ t.Errorf("computed callgraph %v should contain %v (diff: %v)", got, want, diff)
}
})
}
@@ -61,8 +62,9 @@
g := CallGraph(allFuncs, cha.CallGraph(prog))
// VTA over the whole program will produce a call graph that
// includes Baz:(**i).Foo -> A.Foo, B.Foo.
- if got := callGraphStr(g); !subGraph(want, got) {
- t.Errorf("computed callgraph %v should contain %v", got, want)
+ got := callGraphStr(g)
+ if diff := setdiff(want, got); len(diff) > 0 {
+ t.Errorf("computed callgraph %v should contain %v (diff: %v)", got, want, diff)
}
// Prune the set of program functions to exclude Bar(). This should
@@ -78,8 +80,9 @@
}
want = []string{"Baz: Do(i) -> Do; invoke t2.Foo() -> A.Foo"}
g = CallGraph(noBarFuncs, cha.CallGraph(prog))
- if got := callGraphStr(g); !subGraph(want, got) {
- t.Errorf("pruned callgraph %v should contain %v", got, want)
+ got = callGraphStr(g)
+ if diff := setdiff(want, got); len(diff) > 0 {
+ t.Errorf("pruned callgraph %v should contain %v (diff: %v)", got, want, diff)
}
}
@@ -131,7 +134,8 @@
}
g := CallGraph(ssautil.AllFunctions(prog), cha.CallGraph(prog))
- if got := callGraphStr(g); !subGraph(want, got) {
- t.Errorf("computed callgraph %v should contain %v", got, want)
+ got := callGraphStr(g)
+ if diff := setdiff(want, got); len(diff) != 0 {
+ t.Errorf("computed callgraph %v should contain %v (diff: %v)", got, want, diff)
}
}
diff --git a/go/ssa/builder.go b/go/ssa/builder.go
index be8d36a..ffa6667 100644
--- a/go/ssa/builder.go
+++ b/go/ssa/builder.go
@@ -1282,8 +1282,7 @@
case *types.Struct:
if !isZero && len(e.Elts) != t.NumFields() {
// memclear
- sb.store(&address{addr, e.Lbrace, nil},
- zeroValue(fn, deref(addr.Type())))
+ sb.store(&address{addr, e.Lbrace, nil}, zeroConst(deref(addr.Type())))
isZero = true
}
for i, e := range e.Elts {
@@ -1327,8 +1326,7 @@
if !isZero && int64(len(e.Elts)) != at.Len() {
// memclear
- sb.store(&address{array, e.Lbrace, nil},
- zeroValue(fn, deref(array.Type())))
+ sb.store(&address{array, e.Lbrace, nil}, zeroConst(deref(array.Type())))
}
}
diff --git a/go/ssa/emit.go b/go/ssa/emit.go
index 1731c79..4ba049d 100644
--- a/go/ssa/emit.go
+++ b/go/ssa/emit.go
@@ -537,15 +537,46 @@
return v
}
-// zeroValue emits to f code to produce a zero value of type t,
-// and returns it.
-func zeroValue(f *Function, t types.Type) Value {
- switch t.Underlying().(type) {
- case *types.Struct, *types.Array:
- return emitLoad(f, f.addLocal(t, token.NoPos))
- default:
- return zeroConst(t)
- }
+// emitSliceToArray emits to f code to convert a slice value to an array value.
+//
+// Precondition: all types in type set of typ are arrays and convertible to all
+// types in the type set of val.Type().
+func emitSliceToArray(f *Function, val Value, typ types.Type) Value {
+ // Emit the following:
+ // if val == nil && len(typ) == 0 {
+ // ptr = &[0]T{}
+ // } else {
+ // ptr = SliceToArrayPointer(val)
+ // }
+ // v = *ptr
+
+ ptype := types.NewPointer(typ)
+ p := &SliceToArrayPointer{X: val}
+ p.setType(ptype)
+ ptr := f.emit(p)
+
+ nilb := f.newBasicBlock("slicetoarray.nil")
+ nonnilb := f.newBasicBlock("slicetoarray.nonnil")
+ done := f.newBasicBlock("slicetoarray.done")
+
+ cond := emitCompare(f, token.EQL, ptr, zeroConst(ptype), token.NoPos)
+ emitIf(f, cond, nilb, nonnilb)
+ f.currentBlock = nilb
+
+ zero := f.addLocal(typ, token.NoPos)
+ emitJump(f, done)
+ f.currentBlock = nonnilb
+
+ emitJump(f, done)
+ f.currentBlock = done
+
+ phi := &Phi{Edges: []Value{zero, ptr}, Comment: "slicetoarray"}
+ phi.pos = val.Pos()
+ phi.setType(typ)
+ x := f.emit(phi)
+ unOp := &UnOp{Op: token.MUL, X: x}
+ unOp.setType(typ)
+ return f.emit(unOp)
}
// createRecoverBlock emits to f a block of code to return after a
@@ -577,7 +608,7 @@
T := R.At(i).Type()
// Return zero value of each result type.
- results = append(results, zeroValue(f, T))
+ results = append(results, zeroConst(T))
}
}
f.emit(&Return{Results: results})
diff --git a/go/ssa/lift.go b/go/ssa/lift.go
index 945536b..b9cf7bc 100644
--- a/go/ssa/lift.go
+++ b/go/ssa/lift.go
@@ -41,11 +41,8 @@
import (
"fmt"
"go/token"
- "go/types"
"math/big"
"os"
-
- "golang.org/x/tools/internal/typeparams"
)
// If true, show diagnostic information at each step of lifting.
@@ -383,12 +380,6 @@
//
// fresh is a source of fresh ids for phi nodes.
func liftAlloc(df domFrontier, alloc *Alloc, newPhis newPhiMap, fresh *int) bool {
- // TODO(taking): zero constants of aggregated types can now be lifted.
- switch deref(alloc.Type()).Underlying().(type) {
- case *types.Array, *types.Struct, *typeparams.TypeParam:
- return false
- }
-
// Don't lift named return values in functions that defer
// calls that may recover from panic.
if fn := alloc.Parent(); fn.Recover != nil {
diff --git a/go/ssa/testdata/valueforexpr.go b/go/ssa/testdata/valueforexpr.go
index 243ec61..703c316 100644
--- a/go/ssa/testdata/valueforexpr.go
+++ b/go/ssa/testdata/valueforexpr.go
@@ -94,18 +94,18 @@
_ = & /*@Slice*/ ([]int{})
// 2. Arrays
- print( /*@UnOp*/ ([1]int{}))
+ print( /*@Const*/ ([1]int{}))
print( /*@Alloc*/ (&[1]int{}))
print(& /*@Alloc*/ ([1]int{}))
- arr1 := /*@Alloc*/ ([1]int{})
+ arr1 := /*@Const*/ ([1]int{})
arr2 := /*@Alloc*/ (&[1]int{})
arr3 := & /*@Alloc*/ ([1]int{})
_, _, _ = arr1, arr2, arr3
- _ = /*@UnOp*/ ([1]int{})
- _ = /*@Alloc*/ (& /*@Alloc*/ ([1]int{}))
- _ = & /*@Alloc*/ ([1]int{})
+ _ = /*@Const*/ ([1]int{})
+ _ = /*@nil*/ (& /*@Const*/ ([1]int{})) // & optimized away
+ _ = & /*@Const*/ ([1]int{})
// 3. Maps
type M map[int]int
@@ -123,18 +123,18 @@
_ = & /*@MakeMap*/ (M{})
// 4. Structs
- print( /*@UnOp*/ (struct{}{}))
+ print( /*@Const*/ (struct{}{}))
print( /*@Alloc*/ (&struct{}{}))
print(& /*@Alloc*/ (struct{}{}))
- s1 := /*@Alloc*/ (struct{}{})
+ s1 := /*@Const*/ (struct{}{})
s2 := /*@Alloc*/ (&struct{}{})
s3 := & /*@Alloc*/ (struct{}{})
_, _, _ = s1, s2, s3
- _ = /*@UnOp*/ (struct{}{})
- _ = /*@Alloc*/ (& /*@Alloc*/ (struct{}{}))
- _ = & /*@Alloc*/ (struct{}{})
+ _ = /*@Const*/ (struct{}{})
+ _ = /*@nil*/ (& /*@Const*/ (struct{}{})) // & optimized away
+ _ = & /*@Const*/ (struct{}{})
}
type t struct{ x int }