2015-03-23 17:02:11 -07:00
|
|
|
// 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
|
|
|
|
|
|
2015-09-04 06:33:56 -05:00
|
|
|
import (
|
|
|
|
|
"fmt"
|
|
|
|
|
"math"
|
|
|
|
|
)
|
2015-03-23 17:02:11 -07:00
|
|
|
|
2015-05-27 14:52:22 -07:00
|
|
|
func applyRewrite(f *Func, rb func(*Block) bool, rv func(*Value, *Config) bool) {
|
2015-03-23 17:02:11 -07:00
|
|
|
// repeat rewrites until we find no more rewrites
|
2015-05-28 16:45:33 -07:00
|
|
|
var curb *Block
|
2015-04-15 15:51:25 -07:00
|
|
|
var curv *Value
|
|
|
|
|
defer func() {
|
2015-05-28 16:45:33 -07:00
|
|
|
if curb != nil {
|
2015-06-24 14:03:39 -07:00
|
|
|
curb.Fatalf("panic during rewrite of block %s\n", curb.LongString())
|
2015-05-28 16:45:33 -07:00
|
|
|
}
|
2015-04-15 15:51:25 -07:00
|
|
|
if curv != nil {
|
2015-06-24 14:03:39 -07:00
|
|
|
curv.Fatalf("panic during rewrite of value %s\n", curv.LongString())
|
2015-04-15 15:51:25 -07:00
|
|
|
// TODO(khr): print source location also
|
|
|
|
|
}
|
|
|
|
|
}()
|
2015-05-27 14:52:22 -07:00
|
|
|
config := f.Config
|
2015-03-23 17:02:11 -07:00
|
|
|
for {
|
|
|
|
|
change := false
|
|
|
|
|
for _, b := range f.Blocks {
|
2015-07-09 21:24:12 -06:00
|
|
|
if b.Kind == BlockDead {
|
|
|
|
|
continue
|
|
|
|
|
}
|
2015-05-28 16:45:33 -07:00
|
|
|
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
|
2015-03-23 17:02:11 -07:00
|
|
|
for _, v := range b.Values {
|
2015-05-18 16:44:20 -07:00
|
|
|
// elide any copies generated during rewriting
|
|
|
|
|
for i, a := range v.Args {
|
|
|
|
|
if a.Op != OpCopy {
|
|
|
|
|
continue
|
|
|
|
|
}
|
2015-07-17 10:45:48 -06:00
|
|
|
// Rewriting can generate OpCopy loops.
|
|
|
|
|
// They are harmless (see removePredecessor),
|
2015-08-10 14:01:04 -07:00
|
|
|
// but take care to stop if we find a cycle.
|
|
|
|
|
slow := a // advances every other iteration
|
|
|
|
|
var advance bool
|
|
|
|
|
for a.Op == OpCopy {
|
2015-05-18 16:44:20 -07:00
|
|
|
a = a.Args[0]
|
2015-08-10 14:01:04 -07:00
|
|
|
if slow == a {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
if advance {
|
|
|
|
|
slow = a
|
|
|
|
|
}
|
|
|
|
|
advance = !advance
|
2015-05-18 16:44:20 -07:00
|
|
|
}
|
|
|
|
|
v.Args[i] = a
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// apply rewrite function
|
2015-04-15 15:51:25 -07:00
|
|
|
curv = v
|
2015-05-27 14:52:22 -07:00
|
|
|
if rv(v, config) {
|
2015-03-23 17:02:11 -07:00
|
|
|
change = true
|
|
|
|
|
}
|
2015-05-28 16:45:33 -07:00
|
|
|
curv = nil
|
2015-03-23 17:02:11 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if !change {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Common functions called from rewriting rules
|
|
|
|
|
|
2015-08-12 16:38:11 -04:00
|
|
|
func is64BitFloat(t Type) bool {
|
|
|
|
|
return t.Size() == 8 && t.IsFloat()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func is32BitFloat(t Type) bool {
|
|
|
|
|
return t.Size() == 4 && t.IsFloat()
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-23 17:02:11 -07:00
|
|
|
func is64BitInt(t Type) bool {
|
2015-04-15 15:51:25 -07:00
|
|
|
return t.Size() == 8 && t.IsInteger()
|
2015-03-23 17:02:11 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func is32BitInt(t Type) bool {
|
2015-04-15 15:51:25 -07:00
|
|
|
return t.Size() == 4 && t.IsInteger()
|
|
|
|
|
}
|
|
|
|
|
|
2015-06-14 11:38:46 -07:00
|
|
|
func is16BitInt(t Type) bool {
|
|
|
|
|
return t.Size() == 2 && t.IsInteger()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func is8BitInt(t Type) bool {
|
|
|
|
|
return t.Size() == 1 && t.IsInteger()
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-15 15:51:25 -07:00
|
|
|
func isPtr(t Type) bool {
|
|
|
|
|
return t.IsPtr()
|
2015-03-23 17:02:11 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func isSigned(t Type) bool {
|
2015-04-15 15:51:25 -07:00
|
|
|
return t.IsSigned()
|
2015-03-26 10:49:03 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func typeSize(t Type) int64 {
|
2015-04-15 15:51:25 -07:00
|
|
|
return t.Size()
|
2015-03-23 17:02:11 -07:00
|
|
|
}
|
2015-05-18 16:44:20 -07:00
|
|
|
|
2015-06-12 11:01:13 -07:00
|
|
|
// addOff adds two int64 offsets. Fails if wraparound happens.
|
2015-06-11 21:29:25 -07:00
|
|
|
func addOff(x, y int64) int64 {
|
2015-05-18 16:44:20 -07:00
|
|
|
z := x + y
|
|
|
|
|
// x and y have same sign and z has a different sign => overflow
|
|
|
|
|
if x^y >= 0 && x^z < 0 {
|
2015-06-12 11:01:13 -07:00
|
|
|
panic(fmt.Sprintf("offset overflow %d %d", x, y))
|
2015-05-18 16:44:20 -07:00
|
|
|
}
|
|
|
|
|
return z
|
|
|
|
|
}
|
|
|
|
|
|
2015-08-23 21:14:25 -07:00
|
|
|
// mergeSym merges two symbolic offsets. There is no real merging of
|
|
|
|
|
// offsets, we just pick the non-nil one.
|
2015-06-19 21:02:28 -07:00
|
|
|
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
|
|
|
|
|
}
|
2015-08-23 21:14:25 -07:00
|
|
|
func canMergeSym(x, y interface{}) bool {
|
|
|
|
|
return x == nil || y == nil
|
|
|
|
|
}
|
2015-06-19 21:02:28 -07:00
|
|
|
|
2015-11-02 21:28:13 -08:00
|
|
|
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 }
|
|
|
|
|
func sliceInBounds32(idx, len int64) bool { return int32(idx) >= 0 && int32(idx) <= int32(len) }
|
|
|
|
|
func sliceInBounds64(idx, len int64) bool { return idx >= 0 && idx <= len }
|
2015-07-17 12:26:35 +02:00
|
|
|
|
2016-02-11 20:43:15 -06:00
|
|
|
// nlz returns the number of leading zeros.
|
|
|
|
|
func nlz(x int64) int64 {
|
|
|
|
|
// log2(0) == 1, so nlz(0) == 64
|
|
|
|
|
return 63 - log2(x)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ntz returns the number of trailing zeros.
|
|
|
|
|
func ntz(x int64) int64 {
|
|
|
|
|
return 64 - nlz(^x&(x-1))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// nlo returns the number of leading ones.
|
|
|
|
|
func nlo(x int64) int64 {
|
|
|
|
|
return nlz(^x)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// nto returns the number of trailing ones.
|
|
|
|
|
func nto(x int64) int64 {
|
|
|
|
|
return ntz(^x)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// log2 returns logarithm in base of uint64(n), with log2(0) = -1.
|
2015-07-17 12:26:35 +02:00
|
|
|
func log2(n int64) (l int64) {
|
2016-02-11 20:43:15 -06:00
|
|
|
l = -1
|
|
|
|
|
x := uint64(n)
|
|
|
|
|
for ; x >= 0x8000; x >>= 16 {
|
|
|
|
|
l += 16
|
|
|
|
|
}
|
|
|
|
|
if x >= 0x80 {
|
|
|
|
|
x >>= 8
|
|
|
|
|
l += 8
|
|
|
|
|
}
|
|
|
|
|
if x >= 0x8 {
|
|
|
|
|
x >>= 4
|
|
|
|
|
l += 4
|
|
|
|
|
}
|
|
|
|
|
if x >= 0x2 {
|
|
|
|
|
x >>= 2
|
|
|
|
|
l += 2
|
|
|
|
|
}
|
|
|
|
|
if x >= 0x1 {
|
2015-07-17 12:26:35 +02:00
|
|
|
l++
|
|
|
|
|
}
|
2016-02-11 20:43:15 -06:00
|
|
|
return
|
2015-07-17 12:26:35 +02:00
|
|
|
}
|
|
|
|
|
|
2015-07-25 12:53:58 -05:00
|
|
|
// isPowerOfTwo reports whether n is a power of 2.
|
2015-07-17 12:26:35 +02:00
|
|
|
func isPowerOfTwo(n int64) bool {
|
|
|
|
|
return n > 0 && n&(n-1) == 0
|
|
|
|
|
}
|
2015-07-25 12:53:58 -05:00
|
|
|
|
|
|
|
|
// is32Bit reports whether n can be represented as a signed 32 bit integer.
|
|
|
|
|
func is32Bit(n int64) bool {
|
|
|
|
|
return n == int64(int32(n))
|
|
|
|
|
}
|
2015-09-03 18:24:22 -05:00
|
|
|
|
|
|
|
|
// b2i translates a boolean value to 0 or 1 for assigning to auxInt.
|
|
|
|
|
func b2i(b bool) int64 {
|
|
|
|
|
if b {
|
|
|
|
|
return 1
|
|
|
|
|
}
|
|
|
|
|
return 0
|
|
|
|
|
}
|
2015-09-04 06:33:56 -05:00
|
|
|
|
|
|
|
|
// f2i is used in the rules for storing a float in AuxInt.
|
|
|
|
|
func f2i(f float64) int64 {
|
|
|
|
|
return int64(math.Float64bits(f))
|
|
|
|
|
}
|
2015-09-18 18:23:34 -07:00
|
|
|
|
2016-02-03 06:21:24 -05:00
|
|
|
// uaddOvf returns true if unsigned a+b would overflow.
|
|
|
|
|
func uaddOvf(a, b int64) bool {
|
|
|
|
|
return uint64(a)+uint64(b) < uint64(a)
|
|
|
|
|
}
|
|
|
|
|
|
2015-10-19 13:56:55 -07:00
|
|
|
// DUFFZERO consists of repeated blocks of 4 MOVUPSs + ADD,
|
2015-09-18 18:23:34 -07:00
|
|
|
// See runtime/mkduff.go.
|
|
|
|
|
const (
|
2015-10-19 13:56:55 -07:00
|
|
|
dzBlocks = 16 // number of MOV/ADD blocks
|
2015-09-18 18:23:34 -07:00
|
|
|
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
|
2015-10-19 13:56:55 -07:00
|
|
|
dzClearStep = 16 // number of bytes cleared by each MOV instruction
|
2015-09-18 18:23:34 -07:00
|
|
|
|
|
|
|
|
dzTailLen = 4 // number of final STOSQ instructions
|
|
|
|
|
dzTailSize = 2 // size of single STOSQ instruction
|
|
|
|
|
|
2015-10-19 13:56:55 -07:00
|
|
|
dzClearLen = dzClearStep * dzBlockLen // bytes cleared by one block
|
|
|
|
|
dzSize = dzBlocks * dzBlockSize
|
2015-09-18 18:23:34 -07:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
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) {
|
2015-10-19 13:56:55 -07:00
|
|
|
if size < 32 || size > 1024 || size%dzClearStep != 0 {
|
2015-09-18 18:23:34 -07:00
|
|
|
panic("bad duffzero size")
|
|
|
|
|
}
|
|
|
|
|
// TODO: arch-dependent
|
2015-10-19 13:56:55 -07:00
|
|
|
steps := size / dzClearStep
|
|
|
|
|
blocks := steps / dzBlockLen
|
|
|
|
|
steps %= dzBlockLen
|
|
|
|
|
off := dzBlockSize * (dzBlocks - blocks)
|
2015-09-18 18:23:34 -07:00
|
|
|
var adj int64
|
2015-10-19 13:56:55 -07:00
|
|
|
if steps != 0 {
|
|
|
|
|
off -= dzAddSize
|
|
|
|
|
off -= dzMovSize * steps
|
|
|
|
|
adj -= dzClearStep * (dzBlockLen - steps)
|
2015-09-18 18:23:34 -07:00
|
|
|
}
|
|
|
|
|
return off, adj
|
|
|
|
|
}
|