| // Copyright 2015 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 rewritegeneric |
| |
| import ( |
| "fmt" |
| "math" |
| "math/bits" |
| "strings" |
| |
| "cmd/compile/internal/base" |
| "cmd/compile/internal/ir" |
| "cmd/compile/internal/reflectdata" |
| "cmd/compile/internal/rttype" |
| "cmd/compile/internal/ssa" |
| "cmd/compile/internal/ssa/ssaop" |
| "cmd/compile/internal/typecheck" |
| "cmd/compile/internal/types" |
| "cmd/internal/obj" |
| "cmd/internal/objabi" |
| ) |
| |
| func addToSub(op ssaop.Op) ssaop.Op { |
| switch op { |
| case ssaop.OpAdd64: |
| return ssaop.OpSub64 |
| case ssaop.OpAdd32: |
| return ssaop.OpSub32 |
| case ssaop.OpAdd16: |
| return ssaop.OpSub16 |
| case ssaop.OpAdd8: |
| return ssaop.OpSub8 |
| default: |
| panic(fmt.Sprintf("unexpected op %v", op)) |
| } |
| } |
| |
| func bitsAdd64(x, y, carry int64) (r struct{ sum, carry int64 }) { |
| s, c := bits.Add64(uint64(x), uint64(y), uint64(carry)) |
| r.sum, r.carry = int64(s), int64(c) |
| return |
| } |
| |
| func bitsMulU32(x, y int32) (r struct{ hi, lo int32 }) { |
| hi, lo := bits.Mul32(uint32(x), uint32(y)) |
| r.hi, r.lo = int32(hi), int32(lo) |
| return |
| } |
| |
| func bitsMulU64(x, y int64) (r struct{ hi, lo int64 }) { |
| hi, lo := bits.Mul64(uint64(x), uint64(y)) |
| r.hi, r.lo = int64(hi), int64(lo) |
| return |
| } |
| |
| func bitsDiv128u(hi, lo, y int64) (r struct{ quo, rem int64 }) { |
| q, rem := bits.Div64(uint64(hi), uint64(lo), uint64(y)) |
| r.quo, r.rem = int64(q), int64(rem) |
| return |
| } |
| |
| // bool2int converts bool to int: true to 1, false to 0 |
| func bool2int(x bool) int { |
| var b int |
| if x { |
| b = 1 |
| } |
| return b |
| } |
| |
| // canLoadUnaligned reports if the architecture supports unaligned load operations. |
| func canLoadUnaligned(c *ssa.Config) bool { |
| return c.Ctxt.Arch.Alignment == 1 |
| } |
| |
| // canRotate reports whether the architecture supports |
| // rotates of integer registers with the given number of bits. |
| func canRotate(c *ssa.Config, bits int64) bool { |
| if bits > c.PtrSize*8 { |
| // Don't rewrite to rotates bigger than the machine word. |
| return false |
| } |
| switch c.Arch { |
| case "386", "amd64", "arm64", "loong64", "riscv64": |
| return true |
| case "arm", "s390x", "ppc64", "ppc64le", "wasm": |
| return bits >= 32 |
| default: |
| return false |
| } |
| } |
| |
| func copyCompatibleType(t1, t2 *types.Type) bool { |
| if t1.Size() != t2.Size() { |
| return false |
| } |
| if t1.IsInteger() { |
| return t2.IsInteger() |
| } |
| if ssa.IsPtr(t1) { |
| return ssa.IsPtr(t2) |
| } |
| return t1.Compare(t2) == types.CMPeq |
| } |
| |
| func devirtLECall(v *ssa.Value, sym *obj.LSym) *ssa.Value { |
| v.Op = ssaop.OpStaticLECall |
| auxcall := v.Aux.(*ssa.AuxCall) |
| auxcall.Fn = sym |
| // Remove first arg |
| v.Args[0].Uses-- |
| copy(v.Args[0:], v.Args[1:]) |
| v.Args[len(v.Args)-1] = nil // aid GC |
| v.Args = v.Args[:len(v.Args)-1] |
| if f := v.Block.Func; f.Pass.Debug > 0 { |
| f.Warnl(v.Pos, "de-virtualizing call") |
| } |
| return v |
| } |
| |
| // hasSmallRotate reports whether the architecture has rotate instructions |
| // for sizes < 32-bit. This is used to decide whether to promote some rotations. |
| func hasSmallRotate(c *ssa.Config) bool { |
| switch c.Arch { |
| case "amd64", "386": |
| return true |
| default: |
| return false |
| } |
| } |
| |
| func invertibleBool(op ssaop.Op) bool { |
| switch op { |
| case ssaop.OpLess64, ssaop.OpLess32, ssaop.OpLess16, ssaop.OpLess8, |
| ssaop.OpLeq64, ssaop.OpLeq32, ssaop.OpLeq16, ssaop.OpLeq8, |
| ssaop.OpLess64U, ssaop.OpLess32U, ssaop.OpLess16U, ssaop.OpLess8U, |
| ssaop.OpLeq64U, ssaop.OpLeq32U, ssaop.OpLeq16U, ssaop.OpLeq8U, |
| ssaop.OpEq64, ssaop.OpEq32, ssaop.OpEq16, ssaop.OpEq8, |
| ssaop.OpNeq64, ssaop.OpNeq32, ssaop.OpNeq16, ssaop.OpNeq8, |
| ssaop.OpNot: |
| return true |
| default: |
| return false |
| } |
| } |
| |
| func isDictArgSym(sym ssa.Sym) bool { |
| return sym.(*ir.Name).Sym().Name == typecheck.LocalDictName |
| } |
| |
| // isDirectAndComparableIface reports whether v represents an itab |
| // (a *runtime._itab) for a type whose value is stored directly |
| // in an interface (i.e., is pointer or pointer-like) and is comparable. |
| func isDirectAndComparableIface(v *ssa.Value) bool { |
| return isDirectAndComparableIface1(v, 9) |
| } |
| |
| // v is an itab |
| func isDirectAndComparableIface1(v *ssa.Value, depth int) bool { |
| if depth == 0 { |
| return false |
| } |
| switch v.Op { |
| case ssaop.OpITab: |
| return isDirectAndComparableIface2(v.Args[0], depth-1) |
| case ssaop.OpAddr: |
| lsym := v.Aux.(*obj.LSym) |
| if ii := lsym.ItabInfo(); ii != nil { |
| t := ii.Type.(*types.Type) |
| return types.IsDirectIface(t) && types.IsComparable(t) |
| } |
| case ssaop.OpConstNil: |
| // We can treat this as direct, because if the itab is |
| // nil, the data field must be nil also. |
| return true |
| } |
| return false |
| } |
| |
| // v is an interface |
| func isDirectAndComparableIface2(v *ssa.Value, depth int) bool { |
| if depth == 0 { |
| return false |
| } |
| switch v.Op { |
| case ssaop.OpIMake: |
| return isDirectAndComparableIface1(v.Args[0], depth-1) |
| case ssaop.OpPhi: |
| for _, a := range v.Args { |
| if !isDirectAndComparableIface2(a, depth-1) { |
| return false |
| } |
| } |
| return true |
| } |
| return false |
| } |
| |
| // isDirectAndComparableType reports whether v represents a type |
| // (a *runtime._type) whose value is stored directly in an |
| // interface (i.e., is pointer or pointer-like) and is comparable. |
| func isDirectAndComparableType(v *ssa.Value) bool { |
| return isDirectAndComparableType1(v) |
| } |
| |
| // v is a type |
| func isDirectAndComparableType1(v *ssa.Value) bool { |
| switch v.Op { |
| case ssaop.OpITab: |
| return isDirectAndComparableType2(v.Args[0]) |
| case ssaop.OpAddr: |
| lsym := v.Aux.(*obj.LSym) |
| if ti := lsym.TypeInfo(); ti != nil { |
| t := ti.Type.(*types.Type) |
| return types.IsDirectIface(t) && types.IsComparable(t) |
| } |
| } |
| return false |
| } |
| |
| // v is an empty interface |
| func isDirectAndComparableType2(v *ssa.Value) bool { |
| switch v.Op { |
| case ssaop.OpIMake: |
| return isDirectAndComparableType1(v.Args[0]) |
| } |
| return false |
| } |
| |
| // isFixedLoad returns true if the load can be resolved to fixed address or constant, |
| // and can be rewritten by rewriteFixedLoad. |
| func isFixedLoad(v *ssa.Value, sym ssa.Sym, off int64) bool { |
| lsym := sym.(*obj.LSym) |
| if (v.Type.IsPtrShaped() || v.Type.IsUintptr()) && lsym.Type == objabi.SRODATA { |
| for _, r := range lsym.R { |
| if (r.Type == objabi.R_ADDR || r.Type == objabi.R_WEAKADDR) && int64(r.Off) == off && r.Add == 0 { |
| return true |
| } |
| } |
| return false |
| } |
| |
| if ti := lsym.TypeInfo(); ti != nil { |
| // Type symbols do not contain information about their fields, unlike the cases above. |
| // Hand-implement field accesses. |
| // TODO: can this be replaced with reflectdata.writeType and just use the code above? |
| |
| t := ti.Type.(*types.Type) |
| |
| for _, f := range rttype.Type.Fields() { |
| if f.Offset == off && copyCompatibleType(v.Type, f.Type) { |
| switch f.Sym.Name { |
| case "Size_", "PtrBytes", "Hash", "Kind_", "GCData", "TFlag": |
| return true |
| default: |
| // fmt.Println("unknown field", f.Sym.Name) |
| return false |
| } |
| } |
| } |
| |
| if t.IsPtr() && off == rttype.PtrType.OffsetOf("Elem") { |
| return true |
| } |
| |
| return false |
| } |
| |
| return false |
| } |
| |
| func isInlinableMemclr(c *ssa.Config, sz int64) bool { |
| if sz < 0 { |
| return false |
| } |
| // TODO: expand this check to allow other architectures |
| // see CL 454255 and issue 56997 |
| switch c.Arch { |
| case "amd64", "arm64": |
| return true |
| case "ppc64le", "ppc64", "loong64": |
| return sz < 512 |
| } |
| return false |
| } |
| |
| func isMalloc(aux ssa.Aux) bool { |
| return ssa.IsNewObjectCall(aux) || ssa.IsSpecializedMalloc(aux) |
| } |
| |
| // isNonNegative reports whether v is known to be greater or equal to zero. |
| // Note that this is pretty simplistic. The prove pass generates more detailed |
| // nonnegative information about values. |
| func isNonNegative(v *ssa.Value) bool { |
| if !v.Type.IsInteger() { |
| v.Fatalf("isNonNegative bad type: %v", v.Type) |
| } |
| // TODO: return true if !v.Type.IsSigned() |
| // SSA isn't type-safe enough to do that now (issue 37753). |
| // The checks below depend only on the pattern of bits. |
| |
| switch v.Op { |
| case ssaop.OpConst64: |
| return v.AuxInt >= 0 |
| |
| case ssaop.OpConst32: |
| return int32(v.AuxInt) >= 0 |
| |
| case ssaop.OpConst16: |
| return int16(v.AuxInt) >= 0 |
| |
| case ssaop.OpConst8: |
| return int8(v.AuxInt) >= 0 |
| |
| case ssaop.OpStringLen, ssaop.OpSliceLen, ssaop.OpSliceCap, |
| ssaop.OpZeroExt8to64, ssaop.OpZeroExt16to64, ssaop.OpZeroExt32to64, |
| ssaop.OpZeroExt8to32, ssaop.OpZeroExt16to32, ssaop.OpZeroExt8to16, |
| ssaop.OpCtz64, ssaop.OpCtz32, ssaop.OpCtz16, ssaop.OpCtz8, |
| ssaop.OpCtz64NonZero, ssaop.OpCtz32NonZero, ssaop.OpCtz16NonZero, ssaop.OpCtz8NonZero, |
| ssaop.OpBitLen64, ssaop.OpBitLen32, ssaop.OpBitLen16, ssaop.OpBitLen8: |
| return true |
| |
| case ssaop.OpRsh64Ux64, ssaop.OpRsh32Ux64: |
| by := v.Args[1] |
| return by.Op == ssaop.OpConst64 && by.AuxInt > 0 |
| |
| case ssaop.OpRsh64x64, ssaop.OpRsh32x64, ssaop.OpRsh8x64, ssaop.OpRsh16x64, ssaop.OpRsh32x32, ssaop.OpRsh64x32, |
| ssaop.OpSignExt32to64, ssaop.OpSignExt16to64, ssaop.OpSignExt8to64, ssaop.OpSignExt16to32, ssaop.OpSignExt8to32: |
| return isNonNegative(v.Args[0]) |
| |
| case ssaop.OpAnd64, ssaop.OpAnd32, ssaop.OpAnd16, ssaop.OpAnd8: |
| return isNonNegative(v.Args[0]) || isNonNegative(v.Args[1]) |
| |
| case ssaop.OpMod64, ssaop.OpMod32, ssaop.OpMod16, ssaop.OpMod8, |
| ssaop.OpDiv64, ssaop.OpDiv32, ssaop.OpDiv16, ssaop.OpDiv8, |
| ssaop.OpOr64, ssaop.OpOr32, ssaop.OpOr16, ssaop.OpOr8, |
| ssaop.OpXor64, ssaop.OpXor32, ssaop.OpXor16, ssaop.OpXor8: |
| return isNonNegative(v.Args[0]) && isNonNegative(v.Args[1]) |
| |
| // We could handle OpPhi here, but the improvements from doing |
| // so are very minor, and it is neither simple nor cheap. |
| } |
| return false |
| } |
| |
| func isStackPtr(v *ssa.Value) bool { |
| for v.Op == ssaop.OpOffPtr || v.Op == ssaop.OpAddPtr { |
| v = v.Args[0] |
| } |
| return v.Op == ssaop.OpSP || v.Op == ssaop.OpLocalAddr |
| } |
| |
| // needRaceCleanup reports whether this call to racefuncenter/exit isn't needed. |
| func needRaceCleanup(sym *ssa.AuxCall, v *ssa.Value) bool { |
| f := v.Block.Func |
| if !f.Config.Race { |
| return false |
| } |
| if !ssa.IsSameCall(sym, "runtime.racefuncenter") && !ssa.IsSameCall(sym, "runtime.racefuncexit") { |
| return false |
| } |
| for _, b := range f.Blocks { |
| for _, v := range b.Values { |
| switch v.Op { |
| case ssaop.OpStaticCall, ssaop.OpStaticLECall: |
| // Check for racefuncenter will encounter racefuncexit and vice versa. |
| // Allow calls to panic* |
| s := v.Aux.(*ssa.AuxCall).Fn.String() |
| switch s { |
| case "runtime.racefuncenter", "runtime.racefuncexit", |
| "runtime.panicdivide", "runtime.panicwrap", |
| "runtime.panicshift": |
| continue |
| } |
| // If we encountered any call, we need to keep racefunc*, |
| // for accurate stacktraces. |
| return false |
| case ssaop.OpPanicBounds, ssaop.OpPanicExtend: |
| // Note: these are panic generators that are ok (like the static calls above). |
| case ssaop.OpClosureCall, ssaop.OpInterCall, ssaop.OpClosureLECall, ssaop.OpInterLECall: |
| // We must keep the race functions if there are any other call types. |
| return false |
| } |
| } |
| } |
| if ssa.IsSameCall(sym, "runtime.racefuncenter") { |
| // TODO REGISTER ABI this needs to be cleaned up. |
| // If we're removing racefuncenter, remove its argument as well. |
| if v.Args[0].Op != ssaop.OpStore { |
| if v.Op == ssaop.OpStaticLECall { |
| // there is no store, yet. |
| return true |
| } |
| return false |
| } |
| mem := v.Args[0].Args[2] |
| v.Args[0].Reset(ssaop.OpCopy) |
| v.Args[0].AddArg(mem) |
| } |
| return true |
| } |
| |
| func nlz16(x int16) int { return bits.LeadingZeros16(uint16(x)) } |
| |
| func nlz32(x int32) int { return bits.LeadingZeros32(uint32(x)) } |
| |
| // nlzX returns the number of leading zeros. |
| func nlz64(x int64) int { return bits.LeadingZeros64(uint64(x)) } |
| |
| func nlz8(x int8) int { return bits.LeadingZeros8(uint8(x)) } |
| |
| func ntz16(x int16) int { return bits.TrailingZeros16(uint16(x)) } |
| |
| func ntz32(x int32) int { return bits.TrailingZeros32(uint32(x)) } |
| |
| func ntz8(x int8) int { return bits.TrailingZeros8(uint8(x)) } |
| |
| // reciprocalExact32 reports whether 1/c is exactly representable. |
| func reciprocalExact32(c float32) bool { |
| b := math.Float32bits(c) |
| man := b & (1<<23 - 1) |
| if man != 0 { |
| return false // not a power of 2, denormal, or NaN |
| } |
| exp := b >> 23 & (1<<8 - 1) |
| // exponent bias is 0x7f. So taking the reciprocal of a number |
| // changes the exponent to 0xfe-exp. |
| switch exp { |
| case 0: |
| return false // ±0 |
| case 0xff: |
| return false // ±inf |
| case 0xfe: |
| return false // exponent is not representable |
| default: |
| return true |
| } |
| } |
| |
| // reciprocalExact64 reports whether 1/c is exactly representable. |
| func reciprocalExact64(c float64) bool { |
| b := math.Float64bits(c) |
| man := b & (1<<52 - 1) |
| if man != 0 { |
| return false // not a power of 2, denormal, or NaN |
| } |
| exp := b >> 52 & (1<<11 - 1) |
| // exponent bias is 0x3ff. So taking the reciprocal of a number |
| // changes the exponent to 0x7fe-exp. |
| switch exp { |
| case 0: |
| return false // ±0 |
| case 0x7ff: |
| return false // ±inf |
| case 0x7fe: |
| return false // exponent is not representable |
| default: |
| return true |
| } |
| } |
| |
| // registerizable reports whether t is a primitive type that fits in |
| // a register. It assumes float64 values will always fit into registers |
| // even if that isn't strictly true. |
| func registerizable(b *ssa.Block, typ *types.Type) bool { |
| if typ.IsPtrShaped() || typ.IsFloat() || typ.IsBoolean() { |
| return true |
| } |
| if typ.IsInteger() { |
| return typ.Size() <= b.Func.Config.RegSize |
| } |
| return false |
| } |
| |
| // resetCopy resets v to be a copy of arg. |
| // Always returns true. |
| func resetCopy(v *ssa.Value, arg *ssa.Value) bool { |
| v.Reset(ssaop.OpCopy) |
| v.AddArg(arg) |
| return true |
| } |
| |
| // rewriteCondSelectIntoMath reports whether x OP (y * constant) should be used instead of a CondSelect. |
| // x arbitrary, y in [0,1] |
| func rewriteCondSelectIntoMath(config *ssa.Config, op ssaop.Op, constant int64) bool { |
| // at worst this becomes a left shift by a constant which has asymmetric latency (1:3 vs 2:2) |
| // but performs better in accumulation chains. |
| // Various arches do strictly superior for specific cases, but this is a good general default. |
| // FIXME: optimize more constants in arches where this is possible. |
| switch config.Arch { |
| case "arm64": |
| switch op { |
| case ssaop.OpAdd64, ssaop.OpAdd32, ssaop.OpAdd16, ssaop.OpAdd8: |
| if constant == 1 { |
| return false // better done as CSINC |
| } |
| fallthrough |
| default: |
| // add sub or xor & and are implemented using inline LSL |
| // the rest becomes the default LSL |
| return ssa.IsPowerOfTwo(uint64(constant)) |
| } |
| default: |
| return ssa.IsPowerOfTwo(uint64(constant)) |
| } |
| } |
| |
| // rewriteFixedLoad rewrites a load to a fixed address or constant, if isFixedLoad returns true. |
| func rewriteFixedLoad(v *ssa.Value, sym ssa.Sym, sb *ssa.Value, off int64) *ssa.Value { |
| b := v.Block |
| f := b.Func |
| |
| lsym := sym.(*obj.LSym) |
| if (v.Type.IsPtrShaped() || v.Type.IsUintptr()) && lsym.Type == objabi.SRODATA { |
| for _, r := range lsym.R { |
| if (r.Type == objabi.R_ADDR || r.Type == objabi.R_WEAKADDR) && int64(r.Off) == off && r.Add == 0 { |
| if strings.HasPrefix(r.Sym.Name, "type:") { |
| // In case we're loading a type out of a dictionary, we need to record |
| // that the containing function might put that type in an interface. |
| // That information is currently recorded in relocations in the dictionary, |
| // but if we perform this load at compile time then the dictionary |
| // might be dead. |
| reflectdata.MarkTypeSymUsedInInterface(r.Sym, f.Fe.Func().Linksym()) |
| } else if strings.HasPrefix(r.Sym.Name, "go:itab") { |
| // Same, but if we're using an itab we need to record that the |
| // itab._type might be put in an interface. |
| reflectdata.MarkTypeSymUsedInInterface(r.Sym, f.Fe.Func().Linksym()) |
| } |
| v.Reset(ssaop.OpAddr) |
| v.Aux = ssa.SymToAux(r.Sym) |
| v.AddArg(sb) |
| return v |
| } |
| } |
| base.Fatalf("fixedLoad data not known for %s:%d", sym, off) |
| } |
| |
| if ti := lsym.TypeInfo(); ti != nil { |
| // Type symbols do not contain information about their fields, unlike the cases above. |
| // Hand-implement field accesses. |
| // TODO: can this be replaced with reflectdata.writeType and just use the code above? |
| |
| t := ti.Type.(*types.Type) |
| |
| ptrSizedOpConst := ssaop.OpConst64 |
| if f.Config.PtrSize == 4 { |
| ptrSizedOpConst = ssaop.OpConst32 |
| } |
| |
| for _, f := range rttype.Type.Fields() { |
| if f.Offset == off && copyCompatibleType(v.Type, f.Type) { |
| switch f.Sym.Name { |
| case "Size_": |
| v.Reset(ptrSizedOpConst) |
| v.AuxInt = t.Size() |
| return v |
| case "PtrBytes": |
| v.Reset(ptrSizedOpConst) |
| v.AuxInt = types.PtrDataSize(t) |
| return v |
| case "Hash": |
| v.Reset(ssaop.OpConst32) |
| v.AuxInt = int64(int32(types.TypeHash(t))) |
| return v |
| case "TFlag": |
| v.Reset(ssaop.OpConst8) |
| v.AuxInt = int64(t.TFlag()) |
| return v |
| case "Kind_": |
| v.Reset(ssaop.OpConst8) |
| v.AuxInt = int64(int8(reflectdata.ABIKindOfType(t))) |
| return v |
| case "GCData": |
| gcdata, _ := reflectdata.GCSym(t, true) |
| v.Reset(ssaop.OpAddr) |
| v.Aux = ssa.SymToAux(gcdata) |
| v.AddArg(sb) |
| return v |
| default: |
| base.Fatalf("unknown field %s for fixedLoad of %s at offset %d", f.Sym.Name, lsym.Name, off) |
| } |
| } |
| } |
| |
| if t.IsPtr() && off == rttype.PtrType.OffsetOf("Elem") { |
| elemSym := reflectdata.TypeLinksym(t.Elem()) |
| reflectdata.MarkTypeSymUsedInInterface(elemSym, f.Fe.Func().Linksym()) |
| v.Reset(ssaop.OpAddr) |
| v.Aux = ssa.SymToAux(elemSym) |
| v.AddArg(sb) |
| return v |
| } |
| |
| base.Fatalf("fixedLoad data not known for %s:%d", sym, off) |
| } |
| |
| base.Fatalf("fixedLoad data not known for %s:%d", sym, off) |
| return nil |
| } |
| |
| func rewriteStructLoad(v *ssa.Value) *ssa.Value { |
| b := v.Block |
| ptr := v.Args[0] |
| mem := v.Args[1] |
| |
| t := v.Type |
| args := make([]*ssa.Value, t.NumFields()) |
| for i := range args { |
| ft := t.FieldType(i) |
| addr := b.NewValue1I(v.Pos, ssaop.OpOffPtr, ft.PtrTo(), t.FieldOff(i), ptr) |
| args[i] = b.NewValue2(v.Pos, ssaop.OpLoad, ft, addr, mem) |
| } |
| |
| v.Reset(ssaop.OpStructMake) |
| v.AddArgs(args...) |
| return v |
| } |
| |
| // symIsROZero reports whether sym is a read-only global whose data contains all zeros. |
| func symIsROZero(sym ssa.Sym) bool { |
| lsym := sym.(*obj.LSym) |
| if lsym.Type != objabi.SRODATA || len(lsym.R) != 0 { |
| return false |
| } |
| for _, b := range lsym.P { |
| if b != 0 { |
| return false |
| } |
| } |
| return true |
| } |
| |
| // uaddOvf reports whether unsigned a+b would overflow. |
| func uaddOvf(a, b int64) bool { |
| return uint64(a)+uint64(b) < uint64(a) |
| } |
| |
| // warnRule generates compiler debug output with string s when |
| // v is not in autogenerated code, cond is true and the rule has fired. |
| func warnRule(cond bool, v *ssa.Value, s string) bool { |
| if pos := v.Pos; pos.Line() > 1 && cond { |
| v.Block.Func.Warnl(pos, s) |
| } |
| return true |
| } |
| |
| func bitsSub64(x, y, borrow int64) (r struct{ diff, borrow int64 }) { |
| d, b := bits.Sub64(uint64(x), uint64(y), uint64(borrow)) |
| r.diff, r.borrow = int64(d), int64(b) |
| return |
| } |
| |
| func modularMultiplicativeInverse(x uint64) (y uint64) { |
| if x%2 != 1 { |
| panic("even numbers in a power-of-two modulus do not have a multiplicative inverse") |
| } |
| // we start with 3 bits of precision because each odd number is its own multiplicative inverse mod 8 |
| y = x // 3 bits |
| |
| // now use the Newton-Raphson method to double the number of correct bits in each iteration. |
| y *= 2 - x*y // 6 bits |
| y *= 2 - x*y // 12 bits |
| y *= 2 - x*y // 24 bits |
| y *= 2 - x*y // 48 bits |
| y *= 2 - x*y // 96 bits; good enough |
| return |
| } |