| // 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 generic | |
| 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": | |
| return true | |
| case "TFlag": | |
| // A noalg descriptor can be replaced by one with | |
| // algorithms, changing TFlagRegularMemory. | |
| return t.TFlagComputed() && !types.TypeHasNoAlg(t) | |
| 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 r.Sym.IsGoType() { | |
| // 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 | |
| } |