2016-02-26 14:56:31 -08:00
|
|
|
// 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 "fmt"
|
|
|
|
|
|
|
|
const (
|
|
|
|
// These values are known by runtime.
|
|
|
|
ANOEQ = iota
|
|
|
|
AMEM0
|
|
|
|
AMEM8
|
|
|
|
AMEM16
|
|
|
|
AMEM32
|
|
|
|
AMEM64
|
|
|
|
AMEM128
|
|
|
|
ASTRING
|
|
|
|
AINTER
|
|
|
|
ANILINTER
|
|
|
|
AFLOAT32
|
|
|
|
AFLOAT64
|
|
|
|
ACPLX64
|
|
|
|
ACPLX128
|
|
|
|
AMEM = 100
|
|
|
|
)
|
|
|
|
|
|
|
|
func algtype(t *Type) int {
|
|
|
|
a := algtype1(t, nil)
|
|
|
|
if a == AMEM {
|
|
|
|
switch t.Width {
|
|
|
|
case 0:
|
|
|
|
return AMEM0
|
|
|
|
case 1:
|
|
|
|
return AMEM8
|
|
|
|
case 2:
|
|
|
|
return AMEM16
|
|
|
|
case 4:
|
|
|
|
return AMEM32
|
|
|
|
case 8:
|
|
|
|
return AMEM64
|
|
|
|
case 16:
|
|
|
|
return AMEM128
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return a
|
|
|
|
}
|
|
|
|
|
|
|
|
func algtype1(t *Type, bad **Type) int {
|
|
|
|
if bad != nil {
|
|
|
|
*bad = nil
|
|
|
|
}
|
|
|
|
if t.Broke {
|
|
|
|
return AMEM
|
|
|
|
}
|
|
|
|
if t.Noalg {
|
|
|
|
return ANOEQ
|
|
|
|
}
|
|
|
|
|
|
|
|
switch t.Etype {
|
|
|
|
// will be defined later.
|
|
|
|
case TANY, TFORW:
|
|
|
|
*bad = t
|
|
|
|
|
|
|
|
return -1
|
|
|
|
|
|
|
|
case TINT8,
|
|
|
|
TUINT8,
|
|
|
|
TINT16,
|
|
|
|
TUINT16,
|
|
|
|
TINT32,
|
|
|
|
TUINT32,
|
|
|
|
TINT64,
|
|
|
|
TUINT64,
|
|
|
|
TINT,
|
|
|
|
TUINT,
|
|
|
|
TUINTPTR,
|
|
|
|
TBOOL,
|
|
|
|
TPTR32,
|
|
|
|
TPTR64,
|
|
|
|
TCHAN,
|
|
|
|
TUNSAFEPTR:
|
|
|
|
return AMEM
|
|
|
|
|
|
|
|
case TFUNC, TMAP:
|
|
|
|
if bad != nil {
|
|
|
|
*bad = t
|
|
|
|
}
|
|
|
|
return ANOEQ
|
|
|
|
|
|
|
|
case TFLOAT32:
|
|
|
|
return AFLOAT32
|
|
|
|
|
|
|
|
case TFLOAT64:
|
|
|
|
return AFLOAT64
|
|
|
|
|
|
|
|
case TCOMPLEX64:
|
|
|
|
return ACPLX64
|
|
|
|
|
|
|
|
case TCOMPLEX128:
|
|
|
|
return ACPLX128
|
|
|
|
|
|
|
|
case TSTRING:
|
|
|
|
return ASTRING
|
|
|
|
|
|
|
|
case TINTER:
|
|
|
|
if isnilinter(t) {
|
|
|
|
return ANILINTER
|
|
|
|
}
|
|
|
|
return AINTER
|
|
|
|
|
|
|
|
case TARRAY:
|
|
|
|
if Isslice(t) {
|
|
|
|
if bad != nil {
|
|
|
|
*bad = t
|
|
|
|
}
|
|
|
|
return ANOEQ
|
|
|
|
}
|
|
|
|
|
|
|
|
a := algtype1(t.Type, bad)
|
|
|
|
if a == ANOEQ || a == AMEM {
|
|
|
|
if a == ANOEQ && bad != nil {
|
|
|
|
*bad = t
|
|
|
|
}
|
|
|
|
return a
|
|
|
|
}
|
|
|
|
|
|
|
|
switch t.Bound {
|
|
|
|
case 0:
|
|
|
|
// We checked above that the element type is comparable.
|
|
|
|
return AMEM
|
|
|
|
case 1:
|
|
|
|
// Single-element array is same as its lone element.
|
|
|
|
return a
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1 // needs special compare
|
|
|
|
|
|
|
|
case TSTRUCT:
|
|
|
|
if t.Type != nil && t.Type.Down == nil && !isblanksym(t.Type.Sym) {
|
|
|
|
// One-field struct is same as that one field alone.
|
|
|
|
return algtype1(t.Type.Type, bad)
|
|
|
|
}
|
|
|
|
|
|
|
|
ret := AMEM
|
2016-03-08 03:40:50 -08:00
|
|
|
for f := t.Type; f != nil; f = f.Down {
|
2016-02-26 14:56:31 -08:00
|
|
|
// All fields must be comparable.
|
2016-03-08 03:40:50 -08:00
|
|
|
a := algtype1(f.Type, bad)
|
2016-02-26 14:56:31 -08:00
|
|
|
if a == ANOEQ {
|
|
|
|
return ANOEQ
|
|
|
|
}
|
|
|
|
|
|
|
|
// Blank fields, padded fields, fields with non-memory
|
|
|
|
// equality need special compare.
|
2016-03-08 03:40:50 -08:00
|
|
|
if a != AMEM || isblanksym(f.Sym) || ispaddedfield(t, f) {
|
2016-02-26 14:56:31 -08:00
|
|
|
ret = -1
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret
|
|
|
|
}
|
|
|
|
|
|
|
|
Fatalf("algtype1: unexpected type %v", t)
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
|
|
|
// Generate a helper function to compute the hash of a value of type t.
|
|
|
|
func genhash(sym *Sym, t *Type) {
|
|
|
|
if Debug['r'] != 0 {
|
|
|
|
fmt.Printf("genhash %v %v\n", sym, t)
|
|
|
|
}
|
|
|
|
|
|
|
|
lineno = 1 // less confusing than end of input
|
|
|
|
dclcontext = PEXTERN
|
|
|
|
markdcl()
|
|
|
|
|
|
|
|
// func sym(p *T, h uintptr) uintptr
|
|
|
|
fn := Nod(ODCLFUNC, nil, nil)
|
|
|
|
|
|
|
|
fn.Func.Nname = newname(sym)
|
|
|
|
fn.Func.Nname.Class = PFUNC
|
|
|
|
tfn := Nod(OTFUNC, nil, nil)
|
|
|
|
fn.Func.Nname.Name.Param.Ntype = tfn
|
|
|
|
|
|
|
|
n := Nod(ODCLFIELD, newname(Lookup("p")), typenod(Ptrto(t)))
|
2016-03-07 09:36:24 -08:00
|
|
|
appendNodeSeqNode(&tfn.List, n)
|
2016-02-26 14:56:31 -08:00
|
|
|
np := n.Left
|
|
|
|
n = Nod(ODCLFIELD, newname(Lookup("h")), typenod(Types[TUINTPTR]))
|
2016-03-07 09:36:24 -08:00
|
|
|
appendNodeSeqNode(&tfn.List, n)
|
2016-02-26 14:56:31 -08:00
|
|
|
nh := n.Left
|
|
|
|
n = Nod(ODCLFIELD, nil, typenod(Types[TUINTPTR])) // return value
|
2016-03-07 09:36:24 -08:00
|
|
|
appendNodeSeqNode(&tfn.Rlist, n)
|
2016-02-26 14:56:31 -08:00
|
|
|
|
|
|
|
funchdr(fn)
|
|
|
|
typecheck(&fn.Func.Nname.Name.Param.Ntype, Etype)
|
|
|
|
|
|
|
|
// genhash is only called for types that have equality but
|
|
|
|
// cannot be handled by the standard algorithms,
|
|
|
|
// so t must be either an array or a struct.
|
|
|
|
switch t.Etype {
|
|
|
|
default:
|
|
|
|
Fatalf("genhash %v", t)
|
|
|
|
|
|
|
|
case TARRAY:
|
|
|
|
if Isslice(t) {
|
|
|
|
Fatalf("genhash %v", t)
|
|
|
|
}
|
|
|
|
|
|
|
|
// An array of pure memory would be handled by the
|
|
|
|
// standard algorithm, so the element type must not be
|
|
|
|
// pure memory.
|
|
|
|
hashel := hashfor(t.Type)
|
|
|
|
|
|
|
|
n := Nod(ORANGE, nil, Nod(OIND, np, nil))
|
|
|
|
ni := newname(Lookup("i"))
|
|
|
|
ni.Type = Types[TINT]
|
2016-03-07 09:36:24 -08:00
|
|
|
setNodeSeq(&n.List, []*Node{ni})
|
2016-02-26 14:56:31 -08:00
|
|
|
n.Colas = true
|
|
|
|
colasdefn(n.List, n)
|
2016-03-07 09:36:24 -08:00
|
|
|
ni = nodeSeqFirst(n.List)
|
2016-02-26 14:56:31 -08:00
|
|
|
|
|
|
|
// h = hashel(&p[i], h)
|
|
|
|
call := Nod(OCALL, hashel, nil)
|
|
|
|
|
|
|
|
nx := Nod(OINDEX, np, ni)
|
|
|
|
nx.Bounded = true
|
|
|
|
na := Nod(OADDR, nx, nil)
|
|
|
|
na.Etype = 1 // no escape to heap
|
2016-03-07 09:36:24 -08:00
|
|
|
appendNodeSeqNode(&call.List, na)
|
|
|
|
appendNodeSeqNode(&call.List, nh)
|
2016-02-27 14:31:33 -08:00
|
|
|
n.Nbody.Append(Nod(OAS, nh, call))
|
2016-02-26 14:56:31 -08:00
|
|
|
|
2016-02-27 14:31:33 -08:00
|
|
|
fn.Nbody.Append(n)
|
2016-02-26 14:56:31 -08:00
|
|
|
|
|
|
|
case TSTRUCT:
|
2016-03-08 03:40:50 -08:00
|
|
|
// Walk the struct using memhash for runs of AMEM
|
|
|
|
// and calling specific hash functions for the others.
|
|
|
|
for f := t.Type; f != nil; {
|
|
|
|
// Skip blank fields.
|
|
|
|
if isblanksym(f.Sym) {
|
|
|
|
f = f.Down
|
|
|
|
continue
|
|
|
|
}
|
2016-02-26 14:56:31 -08:00
|
|
|
|
2016-03-08 03:40:50 -08:00
|
|
|
// Hash non-memory fields with appropriate hash function.
|
|
|
|
if algtype1(f.Type, nil) != AMEM {
|
|
|
|
hashel := hashfor(f.Type)
|
|
|
|
call := Nod(OCALL, hashel, nil)
|
|
|
|
nx := Nod(OXDOT, np, newname(f.Sym)) // TODO: fields from other packages?
|
|
|
|
na := Nod(OADDR, nx, nil)
|
2016-02-26 14:56:31 -08:00
|
|
|
na.Etype = 1 // no escape to heap
|
2016-03-07 09:36:24 -08:00
|
|
|
appendNodeSeqNode(&call.List, na)
|
|
|
|
appendNodeSeqNode(&call.List, nh)
|
2016-02-27 14:31:33 -08:00
|
|
|
fn.Nbody.Append(Nod(OAS, nh, call))
|
2016-03-08 03:40:50 -08:00
|
|
|
f = f.Down
|
2016-02-26 14:56:31 -08:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2016-03-08 03:40:50 -08:00
|
|
|
// Otherwise, hash a maximal length run of raw memory.
|
|
|
|
size, next := memrun(t, f)
|
|
|
|
|
|
|
|
// h = hashel(&p.first, size, h)
|
|
|
|
hashel := hashmem(f.Type)
|
|
|
|
call := Nod(OCALL, hashel, nil)
|
|
|
|
nx := Nod(OXDOT, np, newname(f.Sym)) // TODO: fields from other packages?
|
|
|
|
na := Nod(OADDR, nx, nil)
|
2016-02-26 14:56:31 -08:00
|
|
|
na.Etype = 1 // no escape to heap
|
2016-03-07 09:36:24 -08:00
|
|
|
appendNodeSeqNode(&call.List, na)
|
|
|
|
appendNodeSeqNode(&call.List, nh)
|
2016-03-08 03:40:50 -08:00
|
|
|
appendNodeSeqNode(&call.List, Nodintconst(size))
|
2016-02-27 14:31:33 -08:00
|
|
|
fn.Nbody.Append(Nod(OAS, nh, call))
|
2016-02-26 14:56:31 -08:00
|
|
|
|
2016-03-08 03:40:50 -08:00
|
|
|
f = next
|
2016-02-26 14:56:31 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
r := Nod(ORETURN, nil, nil)
|
2016-03-07 09:36:24 -08:00
|
|
|
appendNodeSeqNode(&r.List, nh)
|
2016-02-27 14:31:33 -08:00
|
|
|
fn.Nbody.Append(r)
|
2016-02-26 14:56:31 -08:00
|
|
|
|
|
|
|
if Debug['r'] != 0 {
|
2016-03-04 13:16:48 -08:00
|
|
|
dumplist("genhash body", fn.Nbody)
|
2016-02-26 14:56:31 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
funcbody(fn)
|
|
|
|
Curfn = fn
|
|
|
|
fn.Func.Dupok = true
|
|
|
|
typecheck(&fn, Etop)
|
2016-03-08 10:26:20 -08:00
|
|
|
typechecklist(fn.Nbody.Slice(), Etop)
|
2016-02-26 14:56:31 -08:00
|
|
|
Curfn = nil
|
|
|
|
|
|
|
|
// Disable safemode while compiling this code: the code we
|
|
|
|
// generate internally can refer to unsafe.Pointer.
|
|
|
|
// In this case it can happen if we need to generate an ==
|
|
|
|
// for a struct containing a reflect.Value, which itself has
|
|
|
|
// an unexported field of type unsafe.Pointer.
|
|
|
|
old_safemode := safemode
|
|
|
|
|
|
|
|
safemode = 0
|
|
|
|
funccompile(fn)
|
|
|
|
safemode = old_safemode
|
|
|
|
}
|
|
|
|
|
|
|
|
func hashfor(t *Type) *Node {
|
|
|
|
var sym *Sym
|
|
|
|
|
|
|
|
a := algtype1(t, nil)
|
|
|
|
switch a {
|
|
|
|
case AMEM:
|
|
|
|
Fatalf("hashfor with AMEM type")
|
|
|
|
|
|
|
|
case AINTER:
|
|
|
|
sym = Pkglookup("interhash", Runtimepkg)
|
|
|
|
|
|
|
|
case ANILINTER:
|
|
|
|
sym = Pkglookup("nilinterhash", Runtimepkg)
|
|
|
|
|
|
|
|
case ASTRING:
|
|
|
|
sym = Pkglookup("strhash", Runtimepkg)
|
|
|
|
|
|
|
|
case AFLOAT32:
|
|
|
|
sym = Pkglookup("f32hash", Runtimepkg)
|
|
|
|
|
|
|
|
case AFLOAT64:
|
|
|
|
sym = Pkglookup("f64hash", Runtimepkg)
|
|
|
|
|
|
|
|
case ACPLX64:
|
|
|
|
sym = Pkglookup("c64hash", Runtimepkg)
|
|
|
|
|
|
|
|
case ACPLX128:
|
|
|
|
sym = Pkglookup("c128hash", Runtimepkg)
|
|
|
|
|
|
|
|
default:
|
|
|
|
sym = typesymprefix(".hash", t)
|
|
|
|
}
|
|
|
|
|
|
|
|
n := newname(sym)
|
|
|
|
n.Class = PFUNC
|
|
|
|
tfn := Nod(OTFUNC, nil, nil)
|
2016-03-07 09:36:24 -08:00
|
|
|
appendNodeSeqNode(&tfn.List, Nod(ODCLFIELD, nil, typenod(Ptrto(t))))
|
|
|
|
appendNodeSeqNode(&tfn.List, Nod(ODCLFIELD, nil, typenod(Types[TUINTPTR])))
|
|
|
|
appendNodeSeqNode(&tfn.Rlist, Nod(ODCLFIELD, nil, typenod(Types[TUINTPTR])))
|
2016-02-26 14:56:31 -08:00
|
|
|
typecheck(&tfn, Etype)
|
|
|
|
n.Type = tfn.Type
|
|
|
|
return n
|
|
|
|
}
|
|
|
|
|
|
|
|
// geneq generates a helper function to
|
|
|
|
// check equality of two values of type t.
|
|
|
|
func geneq(sym *Sym, t *Type) {
|
|
|
|
if Debug['r'] != 0 {
|
|
|
|
fmt.Printf("geneq %v %v\n", sym, t)
|
|
|
|
}
|
|
|
|
|
|
|
|
lineno = 1 // less confusing than end of input
|
|
|
|
dclcontext = PEXTERN
|
|
|
|
markdcl()
|
|
|
|
|
|
|
|
// func sym(p, q *T) bool
|
|
|
|
fn := Nod(ODCLFUNC, nil, nil)
|
|
|
|
|
|
|
|
fn.Func.Nname = newname(sym)
|
|
|
|
fn.Func.Nname.Class = PFUNC
|
|
|
|
tfn := Nod(OTFUNC, nil, nil)
|
|
|
|
fn.Func.Nname.Name.Param.Ntype = tfn
|
|
|
|
|
|
|
|
n := Nod(ODCLFIELD, newname(Lookup("p")), typenod(Ptrto(t)))
|
2016-03-07 09:36:24 -08:00
|
|
|
appendNodeSeqNode(&tfn.List, n)
|
2016-02-26 14:56:31 -08:00
|
|
|
np := n.Left
|
|
|
|
n = Nod(ODCLFIELD, newname(Lookup("q")), typenod(Ptrto(t)))
|
2016-03-07 09:36:24 -08:00
|
|
|
appendNodeSeqNode(&tfn.List, n)
|
2016-02-26 14:56:31 -08:00
|
|
|
nq := n.Left
|
|
|
|
n = Nod(ODCLFIELD, nil, typenod(Types[TBOOL]))
|
2016-03-07 09:36:24 -08:00
|
|
|
appendNodeSeqNode(&tfn.Rlist, n)
|
2016-02-26 14:56:31 -08:00
|
|
|
|
|
|
|
funchdr(fn)
|
|
|
|
|
|
|
|
// geneq is only called for types that have equality but
|
|
|
|
// cannot be handled by the standard algorithms,
|
|
|
|
// so t must be either an array or a struct.
|
|
|
|
switch t.Etype {
|
|
|
|
default:
|
|
|
|
Fatalf("geneq %v", t)
|
|
|
|
|
|
|
|
case TARRAY:
|
|
|
|
if Isslice(t) {
|
|
|
|
Fatalf("geneq %v", t)
|
|
|
|
}
|
|
|
|
|
|
|
|
// An array of pure memory would be handled by the
|
|
|
|
// standard memequal, so the element type must not be
|
2016-03-01 23:21:55 +00:00
|
|
|
// pure memory. Even if we unrolled the range loop,
|
2016-02-26 14:56:31 -08:00
|
|
|
// each iteration would be a function call, so don't bother
|
|
|
|
// unrolling.
|
|
|
|
nrange := Nod(ORANGE, nil, Nod(OIND, np, nil))
|
|
|
|
|
|
|
|
ni := newname(Lookup("i"))
|
|
|
|
ni.Type = Types[TINT]
|
2016-03-07 09:36:24 -08:00
|
|
|
setNodeSeq(&nrange.List, []*Node{ni})
|
2016-02-26 14:56:31 -08:00
|
|
|
nrange.Colas = true
|
|
|
|
colasdefn(nrange.List, nrange)
|
2016-03-07 09:36:24 -08:00
|
|
|
ni = nodeSeqFirst(nrange.List)
|
2016-02-26 14:56:31 -08:00
|
|
|
|
|
|
|
// if p[i] != q[i] { return false }
|
|
|
|
nx := Nod(OINDEX, np, ni)
|
|
|
|
|
|
|
|
nx.Bounded = true
|
|
|
|
ny := Nod(OINDEX, nq, ni)
|
|
|
|
ny.Bounded = true
|
|
|
|
|
|
|
|
nif := Nod(OIF, nil, nil)
|
|
|
|
nif.Left = Nod(ONE, nx, ny)
|
|
|
|
r := Nod(ORETURN, nil, nil)
|
2016-03-07 09:36:24 -08:00
|
|
|
appendNodeSeqNode(&r.List, Nodbool(false))
|
2016-02-27 14:31:33 -08:00
|
|
|
nif.Nbody.Append(r)
|
|
|
|
nrange.Nbody.Append(nif)
|
|
|
|
fn.Nbody.Append(nrange)
|
2016-02-26 14:56:31 -08:00
|
|
|
|
|
|
|
// return true
|
|
|
|
ret := Nod(ORETURN, nil, nil)
|
2016-03-07 09:36:24 -08:00
|
|
|
appendNodeSeqNode(&ret.List, Nodbool(true))
|
2016-02-27 14:31:33 -08:00
|
|
|
fn.Nbody.Append(ret)
|
2016-02-26 14:56:31 -08:00
|
|
|
|
|
|
|
case TSTRUCT:
|
|
|
|
var conjuncts []*Node
|
|
|
|
|
2016-03-08 03:40:50 -08:00
|
|
|
// Walk the struct using memequal for runs of AMEM
|
|
|
|
// and calling specific equality tests for the others.
|
|
|
|
for f := t.Type; f != nil; {
|
|
|
|
// Skip blank-named fields.
|
|
|
|
if isblanksym(f.Sym) {
|
|
|
|
f = f.Down
|
2016-02-26 14:56:31 -08:00
|
|
|
continue
|
|
|
|
}
|
2016-03-08 03:40:50 -08:00
|
|
|
|
|
|
|
// Compare non-memory fields with field equality.
|
|
|
|
if algtype1(f.Type, nil) != AMEM {
|
|
|
|
conjuncts = append(conjuncts, eqfield(np, nq, newname(f.Sym)))
|
|
|
|
f = f.Down
|
2016-02-26 14:56:31 -08:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2016-03-08 03:40:50 -08:00
|
|
|
// Find maximal length run of memory-only fields.
|
|
|
|
size, next := memrun(t, f)
|
|
|
|
|
|
|
|
// Run memequal on fields from f to next.
|
|
|
|
// TODO(rsc): All the calls to newname are wrong for
|
|
|
|
// cross-package unexported fields.
|
|
|
|
if f.Down == next {
|
|
|
|
conjuncts = append(conjuncts, eqfield(np, nq, newname(f.Sym)))
|
|
|
|
} else if f.Down.Down == next {
|
|
|
|
conjuncts = append(conjuncts, eqfield(np, nq, newname(f.Sym)))
|
|
|
|
conjuncts = append(conjuncts, eqfield(np, nq, newname(f.Down.Sym)))
|
|
|
|
} else {
|
|
|
|
// More than two fields: use memequal.
|
|
|
|
conjuncts = append(conjuncts, eqmem(np, nq, newname(f.Sym), size))
|
|
|
|
}
|
|
|
|
f = next
|
2016-02-26 14:56:31 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
var and *Node
|
|
|
|
switch len(conjuncts) {
|
|
|
|
case 0:
|
|
|
|
and = Nodbool(true)
|
|
|
|
case 1:
|
|
|
|
and = conjuncts[0]
|
|
|
|
default:
|
|
|
|
and = Nod(OANDAND, conjuncts[0], conjuncts[1])
|
|
|
|
for _, conjunct := range conjuncts[2:] {
|
|
|
|
and = Nod(OANDAND, and, conjunct)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ret := Nod(ORETURN, nil, nil)
|
2016-03-07 09:36:24 -08:00
|
|
|
appendNodeSeqNode(&ret.List, and)
|
2016-02-27 14:31:33 -08:00
|
|
|
fn.Nbody.Append(ret)
|
2016-02-26 14:56:31 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
if Debug['r'] != 0 {
|
2016-03-04 13:16:48 -08:00
|
|
|
dumplist("geneq body", fn.Nbody)
|
2016-02-26 14:56:31 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
funcbody(fn)
|
|
|
|
Curfn = fn
|
|
|
|
fn.Func.Dupok = true
|
|
|
|
typecheck(&fn, Etop)
|
2016-03-08 10:26:20 -08:00
|
|
|
typechecklist(fn.Nbody.Slice(), Etop)
|
2016-02-26 14:56:31 -08:00
|
|
|
Curfn = nil
|
|
|
|
|
|
|
|
// Disable safemode while compiling this code: the code we
|
|
|
|
// generate internally can refer to unsafe.Pointer.
|
|
|
|
// In this case it can happen if we need to generate an ==
|
|
|
|
// for a struct containing a reflect.Value, which itself has
|
|
|
|
// an unexported field of type unsafe.Pointer.
|
|
|
|
old_safemode := safemode
|
|
|
|
safemode = 0
|
|
|
|
|
|
|
|
// Disable checknils while compiling this code.
|
|
|
|
// We are comparing a struct or an array,
|
|
|
|
// neither of which can be nil, and our comparisons
|
|
|
|
// are shallow.
|
|
|
|
Disable_checknil++
|
|
|
|
|
|
|
|
funccompile(fn)
|
|
|
|
|
|
|
|
safemode = old_safemode
|
|
|
|
Disable_checknil--
|
|
|
|
}
|
|
|
|
|
|
|
|
// eqfield returns the node
|
|
|
|
// p.field == q.field
|
|
|
|
func eqfield(p *Node, q *Node, field *Node) *Node {
|
|
|
|
nx := Nod(OXDOT, p, field)
|
|
|
|
ny := Nod(OXDOT, q, field)
|
|
|
|
ne := Nod(OEQ, nx, ny)
|
|
|
|
return ne
|
|
|
|
}
|
|
|
|
|
|
|
|
// eqmem returns the node
|
|
|
|
// memequal(&p.field, &q.field [, size])
|
|
|
|
func eqmem(p *Node, q *Node, field *Node, size int64) *Node {
|
|
|
|
var needsize int
|
|
|
|
|
|
|
|
nx := Nod(OADDR, Nod(OXDOT, p, field), nil)
|
|
|
|
nx.Etype = 1 // does not escape
|
|
|
|
ny := Nod(OADDR, Nod(OXDOT, q, field), nil)
|
|
|
|
ny.Etype = 1 // does not escape
|
|
|
|
typecheck(&nx, Erv)
|
|
|
|
typecheck(&ny, Erv)
|
|
|
|
|
|
|
|
call := Nod(OCALL, eqmemfunc(size, nx.Type.Type, &needsize), nil)
|
2016-03-07 09:36:24 -08:00
|
|
|
appendNodeSeqNode(&call.List, nx)
|
|
|
|
appendNodeSeqNode(&call.List, ny)
|
2016-02-26 14:56:31 -08:00
|
|
|
if needsize != 0 {
|
2016-03-07 09:36:24 -08:00
|
|
|
appendNodeSeqNode(&call.List, Nodintconst(size))
|
2016-02-26 14:56:31 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
return call
|
|
|
|
}
|
|
|
|
|
|
|
|
func eqmemfunc(size int64, type_ *Type, needsize *int) *Node {
|
|
|
|
var fn *Node
|
|
|
|
|
|
|
|
switch size {
|
|
|
|
default:
|
2016-03-04 15:19:06 -08:00
|
|
|
fn = syslook("memequal")
|
2016-02-26 14:56:31 -08:00
|
|
|
*needsize = 1
|
|
|
|
|
|
|
|
case 1, 2, 4, 8, 16:
|
|
|
|
buf := fmt.Sprintf("memequal%d", int(size)*8)
|
2016-03-04 15:19:06 -08:00
|
|
|
fn = syslook(buf)
|
2016-02-26 14:56:31 -08:00
|
|
|
*needsize = 0
|
|
|
|
}
|
|
|
|
|
2016-03-04 15:19:06 -08:00
|
|
|
substArgTypes(&fn, type_, type_)
|
2016-02-26 14:56:31 -08:00
|
|
|
return fn
|
|
|
|
}
|
|
|
|
|
|
|
|
// memrun finds runs of struct fields for which memory-only algs are appropriate.
|
2016-03-08 03:40:50 -08:00
|
|
|
// t is the parent struct type, and start is the field that starts the run.
|
2016-02-26 14:56:31 -08:00
|
|
|
// size is the length in bytes of the memory included in the run.
|
|
|
|
// next is the next field after the memory run.
|
2016-03-08 03:40:50 -08:00
|
|
|
func memrun(t *Type, start *Type) (size int64, next *Type) {
|
|
|
|
var last *Type
|
|
|
|
next = start
|
2016-02-26 14:56:31 -08:00
|
|
|
for {
|
2016-03-08 03:40:50 -08:00
|
|
|
last, next = next, next.Down
|
|
|
|
if next == nil {
|
2016-02-26 14:56:31 -08:00
|
|
|
break
|
|
|
|
}
|
2016-03-08 03:40:50 -08:00
|
|
|
// Stop run after a padded field.
|
|
|
|
if ispaddedfield(t, last) {
|
|
|
|
break
|
2016-02-26 14:56:31 -08:00
|
|
|
}
|
2016-03-08 03:40:50 -08:00
|
|
|
// Also, stop before a blank or non-memory field.
|
|
|
|
if isblanksym(next.Sym) || algtype1(next.Type, nil) != AMEM {
|
2016-02-26 14:56:31 -08:00
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
2016-03-08 03:40:50 -08:00
|
|
|
end := last.Width + last.Type.Width
|
|
|
|
return end - start.Width, next
|
2016-02-26 14:56:31 -08:00
|
|
|
}
|
|
|
|
|
2016-03-08 03:40:50 -08:00
|
|
|
// ispaddedfield reports whether the given field f, assumed to be
|
|
|
|
// a field in struct t, is followed by padding.
|
|
|
|
func ispaddedfield(t *Type, f *Type) bool {
|
|
|
|
if t.Etype != TSTRUCT {
|
|
|
|
Fatalf("ispaddedfield called non-struct %v", t)
|
|
|
|
}
|
|
|
|
if f.Etype != TFIELD {
|
|
|
|
Fatalf("ispaddedfield called non-field %v", f)
|
2016-02-26 14:56:31 -08:00
|
|
|
}
|
2016-03-08 03:40:50 -08:00
|
|
|
end := t.Width
|
|
|
|
if f.Down != nil {
|
|
|
|
end = f.Down.Width
|
2016-02-26 14:56:31 -08:00
|
|
|
}
|
2016-03-08 03:40:50 -08:00
|
|
|
return f.Width+f.Type.Width != end
|
2016-02-26 14:56:31 -08:00
|
|
|
}
|