// 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 ( "fmt" "math" ) func applyRewrite(f *Func, rb func(*Block) bool, rv func(*Value, *Config) bool) { // repeat rewrites until we find no more rewrites var curb *Block var curv *Value defer func() { if curb != nil { curb.Fatalf("panic during rewrite of block %s\n", curb.LongString()) } if curv != nil { curv.Fatalf("panic during rewrite of value %s\n", curv.LongString()) // TODO(khr): print source location also } }() config := f.Config for { change := false for _, b := range f.Blocks { if b.Kind == BlockDead { continue } if b.Control != nil && b.Control.Op == OpCopy { for b.Control.Op == OpCopy { b.Control = b.Control.Args[0] } } curb = b if rb(b) { change = true } curb = nil for _, v := range b.Values { // elide any copies generated during rewriting for i, a := range v.Args { if a.Op != OpCopy { continue } // Rewriting can generate OpCopy loops. // They are harmless (see removePredecessor), // but take care to stop if we find a cycle. slow := a // advances every other iteration var advance bool for a.Op == OpCopy { a = a.Args[0] if slow == a { break } if advance { slow = a } advance = !advance } v.Args[i] = a } // apply rewrite function curv = v if rv(v, config) { change = true } curv = nil } } if !change { return } } } // Common functions called from rewriting rules func is64BitFloat(t Type) bool { return t.Size() == 8 && t.IsFloat() } func is32BitFloat(t Type) bool { return t.Size() == 4 && t.IsFloat() } func is64BitInt(t Type) bool { return t.Size() == 8 && t.IsInteger() } func is32BitInt(t Type) bool { return t.Size() == 4 && t.IsInteger() } func is16BitInt(t Type) bool { return t.Size() == 2 && t.IsInteger() } func is8BitInt(t Type) bool { return t.Size() == 1 && t.IsInteger() } func isPtr(t Type) bool { return t.IsPtr() } func isSigned(t Type) bool { return t.IsSigned() } func typeSize(t Type) int64 { return t.Size() } // addOff adds two int64 offsets. Fails if wraparound happens. func addOff(x, y int64) int64 { z := x + y // x and y have same sign and z has a different sign => overflow if x^y >= 0 && x^z < 0 { panic(fmt.Sprintf("offset overflow %d %d", x, y)) } return z } // 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)) return nil } func canMergeSym(x, y interface{}) bool { return x == nil || y == nil } func inBounds8(idx, len int64) bool { return int8(idx) >= 0 && int8(idx) < int8(len) } func inBounds16(idx, len int64) bool { return int16(idx) >= 0 && int16(idx) < int16(len) } func inBounds32(idx, len int64) bool { return int32(idx) >= 0 && int32(idx) < int32(len) } func inBounds64(idx, len int64) bool { return idx >= 0 && idx < len } // log2 returns logarithm in base of n. // expects n to be a power of 2. func log2(n int64) (l int64) { for n > 1 { l++ n >>= 1 } return l } // isPowerOfTwo reports whether n is a power of 2. func isPowerOfTwo(n int64) bool { 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)) } // b2i translates a boolean value to 0 or 1 for assigning to auxInt. func b2i(b bool) int64 { if b { return 1 } return 0 } // f2i is used in the rules for storing a float in AuxInt. func f2i(f float64) int64 { return int64(math.Float64bits(f)) } // DUFFZERO consists of repeated blocks of 4 MOVs + ADD, // with 4 STOSQs at the very end. // The trailing STOSQs prevent the need for a DI preadjustment // for small numbers of words to clear. // See runtime/mkduff.go. const ( dzBlocks = 31 // number of MOV/ADD blocks dzBlockLen = 4 // number of clears per block dzBlockSize = 19 // size of instructions in a single block dzMovSize = 4 // size of single MOV instruction w/ offset dzAddSize = 4 // size of single ADD instruction dzDIStep = 8 // number of bytes cleared by each MOV instruction dzTailLen = 4 // number of final STOSQ instructions dzTailSize = 2 // size of single STOSQ instruction dzSize = dzBlocks*dzBlockSize + dzTailLen*dzTailSize // total size of DUFFZERO routine ) func duffStart(size int64) int64 { x, _ := duff(size) return x } func duffAdj(size int64) int64 { _, x := duff(size) return x } // duff returns the offset (from duffzero, in bytes) and pointer adjust (in bytes) // required to use the duffzero mechanism for a block of the given size. func duff(size int64) (int64, int64) { if size < 32 || size > 1024 || size%8 != 0 { panic("bad duffzero size") } // TODO: arch-dependent off := int64(dzSize) off -= dzTailLen * dzTailSize size -= dzTailLen * dzDIStep q := size / dzDIStep blocks, singles := q/dzBlockLen, q%dzBlockLen off -= dzBlockSize * blocks var adj int64 if singles > 0 { off -= dzAddSize + dzMovSize*singles adj -= dzDIStep * (dzBlockLen - singles) } return off, adj }