| |
| |
| |
|
|
| |
| |
| package compare |
|
|
| import ( |
| "cmd/compile/internal/base" |
| "cmd/compile/internal/ir" |
| "cmd/compile/internal/typecheck" |
| "cmd/compile/internal/types" |
| "fmt" |
| "math/bits" |
| "sort" |
| ) |
|
|
| |
| func IsRegularMemory(t *types.Type) bool { |
| return types.AlgType(t) == types.AMEM |
| } |
|
|
| |
| |
| |
| |
| func Memrun(t *types.Type, start int) (size int64, next int) { |
| next = start |
| for { |
| next++ |
| if next == t.NumFields() { |
| break |
| } |
| |
| if types.IsPaddedField(t, next-1) { |
| break |
| } |
| |
| if f := t.Field(next); f.Sym.IsBlank() || !IsRegularMemory(f.Type) { |
| break |
| } |
| |
| |
| if base.Ctxt.Arch.Alignment > 1 { |
| align := t.Alignment() |
| if off := t.Field(start).Offset; off&(align-1) != 0 { |
| |
| |
| align = 1 << uint(bits.TrailingZeros64(uint64(off))) |
| } |
| size := t.Field(next).End() - t.Field(start).Offset |
| if size > align { |
| break |
| } |
| } |
| } |
| return t.Field(next-1).End() - t.Field(start).Offset, next |
| } |
|
|
| |
| |
| func EqCanPanic(t *types.Type) bool { |
| switch t.Kind() { |
| default: |
| return false |
| case types.TINTER: |
| return true |
| case types.TARRAY: |
| return EqCanPanic(t.Elem()) |
| case types.TSTRUCT: |
| for _, f := range t.Fields() { |
| if !f.Sym.IsBlank() && EqCanPanic(f.Type) { |
| return true |
| } |
| } |
| return false |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| func EqStructCost(t *types.Type) int64 { |
| cost := int64(0) |
|
|
| for i, fields := 0, t.Fields(); i < len(fields); { |
| f := fields[i] |
|
|
| |
| if f.Sym.IsBlank() { |
| i++ |
| continue |
| } |
|
|
| n, _, next := eqStructFieldCost(t, i) |
|
|
| cost += n |
| i = next |
| } |
|
|
| return cost |
| } |
|
|
| |
| |
| |
| |
| |
| |
| func eqStructFieldCost(t *types.Type, i int) (int64, int64, int) { |
| var ( |
| cost = int64(0) |
| regSize = int64(types.RegSize) |
|
|
| size int64 |
| next int |
| ) |
|
|
| if base.Ctxt.Arch.CanMergeLoads { |
| |
| |
| size, next = Memrun(t, i) |
| cost = size / regSize |
| if size%regSize != 0 { |
| cost++ |
| } |
| return cost, size, next |
| } |
|
|
| |
| |
| |
| ft := t.Field(i).Type |
| size = ft.Size() |
| next = i + 1 |
|
|
| return calculateCostForType(ft), size, next |
| } |
|
|
| func calculateCostForType(t *types.Type) int64 { |
| var cost int64 |
| switch t.Kind() { |
| case types.TSTRUCT: |
| return EqStructCost(t) |
| case types.TSLICE: |
| |
| base.Fatalf("calculateCostForType: unexpected slice type") |
| case types.TARRAY: |
| elemCost := calculateCostForType(t.Elem()) |
| cost = t.NumElem() * elemCost |
| case types.TSTRING, types.TINTER, types.TCOMPLEX64, types.TCOMPLEX128: |
| cost = 2 |
| case types.TINT64, types.TUINT64: |
| cost = 8 / int64(types.RegSize) |
| default: |
| cost = 1 |
| } |
| return cost |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| func EqStruct(t *types.Type, np, nq ir.Node) ([]ir.Node, bool) { |
| |
| |
| var conds [][]ir.Node |
| conds = append(conds, []ir.Node{}) |
| and := func(n ir.Node) { |
| i := len(conds) - 1 |
| conds[i] = append(conds[i], n) |
| } |
|
|
| |
| |
| for i, fields := 0, t.Fields(); i < len(fields); { |
| f := fields[i] |
|
|
| |
| if f.Sym.IsBlank() { |
| i++ |
| continue |
| } |
|
|
| typeCanPanic := EqCanPanic(f.Type) |
|
|
| |
| if !IsRegularMemory(f.Type) { |
| if typeCanPanic { |
| |
| conds = append(conds, []ir.Node{}) |
| } |
| switch { |
| case f.Type.IsString(): |
| p := typecheck.DotField(base.Pos, typecheck.Expr(np), i) |
| q := typecheck.DotField(base.Pos, typecheck.Expr(nq), i) |
| eqlen, eqmem := EqString(p, q) |
| and(eqlen) |
| and(eqmem) |
| default: |
| and(eqfield(np, nq, i)) |
| } |
| if typeCanPanic { |
| |
| conds = append(conds, []ir.Node{}) |
| } |
| i++ |
| continue |
| } |
|
|
| cost, size, next := eqStructFieldCost(t, i) |
| if cost <= 4 { |
| |
| for j := i; j < next; j++ { |
| and(eqfield(np, nq, j)) |
| } |
| } else { |
| |
| cc := eqmem(np, nq, i, size) |
| and(cc) |
| } |
| i = next |
| } |
|
|
| |
| |
| var flatConds []ir.Node |
| for _, c := range conds { |
| isCall := func(n ir.Node) bool { |
| return n.Op() == ir.OCALL || n.Op() == ir.OCALLFUNC |
| } |
| sort.SliceStable(c, func(i, j int) bool { |
| return !isCall(c[i]) && isCall(c[j]) |
| }) |
| flatConds = append(flatConds, c...) |
| } |
| return flatConds, len(conds) > 1 |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| func EqString(s, t ir.Node) (eqlen *ir.BinaryExpr, eqmem *ir.CallExpr) { |
| s = typecheck.Conv(s, types.Types[types.TSTRING]) |
| t = typecheck.Conv(t, types.Types[types.TSTRING]) |
| sptr := ir.NewUnaryExpr(base.Pos, ir.OSPTR, s) |
| tptr := ir.NewUnaryExpr(base.Pos, ir.OSPTR, t) |
| slen := typecheck.Conv(ir.NewUnaryExpr(base.Pos, ir.OLEN, s), types.Types[types.TUINTPTR]) |
| tlen := typecheck.Conv(ir.NewUnaryExpr(base.Pos, ir.OLEN, t), types.Types[types.TUINTPTR]) |
|
|
| |
| |
| |
| probablyConstant := func(n ir.Node) bool { |
| if n.Op() == ir.OCONVNOP { |
| n = n.(*ir.ConvExpr).X |
| } |
| if n.Op() == ir.OLITERAL { |
| return true |
| } |
| if n.Op() != ir.ONAME { |
| return false |
| } |
| name := n.(*ir.Name) |
| if name.Class != ir.PAUTO { |
| return false |
| } |
| if def := name.Defn; def == nil { |
| |
| return true |
| } else if def.Op() == ir.OAS && (def.(*ir.AssignStmt).Y == nil || def.(*ir.AssignStmt).Y.Op() == ir.OLITERAL) { |
| |
| return true |
| } |
| return false |
| } |
| cmplen := slen |
| if probablyConstant(t) && !probablyConstant(s) { |
| cmplen = tlen |
| } |
|
|
| fn := typecheck.LookupRuntime("memequal", types.Types[types.TUINT8], types.Types[types.TUINT8]) |
| call := typecheck.Call(base.Pos, fn, []ir.Node{sptr, tptr, ir.Copy(cmplen)}, false).(*ir.CallExpr) |
|
|
| cmp := ir.NewBinaryExpr(base.Pos, ir.OEQ, slen, tlen) |
| cmp = typecheck.Expr(cmp).(*ir.BinaryExpr) |
| cmp.SetType(types.Types[types.TBOOL]) |
| return cmp, call |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| func EqInterface(s, t ir.Node) (eqtab *ir.BinaryExpr, eqdata *ir.CallExpr) { |
| if !types.Identical(s.Type(), t.Type()) { |
| base.Fatalf("EqInterface %v %v", s.Type(), t.Type()) |
| } |
| |
| |
| var fn ir.Node |
| if s.Type().IsEmptyInterface() { |
| fn = typecheck.LookupRuntime("efaceeq") |
| } else { |
| fn = typecheck.LookupRuntime("ifaceeq") |
| } |
|
|
| stab := ir.NewUnaryExpr(base.Pos, ir.OITAB, s) |
| ttab := ir.NewUnaryExpr(base.Pos, ir.OITAB, t) |
| sdata := ir.NewUnaryExpr(base.Pos, ir.OIDATA, s) |
| tdata := ir.NewUnaryExpr(base.Pos, ir.OIDATA, t) |
| sdata.SetType(types.Types[types.TUNSAFEPTR]) |
| tdata.SetType(types.Types[types.TUNSAFEPTR]) |
| sdata.SetTypecheck(1) |
| tdata.SetTypecheck(1) |
|
|
| call := typecheck.Call(base.Pos, fn, []ir.Node{stab, sdata, tdata}, false).(*ir.CallExpr) |
|
|
| cmp := ir.NewBinaryExpr(base.Pos, ir.OEQ, stab, ttab) |
| cmp = typecheck.Expr(cmp).(*ir.BinaryExpr) |
| cmp.SetType(types.Types[types.TBOOL]) |
| return cmp, call |
| } |
|
|
| |
| |
| |
| func eqfield(p, q ir.Node, field int) ir.Node { |
| nx := typecheck.DotField(base.Pos, typecheck.Expr(p), field) |
| ny := typecheck.DotField(base.Pos, typecheck.Expr(q), field) |
| return typecheck.Expr(ir.NewBinaryExpr(base.Pos, ir.OEQ, nx, ny)) |
| } |
|
|
| |
| |
| |
| func eqmem(p, q ir.Node, field int, size int64) ir.Node { |
| nx := typecheck.Expr(typecheck.NodAddr(typecheck.DotField(base.Pos, p, field))) |
| ny := typecheck.Expr(typecheck.NodAddr(typecheck.DotField(base.Pos, q, field))) |
|
|
| fn, needsize := eqmemfunc(size, nx.Type().Elem()) |
| call := ir.NewCallExpr(base.Pos, ir.OCALL, fn, nil) |
| call.Args.Append(nx) |
| call.Args.Append(ny) |
| if needsize { |
| call.Args.Append(ir.NewInt(base.Pos, size)) |
| } |
|
|
| return call |
| } |
|
|
| func eqmemfunc(size int64, t *types.Type) (fn *ir.Name, needsize bool) { |
| if !base.Ctxt.Arch.CanMergeLoads && t.Alignment() < int64(base.Ctxt.Arch.Alignment) && t.Alignment() < t.Size() { |
| |
| |
| size = 0 |
| } |
| switch size { |
| case 1, 2, 4, 8, 16: |
| buf := fmt.Sprintf("memequal%d", int(size)*8) |
| return typecheck.LookupRuntime(buf, t, t), false |
| } |
|
|
| return typecheck.LookupRuntime("memequal", t, t), true |
| } |
|
|