mirror of
https://github.com/golang/go
synced 2024-11-23 15:20:03 -07:00
cmd/compile: implement min/max builtins
Updates #59488. Change-Id: I254da7cca071eeb5af2f8aecdcd9461703fe8677 Reviewed-on: https://go-review.googlesource.com/c/go/+/496257 Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com> Auto-Submit: Matthew Dempsky <mdempsky@google.com> Reviewed-by: Keith Randall <khr@golang.org> TryBot-Result: Gopher Robot <gobot@golang.org> Run-TryBot: Matthew Dempsky <mdempsky@google.com> Reviewed-by: Keith Randall <khr@google.com>
This commit is contained in:
parent
a6a25869f0
commit
b0f15b4ac0
@ -186,7 +186,7 @@ func (e *escape) callCommon(ks []hole, call ir.Node, init *ir.Nodes, wrapper *ir
|
||||
argument(e.discardHole(), &call.X)
|
||||
argument(e.discardHole(), &call.Y)
|
||||
|
||||
case ir.ODELETE, ir.OPRINT, ir.OPRINTN, ir.ORECOVER:
|
||||
case ir.ODELETE, ir.OMAX, ir.OMIN, ir.OPRINT, ir.OPRINTN, ir.ORECOVER:
|
||||
call := call.(*ir.CallExpr)
|
||||
fixRecoverCall(call)
|
||||
for i := range call.Args {
|
||||
|
@ -139,7 +139,7 @@ func (e *escape) exprSkipInit(k hole, n ir.Node) {
|
||||
e.discard(n.X)
|
||||
|
||||
case ir.OCALLMETH, ir.OCALLFUNC, ir.OCALLINTER, ir.OINLCALL,
|
||||
ir.OLEN, ir.OCAP, ir.OCOMPLEX, ir.OREAL, ir.OIMAG, ir.OAPPEND, ir.OCOPY, ir.ORECOVER,
|
||||
ir.OLEN, ir.OCAP, ir.OMIN, ir.OMAX, ir.OCOMPLEX, ir.OREAL, ir.OIMAG, ir.OAPPEND, ir.OCOPY, ir.ORECOVER,
|
||||
ir.OUNSAFEADD, ir.OUNSAFESLICE, ir.OUNSAFESTRING, ir.OUNSAFESTRINGDATA, ir.OUNSAFESLICEDATA:
|
||||
e.call([]hole{k}, n)
|
||||
|
||||
|
@ -174,7 +174,7 @@ func (n *CallExpr) SetOp(op Op) {
|
||||
OCALL, OCALLFUNC, OCALLINTER, OCALLMETH,
|
||||
ODELETE,
|
||||
OGETG, OGETCALLERPC, OGETCALLERSP,
|
||||
OMAKE, OPRINT, OPRINTN,
|
||||
OMAKE, OMAX, OMIN, OPRINT, OPRINTN,
|
||||
ORECOVER, ORECOVERFP:
|
||||
n.op = op
|
||||
}
|
||||
|
@ -63,6 +63,8 @@ var OpNames = []string{
|
||||
OLT: "<",
|
||||
OMAKE: "make",
|
||||
ONEG: "-",
|
||||
OMAX: "max",
|
||||
OMIN: "min",
|
||||
OMOD: "%",
|
||||
OMUL: "*",
|
||||
ONEW: "new",
|
||||
@ -198,6 +200,8 @@ var OpPrec = []int{
|
||||
OMAKESLICECOPY: 8,
|
||||
OMAKE: 8,
|
||||
OMAPLIT: 8,
|
||||
OMAX: 8,
|
||||
OMIN: 8,
|
||||
ONAME: 8,
|
||||
ONEW: 8,
|
||||
ONIL: 8,
|
||||
@ -788,6 +792,8 @@ func exprFmt(n Node, s fmt.State, prec int) {
|
||||
case OAPPEND,
|
||||
ODELETE,
|
||||
OMAKE,
|
||||
OMAX,
|
||||
OMIN,
|
||||
ORECOVER,
|
||||
OPRINT,
|
||||
OPRINTN:
|
||||
|
@ -242,6 +242,8 @@ const (
|
||||
ORECV // <-X
|
||||
ORUNESTR // Type(X) (Type is string, X is rune)
|
||||
OSELRECV2 // like OAS2: Lhs = Rhs where len(Lhs)=2, len(Rhs)=1, Rhs[0].Op = ORECV (appears as .Var of OCASE)
|
||||
OMIN // min(List)
|
||||
OMAX // max(List)
|
||||
OREAL // real(X)
|
||||
OIMAG // imag(X)
|
||||
OCOMPLEX // complex(X, Y)
|
||||
|
@ -115,60 +115,62 @@ func _() {
|
||||
_ = x[ORECV-104]
|
||||
_ = x[ORUNESTR-105]
|
||||
_ = x[OSELRECV2-106]
|
||||
_ = x[OREAL-107]
|
||||
_ = x[OIMAG-108]
|
||||
_ = x[OCOMPLEX-109]
|
||||
_ = x[OALIGNOF-110]
|
||||
_ = x[OOFFSETOF-111]
|
||||
_ = x[OSIZEOF-112]
|
||||
_ = x[OUNSAFEADD-113]
|
||||
_ = x[OUNSAFESLICE-114]
|
||||
_ = x[OUNSAFESLICEDATA-115]
|
||||
_ = x[OUNSAFESTRING-116]
|
||||
_ = x[OUNSAFESTRINGDATA-117]
|
||||
_ = x[OMETHEXPR-118]
|
||||
_ = x[OMETHVALUE-119]
|
||||
_ = x[OBLOCK-120]
|
||||
_ = x[OBREAK-121]
|
||||
_ = x[OCASE-122]
|
||||
_ = x[OCONTINUE-123]
|
||||
_ = x[ODEFER-124]
|
||||
_ = x[OFALL-125]
|
||||
_ = x[OFOR-126]
|
||||
_ = x[OGOTO-127]
|
||||
_ = x[OIF-128]
|
||||
_ = x[OLABEL-129]
|
||||
_ = x[OGO-130]
|
||||
_ = x[ORANGE-131]
|
||||
_ = x[ORETURN-132]
|
||||
_ = x[OSELECT-133]
|
||||
_ = x[OSWITCH-134]
|
||||
_ = x[OTYPESW-135]
|
||||
_ = x[OFUNCINST-136]
|
||||
_ = x[OINLCALL-137]
|
||||
_ = x[OEFACE-138]
|
||||
_ = x[OITAB-139]
|
||||
_ = x[OIDATA-140]
|
||||
_ = x[OSPTR-141]
|
||||
_ = x[OCFUNC-142]
|
||||
_ = x[OCHECKNIL-143]
|
||||
_ = x[ORESULT-144]
|
||||
_ = x[OINLMARK-145]
|
||||
_ = x[OLINKSYMOFFSET-146]
|
||||
_ = x[OJUMPTABLE-147]
|
||||
_ = x[ODYNAMICDOTTYPE-148]
|
||||
_ = x[ODYNAMICDOTTYPE2-149]
|
||||
_ = x[ODYNAMICTYPE-150]
|
||||
_ = x[OTAILCALL-151]
|
||||
_ = x[OGETG-152]
|
||||
_ = x[OGETCALLERPC-153]
|
||||
_ = x[OGETCALLERSP-154]
|
||||
_ = x[OEND-155]
|
||||
_ = x[OMIN-107]
|
||||
_ = x[OMAX-108]
|
||||
_ = x[OREAL-109]
|
||||
_ = x[OIMAG-110]
|
||||
_ = x[OCOMPLEX-111]
|
||||
_ = x[OALIGNOF-112]
|
||||
_ = x[OOFFSETOF-113]
|
||||
_ = x[OSIZEOF-114]
|
||||
_ = x[OUNSAFEADD-115]
|
||||
_ = x[OUNSAFESLICE-116]
|
||||
_ = x[OUNSAFESLICEDATA-117]
|
||||
_ = x[OUNSAFESTRING-118]
|
||||
_ = x[OUNSAFESTRINGDATA-119]
|
||||
_ = x[OMETHEXPR-120]
|
||||
_ = x[OMETHVALUE-121]
|
||||
_ = x[OBLOCK-122]
|
||||
_ = x[OBREAK-123]
|
||||
_ = x[OCASE-124]
|
||||
_ = x[OCONTINUE-125]
|
||||
_ = x[ODEFER-126]
|
||||
_ = x[OFALL-127]
|
||||
_ = x[OFOR-128]
|
||||
_ = x[OGOTO-129]
|
||||
_ = x[OIF-130]
|
||||
_ = x[OLABEL-131]
|
||||
_ = x[OGO-132]
|
||||
_ = x[ORANGE-133]
|
||||
_ = x[ORETURN-134]
|
||||
_ = x[OSELECT-135]
|
||||
_ = x[OSWITCH-136]
|
||||
_ = x[OTYPESW-137]
|
||||
_ = x[OFUNCINST-138]
|
||||
_ = x[OINLCALL-139]
|
||||
_ = x[OEFACE-140]
|
||||
_ = x[OITAB-141]
|
||||
_ = x[OIDATA-142]
|
||||
_ = x[OSPTR-143]
|
||||
_ = x[OCFUNC-144]
|
||||
_ = x[OCHECKNIL-145]
|
||||
_ = x[ORESULT-146]
|
||||
_ = x[OINLMARK-147]
|
||||
_ = x[OLINKSYMOFFSET-148]
|
||||
_ = x[OJUMPTABLE-149]
|
||||
_ = x[ODYNAMICDOTTYPE-150]
|
||||
_ = x[ODYNAMICDOTTYPE2-151]
|
||||
_ = x[ODYNAMICTYPE-152]
|
||||
_ = x[OTAILCALL-153]
|
||||
_ = x[OGETG-154]
|
||||
_ = x[OGETCALLERPC-155]
|
||||
_ = x[OGETCALLERSP-156]
|
||||
_ = x[OEND-157]
|
||||
}
|
||||
|
||||
const _Op_name = "XXXNAMENONAMETYPELITERALNILADDSUBORXORADDSTRADDRANDANDAPPENDBYTES2STRBYTES2STRTMPRUNES2STRSTR2BYTESSTR2BYTESTMPSTR2RUNESSLICE2ARRSLICE2ARRPTRASAS2AS2DOTTYPEAS2FUNCAS2MAPRAS2RECVASOPCALLCALLFUNCCALLMETHCALLINTERCAPCLEARCLOSECLOSURECOMPLITMAPLITSTRUCTLITARRAYLITSLICELITPTRLITCONVCONVIFACECONVIDATACONVNOPCOPYDCLDCLFUNCDCLCONSTDCLTYPEDELETEDOTDOTPTRDOTMETHDOTINTERXDOTDOTTYPEDOTTYPE2EQNELTLEGEGTDEREFINDEXINDEXMAPKEYSTRUCTKEYLENMAKEMAKECHANMAKEMAPMAKESLICEMAKESLICECOPYMULDIVMODLSHRSHANDANDNOTNEWNOTBITNOTPLUSNEGORORPANICPRINTPRINTNPARENSENDSLICESLICEARRSLICESTRSLICE3SLICE3ARRSLICEHEADERSTRINGHEADERRECOVERRECOVERFPRECVRUNESTRSELRECV2REALIMAGCOMPLEXALIGNOFOFFSETOFSIZEOFUNSAFEADDUNSAFESLICEUNSAFESLICEDATAUNSAFESTRINGUNSAFESTRINGDATAMETHEXPRMETHVALUEBLOCKBREAKCASECONTINUEDEFERFALLFORGOTOIFLABELGORANGERETURNSELECTSWITCHTYPESWFUNCINSTINLCALLEFACEITABIDATASPTRCFUNCCHECKNILRESULTINLMARKLINKSYMOFFSETJUMPTABLEDYNAMICDOTTYPEDYNAMICDOTTYPE2DYNAMICTYPETAILCALLGETGGETCALLERPCGETCALLERSPEND"
|
||||
const _Op_name = "XXXNAMENONAMETYPELITERALNILADDSUBORXORADDSTRADDRANDANDAPPENDBYTES2STRBYTES2STRTMPRUNES2STRSTR2BYTESSTR2BYTESTMPSTR2RUNESSLICE2ARRSLICE2ARRPTRASAS2AS2DOTTYPEAS2FUNCAS2MAPRAS2RECVASOPCALLCALLFUNCCALLMETHCALLINTERCAPCLEARCLOSECLOSURECOMPLITMAPLITSTRUCTLITARRAYLITSLICELITPTRLITCONVCONVIFACECONVIDATACONVNOPCOPYDCLDCLFUNCDCLCONSTDCLTYPEDELETEDOTDOTPTRDOTMETHDOTINTERXDOTDOTTYPEDOTTYPE2EQNELTLEGEGTDEREFINDEXINDEXMAPKEYSTRUCTKEYLENMAKEMAKECHANMAKEMAPMAKESLICEMAKESLICECOPYMULDIVMODLSHRSHANDANDNOTNEWNOTBITNOTPLUSNEGORORPANICPRINTPRINTNPARENSENDSLICESLICEARRSLICESTRSLICE3SLICE3ARRSLICEHEADERSTRINGHEADERRECOVERRECOVERFPRECVRUNESTRSELRECV2MINMAXREALIMAGCOMPLEXALIGNOFOFFSETOFSIZEOFUNSAFEADDUNSAFESLICEUNSAFESLICEDATAUNSAFESTRINGUNSAFESTRINGDATAMETHEXPRMETHVALUEBLOCKBREAKCASECONTINUEDEFERFALLFORGOTOIFLABELGORANGERETURNSELECTSWITCHTYPESWFUNCINSTINLCALLEFACEITABIDATASPTRCFUNCCHECKNILRESULTINLMARKLINKSYMOFFSETJUMPTABLEDYNAMICDOTTYPEDYNAMICDOTTYPE2DYNAMICTYPETAILCALLGETGGETCALLERPCGETCALLERSPEND"
|
||||
|
||||
var _Op_index = [...]uint16{0, 3, 7, 13, 17, 24, 27, 30, 33, 35, 38, 44, 48, 54, 60, 69, 81, 90, 99, 111, 120, 129, 141, 143, 146, 156, 163, 170, 177, 181, 185, 193, 201, 210, 213, 218, 223, 230, 237, 243, 252, 260, 268, 274, 278, 287, 296, 303, 307, 310, 317, 325, 332, 338, 341, 347, 354, 362, 366, 373, 381, 383, 385, 387, 389, 391, 393, 398, 403, 411, 414, 423, 426, 430, 438, 445, 454, 467, 470, 473, 476, 479, 482, 485, 491, 494, 497, 503, 507, 510, 514, 519, 524, 530, 535, 539, 544, 552, 560, 566, 575, 586, 598, 605, 614, 618, 625, 633, 637, 641, 648, 655, 663, 669, 678, 689, 704, 716, 732, 740, 749, 754, 759, 763, 771, 776, 780, 783, 787, 789, 794, 796, 801, 807, 813, 819, 825, 833, 840, 845, 849, 854, 858, 863, 871, 877, 884, 897, 906, 920, 935, 946, 954, 958, 969, 980, 983}
|
||||
var _Op_index = [...]uint16{0, 3, 7, 13, 17, 24, 27, 30, 33, 35, 38, 44, 48, 54, 60, 69, 81, 90, 99, 111, 120, 129, 141, 143, 146, 156, 163, 170, 177, 181, 185, 193, 201, 210, 213, 218, 223, 230, 237, 243, 252, 260, 268, 274, 278, 287, 296, 303, 307, 310, 317, 325, 332, 338, 341, 347, 354, 362, 366, 373, 381, 383, 385, 387, 389, 391, 393, 398, 403, 411, 414, 423, 426, 430, 438, 445, 454, 467, 470, 473, 476, 479, 482, 485, 491, 494, 497, 503, 507, 510, 514, 519, 524, 530, 535, 539, 544, 552, 560, 566, 575, 586, 598, 605, 614, 618, 625, 633, 636, 639, 643, 647, 654, 661, 669, 675, 684, 695, 710, 722, 738, 746, 755, 760, 765, 769, 777, 782, 786, 789, 793, 795, 800, 802, 807, 813, 819, 825, 831, 839, 846, 851, 855, 860, 864, 869, 877, 883, 890, 903, 912, 926, 941, 952, 960, 964, 975, 986, 989}
|
||||
|
||||
func (i Op) String() string {
|
||||
if i >= Op(len(_Op_index)-1) {
|
||||
|
@ -3314,6 +3314,9 @@ func (s *state) exprCheckPtr(n ir.Node, checkPtrOK bool) *ssa.Value {
|
||||
case ir.OAPPEND:
|
||||
return s.append(n.(*ir.CallExpr), false)
|
||||
|
||||
case ir.OMIN, ir.OMAX:
|
||||
return s.minMax(n.(*ir.CallExpr))
|
||||
|
||||
case ir.OSTRUCTLIT, ir.OARRAYLIT:
|
||||
// All literals with nonzero fields have already been
|
||||
// rewritten during walk. Any that remain are just T{}
|
||||
@ -3547,6 +3550,92 @@ func (s *state) append(n *ir.CallExpr, inplace bool) *ssa.Value {
|
||||
return s.newValue3(ssa.OpSliceMake, n.Type(), p, l, c)
|
||||
}
|
||||
|
||||
// minMax converts an OMIN/OMAX builtin call into SSA.
|
||||
func (s *state) minMax(n *ir.CallExpr) *ssa.Value {
|
||||
// The OMIN/OMAX builtin is variadic, but its semantics are
|
||||
// equivalent to left-folding a binary min/max operation across the
|
||||
// arguments list.
|
||||
fold := func(op func(x, a *ssa.Value) *ssa.Value) *ssa.Value {
|
||||
x := s.expr(n.Args[0])
|
||||
for _, arg := range n.Args[1:] {
|
||||
x = op(x, s.expr(arg))
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
typ := n.Type()
|
||||
|
||||
if typ.IsFloat() || typ.IsString() {
|
||||
// min/max semantics for floats are tricky because of NaNs and
|
||||
// negative zero, so we let the runtime handle this instead.
|
||||
//
|
||||
// Strings are conceptually simpler, but we currently desugar
|
||||
// string comparisons during walk, not ssagen.
|
||||
|
||||
var name string
|
||||
switch typ.Kind() {
|
||||
case types.TFLOAT32:
|
||||
switch n.Op() {
|
||||
case ir.OMIN:
|
||||
name = "fmin32"
|
||||
case ir.OMAX:
|
||||
name = "fmax32"
|
||||
}
|
||||
case types.TFLOAT64:
|
||||
switch n.Op() {
|
||||
case ir.OMIN:
|
||||
name = "fmin64"
|
||||
case ir.OMAX:
|
||||
name = "fmax64"
|
||||
}
|
||||
case types.TSTRING:
|
||||
switch n.Op() {
|
||||
case ir.OMIN:
|
||||
name = "strmin"
|
||||
case ir.OMAX:
|
||||
name = "strmax"
|
||||
}
|
||||
}
|
||||
fn := typecheck.LookupRuntimeFunc(name)
|
||||
|
||||
return fold(func(x, a *ssa.Value) *ssa.Value {
|
||||
return s.rtcall(fn, true, []*types.Type{typ}, x, a)[0]
|
||||
})
|
||||
}
|
||||
|
||||
lt := s.ssaOp(ir.OLT, typ)
|
||||
|
||||
return fold(func(x, a *ssa.Value) *ssa.Value {
|
||||
switch n.Op() {
|
||||
case ir.OMIN:
|
||||
// a < x ? a : x
|
||||
return s.ternary(s.newValue2(lt, types.Types[types.TBOOL], a, x), a, x)
|
||||
case ir.OMAX:
|
||||
// x < a ? a : x
|
||||
return s.ternary(s.newValue2(lt, types.Types[types.TBOOL], x, a), a, x)
|
||||
}
|
||||
panic("unreachable")
|
||||
})
|
||||
}
|
||||
|
||||
// ternary emits code to evaluate cond ? x : y.
|
||||
func (s *state) ternary(cond, x, y *ssa.Value) *ssa.Value {
|
||||
bThen := s.f.NewBlock(ssa.BlockPlain)
|
||||
bElse := s.f.NewBlock(ssa.BlockPlain)
|
||||
|
||||
b := s.endBlock()
|
||||
b.Kind = ssa.BlockIf
|
||||
b.SetControl(cond)
|
||||
b.AddEdgeTo(bThen)
|
||||
b.AddEdgeTo(bElse)
|
||||
|
||||
s.startBlock(bElse)
|
||||
s.endBlock().AddEdgeTo(bThen)
|
||||
|
||||
s.startBlock(bThen)
|
||||
return s.newValue2(ssa.OpPhi, x.Type, x, y)
|
||||
}
|
||||
|
||||
// condBranch evaluates the boolean expression cond and branches to yes
|
||||
// if cond is true and no if cond is false.
|
||||
// This function is intended to handle && and || better than just calling
|
||||
|
@ -539,6 +539,8 @@ func callOrChan(n ir.Node) bool {
|
||||
ir.OIMAG,
|
||||
ir.OLEN,
|
||||
ir.OMAKE,
|
||||
ir.OMAX,
|
||||
ir.OMIN,
|
||||
ir.ONEW,
|
||||
ir.OPANIC,
|
||||
ir.OPRINT,
|
||||
|
@ -254,7 +254,7 @@ func tcCall(n *ir.CallExpr, top int) ir.Node {
|
||||
default:
|
||||
base.Fatalf("unknown builtin %v", l)
|
||||
|
||||
case ir.OAPPEND, ir.ODELETE, ir.OMAKE, ir.OPRINT, ir.OPRINTN, ir.ORECOVER:
|
||||
case ir.OAPPEND, ir.ODELETE, ir.OMAKE, ir.OMAX, ir.OMIN, ir.OPRINT, ir.OPRINTN, ir.ORECOVER:
|
||||
n.SetOp(l.BuiltinOp)
|
||||
n.X = nil
|
||||
n.SetTypecheck(0) // re-typechecking new op is OK, not a loop
|
||||
@ -803,6 +803,19 @@ func tcPrint(n *ir.CallExpr) ir.Node {
|
||||
return n
|
||||
}
|
||||
|
||||
// tcMinMax typechecks an OMIN or OMAX node.
|
||||
func tcMinMax(n *ir.CallExpr) ir.Node {
|
||||
typecheckargs(n)
|
||||
arg0 := n.Args[0]
|
||||
for _, arg := range n.Args[1:] {
|
||||
if !types.Identical(arg.Type(), arg0.Type()) {
|
||||
base.FatalfAt(n.Pos(), "mismatched arguments: %L and %L", arg0, arg)
|
||||
}
|
||||
}
|
||||
n.SetType(arg0.Type())
|
||||
return n
|
||||
}
|
||||
|
||||
// tcRealImag typechecks an OREAL or OIMAG node.
|
||||
func tcRealImag(n *ir.UnaryExpr) ir.Node {
|
||||
n.X = Expr(n.X)
|
||||
|
@ -278,6 +278,8 @@ func tcGoDefer(n *ir.GoDeferStmt) {
|
||||
ir.OCLOSE,
|
||||
ir.OCOPY,
|
||||
ir.ODELETE,
|
||||
ir.OMAX,
|
||||
ir.OMIN,
|
||||
ir.OPANIC,
|
||||
ir.OPRINT,
|
||||
ir.OPRINTN,
|
||||
|
@ -332,8 +332,8 @@ func typecheck(n ir.Node, top int) (res ir.Node) {
|
||||
isExpr = false
|
||||
}
|
||||
}
|
||||
case ir.OAPPEND:
|
||||
// Must be used (and not BinaryExpr/UnaryExpr).
|
||||
case ir.OAPPEND, ir.OMIN, ir.OMAX:
|
||||
// Must be used.
|
||||
isStmt = false
|
||||
case ir.OCLEAR, ir.OCLOSE, ir.ODELETE, ir.OPANIC, ir.OPRINT, ir.OPRINTN:
|
||||
// Must not be used.
|
||||
@ -605,6 +605,10 @@ func typecheck1(n ir.Node, top int) ir.Node {
|
||||
n := n.(*ir.UnaryExpr)
|
||||
return tcLenCap(n)
|
||||
|
||||
case ir.OMIN, ir.OMAX:
|
||||
n := n.(*ir.CallExpr)
|
||||
return tcMinMax(n)
|
||||
|
||||
case ir.OREAL, ir.OIMAG:
|
||||
n := n.(*ir.UnaryExpr)
|
||||
return tcRealImag(n)
|
||||
|
@ -42,6 +42,8 @@ var builtinFuncs = [...]struct {
|
||||
{"imag", ir.OIMAG},
|
||||
{"len", ir.OLEN},
|
||||
{"make", ir.OMAKE},
|
||||
{"max", ir.OMAX},
|
||||
{"min", ir.OMIN},
|
||||
{"new", ir.ONEW},
|
||||
{"panic", ir.OPANIC},
|
||||
{"print", ir.OPRINT},
|
||||
|
@ -525,6 +525,12 @@ func walkNew(n *ir.UnaryExpr, init *ir.Nodes) ir.Node {
|
||||
return n
|
||||
}
|
||||
|
||||
func walkMinMax(n *ir.CallExpr, init *ir.Nodes) ir.Node {
|
||||
init.Append(ir.TakeInit(n)...)
|
||||
walkExprList(n.Args, init)
|
||||
return n
|
||||
}
|
||||
|
||||
// generate code for print.
|
||||
func walkPrint(nn *ir.CallExpr, init *ir.Nodes) ir.Node {
|
||||
// Hoist all the argument evaluation up before the lock.
|
||||
|
@ -98,6 +98,10 @@ func walkExpr1(n ir.Node, init *ir.Nodes) ir.Node {
|
||||
n := n.(*ir.SelectorExpr)
|
||||
return n.FuncName()
|
||||
|
||||
case ir.OMIN, ir.OMAX:
|
||||
n := n.(*ir.CallExpr)
|
||||
return walkMinMax(n, init)
|
||||
|
||||
case ir.ONOT, ir.ONEG, ir.OPLUS, ir.OBITNOT, ir.OREAL, ir.OIMAG, ir.OSPTR, ir.OITAB, ir.OIDATA:
|
||||
n := n.(*ir.UnaryExpr)
|
||||
n.X = walkExpr(n.X, init)
|
||||
|
@ -755,7 +755,7 @@ func (o *orderState) stmt(n ir.Node) {
|
||||
o.out = append(o.out, n)
|
||||
o.popTemp(t)
|
||||
|
||||
case ir.OPRINT, ir.OPRINTN, ir.ORECOVERFP:
|
||||
case ir.OMAX, ir.OMIN, ir.OPRINT, ir.OPRINTN, ir.ORECOVERFP:
|
||||
n := n.(*ir.CallExpr)
|
||||
t := o.markTemp()
|
||||
o.call(n)
|
||||
|
72
src/runtime/minmax.go
Normal file
72
src/runtime/minmax.go
Normal file
@ -0,0 +1,72 @@
|
||||
// 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.
|
||||
|
||||
package runtime
|
||||
|
||||
import "unsafe"
|
||||
|
||||
func strmin(x, y string) string {
|
||||
if y < x {
|
||||
return y
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
func strmax(x, y string) string {
|
||||
if y > x {
|
||||
return y
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
func fmin32(x, y float32) float32 { return fmin(x, y) }
|
||||
func fmin64(x, y float64) float64 { return fmin(x, y) }
|
||||
func fmax32(x, y float32) float32 { return fmax(x, y) }
|
||||
func fmax64(x, y float64) float64 { return fmax(x, y) }
|
||||
|
||||
type floaty interface{ ~float32 | ~float64 }
|
||||
|
||||
func fmin[F floaty](x, y F) F {
|
||||
if y != y || y < x {
|
||||
return y
|
||||
}
|
||||
if x != x || x < y || x != 0 {
|
||||
return x
|
||||
}
|
||||
// x and y are both ±0
|
||||
// if either is -0, return -0; else return +0
|
||||
return forbits(x, y)
|
||||
}
|
||||
|
||||
func fmax[F floaty](x, y F) F {
|
||||
if y != y || y > x {
|
||||
return y
|
||||
}
|
||||
if x != x || x > y || x != 0 {
|
||||
return x
|
||||
}
|
||||
// x and y are both ±0
|
||||
// if both are -0, return -0; else return +0
|
||||
return fandbits(x, y)
|
||||
}
|
||||
|
||||
func forbits[F floaty](x, y F) F {
|
||||
switch unsafe.Sizeof(x) {
|
||||
case 4:
|
||||
*(*uint32)(unsafe.Pointer(&x)) |= *(*uint32)(unsafe.Pointer(&y))
|
||||
case 8:
|
||||
*(*uint64)(unsafe.Pointer(&x)) |= *(*uint64)(unsafe.Pointer(&y))
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
func fandbits[F floaty](x, y F) F {
|
||||
switch unsafe.Sizeof(x) {
|
||||
case 4:
|
||||
*(*uint32)(unsafe.Pointer(&x)) &= *(*uint32)(unsafe.Pointer(&y))
|
||||
case 8:
|
||||
*(*uint64)(unsafe.Pointer(&x)) &= *(*uint64)(unsafe.Pointer(&y))
|
||||
}
|
||||
return x
|
||||
}
|
129
src/runtime/minmax_test.go
Normal file
129
src/runtime/minmax_test.go
Normal file
@ -0,0 +1,129 @@
|
||||
// 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.
|
||||
|
||||
package runtime_test
|
||||
|
||||
import (
|
||||
"math"
|
||||
"strings"
|
||||
"testing"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var (
|
||||
zero = math.Copysign(0, +1)
|
||||
negZero = math.Copysign(0, -1)
|
||||
inf = math.Inf(+1)
|
||||
negInf = math.Inf(-1)
|
||||
nan = math.NaN()
|
||||
)
|
||||
|
||||
var tests = []struct{ min, max float64 }{
|
||||
{1, 2},
|
||||
{-2, 1},
|
||||
{negZero, zero},
|
||||
{zero, inf},
|
||||
{negInf, zero},
|
||||
{negInf, inf},
|
||||
{1, inf},
|
||||
{negInf, 1},
|
||||
}
|
||||
|
||||
var all = []float64{1, 2, -1, -2, zero, negZero, inf, negInf, nan}
|
||||
|
||||
func eq(x, y float64) bool {
|
||||
return x == y && math.Signbit(x) == math.Signbit(y)
|
||||
}
|
||||
|
||||
func TestMinFloat(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
if z := min(tt.min, tt.max); !eq(z, tt.min) {
|
||||
t.Errorf("min(%v, %v) = %v, want %v", tt.min, tt.max, z, tt.min)
|
||||
}
|
||||
if z := min(tt.max, tt.min); !eq(z, tt.min) {
|
||||
t.Errorf("min(%v, %v) = %v, want %v", tt.max, tt.min, z, tt.min)
|
||||
}
|
||||
}
|
||||
for _, x := range all {
|
||||
if z := min(nan, x); !math.IsNaN(z) {
|
||||
t.Errorf("min(%v, %v) = %v, want %v", nan, x, z, nan)
|
||||
}
|
||||
if z := min(x, nan); !math.IsNaN(z) {
|
||||
t.Errorf("min(%v, %v) = %v, want %v", nan, x, z, nan)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMaxFloat(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
if z := max(tt.min, tt.max); !eq(z, tt.max) {
|
||||
t.Errorf("max(%v, %v) = %v, want %v", tt.min, tt.max, z, tt.max)
|
||||
}
|
||||
if z := max(tt.max, tt.min); !eq(z, tt.max) {
|
||||
t.Errorf("max(%v, %v) = %v, want %v", tt.max, tt.min, z, tt.max)
|
||||
}
|
||||
}
|
||||
for _, x := range all {
|
||||
if z := max(nan, x); !math.IsNaN(z) {
|
||||
t.Errorf("min(%v, %v) = %v, want %v", nan, x, z, nan)
|
||||
}
|
||||
if z := max(x, nan); !math.IsNaN(z) {
|
||||
t.Errorf("min(%v, %v) = %v, want %v", nan, x, z, nan)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// testMinMax tests that min/max behave correctly on every pair of
|
||||
// values in vals.
|
||||
//
|
||||
// vals should be a sequence of values in strictly ascending order.
|
||||
func testMinMax[T int | uint8 | string](t *testing.T, vals ...T) {
|
||||
for i, x := range vals {
|
||||
for _, y := range vals[i+1:] {
|
||||
if !(x < y) {
|
||||
t.Fatalf("values out of order: !(%v < %v)", x, y)
|
||||
}
|
||||
|
||||
if z := min(x, y); z != x {
|
||||
t.Errorf("min(%v, %v) = %v, want %v", x, y, z, x)
|
||||
}
|
||||
if z := min(y, x); z != x {
|
||||
t.Errorf("min(%v, %v) = %v, want %v", y, x, z, x)
|
||||
}
|
||||
|
||||
if z := max(x, y); z != y {
|
||||
t.Errorf("max(%v, %v) = %v, want %v", x, y, z, y)
|
||||
}
|
||||
if z := max(y, x); z != y {
|
||||
t.Errorf("max(%v, %v) = %v, want %v", y, x, z, y)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMinMaxInt(t *testing.T) { testMinMax[int](t, -7, 0, 9) }
|
||||
func TestMinMaxUint8(t *testing.T) { testMinMax[uint8](t, 0, 1, 2, 4, 7) }
|
||||
func TestMinMaxString(t *testing.T) { testMinMax[string](t, "a", "b", "c") }
|
||||
|
||||
// TestMinMaxStringTies ensures that min(a, b) returns a when a == b.
|
||||
func TestMinMaxStringTies(t *testing.T) {
|
||||
s := "xxx"
|
||||
x := strings.Split(s, "")
|
||||
|
||||
test := func(i, j, k int) {
|
||||
if z := min(x[i], x[j], x[k]); unsafe.StringData(z) != unsafe.StringData(x[i]) {
|
||||
t.Errorf("min(x[%v], x[%v], x[%v]) = %p, want %p", i, j, k, unsafe.StringData(z), unsafe.StringData(x[i]))
|
||||
}
|
||||
if z := max(x[i], x[j], x[k]); unsafe.StringData(z) != unsafe.StringData(x[i]) {
|
||||
t.Errorf("max(x[%v], x[%v], x[%v]) = %p, want %p", i, j, k, unsafe.StringData(z), unsafe.StringData(x[i]))
|
||||
}
|
||||
}
|
||||
|
||||
test(0, 1, 2)
|
||||
test(0, 2, 1)
|
||||
test(1, 0, 2)
|
||||
test(1, 2, 0)
|
||||
test(2, 0, 1)
|
||||
test(2, 1, 0)
|
||||
}
|
Loading…
Reference in New Issue
Block a user