Keith Randall | 7b96284 | 2015-03-23 17:02:11 -0700 | [diff] [blame] | 1 | // Copyright 2015 The Go Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style |
| 3 | // license that can be found in the LICENSE file. |
| 4 | |
| 5 | package ssa |
| 6 | |
Todd Neal | 19447a6 | 2015-09-04 06:33:56 -0500 | [diff] [blame] | 7 | import ( |
| 8 | "fmt" |
| 9 | "math" |
| 10 | ) |
Keith Randall | 7b96284 | 2015-03-23 17:02:11 -0700 | [diff] [blame] | 11 | |
Keith Randall | f7f604e | 2015-05-27 14:52:22 -0700 | [diff] [blame] | 12 | func applyRewrite(f *Func, rb func(*Block) bool, rv func(*Value, *Config) bool) { |
Keith Randall | 7b96284 | 2015-03-23 17:02:11 -0700 | [diff] [blame] | 13 | // repeat rewrites until we find no more rewrites |
Keith Randall | a9cec30 | 2015-05-28 16:45:33 -0700 | [diff] [blame] | 14 | var curb *Block |
Keith Randall | d2fd43a | 2015-04-15 15:51:25 -0700 | [diff] [blame] | 15 | var curv *Value |
| 16 | defer func() { |
Keith Randall | a9cec30 | 2015-05-28 16:45:33 -0700 | [diff] [blame] | 17 | if curb != nil { |
Josh Bleecher Snyder | 37ddc27 | 2015-06-24 14:03:39 -0700 | [diff] [blame] | 18 | curb.Fatalf("panic during rewrite of block %s\n", curb.LongString()) |
Keith Randall | a9cec30 | 2015-05-28 16:45:33 -0700 | [diff] [blame] | 19 | } |
Keith Randall | d2fd43a | 2015-04-15 15:51:25 -0700 | [diff] [blame] | 20 | if curv != nil { |
Josh Bleecher Snyder | 37ddc27 | 2015-06-24 14:03:39 -0700 | [diff] [blame] | 21 | curv.Fatalf("panic during rewrite of value %s\n", curv.LongString()) |
Keith Randall | d2fd43a | 2015-04-15 15:51:25 -0700 | [diff] [blame] | 22 | // TODO(khr): print source location also |
| 23 | } |
| 24 | }() |
Keith Randall | f7f604e | 2015-05-27 14:52:22 -0700 | [diff] [blame] | 25 | config := f.Config |
Keith Randall | 7b96284 | 2015-03-23 17:02:11 -0700 | [diff] [blame] | 26 | for { |
| 27 | change := false |
| 28 | for _, b := range f.Blocks { |
Josh Bleecher Snyder | 9b04852 | 2015-07-09 21:24:12 -0600 | [diff] [blame] | 29 | if b.Kind == BlockDead { |
| 30 | continue |
| 31 | } |
Keith Randall | a9cec30 | 2015-05-28 16:45:33 -0700 | [diff] [blame] | 32 | if b.Control != nil && b.Control.Op == OpCopy { |
| 33 | for b.Control.Op == OpCopy { |
Keith Randall | 56e0ecc | 2016-03-15 20:45:50 -0700 | [diff] [blame] | 34 | b.SetControl(b.Control.Args[0]) |
Keith Randall | a9cec30 | 2015-05-28 16:45:33 -0700 | [diff] [blame] | 35 | } |
| 36 | } |
| 37 | curb = b |
| 38 | if rb(b) { |
| 39 | change = true |
| 40 | } |
| 41 | curb = nil |
Keith Randall | 7b96284 | 2015-03-23 17:02:11 -0700 | [diff] [blame] | 42 | for _, v := range b.Values { |
Keith Randall | 56e0ecc | 2016-03-15 20:45:50 -0700 | [diff] [blame] | 43 | change = copyelimValue(v) || change |
Alexandru Moșoi | 65855cf | 2016-02-11 20:46:43 +0100 | [diff] [blame] | 44 | change = phielimValue(v) || change |
Keith Randall | cfc2aa5 | 2015-05-18 16:44:20 -0700 | [diff] [blame] | 45 | |
| 46 | // apply rewrite function |
Keith Randall | d2fd43a | 2015-04-15 15:51:25 -0700 | [diff] [blame] | 47 | curv = v |
Keith Randall | f7f604e | 2015-05-27 14:52:22 -0700 | [diff] [blame] | 48 | if rv(v, config) { |
Keith Randall | 7b96284 | 2015-03-23 17:02:11 -0700 | [diff] [blame] | 49 | change = true |
| 50 | } |
Keith Randall | a9cec30 | 2015-05-28 16:45:33 -0700 | [diff] [blame] | 51 | curv = nil |
Keith Randall | 7b96284 | 2015-03-23 17:02:11 -0700 | [diff] [blame] | 52 | } |
| 53 | } |
| 54 | if !change { |
| 55 | return |
| 56 | } |
| 57 | } |
| 58 | } |
| 59 | |
| 60 | // Common functions called from rewriting rules |
| 61 | |
David Chase | 997a9f3 | 2015-08-12 16:38:11 -0400 | [diff] [blame] | 62 | func is64BitFloat(t Type) bool { |
| 63 | return t.Size() == 8 && t.IsFloat() |
| 64 | } |
| 65 | |
| 66 | func is32BitFloat(t Type) bool { |
| 67 | return t.Size() == 4 && t.IsFloat() |
| 68 | } |
| 69 | |
Keith Randall | 7b96284 | 2015-03-23 17:02:11 -0700 | [diff] [blame] | 70 | func is64BitInt(t Type) bool { |
Keith Randall | d2fd43a | 2015-04-15 15:51:25 -0700 | [diff] [blame] | 71 | return t.Size() == 8 && t.IsInteger() |
Keith Randall | 7b96284 | 2015-03-23 17:02:11 -0700 | [diff] [blame] | 72 | } |
| 73 | |
| 74 | func is32BitInt(t Type) bool { |
Keith Randall | d2fd43a | 2015-04-15 15:51:25 -0700 | [diff] [blame] | 75 | return t.Size() == 4 && t.IsInteger() |
| 76 | } |
| 77 | |
Michael Matloob | 73054f5 | 2015-06-14 11:38:46 -0700 | [diff] [blame] | 78 | func is16BitInt(t Type) bool { |
| 79 | return t.Size() == 2 && t.IsInteger() |
| 80 | } |
| 81 | |
| 82 | func is8BitInt(t Type) bool { |
| 83 | return t.Size() == 1 && t.IsInteger() |
| 84 | } |
| 85 | |
Keith Randall | d2fd43a | 2015-04-15 15:51:25 -0700 | [diff] [blame] | 86 | func isPtr(t Type) bool { |
Matthew Dempsky | 788f112 | 2016-03-28 10:55:44 -0700 | [diff] [blame] | 87 | return t.IsPtrShaped() |
Keith Randall | 7b96284 | 2015-03-23 17:02:11 -0700 | [diff] [blame] | 88 | } |
| 89 | |
| 90 | func isSigned(t Type) bool { |
Keith Randall | d2fd43a | 2015-04-15 15:51:25 -0700 | [diff] [blame] | 91 | return t.IsSigned() |
Keith Randall | 7b96284 | 2015-03-23 17:02:11 -0700 | [diff] [blame] | 92 | } |
| 93 | |
Keith Randall | 2c9b491 | 2015-03-26 10:49:03 -0700 | [diff] [blame] | 94 | func typeSize(t Type) int64 { |
Keith Randall | d2fd43a | 2015-04-15 15:51:25 -0700 | [diff] [blame] | 95 | return t.Size() |
Keith Randall | 7b96284 | 2015-03-23 17:02:11 -0700 | [diff] [blame] | 96 | } |
Keith Randall | cfc2aa5 | 2015-05-18 16:44:20 -0700 | [diff] [blame] | 97 | |
Brad Fitzpatrick | 5fea2cc | 2016-03-01 23:21:55 +0000 | [diff] [blame] | 98 | // mergeSym merges two symbolic offsets. There is no real merging of |
Keith Randall | 01490eb | 2015-08-23 21:14:25 -0700 | [diff] [blame] | 99 | // offsets, we just pick the non-nil one. |
Keith Randall | 8c46aa5 | 2015-06-19 21:02:28 -0700 | [diff] [blame] | 100 | func mergeSym(x, y interface{}) interface{} { |
| 101 | if x == nil { |
| 102 | return y |
| 103 | } |
| 104 | if y == nil { |
| 105 | return x |
| 106 | } |
| 107 | panic(fmt.Sprintf("mergeSym with two non-nil syms %s %s", x, y)) |
| 108 | return nil |
| 109 | } |
Keith Randall | 01490eb | 2015-08-23 21:14:25 -0700 | [diff] [blame] | 110 | func canMergeSym(x, y interface{}) bool { |
| 111 | return x == nil || y == nil |
| 112 | } |
Keith Randall | 8c46aa5 | 2015-06-19 21:02:28 -0700 | [diff] [blame] | 113 | |
Todd Neal | adc8d49 | 2016-02-11 20:43:15 -0600 | [diff] [blame] | 114 | // nlz returns the number of leading zeros. |
| 115 | func nlz(x int64) int64 { |
| 116 | // log2(0) == 1, so nlz(0) == 64 |
| 117 | return 63 - log2(x) |
| 118 | } |
| 119 | |
| 120 | // ntz returns the number of trailing zeros. |
| 121 | func ntz(x int64) int64 { |
| 122 | return 64 - nlz(^x&(x-1)) |
| 123 | } |
| 124 | |
| 125 | // nlo returns the number of leading ones. |
| 126 | func nlo(x int64) int64 { |
| 127 | return nlz(^x) |
| 128 | } |
| 129 | |
| 130 | // nto returns the number of trailing ones. |
| 131 | func nto(x int64) int64 { |
| 132 | return ntz(^x) |
| 133 | } |
| 134 | |
| 135 | // log2 returns logarithm in base of uint64(n), with log2(0) = -1. |
Alexandru Moșoi | 3e7e519 | 2015-07-17 12:26:35 +0200 | [diff] [blame] | 136 | func log2(n int64) (l int64) { |
Todd Neal | adc8d49 | 2016-02-11 20:43:15 -0600 | [diff] [blame] | 137 | l = -1 |
| 138 | x := uint64(n) |
| 139 | for ; x >= 0x8000; x >>= 16 { |
| 140 | l += 16 |
Alexandru Moșoi | 3e7e519 | 2015-07-17 12:26:35 +0200 | [diff] [blame] | 141 | } |
Todd Neal | adc8d49 | 2016-02-11 20:43:15 -0600 | [diff] [blame] | 142 | if x >= 0x80 { |
| 143 | x >>= 8 |
| 144 | l += 8 |
| 145 | } |
| 146 | if x >= 0x8 { |
| 147 | x >>= 4 |
| 148 | l += 4 |
| 149 | } |
| 150 | if x >= 0x2 { |
| 151 | x >>= 2 |
| 152 | l += 2 |
| 153 | } |
| 154 | if x >= 0x1 { |
| 155 | l++ |
| 156 | } |
| 157 | return |
Alexandru Moșoi | 3e7e519 | 2015-07-17 12:26:35 +0200 | [diff] [blame] | 158 | } |
| 159 | |
Todd Neal | db52326 | 2015-07-25 12:53:58 -0500 | [diff] [blame] | 160 | // isPowerOfTwo reports whether n is a power of 2. |
Alexandru Moșoi | 3e7e519 | 2015-07-17 12:26:35 +0200 | [diff] [blame] | 161 | func isPowerOfTwo(n int64) bool { |
| 162 | return n > 0 && n&(n-1) == 0 |
| 163 | } |
Todd Neal | db52326 | 2015-07-25 12:53:58 -0500 | [diff] [blame] | 164 | |
| 165 | // is32Bit reports whether n can be represented as a signed 32 bit integer. |
| 166 | func is32Bit(n int64) bool { |
| 167 | return n == int64(int32(n)) |
| 168 | } |
Todd Neal | 991036a | 2015-09-03 18:24:22 -0500 | [diff] [blame] | 169 | |
| 170 | // b2i translates a boolean value to 0 or 1 for assigning to auxInt. |
| 171 | func b2i(b bool) int64 { |
| 172 | if b { |
| 173 | return 1 |
| 174 | } |
| 175 | return 0 |
| 176 | } |
Todd Neal | 19447a6 | 2015-09-04 06:33:56 -0500 | [diff] [blame] | 177 | |
Todd Neal | f6ceed2 | 2016-03-11 19:36:54 -0600 | [diff] [blame] | 178 | // i2f is used in rules for converting from an AuxInt to a float. |
| 179 | func i2f(i int64) float64 { |
| 180 | return math.Float64frombits(uint64(i)) |
| 181 | } |
| 182 | |
| 183 | // i2f32 is used in rules for converting from an AuxInt to a float32. |
| 184 | func i2f32(i int64) float32 { |
| 185 | return float32(math.Float64frombits(uint64(i))) |
| 186 | } |
| 187 | |
Todd Neal | 19447a6 | 2015-09-04 06:33:56 -0500 | [diff] [blame] | 188 | // f2i is used in the rules for storing a float in AuxInt. |
| 189 | func f2i(f float64) int64 { |
| 190 | return int64(math.Float64bits(f)) |
| 191 | } |
Keith Randall | 04d6edc | 2015-09-18 18:23:34 -0700 | [diff] [blame] | 192 | |
Todd Neal | 93a0b0f | 2016-02-03 06:21:24 -0500 | [diff] [blame] | 193 | // uaddOvf returns true if unsigned a+b would overflow. |
| 194 | func uaddOvf(a, b int64) bool { |
| 195 | return uint64(a)+uint64(b) < uint64(a) |
| 196 | } |
| 197 | |
Todd Neal | 9dc1334 | 2016-02-13 17:37:19 -0600 | [diff] [blame] | 198 | // isSamePtr reports whether p1 and p2 point to the same address. |
| 199 | func isSamePtr(p1, p2 *Value) bool { |
Keith Randall | a532576 | 2016-02-24 12:58:47 -0800 | [diff] [blame] | 200 | if p1 == p2 { |
| 201 | return true |
| 202 | } |
Josh Bleecher Snyder | 6ed1038 | 2016-03-04 18:55:09 -0800 | [diff] [blame] | 203 | if p1.Op != p2.Op { |
| 204 | return false |
| 205 | } |
| 206 | switch p1.Op { |
| 207 | case OpOffPtr: |
| 208 | return p1.AuxInt == p2.AuxInt && isSamePtr(p1.Args[0], p2.Args[0]) |
| 209 | case OpAddr: |
| 210 | // OpAddr's 0th arg is either OpSP or OpSB, which means that it is uniquely identified by its Op. |
| 211 | // Checking for value equality only works after [z]cse has run. |
| 212 | return p1.Aux == p2.Aux && p1.Args[0].Op == p2.Args[0].Op |
| 213 | case OpAddPtr: |
| 214 | return p1.Args[1] == p2.Args[1] && isSamePtr(p1.Args[0], p2.Args[0]) |
| 215 | } |
| 216 | return false |
Todd Neal | 9dc1334 | 2016-02-13 17:37:19 -0600 | [diff] [blame] | 217 | } |
| 218 | |
Keith Randall | 7c4fbb6 | 2015-10-19 13:56:55 -0700 | [diff] [blame] | 219 | // DUFFZERO consists of repeated blocks of 4 MOVUPSs + ADD, |
Keith Randall | 04d6edc | 2015-09-18 18:23:34 -0700 | [diff] [blame] | 220 | // See runtime/mkduff.go. |
| 221 | const ( |
Keith Randall | 7c4fbb6 | 2015-10-19 13:56:55 -0700 | [diff] [blame] | 222 | dzBlocks = 16 // number of MOV/ADD blocks |
Keith Randall | 04d6edc | 2015-09-18 18:23:34 -0700 | [diff] [blame] | 223 | dzBlockLen = 4 // number of clears per block |
| 224 | dzBlockSize = 19 // size of instructions in a single block |
| 225 | dzMovSize = 4 // size of single MOV instruction w/ offset |
| 226 | dzAddSize = 4 // size of single ADD instruction |
Keith Randall | 7c4fbb6 | 2015-10-19 13:56:55 -0700 | [diff] [blame] | 227 | dzClearStep = 16 // number of bytes cleared by each MOV instruction |
Keith Randall | 04d6edc | 2015-09-18 18:23:34 -0700 | [diff] [blame] | 228 | |
| 229 | dzTailLen = 4 // number of final STOSQ instructions |
| 230 | dzTailSize = 2 // size of single STOSQ instruction |
| 231 | |
Keith Randall | 7c4fbb6 | 2015-10-19 13:56:55 -0700 | [diff] [blame] | 232 | dzClearLen = dzClearStep * dzBlockLen // bytes cleared by one block |
| 233 | dzSize = dzBlocks * dzBlockSize |
Keith Randall | 04d6edc | 2015-09-18 18:23:34 -0700 | [diff] [blame] | 234 | ) |
| 235 | |
| 236 | func duffStart(size int64) int64 { |
| 237 | x, _ := duff(size) |
| 238 | return x |
| 239 | } |
| 240 | func duffAdj(size int64) int64 { |
| 241 | _, x := duff(size) |
| 242 | return x |
| 243 | } |
| 244 | |
| 245 | // duff returns the offset (from duffzero, in bytes) and pointer adjust (in bytes) |
| 246 | // required to use the duffzero mechanism for a block of the given size. |
| 247 | func duff(size int64) (int64, int64) { |
Keith Randall | 7c4fbb6 | 2015-10-19 13:56:55 -0700 | [diff] [blame] | 248 | if size < 32 || size > 1024 || size%dzClearStep != 0 { |
Keith Randall | 04d6edc | 2015-09-18 18:23:34 -0700 | [diff] [blame] | 249 | panic("bad duffzero size") |
| 250 | } |
| 251 | // TODO: arch-dependent |
Keith Randall | 7c4fbb6 | 2015-10-19 13:56:55 -0700 | [diff] [blame] | 252 | steps := size / dzClearStep |
| 253 | blocks := steps / dzBlockLen |
| 254 | steps %= dzBlockLen |
| 255 | off := dzBlockSize * (dzBlocks - blocks) |
Keith Randall | 04d6edc | 2015-09-18 18:23:34 -0700 | [diff] [blame] | 256 | var adj int64 |
Keith Randall | 7c4fbb6 | 2015-10-19 13:56:55 -0700 | [diff] [blame] | 257 | if steps != 0 { |
| 258 | off -= dzAddSize |
| 259 | off -= dzMovSize * steps |
| 260 | adj -= dzClearStep * (dzBlockLen - steps) |
Keith Randall | 04d6edc | 2015-09-18 18:23:34 -0700 | [diff] [blame] | 261 | } |
| 262 | return off, adj |
| 263 | } |
Keith Randall | b81f2f1 | 2016-03-28 21:45:33 -0700 | [diff] [blame] | 264 | |
| 265 | // mergePoint finds a block among a's blocks which dominates b and is itself |
| 266 | // dominated by all of a's blocks. Returns nil if it can't find one. |
| 267 | // Might return nil even if one does exist. |
| 268 | func mergePoint(b *Block, a ...*Value) *Block { |
| 269 | // Walk backward from b looking for one of the a's blocks. |
| 270 | |
| 271 | // Max distance |
| 272 | d := 100 |
| 273 | |
| 274 | for d > 0 { |
| 275 | for _, x := range a { |
| 276 | if b == x.Block { |
| 277 | goto found |
| 278 | } |
| 279 | } |
| 280 | if len(b.Preds) > 1 { |
| 281 | // Don't know which way to go back. Abort. |
| 282 | return nil |
| 283 | } |
| 284 | b = b.Preds[0] |
| 285 | d-- |
| 286 | } |
| 287 | return nil // too far away |
| 288 | found: |
| 289 | // At this point, r is the first value in a that we find by walking backwards. |
| 290 | // if we return anything, r will be it. |
| 291 | r := b |
| 292 | |
| 293 | // Keep going, counting the other a's that we find. They must all dominate r. |
| 294 | na := 0 |
| 295 | for d > 0 { |
| 296 | for _, x := range a { |
| 297 | if b == x.Block { |
| 298 | na++ |
| 299 | } |
| 300 | } |
| 301 | if na == len(a) { |
| 302 | // Found all of a in a backwards walk. We can return r. |
| 303 | return r |
| 304 | } |
| 305 | if len(b.Preds) > 1 { |
| 306 | return nil |
| 307 | } |
| 308 | b = b.Preds[0] |
| 309 | d-- |
| 310 | |
| 311 | } |
| 312 | return nil // too far away |
| 313 | } |