| // 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 ssa | |
| import ( | |
| "cmd/compile/internal/logopt" | |
| "cmd/compile/internal/types" | |
| "cmd/internal/obj" | |
| "cmd/internal/obj/s390x" | |
| "cmd/internal/objabi" | |
| "cmd/internal/src" | |
| "encoding/binary" | |
| "fmt" | |
| "io" | |
| "math" | |
| "math/bits" | |
| "os" | |
| "path/filepath" | |
| ) | |
| type deadValueChoice bool | |
| const ( | |
| leaveDeadValues deadValueChoice = false | |
| removeDeadValues = true | |
| ) | |
| // deadcode indicates that rewrite should try to remove any values that become dead. | |
| func applyRewrite(f *Func, rb blockRewriter, rv valueRewriter, deadcode deadValueChoice) { | |
| // repeat rewrites until we find no more rewrites | |
| pendingLines := f.cachedLineStarts // Holds statement boundaries that need to be moved to a new value/block | |
| pendingLines.clear() | |
| debug := f.pass.debug | |
| if debug > 1 { | |
| fmt.Printf("%s: rewriting for %s\n", f.pass.name, f.Name) | |
| } | |
| for { | |
| change := false | |
| for _, b := range f.Blocks { | |
| var b0 *Block | |
| if debug > 1 { | |
| b0 = new(Block) | |
| *b0 = *b | |
| b0.Succs = append([]Edge{}, b.Succs...) // make a new copy, not aliasing | |
| } | |
| for i, c := range b.ControlValues() { | |
| for c.Op == OpCopy { | |
| c = c.Args[0] | |
| b.ReplaceControl(i, c) | |
| } | |
| } | |
| if rb(b) { | |
| change = true | |
| if debug > 1 { | |
| fmt.Printf("rewriting %s -> %s\n", b0.LongString(), b.LongString()) | |
| } | |
| } | |
| for j, v := range b.Values { | |
| var v0 *Value | |
| if debug > 1 { | |
| v0 = new(Value) | |
| *v0 = *v | |
| v0.Args = append([]*Value{}, v.Args...) // make a new copy, not aliasing | |
| } | |
| if v.Uses == 0 && v.removeable() { | |
| if v.Op != OpInvalid && deadcode == removeDeadValues { | |
| // Reset any values that are now unused, so that we decrement | |
| // the use count of all of its arguments. | |
| // Not quite a deadcode pass, because it does not handle cycles. | |
| // But it should help Uses==1 rules to fire. | |
| v.reset(OpInvalid) | |
| change = true | |
| } | |
| // No point rewriting values which aren't used. | |
| continue | |
| } | |
| vchange := phielimValue(v) | |
| if vchange && debug > 1 { | |
| fmt.Printf("rewriting %s -> %s\n", v0.LongString(), v.LongString()) | |
| } | |
| // Eliminate copy inputs. | |
| // If any copy input becomes unused, mark it | |
| // as invalid and discard its argument. Repeat | |
| // recursively on the discarded argument. | |
| // This phase helps remove phantom "dead copy" uses | |
| // of a value so that a x.Uses==1 rule condition | |
| // fires reliably. | |
| for i, a := range v.Args { | |
| if a.Op != OpCopy { | |
| continue | |
| } | |
| aa := copySource(a) | |
| v.SetArg(i, aa) | |
| // If a, a copy, has a line boundary indicator, attempt to find a new value | |
| // to hold it. The first candidate is the value that will replace a (aa), | |
| // if it shares the same block and line and is eligible. | |
| // The second option is v, which has a as an input. Because aa is earlier in | |
| // the data flow, it is the better choice. | |
| if a.Pos.IsStmt() == src.PosIsStmt { | |
| if aa.Block == a.Block && aa.Pos.Line() == a.Pos.Line() && aa.Pos.IsStmt() != src.PosNotStmt { | |
| aa.Pos = aa.Pos.WithIsStmt() | |
| } else if v.Block == a.Block && v.Pos.Line() == a.Pos.Line() && v.Pos.IsStmt() != src.PosNotStmt { | |
| v.Pos = v.Pos.WithIsStmt() | |
| } else { | |
| // Record the lost line and look for a new home after all rewrites are complete. | |
| // TODO: it's possible (in FOR loops, in particular) for statement boundaries for the same | |
| // line to appear in more than one block, but only one block is stored, so if both end | |
| // up here, then one will be lost. | |
| pendingLines.set(a.Pos, int32(a.Block.ID)) | |
| } | |
| a.Pos = a.Pos.WithNotStmt() | |
| } | |
| vchange = true | |
| for a.Uses == 0 { | |
| b := a.Args[0] | |
| a.reset(OpInvalid) | |
| a = b | |
| } | |
| } | |
| if vchange && debug > 1 { | |
| fmt.Printf("rewriting %s -> %s\n", v0.LongString(), v.LongString()) | |
| } | |
| // apply rewrite function | |
| if rv(v) { | |
| vchange = true | |
| // If value changed to a poor choice for a statement boundary, move the boundary | |
| if v.Pos.IsStmt() == src.PosIsStmt { | |
| if k := nextGoodStatementIndex(v, j, b); k != j { | |
| v.Pos = v.Pos.WithNotStmt() | |
| b.Values[k].Pos = b.Values[k].Pos.WithIsStmt() | |
| } | |
| } | |
| } | |
| change = change || vchange | |
| if vchange && debug > 1 { | |
| fmt.Printf("rewriting %s -> %s\n", v0.LongString(), v.LongString()) | |
| } | |
| } | |
| } | |
| if !change { | |
| break | |
| } | |
| } | |
| // remove clobbered values | |
| for _, b := range f.Blocks { | |
| j := 0 | |
| for i, v := range b.Values { | |
| vl := v.Pos | |
| if v.Op == OpInvalid { | |
| if v.Pos.IsStmt() == src.PosIsStmt { | |
| pendingLines.set(vl, int32(b.ID)) | |
| } | |
| f.freeValue(v) | |
| continue | |
| } | |
| if v.Pos.IsStmt() != src.PosNotStmt && pendingLines.get(vl) == int32(b.ID) { | |
| pendingLines.remove(vl) | |
| v.Pos = v.Pos.WithIsStmt() | |
| } | |
| if i != j { | |
| b.Values[j] = v | |
| } | |
| j++ | |
| } | |
| if pendingLines.get(b.Pos) == int32(b.ID) { | |
| b.Pos = b.Pos.WithIsStmt() | |
| pendingLines.remove(b.Pos) | |
| } | |
| b.truncateValues(j) | |
| } | |
| } | |
| // Common functions called from rewriting rules | |
| func is64BitFloat(t *types.Type) bool { | |
| return t.Size() == 8 && t.IsFloat() | |
| } | |
| func is32BitFloat(t *types.Type) bool { | |
| return t.Size() == 4 && t.IsFloat() | |
| } | |
| func is64BitInt(t *types.Type) bool { | |
| return t.Size() == 8 && t.IsInteger() | |
| } | |
| func is32BitInt(t *types.Type) bool { | |
| return t.Size() == 4 && t.IsInteger() | |
| } | |
| func is16BitInt(t *types.Type) bool { | |
| return t.Size() == 2 && t.IsInteger() | |
| } | |
| func is8BitInt(t *types.Type) bool { | |
| return t.Size() == 1 && t.IsInteger() | |
| } | |
| func isPtr(t *types.Type) bool { | |
| return t.IsPtrShaped() | |
| } | |
| func isSigned(t *types.Type) bool { | |
| return t.IsSigned() | |
| } | |
| // mergeSym merges two symbolic offsets. There is no real merging of | |
| // offsets, we just pick the non-nil one. | |
| func mergeSym(x, y interface{}) interface{} { | |
| if x == nil { | |
| return y | |
| } | |
| if y == nil { | |
| return x | |
| } | |
| panic(fmt.Sprintf("mergeSym with two non-nil syms %s %s", x, y)) | |
| } | |
| func canMergeSym(x, y interface{}) bool { | |
| return x == nil || y == nil | |
| } | |
| func mergeSymTyped(x, y Sym) Sym { | |
| if x == nil { | |
| return y | |
| } | |
| if y == nil { | |
| return x | |
| } | |
| panic(fmt.Sprintf("mergeSym with two non-nil syms %v %v", x, y)) | |
| } | |
| // canMergeLoadClobber reports whether the load can be merged into target without | |
| // invalidating the schedule. | |
| // It also checks that the other non-load argument x is something we | |
| // are ok with clobbering. | |
| func canMergeLoadClobber(target, load, x *Value) bool { | |
| // The register containing x is going to get clobbered. | |
| // Don't merge if we still need the value of x. | |
| // We don't have liveness information here, but we can | |
| // approximate x dying with: | |
| // 1) target is x's only use. | |
| // 2) target is not in a deeper loop than x. | |
| if x.Uses != 1 { | |
| return false | |
| } | |
| loopnest := x.Block.Func.loopnest() | |
| loopnest.calculateDepths() | |
| if loopnest.depth(target.Block.ID) > loopnest.depth(x.Block.ID) { | |
| return false | |
| } | |
| return canMergeLoad(target, load) | |
| } | |
| // canMergeLoad reports whether the load can be merged into target without | |
| // invalidating the schedule. | |
| func canMergeLoad(target, load *Value) bool { | |
| if target.Block.ID != load.Block.ID { | |
| // If the load is in a different block do not merge it. | |
| return false | |
| } | |
| // We can't merge the load into the target if the load | |
| // has more than one use. | |
| if load.Uses != 1 { | |
| return false | |
| } | |
| mem := load.MemoryArg() | |
| // We need the load's memory arg to still be alive at target. That | |
| // can't be the case if one of target's args depends on a memory | |
| // state that is a successor of load's memory arg. | |
| // | |
| // For example, it would be invalid to merge load into target in | |
| // the following situation because newmem has killed oldmem | |
| // before target is reached: | |
| // load = read ... oldmem | |
| // newmem = write ... oldmem | |
| // arg0 = read ... newmem | |
| // target = add arg0 load | |
| // | |
| // If the argument comes from a different block then we can exclude | |
| // it immediately because it must dominate load (which is in the | |
| // same block as target). | |
| var args []*Value | |
| for _, a := range target.Args { | |
| if a != load && a.Block.ID == target.Block.ID { | |
| args = append(args, a) | |
| } | |
| } | |
| // memPreds contains memory states known to be predecessors of load's | |
| // memory state. It is lazily initialized. | |
| var memPreds map[*Value]bool | |
| for i := 0; len(args) > 0; i++ { | |
| const limit = 100 | |
| if i >= limit { | |
| // Give up if we have done a lot of iterations. | |
| return false | |
| } | |
| v := args[len(args)-1] | |
| args = args[:len(args)-1] | |
| if target.Block.ID != v.Block.ID { | |
| // Since target and load are in the same block | |
| // we can stop searching when we leave the block. | |
| continue | |
| } | |
| if v.Op == OpPhi { | |
| // A Phi implies we have reached the top of the block. | |
| // The memory phi, if it exists, is always | |
| // the first logical store in the block. | |
| continue | |
| } | |
| if v.Type.IsTuple() && v.Type.FieldType(1).IsMemory() { | |
| // We could handle this situation however it is likely | |
| // to be very rare. | |
| return false | |
| } | |
| if v.Op.SymEffect()&SymAddr != 0 { | |
| // This case prevents an operation that calculates the | |
| // address of a local variable from being forced to schedule | |
| // before its corresponding VarDef. | |
| // See issue 28445. | |
| // v1 = LOAD ... | |
| // v2 = VARDEF | |
| // v3 = LEAQ | |
| // v4 = CMPQ v1 v3 | |
| // We don't want to combine the CMPQ with the load, because | |
| // that would force the CMPQ to schedule before the VARDEF, which | |
| // in turn requires the LEAQ to schedule before the VARDEF. | |
| return false | |
| } | |
| if v.Type.IsMemory() { | |
| if memPreds == nil { | |
| // Initialise a map containing memory states | |
| // known to be predecessors of load's memory | |
| // state. | |
| memPreds = make(map[*Value]bool) | |
| m := mem | |
| const limit = 50 | |
| for i := 0; i < limit; i++ { | |
| if m.Op == OpPhi { | |
| // The memory phi, if it exists, is always | |
| // the first logical store in the block. | |
| break | |
| } | |
| if m.Block.ID != target.Block.ID { | |
| break | |
| } | |
| if !m.Type.IsMemory() { | |
| break | |
| } | |
| memPreds[m] = true | |
| if len(m.Args) == 0 { | |
| break | |
| } | |
| m = m.MemoryArg() | |
| } | |
| } | |
| // We can merge if v is a predecessor of mem. | |
| // | |
| // For example, we can merge load into target in the | |
| // following scenario: | |
| // x = read ... v | |
| // mem = write ... v | |
| // load = read ... mem | |
| // target = add x load | |
| if memPreds[v] { | |
| continue | |
| } | |
| return false | |
| } | |
| if len(v.Args) > 0 && v.Args[len(v.Args)-1] == mem { | |
| // If v takes mem as an input then we know mem | |
| // is valid at this point. | |
| continue | |
| } | |
| for _, a := range v.Args { | |
| if target.Block.ID == a.Block.ID { | |
| args = append(args, a) | |
| } | |
| } | |
| } | |
| return true | |
| } | |
| // isSameCall reports whether sym is the same as the given named symbol | |
| func isSameCall(sym interface{}, name string) bool { | |
| fn := sym.(*AuxCall).Fn | |
| return fn != nil && fn.String() == name | |
| } | |
| // nlz returns the number of leading zeros. | |
| func nlz64(x int64) int { return bits.LeadingZeros64(uint64(x)) } | |
| func nlz32(x int32) int { return bits.LeadingZeros32(uint32(x)) } | |
| func nlz16(x int16) int { return bits.LeadingZeros16(uint16(x)) } | |
| func nlz8(x int8) int { return bits.LeadingZeros8(uint8(x)) } | |
| // ntzX returns the number of trailing zeros. | |
| func ntz64(x int64) int { return bits.TrailingZeros64(uint64(x)) } | |
| func ntz32(x int32) int { return bits.TrailingZeros32(uint32(x)) } | |
| func ntz16(x int16) int { return bits.TrailingZeros16(uint16(x)) } | |
| func ntz8(x int8) int { return bits.TrailingZeros8(uint8(x)) } | |
| func oneBit(x int64) bool { return x&(x-1) == 0 && x != 0 } | |
| func oneBit8(x int8) bool { return x&(x-1) == 0 && x != 0 } | |
| func oneBit16(x int16) bool { return x&(x-1) == 0 && x != 0 } | |
| func oneBit32(x int32) bool { return x&(x-1) == 0 && x != 0 } | |
| func oneBit64(x int64) bool { return x&(x-1) == 0 && x != 0 } | |
| // nto returns the number of trailing ones. | |
| func nto(x int64) int64 { | |
| return int64(ntz64(^x)) | |
| } | |
| // log2 returns logarithm in base 2 of uint64(n), with log2(0) = -1. | |
| // Rounds down. | |
| func log2(n int64) int64 { | |
| return int64(bits.Len64(uint64(n))) - 1 | |
| } | |
| // logX returns logarithm of n base 2. | |
| // n must be a positive power of 2 (isPowerOfTwoX returns true). | |
| func log8(n int8) int64 { | |
| return int64(bits.Len8(uint8(n))) - 1 | |
| } | |
| func log16(n int16) int64 { | |
| return int64(bits.Len16(uint16(n))) - 1 | |
| } | |
| func log32(n int32) int64 { | |
| return int64(bits.Len32(uint32(n))) - 1 | |
| } | |
| func log64(n int64) int64 { | |
| return int64(bits.Len64(uint64(n))) - 1 | |
| } | |
| // log2uint32 returns logarithm in base 2 of uint32(n), with log2(0) = -1. | |
| // Rounds down. | |
| func log2uint32(n int64) int64 { | |
| return int64(bits.Len32(uint32(n))) - 1 | |
| } | |
| // isPowerOfTwo reports whether n is a power of 2. | |
| func isPowerOfTwo(n int64) bool { | |
| return n > 0 && n&(n-1) == 0 | |
| } | |
| func isPowerOfTwo8(n int8) bool { | |
| return n > 0 && n&(n-1) == 0 | |
| } | |
| func isPowerOfTwo16(n int16) bool { | |
| return n > 0 && n&(n-1) == 0 | |
| } | |
| func isPowerOfTwo32(n int32) bool { | |
| return n > 0 && n&(n-1) == 0 | |
| } | |
| func isPowerOfTwo64(n int64) bool { | |
| return n > 0 && n&(n-1) == 0 | |
| } | |
| // isUint64PowerOfTwo reports whether uint64(n) is a power of 2. | |
| func isUint64PowerOfTwo(in int64) bool { | |
| n := uint64(in) | |
| return n > 0 && n&(n-1) == 0 | |
| } | |
| // isUint32PowerOfTwo reports whether uint32(n) is a power of 2. | |
| func isUint32PowerOfTwo(in int64) bool { | |
| n := uint64(uint32(in)) | |
| return n > 0 && n&(n-1) == 0 | |
| } | |
| // is32Bit reports whether n can be represented as a signed 32 bit integer. | |
| func is32Bit(n int64) bool { | |
| return n == int64(int32(n)) | |
| } | |
| // is16Bit reports whether n can be represented as a signed 16 bit integer. | |
| func is16Bit(n int64) bool { | |
| return n == int64(int16(n)) | |
| } | |
| // is8Bit reports whether n can be represented as a signed 8 bit integer. | |
| func is8Bit(n int64) bool { | |
| return n == int64(int8(n)) | |
| } | |
| // isU8Bit reports whether n can be represented as an unsigned 8 bit integer. | |
| func isU8Bit(n int64) bool { | |
| return n == int64(uint8(n)) | |
| } | |
| // isU12Bit reports whether n can be represented as an unsigned 12 bit integer. | |
| func isU12Bit(n int64) bool { | |
| return 0 <= n && n < (1<<12) | |
| } | |
| // isU16Bit reports whether n can be represented as an unsigned 16 bit integer. | |
| func isU16Bit(n int64) bool { | |
| return n == int64(uint16(n)) | |
| } | |
| // isU32Bit reports whether n can be represented as an unsigned 32 bit integer. | |
| func isU32Bit(n int64) bool { | |
| return n == int64(uint32(n)) | |
| } | |
| // is20Bit reports whether n can be represented as a signed 20 bit integer. | |
| func is20Bit(n int64) bool { | |
| return -(1<<19) <= n && n < (1<<19) | |
| } | |
| // b2i translates a boolean value to 0 or 1 for assigning to auxInt. | |
| func b2i(b bool) int64 { | |
| if b { | |
| return 1 | |
| } | |
| return 0 | |
| } | |
| // b2i32 translates a boolean value to 0 or 1. | |
| func b2i32(b bool) int32 { | |
| if b { | |
| return 1 | |
| } | |
| return 0 | |
| } | |
| // shiftIsBounded reports whether (left/right) shift Value v is known to be bounded. | |
| // A shift is bounded if it is shifting by less than the width of the shifted value. | |
| func shiftIsBounded(v *Value) bool { | |
| return v.AuxInt != 0 | |
| } | |
| // truncate64Fto32F converts a float64 value to a float32 preserving the bit pattern | |
| // of the mantissa. It will panic if the truncation results in lost information. | |
| func truncate64Fto32F(f float64) float32 { | |
| if !isExactFloat32(f) { | |
| panic("truncate64Fto32F: truncation is not exact") | |
| } | |
| if !math.IsNaN(f) { | |
| return float32(f) | |
| } | |
| // NaN bit patterns aren't necessarily preserved across conversion | |
| // instructions so we need to do the conversion manually. | |
| b := math.Float64bits(f) | |
| m := b & ((1 << 52) - 1) // mantissa (a.k.a. significand) | |
| // | sign | exponent | mantissa | | |
| r := uint32(((b >> 32) & (1 << 31)) | 0x7f800000 | (m >> (52 - 23))) | |
| return math.Float32frombits(r) | |
| } | |
| // extend32Fto64F converts a float32 value to a float64 value preserving the bit | |
| // pattern of the mantissa. | |
| func extend32Fto64F(f float32) float64 { | |
| if !math.IsNaN(float64(f)) { | |
| return float64(f) | |
| } | |
| // NaN bit patterns aren't necessarily preserved across conversion | |
| // instructions so we need to do the conversion manually. | |
| b := uint64(math.Float32bits(f)) | |
| // | sign | exponent | mantissa | | |
| r := ((b << 32) & (1 << 63)) | (0x7ff << 52) | ((b & 0x7fffff) << (52 - 23)) | |
| return math.Float64frombits(r) | |
| } | |
| // DivisionNeedsFixUp reports whether the division needs fix-up code. | |
| func DivisionNeedsFixUp(v *Value) bool { | |
| return v.AuxInt == 0 | |
| } | |
| // auxFrom64F encodes a float64 value so it can be stored in an AuxInt. | |
| func auxFrom64F(f float64) int64 { | |
| if f != f { | |
| panic("can't encode a NaN in AuxInt field") | |
| } | |
| return int64(math.Float64bits(f)) | |
| } | |
| // auxFrom32F encodes a float32 value so it can be stored in an AuxInt. | |
| func auxFrom32F(f float32) int64 { | |
| if f != f { | |
| panic("can't encode a NaN in AuxInt field") | |
| } | |
| return int64(math.Float64bits(extend32Fto64F(f))) | |
| } | |
| // auxTo32F decodes a float32 from the AuxInt value provided. | |
| func auxTo32F(i int64) float32 { | |
| return truncate64Fto32F(math.Float64frombits(uint64(i))) | |
| } | |
| // auxTo64F decodes a float64 from the AuxInt value provided. | |
| func auxTo64F(i int64) float64 { | |
| return math.Float64frombits(uint64(i)) | |
| } | |
| func auxIntToBool(i int64) bool { | |
| if i == 0 { | |
| return false | |
| } | |
| return true | |
| } | |
| func auxIntToInt8(i int64) int8 { | |
| return int8(i) | |
| } | |
| func auxIntToInt16(i int64) int16 { | |
| return int16(i) | |
| } | |
| func auxIntToInt32(i int64) int32 { | |
| return int32(i) | |
| } | |
| func auxIntToInt64(i int64) int64 { | |
| return i | |
| } | |
| func auxIntToUint8(i int64) uint8 { | |
| return uint8(i) | |
| } | |
| func auxIntToFloat32(i int64) float32 { | |
| return float32(math.Float64frombits(uint64(i))) | |
| } | |
| func auxIntToFloat64(i int64) float64 { | |
| return math.Float64frombits(uint64(i)) | |
| } | |
| func auxIntToValAndOff(i int64) ValAndOff { | |
| return ValAndOff(i) | |
| } | |
| func auxIntToArm64BitField(i int64) arm64BitField { | |
| return arm64BitField(i) | |
| } | |
| func auxIntToInt128(x int64) int128 { | |
| if x != 0 { | |
| panic("nonzero int128 not allowed") | |
| } | |
| return 0 | |
| } | |
| func auxIntToFlagConstant(x int64) flagConstant { | |
| return flagConstant(x) | |
| } | |
| func auxIntToOp(cc int64) Op { | |
| return Op(cc) | |
| } | |
| func boolToAuxInt(b bool) int64 { | |
| if b { | |
| return 1 | |
| } | |
| return 0 | |
| } | |
| func int8ToAuxInt(i int8) int64 { | |
| return int64(i) | |
| } | |
| func int16ToAuxInt(i int16) int64 { | |
| return int64(i) | |
| } | |
| func int32ToAuxInt(i int32) int64 { | |
| return int64(i) | |
| } | |
| func int64ToAuxInt(i int64) int64 { | |
| return int64(i) | |
| } | |
| func uint8ToAuxInt(i uint8) int64 { | |
| return int64(int8(i)) | |
| } | |
| func float32ToAuxInt(f float32) int64 { | |
| return int64(math.Float64bits(float64(f))) | |
| } | |
| func float64ToAuxInt(f float64) int64 { | |
| return int64(math.Float64bits(f)) | |
| } | |
| func valAndOffToAuxInt(v ValAndOff) int64 { | |
| return int64(v) | |
| } | |
| func arm64BitFieldToAuxInt(v arm64BitField) int64 { | |
| return int64(v) | |
| } | |
| func int128ToAuxInt(x int128) int64 { | |
| if x != 0 { | |
| panic("nonzero int128 not allowed") | |
| } | |
| return 0 | |
| } | |
| func flagConstantToAuxInt(x flagConstant) int64 { | |
| return int64(x) | |
| } | |
| func opToAuxInt(o Op) int64 { | |
| return int64(o) | |
| } | |
| func auxToString(i interface{}) string { | |
| return i.(string) | |
| } | |
| func auxToSym(i interface{}) Sym { | |
| // TODO: kind of a hack - allows nil interface through | |
| s, _ := i.(Sym) | |
| return s | |
| } | |
| func auxToType(i interface{}) *types.Type { | |
| return i.(*types.Type) | |
| } | |
| func auxToCall(i interface{}) *AuxCall { | |
| return i.(*AuxCall) | |
| } | |
| func auxToS390xCCMask(i interface{}) s390x.CCMask { | |
| return i.(s390x.CCMask) | |
| } | |
| func auxToS390xRotateParams(i interface{}) s390x.RotateParams { | |
| return i.(s390x.RotateParams) | |
| } | |
| func stringToAux(s string) interface{} { | |
| return s | |
| } | |
| func symToAux(s Sym) interface{} { | |
| return s | |
| } | |
| func callToAux(s *AuxCall) interface{} { | |
| return s | |
| } | |
| func typeToAux(t *types.Type) interface{} { | |
| return t | |
| } | |
| func s390xCCMaskToAux(c s390x.CCMask) interface{} { | |
| return c | |
| } | |
| func s390xRotateParamsToAux(r s390x.RotateParams) interface{} { | |
| return r | |
| } | |
| // uaddOvf reports whether unsigned a+b would overflow. | |
| func uaddOvf(a, b int64) bool { | |
| return uint64(a)+uint64(b) < uint64(a) | |
| } | |
| // de-virtualize an InterCall | |
| // 'sym' is the symbol for the itab | |
| func devirt(v *Value, aux interface{}, sym Sym, offset int64) *AuxCall { | |
| f := v.Block.Func | |
| n, ok := sym.(*obj.LSym) | |
| if !ok { | |
| return nil | |
| } | |
| lsym := f.fe.DerefItab(n, offset) | |
| if f.pass.debug > 0 { | |
| if lsym != nil { | |
| f.Warnl(v.Pos, "de-virtualizing call") | |
| } else { | |
| f.Warnl(v.Pos, "couldn't de-virtualize call") | |
| } | |
| } | |
| if lsym == nil { | |
| return nil | |
| } | |
| va := aux.(*AuxCall) | |
| return StaticAuxCall(lsym, va.args, va.results) | |
| } | |
| // de-virtualize an InterLECall | |
| // 'sym' is the symbol for the itab | |
| func devirtLESym(v *Value, aux interface{}, sym Sym, offset int64) *obj.LSym { | |
| n, ok := sym.(*obj.LSym) | |
| if !ok { | |
| return nil | |
| } | |
| f := v.Block.Func | |
| lsym := f.fe.DerefItab(n, offset) | |
| if f.pass.debug > 0 { | |
| if lsym != nil { | |
| f.Warnl(v.Pos, "de-virtualizing call") | |
| } else { | |
| f.Warnl(v.Pos, "couldn't de-virtualize call") | |
| } | |
| } | |
| if lsym == nil { | |
| return nil | |
| } | |
| return lsym | |
| } | |
| func devirtLECall(v *Value, sym *obj.LSym) *Value { | |
| v.Op = OpStaticLECall | |
| v.Aux.(*AuxCall).Fn = sym | |
| v.RemoveArg(0) | |
| return v | |
| } | |
| // isSamePtr reports whether p1 and p2 point to the same address. | |
| func isSamePtr(p1, p2 *Value) bool { | |
| if p1 == p2 { | |
| return true | |
| } | |
| if p1.Op != p2.Op { | |
| return false | |
| } | |
| switch p1.Op { | |
| case OpOffPtr: | |
| return p1.AuxInt == p2.AuxInt && isSamePtr(p1.Args[0], p2.Args[0]) | |
| case OpAddr, OpLocalAddr: | |
| // OpAddr's 0th arg is either OpSP or OpSB, which means that it is uniquely identified by its Op. | |
| // Checking for value equality only works after [z]cse has run. | |
| return p1.Aux == p2.Aux && p1.Args[0].Op == p2.Args[0].Op | |
| case OpAddPtr: | |
| return p1.Args[1] == p2.Args[1] && isSamePtr(p1.Args[0], p2.Args[0]) | |
| } | |
| return false | |
| } | |
| func isStackPtr(v *Value) bool { | |
| for v.Op == OpOffPtr || v.Op == OpAddPtr { | |
| v = v.Args[0] | |
| } | |
| return v.Op == OpSP || v.Op == OpLocalAddr | |
| } | |
| // disjoint reports whether the memory region specified by [p1:p1+n1) | |
| // does not overlap with [p2:p2+n2). | |
| // A return value of false does not imply the regions overlap. | |
| func disjoint(p1 *Value, n1 int64, p2 *Value, n2 int64) bool { | |
| if n1 == 0 || n2 == 0 { | |
| return true | |
| } | |
| if p1 == p2 { | |
| return false | |
| } | |
| baseAndOffset := func(ptr *Value) (base *Value, offset int64) { | |
| base, offset = ptr, 0 | |
| for base.Op == OpOffPtr { | |
| offset += base.AuxInt | |
| base = base.Args[0] | |
| } | |
| return base, offset | |
| } | |
| p1, off1 := baseAndOffset(p1) | |
| p2, off2 := baseAndOffset(p2) | |
| if isSamePtr(p1, p2) { | |
| return !overlap(off1, n1, off2, n2) | |
| } | |
| // p1 and p2 are not the same, so if they are both OpAddrs then | |
| // they point to different variables. | |
| // If one pointer is on the stack and the other is an argument | |
| // then they can't overlap. | |
| switch p1.Op { | |
| case OpAddr, OpLocalAddr: | |
| if p2.Op == OpAddr || p2.Op == OpLocalAddr || p2.Op == OpSP { | |
| return true | |
| } | |
| return p2.Op == OpArg && p1.Args[0].Op == OpSP | |
| case OpArg: | |
| if p2.Op == OpSP || p2.Op == OpLocalAddr { | |
| return true | |
| } | |
| case OpSP: | |
| return p2.Op == OpAddr || p2.Op == OpLocalAddr || p2.Op == OpArg || p2.Op == OpSP | |
| } | |
| return false | |
| } | |
| // moveSize returns the number of bytes an aligned MOV instruction moves | |
| func moveSize(align int64, c *Config) int64 { | |
| switch { | |
| case align%8 == 0 && c.PtrSize == 8: | |
| return 8 | |
| case align%4 == 0: | |
| return 4 | |
| case align%2 == 0: | |
| return 2 | |
| } | |
| return 1 | |
| } | |
| // mergePoint finds a block among a's blocks which dominates b and is itself | |
| // dominated by all of a's blocks. Returns nil if it can't find one. | |
| // Might return nil even if one does exist. | |
| func mergePoint(b *Block, a ...*Value) *Block { | |
| // Walk backward from b looking for one of the a's blocks. | |
| // Max distance | |
| d := 100 | |
| for d > 0 { | |
| for _, x := range a { | |
| if b == x.Block { | |
| goto found | |
| } | |
| } | |
| if len(b.Preds) > 1 { | |
| // Don't know which way to go back. Abort. | |
| return nil | |
| } | |
| b = b.Preds[0].b | |
| d-- | |
| } | |
| return nil // too far away | |
| found: | |
| // At this point, r is the first value in a that we find by walking backwards. | |
| // if we return anything, r will be it. | |
| r := b | |
| // Keep going, counting the other a's that we find. They must all dominate r. | |
| na := 0 | |
| for d > 0 { | |
| for _, x := range a { | |
| if b == x.Block { | |
| na++ | |
| } | |
| } | |
| if na == len(a) { | |
| // Found all of a in a backwards walk. We can return r. | |
| return r | |
| } | |
| if len(b.Preds) > 1 { | |
| return nil | |
| } | |
| b = b.Preds[0].b | |
| d-- | |
| } | |
| return nil // too far away | |
| } | |
| // clobber invalidates values. Returns true. | |
| // clobber is used by rewrite rules to: | |
| // A) make sure the values are really dead and never used again. | |
| // B) decrement use counts of the values' args. | |
| func clobber(vv ...*Value) bool { | |
| for _, v := range vv { | |
| v.reset(OpInvalid) | |
| // Note: leave v.Block intact. The Block field is used after clobber. | |
| } | |
| return true | |
| } | |
| // clobberIfDead resets v when use count is 1. Returns true. | |
| // clobberIfDead is used by rewrite rules to decrement | |
| // use counts of v's args when v is dead and never used. | |
| func clobberIfDead(v *Value) bool { | |
| if v.Uses == 1 { | |
| v.reset(OpInvalid) | |
| } | |
| // Note: leave v.Block intact. The Block field is used after clobberIfDead. | |
| return true | |
| } | |
| // noteRule is an easy way to track if a rule is matched when writing | |
| // new ones. Make the rule of interest also conditional on | |
| // noteRule("note to self: rule of interest matched") | |
| // and that message will print when the rule matches. | |
| func noteRule(s string) bool { | |
| fmt.Println(s) | |
| return true | |
| } | |
| // countRule increments Func.ruleMatches[key]. | |
| // If Func.ruleMatches is non-nil at the end | |
| // of compilation, it will be printed to stdout. | |
| // This is intended to make it easier to find which functions | |
| // which contain lots of rules matches when developing new rules. | |
| func countRule(v *Value, key string) bool { | |
| f := v.Block.Func | |
| if f.ruleMatches == nil { | |
| f.ruleMatches = make(map[string]int) | |
| } | |
| f.ruleMatches[key]++ | |
| return true | |
| } | |
| // 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 *Value, s string) bool { | |
| if pos := v.Pos; pos.Line() > 1 && cond { | |
| v.Block.Func.Warnl(pos, s) | |
| } | |
| return true | |
| } | |
| // for a pseudo-op like (LessThan x), extract x | |
| func flagArg(v *Value) *Value { | |
| if len(v.Args) != 1 || !v.Args[0].Type.IsFlags() { | |
| return nil | |
| } | |
| return v.Args[0] | |
| } | |
| // arm64Negate finds the complement to an ARM64 condition code, | |
| // for example Equal -> NotEqual or LessThan -> GreaterEqual | |
| // | |
| // TODO: add floating-point conditions | |
| func arm64Negate(op Op) Op { | |
| switch op { | |
| case OpARM64LessThan: | |
| return OpARM64GreaterEqual | |
| case OpARM64LessThanU: | |
| return OpARM64GreaterEqualU | |
| case OpARM64GreaterThan: | |
| return OpARM64LessEqual | |
| case OpARM64GreaterThanU: | |
| return OpARM64LessEqualU | |
| case OpARM64LessEqual: | |
| return OpARM64GreaterThan | |
| case OpARM64LessEqualU: | |
| return OpARM64GreaterThanU | |
| case OpARM64GreaterEqual: | |
| return OpARM64LessThan | |
| case OpARM64GreaterEqualU: | |
| return OpARM64LessThanU | |
| case OpARM64Equal: | |
| return OpARM64NotEqual | |
| case OpARM64NotEqual: | |
| return OpARM64Equal | |
| case OpARM64LessThanF: | |
| return OpARM64GreaterEqualF | |
| case OpARM64GreaterThanF: | |
| return OpARM64LessEqualF | |
| case OpARM64LessEqualF: | |
| return OpARM64GreaterThanF | |
| case OpARM64GreaterEqualF: | |
| return OpARM64LessThanF | |
| default: | |
| panic("unreachable") | |
| } | |
| } | |
| // arm64Invert evaluates (InvertFlags op), which | |
| // is the same as altering the condition codes such | |
| // that the same result would be produced if the arguments | |
| // to the flag-generating instruction were reversed, e.g. | |
| // (InvertFlags (CMP x y)) -> (CMP y x) | |
| // | |
| // TODO: add floating-point conditions | |
| func arm64Invert(op Op) Op { | |
| switch op { | |
| case OpARM64LessThan: | |
| return OpARM64GreaterThan | |
| case OpARM64LessThanU: | |
| return OpARM64GreaterThanU | |
| case OpARM64GreaterThan: | |
| return OpARM64LessThan | |
| case OpARM64GreaterThanU: | |
| return OpARM64LessThanU | |
| case OpARM64LessEqual: | |
| return OpARM64GreaterEqual | |
| case OpARM64LessEqualU: | |
| return OpARM64GreaterEqualU | |
| case OpARM64GreaterEqual: | |
| return OpARM64LessEqual | |
| case OpARM64GreaterEqualU: | |
| return OpARM64LessEqualU | |
| case OpARM64Equal, OpARM64NotEqual: | |
| return op | |
| case OpARM64LessThanF: | |
| return OpARM64GreaterThanF | |
| case OpARM64GreaterThanF: | |
| return OpARM64LessThanF | |
| case OpARM64LessEqualF: | |
| return OpARM64GreaterEqualF | |
| case OpARM64GreaterEqualF: | |
| return OpARM64LessEqualF | |
| default: | |
| panic("unreachable") | |
| } | |
| } | |
| // evaluate an ARM64 op against a flags value | |
| // that is potentially constant; return 1 for true, | |
| // -1 for false, and 0 for not constant. | |
| func ccARM64Eval(op Op, flags *Value) int { | |
| fop := flags.Op | |
| if fop == OpARM64InvertFlags { | |
| return -ccARM64Eval(op, flags.Args[0]) | |
| } | |
| if fop != OpARM64FlagConstant { | |
| return 0 | |
| } | |
| fc := flagConstant(flags.AuxInt) | |
| b2i := func(b bool) int { | |
| if b { | |
| return 1 | |
| } | |
| return -1 | |
| } | |
| switch op { | |
| case OpARM64Equal: | |
| return b2i(fc.eq()) | |
| case OpARM64NotEqual: | |
| return b2i(fc.ne()) | |
| case OpARM64LessThan: | |
| return b2i(fc.lt()) | |
| case OpARM64LessThanU: | |
| return b2i(fc.ult()) | |
| case OpARM64GreaterThan: | |
| return b2i(fc.gt()) | |
| case OpARM64GreaterThanU: | |
| return b2i(fc.ugt()) | |
| case OpARM64LessEqual: | |
| return b2i(fc.le()) | |
| case OpARM64LessEqualU: | |
| return b2i(fc.ule()) | |
| case OpARM64GreaterEqual: | |
| return b2i(fc.ge()) | |
| case OpARM64GreaterEqualU: | |
| return b2i(fc.uge()) | |
| } | |
| return 0 | |
| } | |
| // logRule logs the use of the rule s. This will only be enabled if | |
| // rewrite rules were generated with the -log option, see gen/rulegen.go. | |
| func logRule(s string) { | |
| if ruleFile == nil { | |
| // Open a log file to write log to. We open in append | |
| // mode because all.bash runs the compiler lots of times, | |
| // and we want the concatenation of all of those logs. | |
| // This means, of course, that users need to rm the old log | |
| // to get fresh data. | |
| // TODO: all.bash runs compilers in parallel. Need to synchronize logging somehow? | |
| w, err := os.OpenFile(filepath.Join(os.Getenv("GOROOT"), "src", "rulelog"), | |
| os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) | |
| if err != nil { | |
| panic(err) | |
| } | |
| ruleFile = w | |
| } | |
| _, err := fmt.Fprintln(ruleFile, s) | |
| if err != nil { | |
| panic(err) | |
| } | |
| } | |
| var ruleFile io.Writer | |
| func min(x, y int64) int64 { | |
| if x < y { | |
| return x | |
| } | |
| return y | |
| } | |
| func isConstZero(v *Value) bool { | |
| switch v.Op { | |
| case OpConstNil: | |
| return true | |
| case OpConst64, OpConst32, OpConst16, OpConst8, OpConstBool, OpConst32F, OpConst64F: | |
| return v.AuxInt == 0 | |
| } | |
| return false | |
| } | |
| // 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 | |
| } | |
| } | |
| // 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 | |
| } | |
| } | |
| // check if an immediate can be directly encoded into an ARM's instruction | |
| func isARMImmRot(v uint32) bool { | |
| for i := 0; i < 16; i++ { | |
| if v&^0xff == 0 { | |
| return true | |
| } | |
| v = v<<2 | v>>30 | |
| } | |
| return false | |
| } | |
| // overlap reports whether the ranges given by the given offset and | |
| // size pairs overlap. | |
| func overlap(offset1, size1, offset2, size2 int64) bool { | |
| if offset1 >= offset2 && offset2+size2 > offset1 { | |
| return true | |
| } | |
| if offset2 >= offset1 && offset1+size1 > offset2 { | |
| return true | |
| } | |
| return false | |
| } | |
| func areAdjacentOffsets(off1, off2, size int64) bool { | |
| return off1+size == off2 || off1 == off2+size | |
| } | |
| // check if value zeroes out upper 32-bit of 64-bit register. | |
| // depth limits recursion depth. In AMD64.rules 3 is used as limit, | |
| // because it catches same amount of cases as 4. | |
| func zeroUpper32Bits(x *Value, depth int) bool { | |
| switch x.Op { | |
| case OpAMD64MOVLconst, OpAMD64MOVLload, OpAMD64MOVLQZX, OpAMD64MOVLloadidx1, | |
| OpAMD64MOVWload, OpAMD64MOVWloadidx1, OpAMD64MOVBload, OpAMD64MOVBloadidx1, | |
| OpAMD64MOVLloadidx4, OpAMD64ADDLload, OpAMD64SUBLload, OpAMD64ANDLload, | |
| OpAMD64ORLload, OpAMD64XORLload, OpAMD64CVTTSD2SL, | |
| OpAMD64ADDL, OpAMD64ADDLconst, OpAMD64SUBL, OpAMD64SUBLconst, | |
| OpAMD64ANDL, OpAMD64ANDLconst, OpAMD64ORL, OpAMD64ORLconst, | |
| OpAMD64XORL, OpAMD64XORLconst, OpAMD64NEGL, OpAMD64NOTL, | |
| OpAMD64SHRL, OpAMD64SHRLconst, OpAMD64SARL, OpAMD64SARLconst, | |
| OpAMD64SHLL, OpAMD64SHLLconst: | |
| return true | |
| case OpArg: | |
| return x.Type.Width == 4 | |
| case OpPhi, OpSelect0, OpSelect1: | |
| // Phis can use each-other as an arguments, instead of tracking visited values, | |
| // just limit recursion depth. | |
| if depth <= 0 { | |
| return false | |
| } | |
| for i := range x.Args { | |
| if !zeroUpper32Bits(x.Args[i], depth-1) { | |
| return false | |
| } | |
| } | |
| return true | |
| } | |
| return false | |
| } | |
| // zeroUpper48Bits is similar to zeroUpper32Bits, but for upper 48 bits | |
| func zeroUpper48Bits(x *Value, depth int) bool { | |
| switch x.Op { | |
| case OpAMD64MOVWQZX, OpAMD64MOVWload, OpAMD64MOVWloadidx1, OpAMD64MOVWloadidx2: | |
| return true | |
| case OpArg: | |
| return x.Type.Width == 2 | |
| case OpPhi, OpSelect0, OpSelect1: | |
| // Phis can use each-other as an arguments, instead of tracking visited values, | |
| // just limit recursion depth. | |
| if depth <= 0 { | |
| return false | |
| } | |
| for i := range x.Args { | |
| if !zeroUpper48Bits(x.Args[i], depth-1) { | |
| return false | |
| } | |
| } | |
| return true | |
| } | |
| return false | |
| } | |
| // zeroUpper56Bits is similar to zeroUpper32Bits, but for upper 56 bits | |
| func zeroUpper56Bits(x *Value, depth int) bool { | |
| switch x.Op { | |
| case OpAMD64MOVBQZX, OpAMD64MOVBload, OpAMD64MOVBloadidx1: | |
| return true | |
| case OpArg: | |
| return x.Type.Width == 1 | |
| case OpPhi, OpSelect0, OpSelect1: | |
| // Phis can use each-other as an arguments, instead of tracking visited values, | |
| // just limit recursion depth. | |
| if depth <= 0 { | |
| return false | |
| } | |
| for i := range x.Args { | |
| if !zeroUpper56Bits(x.Args[i], depth-1) { | |
| return false | |
| } | |
| } | |
| return true | |
| } | |
| return false | |
| } | |
| // isInlinableMemmove reports whether the given arch performs a Move of the given size | |
| // faster than memmove. It will only return true if replacing the memmove with a Move is | |
| // safe, either because Move is small or because the arguments are disjoint. | |
| // This is used as a check for replacing memmove with Move ops. | |
| func isInlinableMemmove(dst, src *Value, sz int64, c *Config) bool { | |
| // It is always safe to convert memmove into Move when its arguments are disjoint. | |
| // Move ops may or may not be faster for large sizes depending on how the platform | |
| // lowers them, so we only perform this optimization on platforms that we know to | |
| // have fast Move ops. | |
| switch c.arch { | |
| case "amd64": | |
| return sz <= 16 || (sz < 1024 && disjoint(dst, sz, src, sz)) | |
| case "386", "arm64": | |
| return sz <= 8 | |
| case "s390x", "ppc64", "ppc64le": | |
| return sz <= 8 || disjoint(dst, sz, src, sz) | |
| case "arm", "mips", "mips64", "mipsle", "mips64le": | |
| return sz <= 4 | |
| } | |
| return false | |
| } | |
| // logLargeCopy logs the occurrence of a large copy. | |
| // The best place to do this is in the rewrite rules where the size of the move is easy to find. | |
| // "Large" is arbitrarily chosen to be 128 bytes; this may change. | |
| func logLargeCopy(v *Value, s int64) bool { | |
| if s < 128 { | |
| return true | |
| } | |
| if logopt.Enabled() { | |
| logopt.LogOpt(v.Pos, "copy", "lower", v.Block.Func.Name, fmt.Sprintf("%d bytes", s)) | |
| } | |
| return true | |
| } | |
| // 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 *Config) bool { | |
| switch c.arch { | |
| case "amd64", "386": | |
| return true | |
| default: | |
| return false | |
| } | |
| } | |
| func newPPC64ShiftAuxInt(sh, mb, me, sz int64) int32 { | |
| if sh < 0 || sh >= sz { | |
| panic("PPC64 shift arg sh out of range") | |
| } | |
| if mb < 0 || mb >= sz { | |
| panic("PPC64 shift arg mb out of range") | |
| } | |
| if me < 0 || me >= sz { | |
| panic("PPC64 shift arg me out of range") | |
| } | |
| return int32(sh<<16 | mb<<8 | me) | |
| } | |
| func GetPPC64Shiftsh(auxint int64) int64 { | |
| return int64(int8(auxint >> 16)) | |
| } | |
| func GetPPC64Shiftmb(auxint int64) int64 { | |
| return int64(int8(auxint >> 8)) | |
| } | |
| func GetPPC64Shiftme(auxint int64) int64 { | |
| return int64(int8(auxint)) | |
| } | |
| // Test if this value can encoded as a mask for a rlwinm like | |
| // operation. Masks can also extend from the msb and wrap to | |
| // the lsb too. That is, the valid masks are 32 bit strings | |
| // of the form: 0..01..10..0 or 1..10..01..1 or 1...1 | |
| func isPPC64WordRotateMask(v64 int64) bool { | |
| // Isolate rightmost 1 (if none 0) and add. | |
| v := uint32(v64) | |
| vp := (v & -v) + v | |
| // Likewise, for the wrapping case. | |
| vn := ^v | |
| vpn := (vn & -vn) + vn | |
| return (v&vp == 0 || vn&vpn == 0) && v != 0 | |
| } | |
| // Compress mask and and shift into single value of the form | |
| // me | mb<<8 | rotate<<16 | nbits<<24 where me and mb can | |
| // be used to regenerate the input mask. | |
| func encodePPC64RotateMask(rotate, mask, nbits int64) int64 { | |
| var mb, me, mbn, men int | |
| // Determine boundaries and then decode them | |
| if mask == 0 || ^mask == 0 || rotate >= nbits { | |
| panic("Invalid PPC64 rotate mask") | |
| } else if nbits == 32 { | |
| mb = bits.LeadingZeros32(uint32(mask)) | |
| me = 32 - bits.TrailingZeros32(uint32(mask)) | |
| mbn = bits.LeadingZeros32(^uint32(mask)) | |
| men = 32 - bits.TrailingZeros32(^uint32(mask)) | |
| } else { | |
| mb = bits.LeadingZeros64(uint64(mask)) | |
| me = 64 - bits.TrailingZeros64(uint64(mask)) | |
| mbn = bits.LeadingZeros64(^uint64(mask)) | |
| men = 64 - bits.TrailingZeros64(^uint64(mask)) | |
| } | |
| // Check for a wrapping mask (e.g bits at 0 and 63) | |
| if mb == 0 && me == int(nbits) { | |
| // swap the inverted values | |
| mb, me = men, mbn | |
| } | |
| return int64(me) | int64(mb<<8) | int64(rotate<<16) | int64(nbits<<24) | |
| } | |
| // The inverse operation of encodePPC64RotateMask. The values returned as | |
| // mb and me satisfy the POWER ISA definition of MASK(x,y) where MASK(mb,me) = mask. | |
| func DecodePPC64RotateMask(sauxint int64) (rotate, mb, me int64, mask uint64) { | |
| auxint := uint64(sauxint) | |
| rotate = int64((auxint >> 16) & 0xFF) | |
| mb = int64((auxint >> 8) & 0xFF) | |
| me = int64((auxint >> 0) & 0xFF) | |
| nbits := int64((auxint >> 24) & 0xFF) | |
| mask = ((1 << uint(nbits-mb)) - 1) ^ ((1 << uint(nbits-me)) - 1) | |
| if mb > me { | |
| mask = ^mask | |
| } | |
| if nbits == 32 { | |
| mask = uint64(uint32(mask)) | |
| } | |
| // Fixup ME to match ISA definition. The second argument to MASK(..,me) | |
| // is inclusive. | |
| me = (me - 1) & (nbits - 1) | |
| return | |
| } | |
| // This verifies that the mask occupies the | |
| // rightmost bits. | |
| func isPPC64ValidShiftMask(v int64) bool { | |
| if ((v + 1) & v) == 0 { | |
| return true | |
| } | |
| return false | |
| } | |
| func getPPC64ShiftMaskLength(v int64) int64 { | |
| return int64(bits.Len64(uint64(v))) | |
| } | |
| // Decompose a shift right into an equivalent rotate/mask, | |
| // and return mask & m. | |
| func mergePPC64RShiftMask(m, s, nbits int64) int64 { | |
| smask := uint64((1<<uint(nbits))-1) >> uint(s) | |
| return m & int64(smask) | |
| } | |
| // Combine (ANDconst [m] (SRWconst [s])) into (RLWINM [y]) or return 0 | |
| func mergePPC64AndSrwi(m, s int64) int64 { | |
| mask := mergePPC64RShiftMask(m, s, 32) | |
| if !isPPC64WordRotateMask(mask) { | |
| return 0 | |
| } | |
| return encodePPC64RotateMask(32-s, mask, 32) | |
| } | |
| // Test if a shift right feeding into a CLRLSLDI can be merged into RLWINM. | |
| // Return the encoded RLWINM constant, or 0 if they cannot be merged. | |
| func mergePPC64ClrlsldiSrw(sld, srw int64) int64 { | |
| mask_1 := uint64(0xFFFFFFFF >> uint(srw)) | |
| // for CLRLSLDI, it's more convient to think of it as a mask left bits then rotate left. | |
| mask_2 := uint64(0xFFFFFFFFFFFFFFFF) >> uint(GetPPC64Shiftmb(int64(sld))) | |
| // Rewrite mask to apply after the final left shift. | |
| mask_3 := (mask_1 & mask_2) << uint(GetPPC64Shiftsh(sld)) | |
| r_1 := 32 - srw | |
| r_2 := GetPPC64Shiftsh(sld) | |
| r_3 := (r_1 + r_2) & 31 // This can wrap. | |
| if uint64(uint32(mask_3)) != mask_3 || mask_3 == 0 { | |
| return 0 | |
| } | |
| return encodePPC64RotateMask(int64(r_3), int64(mask_3), 32) | |
| } | |
| // Test if a RLWINM feeding into a CLRLSLDI can be merged into RLWINM. Return | |
| // the encoded RLWINM constant, or 0 if they cannot be merged. | |
| func mergePPC64ClrlsldiRlwinm(sld int32, rlw int64) int64 { | |
| r_1, _, _, mask_1 := DecodePPC64RotateMask(rlw) | |
| // for CLRLSLDI, it's more convient to think of it as a mask left bits then rotate left. | |
| mask_2 := uint64(0xFFFFFFFFFFFFFFFF) >> uint(GetPPC64Shiftmb(int64(sld))) | |
| // combine the masks, and adjust for the final left shift. | |
| mask_3 := (mask_1 & mask_2) << uint(GetPPC64Shiftsh(int64(sld))) | |
| r_2 := GetPPC64Shiftsh(int64(sld)) | |
| r_3 := (r_1 + r_2) & 31 // This can wrap. | |
| // Verify the result is still a valid bitmask of <= 32 bits. | |
| if !isPPC64WordRotateMask(int64(mask_3)) || uint64(uint32(mask_3)) != mask_3 { | |
| return 0 | |
| } | |
| return encodePPC64RotateMask(r_3, int64(mask_3), 32) | |
| } | |
| // Compute the encoded RLWINM constant from combining (SLDconst [sld] (SRWconst [srw] x)), | |
| // or return 0 if they cannot be combined. | |
| func mergePPC64SldiSrw(sld, srw int64) int64 { | |
| if sld > srw || srw >= 32 { | |
| return 0 | |
| } | |
| mask_r := uint32(0xFFFFFFFF) >> uint(srw) | |
| mask_l := uint32(0xFFFFFFFF) >> uint(sld) | |
| mask := (mask_r & mask_l) << uint(sld) | |
| return encodePPC64RotateMask((32-srw+sld)&31, int64(mask), 32) | |
| } | |
| // Convenience function to rotate a 32 bit constant value by another constant. | |
| func rotateLeft32(v, rotate int64) int64 { | |
| return int64(bits.RotateLeft32(uint32(v), int(rotate))) | |
| } | |
| // encodes the lsb and width for arm(64) bitfield ops into the expected auxInt format. | |
| func armBFAuxInt(lsb, width int64) arm64BitField { | |
| if lsb < 0 || lsb > 63 { | |
| panic("ARM(64) bit field lsb constant out of range") | |
| } | |
| if width < 1 || width > 64 { | |
| panic("ARM(64) bit field width constant out of range") | |
| } | |
| return arm64BitField(width | lsb<<8) | |
| } | |
| // returns the lsb part of the auxInt field of arm64 bitfield ops. | |
| func (bfc arm64BitField) getARM64BFlsb() int64 { | |
| return int64(uint64(bfc) >> 8) | |
| } | |
| // returns the width part of the auxInt field of arm64 bitfield ops. | |
| func (bfc arm64BitField) getARM64BFwidth() int64 { | |
| return int64(bfc) & 0xff | |
| } | |
| // checks if mask >> rshift applied at lsb is a valid arm64 bitfield op mask. | |
| func isARM64BFMask(lsb, mask, rshift int64) bool { | |
| shiftedMask := int64(uint64(mask) >> uint64(rshift)) | |
| return shiftedMask != 0 && isPowerOfTwo(shiftedMask+1) && nto(shiftedMask)+lsb < 64 | |
| } | |
| // returns the bitfield width of mask >> rshift for arm64 bitfield ops | |
| func arm64BFWidth(mask, rshift int64) int64 { | |
| shiftedMask := int64(uint64(mask) >> uint64(rshift)) | |
| if shiftedMask == 0 { | |
| panic("ARM64 BF mask is zero") | |
| } | |
| return nto(shiftedMask) | |
| } | |
| // sizeof returns the size of t in bytes. | |
| // It will panic if t is not a *types.Type. | |
| func sizeof(t interface{}) int64 { | |
| return t.(*types.Type).Size() | |
| } | |
| // 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 *Block, typ *types.Type) bool { | |
| if typ.IsPtrShaped() || typ.IsFloat() { | |
| return true | |
| } | |
| if typ.IsInteger() { | |
| return typ.Size() <= b.Func.Config.RegSize | |
| } | |
| return false | |
| } | |
| // needRaceCleanup reports whether this call to racefuncenter/exit isn't needed. | |
| func needRaceCleanup(sym *AuxCall, v *Value) bool { | |
| f := v.Block.Func | |
| if !f.Config.Race { | |
| return false | |
| } | |
| if !isSameCall(sym, "runtime.racefuncenter") && !isSameCall(sym, "runtime.racefuncexit") { | |
| return false | |
| } | |
| for _, b := range f.Blocks { | |
| for _, v := range b.Values { | |
| switch v.Op { | |
| case OpStaticCall: | |
| // Check for racefuncenter will encounter racefuncexit and vice versa. | |
| // Allow calls to panic* | |
| s := v.Aux.(*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 OpPanicBounds, OpPanicExtend: | |
| // Note: these are panic generators that are ok (like the static calls above). | |
| case OpClosureCall, OpInterCall: | |
| // We must keep the race functions if there are any other call types. | |
| return false | |
| } | |
| } | |
| } | |
| if isSameCall(sym, "runtime.racefuncenter") { | |
| // If we're removing racefuncenter, remove its argument as well. | |
| if v.Args[0].Op != OpStore { | |
| return false | |
| } | |
| mem := v.Args[0].Args[2] | |
| v.Args[0].reset(OpCopy) | |
| v.Args[0].AddArg(mem) | |
| } | |
| return true | |
| } | |
| // symIsRO reports whether sym is a read-only global. | |
| func symIsRO(sym interface{}) bool { | |
| lsym := sym.(*obj.LSym) | |
| return lsym.Type == objabi.SRODATA && len(lsym.R) == 0 | |
| } | |
| // symIsROZero reports whether sym is a read-only global whose data contains all zeros. | |
| func symIsROZero(sym 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 | |
| } | |
| // read8 reads one byte from the read-only global sym at offset off. | |
| func read8(sym interface{}, off int64) uint8 { | |
| lsym := sym.(*obj.LSym) | |
| if off >= int64(len(lsym.P)) || off < 0 { | |
| // Invalid index into the global sym. | |
| // This can happen in dead code, so we don't want to panic. | |
| // Just return any value, it will eventually get ignored. | |
| // See issue 29215. | |
| return 0 | |
| } | |
| return lsym.P[off] | |
| } | |
| // read16 reads two bytes from the read-only global sym at offset off. | |
| func read16(sym interface{}, off int64, byteorder binary.ByteOrder) uint16 { | |
| lsym := sym.(*obj.LSym) | |
| // lsym.P is written lazily. | |
| // Bytes requested after the end of lsym.P are 0. | |
| var src []byte | |
| if 0 <= off && off < int64(len(lsym.P)) { | |
| src = lsym.P[off:] | |
| } | |
| buf := make([]byte, 2) | |
| copy(buf, src) | |
| return byteorder.Uint16(buf) | |
| } | |
| // read32 reads four bytes from the read-only global sym at offset off. | |
| func read32(sym interface{}, off int64, byteorder binary.ByteOrder) uint32 { | |
| lsym := sym.(*obj.LSym) | |
| var src []byte | |
| if 0 <= off && off < int64(len(lsym.P)) { | |
| src = lsym.P[off:] | |
| } | |
| buf := make([]byte, 4) | |
| copy(buf, src) | |
| return byteorder.Uint32(buf) | |
| } | |
| // read64 reads eight bytes from the read-only global sym at offset off. | |
| func read64(sym interface{}, off int64, byteorder binary.ByteOrder) uint64 { | |
| lsym := sym.(*obj.LSym) | |
| var src []byte | |
| if 0 <= off && off < int64(len(lsym.P)) { | |
| src = lsym.P[off:] | |
| } | |
| buf := make([]byte, 8) | |
| copy(buf, src) | |
| return byteorder.Uint64(buf) | |
| } | |
| // sequentialAddresses reports true if it can prove that x + n == y | |
| func sequentialAddresses(x, y *Value, n int64) bool { | |
| if x.Op == Op386ADDL && y.Op == Op386LEAL1 && y.AuxInt == n && y.Aux == nil && | |
| (x.Args[0] == y.Args[0] && x.Args[1] == y.Args[1] || | |
| x.Args[0] == y.Args[1] && x.Args[1] == y.Args[0]) { | |
| return true | |
| } | |
| if x.Op == Op386LEAL1 && y.Op == Op386LEAL1 && y.AuxInt == x.AuxInt+n && x.Aux == y.Aux && | |
| (x.Args[0] == y.Args[0] && x.Args[1] == y.Args[1] || | |
| x.Args[0] == y.Args[1] && x.Args[1] == y.Args[0]) { | |
| return true | |
| } | |
| if x.Op == OpAMD64ADDQ && y.Op == OpAMD64LEAQ1 && y.AuxInt == n && y.Aux == nil && | |
| (x.Args[0] == y.Args[0] && x.Args[1] == y.Args[1] || | |
| x.Args[0] == y.Args[1] && x.Args[1] == y.Args[0]) { | |
| return true | |
| } | |
| if x.Op == OpAMD64LEAQ1 && y.Op == OpAMD64LEAQ1 && y.AuxInt == x.AuxInt+n && x.Aux == y.Aux && | |
| (x.Args[0] == y.Args[0] && x.Args[1] == y.Args[1] || | |
| x.Args[0] == y.Args[1] && x.Args[1] == y.Args[0]) { | |
| return true | |
| } | |
| return false | |
| } | |
| // flagConstant represents the result of a compile-time comparison. | |
| // The sense of these flags does not necessarily represent the hardware's notion | |
| // of a flags register - these are just a compile-time construct. | |
| // We happen to match the semantics to those of arm/arm64. | |
| // Note that these semantics differ from x86: the carry flag has the opposite | |
| // sense on a subtraction! | |
| // On amd64, C=1 represents a borrow, e.g. SBB on amd64 does x - y - C. | |
| // On arm64, C=0 represents a borrow, e.g. SBC on arm64 does x - y - ^C. | |
| // (because it does x + ^y + C). | |
| // See https://en.wikipedia.org/wiki/Carry_flag#Vs._borrow_flag | |
| type flagConstant uint8 | |
| // N reports whether the result of an operation is negative (high bit set). | |
| func (fc flagConstant) N() bool { | |
| return fc&1 != 0 | |
| } | |
| // Z reports whether the result of an operation is 0. | |
| func (fc flagConstant) Z() bool { | |
| return fc&2 != 0 | |
| } | |
| // C reports whether an unsigned add overflowed (carry), or an | |
| // unsigned subtract did not underflow (borrow). | |
| func (fc flagConstant) C() bool { | |
| return fc&4 != 0 | |
| } | |
| // V reports whether a signed operation overflowed or underflowed. | |
| func (fc flagConstant) V() bool { | |
| return fc&8 != 0 | |
| } | |
| func (fc flagConstant) eq() bool { | |
| return fc.Z() | |
| } | |
| func (fc flagConstant) ne() bool { | |
| return !fc.Z() | |
| } | |
| func (fc flagConstant) lt() bool { | |
| return fc.N() != fc.V() | |
| } | |
| func (fc flagConstant) le() bool { | |
| return fc.Z() || fc.lt() | |
| } | |
| func (fc flagConstant) gt() bool { | |
| return !fc.Z() && fc.ge() | |
| } | |
| func (fc flagConstant) ge() bool { | |
| return fc.N() == fc.V() | |
| } | |
| func (fc flagConstant) ult() bool { | |
| return !fc.C() | |
| } | |
| func (fc flagConstant) ule() bool { | |
| return fc.Z() || fc.ult() | |
| } | |
| func (fc flagConstant) ugt() bool { | |
| return !fc.Z() && fc.uge() | |
| } | |
| func (fc flagConstant) uge() bool { | |
| return fc.C() | |
| } | |
| func (fc flagConstant) ltNoov() bool { | |
| return fc.lt() && !fc.V() | |
| } | |
| func (fc flagConstant) leNoov() bool { | |
| return fc.le() && !fc.V() | |
| } | |
| func (fc flagConstant) gtNoov() bool { | |
| return fc.gt() && !fc.V() | |
| } | |
| func (fc flagConstant) geNoov() bool { | |
| return fc.ge() && !fc.V() | |
| } | |
| func (fc flagConstant) String() string { | |
| return fmt.Sprintf("N=%v,Z=%v,C=%v,V=%v", fc.N(), fc.Z(), fc.C(), fc.V()) | |
| } | |
| type flagConstantBuilder struct { | |
| N bool | |
| Z bool | |
| C bool | |
| V bool | |
| } | |
| func (fcs flagConstantBuilder) encode() flagConstant { | |
| var fc flagConstant | |
| if fcs.N { | |
| fc |= 1 | |
| } | |
| if fcs.Z { | |
| fc |= 2 | |
| } | |
| if fcs.C { | |
| fc |= 4 | |
| } | |
| if fcs.V { | |
| fc |= 8 | |
| } | |
| return fc | |
| } | |
| // Note: addFlags(x,y) != subFlags(x,-y) in some situations: | |
| // - the results of the C flag are different | |
| // - the results of the V flag when y==minint are different | |
| // addFlags64 returns the flags that would be set from computing x+y. | |
| func addFlags64(x, y int64) flagConstant { | |
| var fcb flagConstantBuilder | |
| fcb.Z = x+y == 0 | |
| fcb.N = x+y < 0 | |
| fcb.C = uint64(x+y) < uint64(x) | |
| fcb.V = x >= 0 && y >= 0 && x+y < 0 || x < 0 && y < 0 && x+y >= 0 | |
| return fcb.encode() | |
| } | |
| // subFlags64 returns the flags that would be set from computing x-y. | |
| func subFlags64(x, y int64) flagConstant { | |
| var fcb flagConstantBuilder | |
| fcb.Z = x-y == 0 | |
| fcb.N = x-y < 0 | |
| fcb.C = uint64(y) <= uint64(x) // This code follows the arm carry flag model. | |
| fcb.V = x >= 0 && y < 0 && x-y < 0 || x < 0 && y >= 0 && x-y >= 0 | |
| return fcb.encode() | |
| } | |
| // addFlags32 returns the flags that would be set from computing x+y. | |
| func addFlags32(x, y int32) flagConstant { | |
| var fcb flagConstantBuilder | |
| fcb.Z = x+y == 0 | |
| fcb.N = x+y < 0 | |
| fcb.C = uint32(x+y) < uint32(x) | |
| fcb.V = x >= 0 && y >= 0 && x+y < 0 || x < 0 && y < 0 && x+y >= 0 | |
| return fcb.encode() | |
| } | |
| // subFlags32 returns the flags that would be set from computing x-y. | |
| func subFlags32(x, y int32) flagConstant { | |
| var fcb flagConstantBuilder | |
| fcb.Z = x-y == 0 | |
| fcb.N = x-y < 0 | |
| fcb.C = uint32(y) <= uint32(x) // This code follows the arm carry flag model. | |
| fcb.V = x >= 0 && y < 0 && x-y < 0 || x < 0 && y >= 0 && x-y >= 0 | |
| return fcb.encode() | |
| } | |
| // logicFlags64 returns flags set to the sign/zeroness of x. | |
| // C and V are set to false. | |
| func logicFlags64(x int64) flagConstant { | |
| var fcb flagConstantBuilder | |
| fcb.Z = x == 0 | |
| fcb.N = x < 0 | |
| return fcb.encode() | |
| } | |
| // logicFlags32 returns flags set to the sign/zeroness of x. | |
| // C and V are set to false. | |
| func logicFlags32(x int32) flagConstant { | |
| var fcb flagConstantBuilder | |
| fcb.Z = x == 0 | |
| fcb.N = x < 0 | |
| return fcb.encode() | |
| } |