[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},