[dev.ssa] Merge remote-tracking branch 'origin/master' into mergebranch
Semi-regular merge from tip to dev.ssa.
Conflicts:
src/runtime/sys_windows_amd64.s
Change-Id: I5f733130049c810e6ceacd46dad85faebca52b29
diff --git a/src/cmd/compile/internal/amd64/prog.go b/src/cmd/compile/internal/amd64/prog.go
index b4cc781..b43dde6 100644
--- a/src/cmd/compile/internal/amd64/prog.go
+++ b/src/cmd/compile/internal/amd64/prog.go
@@ -34,6 +34,7 @@
obj.ACHECKNIL: {Flags: gc.LeftRead},
obj.AVARDEF: {Flags: gc.Pseudo | gc.RightWrite},
obj.AVARKILL: {Flags: gc.Pseudo | gc.RightWrite},
+ obj.AVARLIVE: {Flags: gc.Pseudo | gc.LeftRead},
// NOP is an internal no-op that also stands
// for USED and SET annotations, not the Intel opcode.
diff --git a/src/cmd/compile/internal/arm/peep.go b/src/cmd/compile/internal/arm/peep.go
index d7a9c5f..bc49ebc 100644
--- a/src/cmd/compile/internal/arm/peep.go
+++ b/src/cmd/compile/internal/arm/peep.go
@@ -1366,6 +1366,7 @@
obj.AFUNCDATA,
obj.AVARDEF,
obj.AVARKILL,
+ obj.AVARLIVE,
obj.AUSEFIELD:
return 0
}
diff --git a/src/cmd/compile/internal/arm/prog.go b/src/cmd/compile/internal/arm/prog.go
index 8a304e2..81be77a 100644
--- a/src/cmd/compile/internal/arm/prog.go
+++ b/src/cmd/compile/internal/arm/prog.go
@@ -33,6 +33,7 @@
obj.ACHECKNIL: {Flags: gc.LeftRead},
obj.AVARDEF: {Flags: gc.Pseudo | gc.RightWrite},
obj.AVARKILL: {Flags: gc.Pseudo | gc.RightWrite},
+ obj.AVARLIVE: {Flags: gc.Pseudo | gc.LeftRead},
// NOP is an internal no-op that also stands
// for USED and SET annotations, not the Intel opcode.
diff --git a/src/cmd/compile/internal/arm64/peep.go b/src/cmd/compile/internal/arm64/peep.go
index b61ac6e..daa626f 100644
--- a/src/cmd/compile/internal/arm64/peep.go
+++ b/src/cmd/compile/internal/arm64/peep.go
@@ -711,6 +711,7 @@
obj.AFUNCDATA,
obj.AVARDEF,
obj.AVARKILL,
+ obj.AVARLIVE,
obj.AUSEFIELD:
return 0
}
diff --git a/src/cmd/compile/internal/arm64/prog.go b/src/cmd/compile/internal/arm64/prog.go
index a4b8ebe..a8e8bc5 100644
--- a/src/cmd/compile/internal/arm64/prog.go
+++ b/src/cmd/compile/internal/arm64/prog.go
@@ -34,6 +34,7 @@
obj.ACHECKNIL: {Flags: gc.LeftRead},
obj.AVARDEF: {Flags: gc.Pseudo | gc.RightWrite},
obj.AVARKILL: {Flags: gc.Pseudo | gc.RightWrite},
+ obj.AVARLIVE: {Flags: gc.Pseudo | gc.LeftRead},
// NOP is an internal no-op that also stands
// for USED and SET annotations, not the Power opcode.
diff --git a/src/cmd/compile/internal/gc/const.go b/src/cmd/compile/internal/gc/const.go
index d30515a..795b53d 100644
--- a/src/cmd/compile/internal/gc/const.go
+++ b/src/cmd/compile/internal/gc/const.go
@@ -634,6 +634,7 @@
var wr int
var v Val
var norig *Node
+ var nn *Node
if nr == nil {
// copy numeric value to avoid modifying
// nl, in case someone still refers to it (e.g. iota).
@@ -1115,15 +1116,21 @@
return
settrue:
- norig = saveorig(n)
- *n = *Nodbool(true)
- n.Orig = norig
+ nn = Nodbool(true)
+ nn.Orig = saveorig(n)
+ if !iscmp[n.Op] {
+ nn.Type = nl.Type
+ }
+ *n = *nn
return
setfalse:
- norig = saveorig(n)
- *n = *Nodbool(false)
- n.Orig = norig
+ nn = Nodbool(false)
+ nn.Orig = saveorig(n)
+ if !iscmp[n.Op] {
+ nn.Type = nl.Type
+ }
+ *n = *nn
return
illegal:
diff --git a/src/cmd/compile/internal/gc/esc.go b/src/cmd/compile/internal/gc/esc.go
index 293f916..ff983e7 100644
--- a/src/cmd/compile/internal/gc/esc.go
+++ b/src/cmd/compile/internal/gc/esc.go
@@ -299,12 +299,13 @@
}
type NodeEscState struct {
- Curfn *Node
- Escflowsrc *NodeList // flow(this, src)
- Escretval *NodeList // on OCALLxxx, list of dummy return values
- Escloopdepth int32 // -1: global, 0: return variables, 1:function top level, increased inside function for every loop or label to mark scopes
- Esclevel Level
- Walkgen uint32
+ Curfn *Node
+ Escflowsrc *NodeList // flow(this, src)
+ Escretval *NodeList // on OCALLxxx, list of dummy return values
+ Escloopdepth int32 // -1: global, 0: return variables, 1:function top level, increased inside function for every loop or label to mark scopes
+ Esclevel Level
+ Walkgen uint32
+ Maxextraloopdepth int32
}
func (e *EscState) nodeEscState(n *Node) *NodeEscState {
@@ -1579,7 +1580,13 @@
src.Op == ONAME && src.Class == PPARAM && src.Name.Curfn == dst.Name.Curfn
}
+const NOTALOOPDEPTH = -1
+
func escwalk(e *EscState, level Level, dst *Node, src *Node) {
+ escwalkBody(e, level, dst, src, NOTALOOPDEPTH)
+}
+
+func escwalkBody(e *EscState, level Level, dst *Node, src *Node, extraloopdepth int32) {
if src.Op == OLITERAL {
return
}
@@ -1590,16 +1597,29 @@
// convergence.
level = level.min(srcE.Esclevel)
if level == srcE.Esclevel {
- return
+ // Have we been here already with an extraloopdepth,
+ // or is the extraloopdepth provided no improvement on
+ // what's already been seen?
+ if srcE.Maxextraloopdepth >= extraloopdepth || srcE.Escloopdepth >= extraloopdepth {
+ return
+ }
+ srcE.Maxextraloopdepth = extraloopdepth
}
+ } else { // srcE.Walkgen < e.walkgen -- first time, reset this.
+ srcE.Maxextraloopdepth = NOTALOOPDEPTH
}
srcE.Walkgen = e.walkgen
srcE.Esclevel = level
+ modSrcLoopdepth := srcE.Escloopdepth
+
+ if extraloopdepth > modSrcLoopdepth {
+ modSrcLoopdepth = extraloopdepth
+ }
if Debug['m'] > 1 {
- fmt.Printf("escwalk: level:%d depth:%d %.*s op=%v %v(%v) scope:%v[%d]\n",
- level, e.pdepth, e.pdepth, "\t\t\t\t\t\t\t\t\t\t", Oconv(int(src.Op), 0), Nconv(src, obj.FmtShort), Jconv(src, obj.FmtShort), e.curfnSym(src), srcE.Escloopdepth)
+ fmt.Printf("escwalk: level:%d depth:%d %.*s op=%v %v(%v) scope:%v[%d] extraloopdepth=%v\n",
+ level, e.pdepth, e.pdepth, "\t\t\t\t\t\t\t\t\t\t", Oconv(int(src.Op), 0), Nconv(src, obj.FmtShort), Jconv(src, obj.FmtShort), e.curfnSym(src), srcE.Escloopdepth, extraloopdepth)
}
e.pdepth++
@@ -1638,7 +1658,7 @@
}
}
- leaks = level.int() <= 0 && level.guaranteedDereference() <= 0 && dstE.Escloopdepth < srcE.Escloopdepth
+ leaks = level.int() <= 0 && level.guaranteedDereference() <= 0 && dstE.Escloopdepth < modSrcLoopdepth
switch src.Op {
case ONAME:
@@ -1650,7 +1670,7 @@
Warnl(int(src.Lineno), "leaking param content: %v", Nconv(src, obj.FmtShort))
} else {
Warnl(int(src.Lineno), "leaking param content: %v level=%v dst.eld=%v src.eld=%v dst=%v",
- Nconv(src, obj.FmtShort), level, dstE.Escloopdepth, srcE.Escloopdepth, Nconv(dst, obj.FmtShort))
+ Nconv(src, obj.FmtShort), level, dstE.Escloopdepth, modSrcLoopdepth, Nconv(dst, obj.FmtShort))
}
}
} else {
@@ -1660,7 +1680,7 @@
Warnl(int(src.Lineno), "leaking param: %v", Nconv(src, obj.FmtShort))
} else {
Warnl(int(src.Lineno), "leaking param: %v level=%v dst.eld=%v src.eld=%v dst=%v",
- Nconv(src, obj.FmtShort), level, dstE.Escloopdepth, srcE.Escloopdepth, Nconv(dst, obj.FmtShort))
+ Nconv(src, obj.FmtShort), level, dstE.Escloopdepth, modSrcLoopdepth, Nconv(dst, obj.FmtShort))
}
}
}
@@ -1686,15 +1706,17 @@
}
if Debug['m'] > 1 {
Warnl(int(src.Lineno), "%v escapes to heap, level=%v, dst.eld=%v, src.eld=%v",
- Nconv(p, obj.FmtShort), level, dstE.Escloopdepth, srcE.Escloopdepth)
+ Nconv(p, obj.FmtShort), level, dstE.Escloopdepth, modSrcLoopdepth)
} else {
Warnl(int(src.Lineno), "%v escapes to heap", Nconv(p, obj.FmtShort))
}
}
+ escwalkBody(e, level.dec(), dst, src.Left, modSrcLoopdepth)
+ extraloopdepth = modSrcLoopdepth // passes to recursive case, seems likely a no-op
+ } else {
+ escwalk(e, level.dec(), dst, src.Left)
}
- escwalk(e, level.dec(), dst, src.Left)
-
case OAPPEND:
escwalk(e, level, dst, src.List.N)
@@ -1704,6 +1726,7 @@
if Debug['m'] != 0 {
Warnl(int(src.Lineno), "%v escapes to heap", Nconv(src, obj.FmtShort))
}
+ extraloopdepth = modSrcLoopdepth
}
// similar to a slice arraylit and its args.
level = level.dec()
@@ -1737,6 +1760,7 @@
if Debug['m'] != 0 {
Warnl(int(src.Lineno), "%v escapes to heap", Nconv(src, obj.FmtShort))
}
+ extraloopdepth = modSrcLoopdepth
}
case ODOT,
@@ -1778,12 +1802,19 @@
recurse:
level = level.copy()
for ll := srcE.Escflowsrc; ll != nil; ll = ll.Next {
- escwalk(e, level, dst, ll.N)
+ escwalkBody(e, level, dst, ll.N, extraloopdepth)
}
e.pdepth--
}
+// This special tag is applied to uintptr variables
+// that we believe may hold unsafe.Pointers for
+// calls into assembly functions.
+// It is logically a constant, but using a var
+// lets us take the address below to get a *string.
+var unsafeUintptrTag = "unsafe-uintptr"
+
func esctag(e *EscState, func_ *Node) {
func_.Esc = EscFuncTagged
@@ -1798,6 +1829,29 @@
}
}
+ // Assume that uintptr arguments must be held live across the call.
+ // This is most important for syscall.Syscall.
+ // See golang.org/issue/13372.
+ // This really doesn't have much to do with escape analysis per se,
+ // but we are reusing the ability to annotate an individual function
+ // argument and pass those annotations along to importing code.
+ narg := 0
+ for t := getinargx(func_.Type).Type; t != nil; t = t.Down {
+ narg++
+ if t.Type.Etype == TUINTPTR {
+ if Debug['m'] != 0 {
+ var name string
+ if t.Sym != nil {
+ name = t.Sym.Name
+ } else {
+ name = fmt.Sprintf("arg#%d", narg)
+ }
+ Warnl(int(func_.Lineno), "%v assuming %v is unsafe uintptr", funcSym(func_), name)
+ }
+ t.Note = &unsafeUintptrTag
+ }
+ }
+
return
}
diff --git a/src/cmd/compile/internal/gc/float_test.go b/src/cmd/compile/internal/gc/float_test.go
new file mode 100644
index 0000000..c761e96
--- /dev/null
+++ b/src/cmd/compile/internal/gc/float_test.go
@@ -0,0 +1,102 @@
+// Copyright 2016 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.
+
+package gc
+
+import "testing"
+
+// For GO386=387, make sure fucomi* opcodes are not used
+// for comparison operations.
+// Note that this test will fail only on a Pentium MMX
+// processor (with GOARCH=386 GO386=387), as it just runs
+// some code and looks for an unimplemented instruction fault.
+
+//go:noinline
+func compare1(a, b float64) bool {
+ return a < b
+}
+
+//go:noinline
+func compare2(a, b float32) bool {
+ return a < b
+}
+
+func TestFloatCompare(t *testing.T) {
+ if !compare1(3, 5) {
+ t.Errorf("compare1 returned false")
+ }
+ if !compare2(3, 5) {
+ t.Errorf("compare2 returned false")
+ }
+}
+
+// For GO386=387, make sure fucomi* opcodes are not used
+// for float->int conversions.
+
+//go:noinline
+func cvt1(a float64) uint64 {
+ return uint64(a)
+}
+
+//go:noinline
+func cvt2(a float64) uint32 {
+ return uint32(a)
+}
+
+//go:noinline
+func cvt3(a float32) uint64 {
+ return uint64(a)
+}
+
+//go:noinline
+func cvt4(a float32) uint32 {
+ return uint32(a)
+}
+
+//go:noinline
+func cvt5(a float64) int64 {
+ return int64(a)
+}
+
+//go:noinline
+func cvt6(a float64) int32 {
+ return int32(a)
+}
+
+//go:noinline
+func cvt7(a float32) int64 {
+ return int64(a)
+}
+
+//go:noinline
+func cvt8(a float32) int32 {
+ return int32(a)
+}
+
+func TestFloatConvert(t *testing.T) {
+ if got := cvt1(3.5); got != 3 {
+ t.Errorf("cvt1 got %d, wanted 3", got)
+ }
+ if got := cvt2(3.5); got != 3 {
+ t.Errorf("cvt2 got %d, wanted 3", got)
+ }
+ if got := cvt3(3.5); got != 3 {
+ t.Errorf("cvt3 got %d, wanted 3", got)
+ }
+ if got := cvt4(3.5); got != 3 {
+ t.Errorf("cvt4 got %d, wanted 3", got)
+ }
+ if got := cvt5(3.5); got != 3 {
+ t.Errorf("cvt5 got %d, wanted 3", got)
+ }
+ if got := cvt6(3.5); got != 3 {
+ t.Errorf("cvt6 got %d, wanted 3", got)
+ }
+ if got := cvt7(3.5); got != 3 {
+ t.Errorf("cvt7 got %d, wanted 3", got)
+ }
+ if got := cvt8(3.5); got != 3 {
+ t.Errorf("cvt8 got %d, wanted 3", got)
+ }
+}
diff --git a/src/cmd/compile/internal/gc/gen.go b/src/cmd/compile/internal/gc/gen.go
index c9208d9..ac55d4f 100644
--- a/src/cmd/compile/internal/gc/gen.go
+++ b/src/cmd/compile/internal/gc/gen.go
@@ -607,6 +607,9 @@
n.Esc = EscNever
n.Name.Curfn = Curfn
Curfn.Func.Dcl = list(Curfn.Func.Dcl, n)
+ if Debug['h'] != 0 {
+ println("H", n, n.Orig, funcSym(Curfn).Name)
+ }
dowidth(t)
n.Xoffset = 0
@@ -870,6 +873,9 @@
case OVARKILL:
gvarkill(n.Left)
+
+ case OVARLIVE:
+ gvarlive(n.Left)
}
ret:
diff --git a/src/cmd/compile/internal/gc/gsubr.go b/src/cmd/compile/internal/gc/gsubr.go
index f17a701..be0a0fb 100644
--- a/src/cmd/compile/internal/gc/gsubr.go
+++ b/src/cmd/compile/internal/gc/gsubr.go
@@ -185,7 +185,7 @@
continue
}
- if (p.As == obj.AVARDEF || p.As == obj.AVARKILL) && p.To.Node != nil && !((p.To.Node).(*Node)).Used {
+ if (p.As == obj.AVARDEF || p.As == obj.AVARKILL || p.As == obj.AVARLIVE) && p.To.Node != nil && !((p.To.Node).(*Node)).Used {
// Cannot remove VARDEF instruction, because - unlike TYPE handled above -
// VARDEFs are interspersed with other code, and a jump might be using the
// VARDEF as a target. Replace with a no-op instead. A later pass will remove
diff --git a/src/cmd/compile/internal/gc/lex.go b/src/cmd/compile/internal/gc/lex.go
index 830c56d..fb30d58 100644
--- a/src/cmd/compile/internal/gc/lex.go
+++ b/src/cmd/compile/internal/gc/lex.go
@@ -694,7 +694,13 @@
errorexit()
}
- if f.U.(string) == "unsafe" {
+ path_ := f.U.(string)
+
+ if mapped, ok := importMap[path_]; ok {
+ path_ = mapped
+ }
+
+ if path_ == "unsafe" {
if safemode != 0 {
Yyerror("cannot import package unsafe")
errorexit()
@@ -706,12 +712,6 @@
return
}
- path_ := f.U.(string)
-
- if mapped, ok := importMap[path_]; ok {
- path_ = mapped
- }
-
if islocalname(path_) {
if path_[0] == '/' {
Yyerror("import path cannot be absolute path")
diff --git a/src/cmd/compile/internal/gc/order.go b/src/cmd/compile/internal/gc/order.go
index 84b96c2d..05cd53a 100644
--- a/src/cmd/compile/internal/gc/order.go
+++ b/src/cmd/compile/internal/gc/order.go
@@ -243,6 +243,13 @@
var kill *Node
for l := order.temp; l != mark; l = l.Next {
+ if l.N.Name.Keepalive {
+ l.N.Name.Keepalive = false
+ l.N.Addrtaken = true // ensure SSA keeps the l.N variable
+ kill = Nod(OVARLIVE, l.N, nil)
+ typecheck(&kill, Etop)
+ *out = list(*out, kill)
+ }
kill = Nod(OVARKILL, l.N, nil)
typecheck(&kill, Etop)
*out = list(*out, kill)
@@ -375,6 +382,28 @@
orderexpr(&n.Left, order, nil)
orderexpr(&n.Right, order, nil) // ODDDARG temp
ordercallargs(&n.List, order)
+
+ if n.Op == OCALLFUNC {
+ for l, t := n.List, getinargx(n.Left.Type).Type; l != nil && t != nil; l, t = l.Next, t.Down {
+ // Check for "unsafe-uintptr" tag provided by escape analysis.
+ // If present and the argument is really a pointer being converted
+ // to uintptr, arrange for the pointer to be kept alive until the call
+ // returns, by copying it into a temp and marking that temp
+ // still alive when we pop the temp stack.
+ if t.Note != nil && *t.Note == unsafeUintptrTag {
+ xp := &l.N
+ for (*xp).Op == OCONVNOP && !Isptr[(*xp).Type.Etype] {
+ xp = &(*xp).Left
+ }
+ x := *xp
+ if Isptr[x.Type.Etype] {
+ x = ordercopyexpr(x, x.Type, order, 0)
+ x.Name.Keepalive = true
+ *xp = x
+ }
+ }
+ }
+ }
}
// Ordermapassign appends n to order->out, introducing temporaries
@@ -464,7 +493,7 @@
default:
Fatalf("orderstmt %v", Oconv(int(n.Op), 0))
- case OVARKILL:
+ case OVARKILL, OVARLIVE:
order.out = list(order.out, n)
case OAS:
diff --git a/src/cmd/compile/internal/gc/pgen.go b/src/cmd/compile/internal/gc/pgen.go
index 9b65f9c..6e7e10e 100644
--- a/src/cmd/compile/internal/gc/pgen.go
+++ b/src/cmd/compile/internal/gc/pgen.go
@@ -95,7 +95,11 @@
switch n.Class {
case PAUTO, PPARAM, PPARAMOUT:
- Thearch.Gins(as, nil, n)
+ if as == obj.AVARLIVE {
+ Thearch.Gins(as, n, nil)
+ } else {
+ Thearch.Gins(as, nil, n)
+ }
}
}
@@ -107,13 +111,17 @@
gvardefx(n, obj.AVARKILL)
}
+func gvarlive(n *Node) {
+ gvardefx(n, obj.AVARLIVE)
+}
+
func removevardef(firstp *obj.Prog) {
for p := firstp; p != nil; p = p.Link {
- for p.Link != nil && (p.Link.As == obj.AVARDEF || p.Link.As == obj.AVARKILL) {
+ for p.Link != nil && (p.Link.As == obj.AVARDEF || p.Link.As == obj.AVARKILL || p.Link.As == obj.AVARLIVE) {
p.Link = p.Link.Link
}
if p.To.Type == obj.TYPE_BRANCH {
- for p.To.Val.(*obj.Prog) != nil && (p.To.Val.(*obj.Prog).As == obj.AVARDEF || p.To.Val.(*obj.Prog).As == obj.AVARKILL) {
+ for p.To.Val.(*obj.Prog) != nil && (p.To.Val.(*obj.Prog).As == obj.AVARDEF || p.To.Val.(*obj.Prog).As == obj.AVARKILL || p.To.Val.(*obj.Prog).As == obj.AVARLIVE) {
p.To.Val = p.To.Val.(*obj.Prog).Link
}
}
diff --git a/src/cmd/compile/internal/gc/plive.go b/src/cmd/compile/internal/gc/plive.go
index 7765d2d..458497d 100644
--- a/src/cmd/compile/internal/gc/plive.go
+++ b/src/cmd/compile/internal/gc/plive.go
@@ -809,7 +809,7 @@
return
}
- fmt.Printf("checkauto %v: %v (%p; class=%d) not found in %v\n", Curfn, n, n, n.Class, p)
+ fmt.Printf("checkauto %v: %v (%p; class=%d) not found in %p %v\n", funcSym(Curfn), n, n, n.Class, p, p)
for l := fn.Func.Dcl; l != nil; l = l.Next {
fmt.Printf("\t%v (%p; class=%d)\n", l.N, l.N, l.N.Class)
}
diff --git a/src/cmd/compile/internal/gc/racewalk.go b/src/cmd/compile/internal/gc/racewalk.go
index 5f35c44..ee4f3ba 100644
--- a/src/cmd/compile/internal/gc/racewalk.go
+++ b/src/cmd/compile/internal/gc/racewalk.go
@@ -143,7 +143,7 @@
goto ret
// can't matter
- case OCFUNC, OVARKILL:
+ case OCFUNC, OVARKILL, OVARLIVE:
goto ret
case OBLOCK:
diff --git a/src/cmd/compile/internal/gc/reg.go b/src/cmd/compile/internal/gc/reg.go
index f575094..14dc03b 100644
--- a/src/cmd/compile/internal/gc/reg.go
+++ b/src/cmd/compile/internal/gc/reg.go
@@ -1073,6 +1073,9 @@
for f := firstf; f != nil; f = f.Link {
p := f.Prog
+ // AVARLIVE must be considered a use, do not skip it.
+ // Otherwise the variable will be optimized away,
+ // and the whole point of AVARLIVE is to keep it on the stack.
if p.As == obj.AVARDEF || p.As == obj.AVARKILL {
continue
}
diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go
index 46aaaa7..be9af60 100644
--- a/src/cmd/compile/internal/gc/ssa.go
+++ b/src/cmd/compile/internal/gc/ssa.go
@@ -849,6 +849,13 @@
s.vars[&memVar] = s.newValue1A(ssa.OpVarKill, ssa.TypeMem, n.Left, s.mem())
}
+ case OVARLIVE:
+ // Insert a varlive op to record that a variable is still live.
+ if !n.Left.Addrtaken {
+ s.Fatalf("VARLIVE variable %s must have Addrtaken set", n.Left)
+ }
+ s.vars[&memVar] = s.newValue1A(ssa.OpVarLive, ssa.TypeMem, n.Left, s.mem())
+
case OCHECKNIL:
p := s.expr(n.Left)
s.nilCheck(p)
@@ -4122,6 +4129,8 @@
Gvardef(v.Aux.(*Node))
case ssa.OpVarKill:
gvarkill(v.Aux.(*Node))
+ case ssa.OpVarLive:
+ gvarlive(v.Aux.(*Node))
case ssa.OpAMD64LoweredNilCheck:
// Optimization - if the subsequent block has a load or store
// at the same address, we don't need to issue this instruction.
diff --git a/src/cmd/compile/internal/gc/syntax.go b/src/cmd/compile/internal/gc/syntax.go
index da23e05..b97cb3f 100644
--- a/src/cmd/compile/internal/gc/syntax.go
+++ b/src/cmd/compile/internal/gc/syntax.go
@@ -128,6 +128,7 @@
Captured bool // is the variable captured by a closure
Byval bool // is the variable captured by value or by reference
Needzero bool // if it contains pointers, needs to be zeroed on function entry
+ Keepalive bool // mark value live across unknown assembly call
}
type Param struct {
@@ -342,6 +343,7 @@
OCFUNC // reference to c function pointer (not go func value)
OCHECKNIL // emit code to ensure pointer/interface not nil
OVARKILL // variable is dead
+ OVARLIVE // variable is alive
// thearch-specific registers
OREGISTER // a register, such as AX.
diff --git a/src/cmd/compile/internal/gc/typecheck.go b/src/cmd/compile/internal/gc/typecheck.go
index 2244802..f74bb33 100644
--- a/src/cmd/compile/internal/gc/typecheck.go
+++ b/src/cmd/compile/internal/gc/typecheck.go
@@ -687,8 +687,6 @@
n.Left = l
n.Right = r
}
- } else if n.Op == OANDAND || n.Op == OOROR {
- evconst(n)
}
if et == TSTRING {
@@ -2025,7 +2023,8 @@
OEMPTY,
OGOTO,
OXFALL,
- OVARKILL:
+ OVARKILL,
+ OVARLIVE:
ok |= Etop
break OpSwitch
diff --git a/src/cmd/compile/internal/gc/walk.go b/src/cmd/compile/internal/gc/walk.go
index 838def9..dddcb68 100644
--- a/src/cmd/compile/internal/gc/walk.go
+++ b/src/cmd/compile/internal/gc/walk.go
@@ -216,7 +216,8 @@
ODCLCONST,
ODCLTYPE,
OCHECKNIL,
- OVARKILL:
+ OVARKILL,
+ OVARLIVE:
break
case OBLOCK:
diff --git a/src/cmd/compile/internal/mips64/peep.go b/src/cmd/compile/internal/mips64/peep.go
index 3d82c81..f97be60 100644
--- a/src/cmd/compile/internal/mips64/peep.go
+++ b/src/cmd/compile/internal/mips64/peep.go
@@ -688,6 +688,7 @@
obj.AFUNCDATA,
obj.AVARDEF,
obj.AVARKILL,
+ obj.AVARLIVE,
obj.AUSEFIELD:
return 0
}
diff --git a/src/cmd/compile/internal/mips64/prog.go b/src/cmd/compile/internal/mips64/prog.go
index bf13d82..b07c7fe 100644
--- a/src/cmd/compile/internal/mips64/prog.go
+++ b/src/cmd/compile/internal/mips64/prog.go
@@ -34,6 +34,7 @@
obj.ACHECKNIL: {Flags: gc.LeftRead},
obj.AVARDEF: {Flags: gc.Pseudo | gc.RightWrite},
obj.AVARKILL: {Flags: gc.Pseudo | gc.RightWrite},
+ obj.AVARLIVE: {Flags: gc.Pseudo | gc.LeftRead},
// NOP is an internal no-op that also stands
// for USED and SET annotations, not the MIPS opcode.
diff --git a/src/cmd/compile/internal/ppc64/peep.go b/src/cmd/compile/internal/ppc64/peep.go
index fadaa4a..1ff3109 100644
--- a/src/cmd/compile/internal/ppc64/peep.go
+++ b/src/cmd/compile/internal/ppc64/peep.go
@@ -953,6 +953,7 @@
obj.AFUNCDATA,
obj.AVARDEF,
obj.AVARKILL,
+ obj.AVARLIVE,
obj.AUSEFIELD:
return 0
}
diff --git a/src/cmd/compile/internal/ppc64/prog.go b/src/cmd/compile/internal/ppc64/prog.go
index 92293be..6b48256 100644
--- a/src/cmd/compile/internal/ppc64/prog.go
+++ b/src/cmd/compile/internal/ppc64/prog.go
@@ -34,6 +34,7 @@
obj.ACHECKNIL: {Flags: gc.LeftRead},
obj.AVARDEF: {Flags: gc.Pseudo | gc.RightWrite},
obj.AVARKILL: {Flags: gc.Pseudo | gc.RightWrite},
+ obj.AVARLIVE: {Flags: gc.Pseudo | gc.LeftRead},
// NOP is an internal no-op that also stands
// for USED and SET annotations, not the Power opcode.
diff --git a/src/cmd/compile/internal/ssa/gen/genericOps.go b/src/cmd/compile/internal/ssa/gen/genericOps.go
index e57dd93..d17f558 100644
--- a/src/cmd/compile/internal/ssa/gen/genericOps.go
+++ b/src/cmd/compile/internal/ssa/gen/genericOps.go
@@ -373,6 +373,7 @@
{name: "VarDef", typ: "Mem"}, // aux is a *gc.Node of a variable that is about to be initialized. arg0=mem, returns mem
{name: "VarKill"}, // aux is a *gc.Node of a variable that is known to be dead. arg0=mem, returns mem
+ {name: "VarLive"}, // aux is a *gc.Node of a variable that must be kept live. arg0=mem, returns mem
}
// kind control successors implicit exit
diff --git a/src/cmd/compile/internal/ssa/lower.go b/src/cmd/compile/internal/ssa/lower.go
index 1b50eb6..af0ee4c 100644
--- a/src/cmd/compile/internal/ssa/lower.go
+++ b/src/cmd/compile/internal/ssa/lower.go
@@ -21,7 +21,7 @@
continue // lowered
}
switch v.Op {
- case OpSP, OpSB, OpInitMem, OpArg, OpPhi, OpVarDef, OpVarKill:
+ case OpSP, OpSB, OpInitMem, OpArg, OpPhi, OpVarDef, OpVarKill, OpVarLive:
continue // ok not to lower
}
s := "not lowered: " + v.Op.String() + " " + v.Type.SimpleString()
diff --git a/src/cmd/compile/internal/ssa/opGen.go b/src/cmd/compile/internal/ssa/opGen.go
index 2fd7f6b..433794a 100644
--- a/src/cmd/compile/internal/ssa/opGen.go
+++ b/src/cmd/compile/internal/ssa/opGen.go
@@ -552,6 +552,7 @@
OpFwdRef
OpVarDef
OpVarKill
+ OpVarLive
)
var opcodeTable = [...]opInfo{
@@ -4310,6 +4311,10 @@
name: "VarKill",
generic: true,
},
+ {
+ name: "VarLive",
+ generic: true,
+ },
}
func (o Op) Asm() int { return opcodeTable[o].asm }
diff --git a/src/cmd/compile/internal/x86/ggen.go b/src/cmd/compile/internal/x86/ggen.go
index e559a9f..139b199 100644
--- a/src/cmd/compile/internal/x86/ggen.go
+++ b/src/cmd/compile/internal/x86/ggen.go
@@ -764,9 +764,7 @@
gc.Cgen(nr, &tmp)
gc.Cgen(nl, &tmp)
}
-
- gins(x86.AFUCOMIP, &tmp, &n2)
- gins(x86.AFMOVDP, &tmp, &tmp) // annoying pop but still better than STSW+SAHF
+ gins(x86.AFUCOMPP, &tmp, &n2)
} else {
// TODO(rsc): The moves back and forth to memory
// here are for truncating the value to 32 bits.
@@ -783,9 +781,9 @@
gc.Cgen(nl, &t2)
gmove(&t2, &tmp)
gins(x86.AFCOMFP, &t1, &tmp)
- gins(x86.AFSTSW, nil, &ax)
- gins(x86.ASAHF, nil, nil)
}
+ gins(x86.AFSTSW, nil, &ax)
+ gins(x86.ASAHF, nil, nil)
} else {
// Not 387
if !nl.Addable {
diff --git a/src/cmd/compile/internal/x86/gsubr.go b/src/cmd/compile/internal/x86/gsubr.go
index 0397857..9859571 100644
--- a/src/cmd/compile/internal/x86/gsubr.go
+++ b/src/cmd/compile/internal/x86/gsubr.go
@@ -1198,14 +1198,17 @@
// if 0 > v { answer = 0 }
gins(x86.AFMOVD, &zerof, &f0)
-
- gins(x86.AFUCOMIP, &f0, &f1)
+ gins(x86.AFUCOMP, &f0, &f1)
+ gins(x86.AFSTSW, nil, &ax)
+ gins(x86.ASAHF, nil, nil)
p1 := gc.Gbranch(optoas(gc.OGT, gc.Types[tt]), nil, 0)
// if 1<<64 <= v { answer = 0 too }
gins(x86.AFMOVD, &two64f, &f0)
- gins(x86.AFUCOMIP, &f0, &f1)
+ gins(x86.AFUCOMP, &f0, &f1)
+ gins(x86.AFSTSW, nil, &ax)
+ gins(x86.ASAHF, nil, nil)
p2 := gc.Gbranch(optoas(gc.OGT, gc.Types[tt]), nil, 0)
gc.Patch(p1, gc.Pc)
gins(x86.AFMOVVP, &f0, t) // don't care about t, but will pop the stack
@@ -1235,7 +1238,9 @@
// actual work
gins(x86.AFMOVD, &two63f, &f0)
- gins(x86.AFUCOMIP, &f0, &f1)
+ gins(x86.AFUCOMP, &f0, &f1)
+ gins(x86.AFSTSW, nil, &ax)
+ gins(x86.ASAHF, nil, nil)
p2 = gc.Gbranch(optoas(gc.OLE, gc.Types[tt]), nil, 0)
gins(x86.AFMOVVP, &f0, t)
p3 := gc.Gbranch(obj.AJMP, nil, 0)
diff --git a/src/cmd/compile/internal/x86/prog.go b/src/cmd/compile/internal/x86/prog.go
index 465a21f..3399a28 100644
--- a/src/cmd/compile/internal/x86/prog.go
+++ b/src/cmd/compile/internal/x86/prog.go
@@ -40,6 +40,7 @@
obj.ACHECKNIL: {Flags: gc.LeftRead},
obj.AVARDEF: {Flags: gc.Pseudo | gc.RightWrite},
obj.AVARKILL: {Flags: gc.Pseudo | gc.RightWrite},
+ obj.AVARLIVE: {Flags: gc.Pseudo | gc.LeftRead},
// NOP is an internal no-op that also stands
// for USED and SET annotations, not the Intel opcode.
@@ -91,8 +92,12 @@
x86.AFCOMDPP: {Flags: gc.SizeD | gc.LeftAddr | gc.RightRead},
x86.AFCOMF: {Flags: gc.SizeF | gc.LeftAddr | gc.RightRead},
x86.AFCOMFP: {Flags: gc.SizeF | gc.LeftAddr | gc.RightRead},
- x86.AFUCOMIP: {Flags: gc.SizeF | gc.LeftAddr | gc.RightRead},
- x86.AFCHS: {Flags: gc.SizeD | RightRdwr}, // also SizeF
+ // NOTE(khr): don't use FUCOMI* instructions, not available
+ // on Pentium MMX. See issue 13923.
+ //x86.AFUCOMIP: {Flags: gc.SizeF | gc.LeftAddr | gc.RightRead},
+ x86.AFUCOMP: {Flags: gc.SizeD | gc.LeftRead | gc.RightRead},
+ x86.AFUCOMPP: {Flags: gc.SizeD | gc.LeftRead | gc.RightRead},
+ x86.AFCHS: {Flags: gc.SizeD | RightRdwr}, // also SizeF
x86.AFDIVDP: {Flags: gc.SizeD | gc.LeftAddr | RightRdwr},
x86.AFDIVF: {Flags: gc.SizeF | gc.LeftAddr | RightRdwr},