Source file src/cmd/compile/internal/ssa/op.go
Documentation: cmd/compile/internal/ssa
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 7 import ( 8 "cmd/compile/internal/types" 9 "cmd/internal/obj" 10 "fmt" 11 ) 12 13 // An Op encodes the specific operation that a Value performs. 14 // Opcodes' semantics can be modified by the type and aux fields of the Value. 15 // For instance, OpAdd can be 32 or 64 bit, signed or unsigned, float or complex, depending on Value.Type. 16 // Semantics of each op are described in the opcode files in gen/*Ops.go. 17 // There is one file for generic (architecture-independent) ops and one file 18 // for each architecture. 19 type Op int32 20 21 type opInfo struct { 22 name string 23 reg regInfo 24 auxType auxType 25 argLen int32 // the number of arguments, -1 if variable length 26 asm obj.As 27 generic bool // this is a generic (arch-independent) opcode 28 rematerializeable bool // this op is rematerializeable 29 commutative bool // this operation is commutative (e.g. addition) 30 resultInArg0 bool // (first, if a tuple) output of v and v.Args[0] must be allocated to the same register 31 resultNotInArgs bool // outputs must not be allocated to the same registers as inputs 32 clobberFlags bool // this op clobbers flags register 33 call bool // is a function call 34 nilCheck bool // this op is a nil check on arg0 35 faultOnNilArg0 bool // this op will fault if arg0 is nil (and aux encodes a small offset) 36 faultOnNilArg1 bool // this op will fault if arg1 is nil (and aux encodes a small offset) 37 usesScratch bool // this op requires scratch memory space 38 hasSideEffects bool // for "reasons", not to be eliminated. E.g., atomic store, #19182. 39 zeroWidth bool // op never translates into any machine code. example: copy, which may sometimes translate to machine code, is not zero-width. 40 unsafePoint bool // this op is an unsafe point, i.e. not safe for async preemption 41 symEffect SymEffect // effect this op has on symbol in aux 42 scale uint8 // amd64/386 indexed load scale 43 } 44 45 type inputInfo struct { 46 idx int // index in Args array 47 regs regMask // allowed input registers 48 } 49 50 type outputInfo struct { 51 idx int // index in output tuple 52 regs regMask // allowed output registers 53 } 54 55 type regInfo struct { 56 // inputs encodes the register restrictions for an instruction's inputs. 57 // Each entry specifies an allowed register set for a particular input. 58 // They are listed in the order in which regalloc should pick a register 59 // from the register set (most constrained first). 60 // Inputs which do not need registers are not listed. 61 inputs []inputInfo 62 // clobbers encodes the set of registers that are overwritten by 63 // the instruction (other than the output registers). 64 clobbers regMask 65 // outputs is the same as inputs, but for the outputs of the instruction. 66 outputs []outputInfo 67 } 68 69 type auxType int8 70 71 type Param struct { 72 Type *types.Type 73 Offset int32 // TODO someday this will be a register 74 } 75 76 type AuxCall struct { 77 Fn *obj.LSym 78 args []Param // Includes receiver for method calls. Does NOT include hidden closure pointer. 79 results []Param 80 } 81 82 // ResultForOffset returns the index of the result at a particular offset among the results 83 // This does not include the mem result for the call opcode. 84 func (a *AuxCall) ResultForOffset(offset int64) int64 { 85 which := int64(-1) 86 for i := int64(0); i < a.NResults(); i++ { // note aux NResults does not include mem result. 87 if a.OffsetOfResult(i) == offset { 88 which = i 89 break 90 } 91 } 92 return which 93 } 94 95 // OffsetOfResult returns the SP offset of result which (indexed 0, 1, etc). 96 func (a *AuxCall) OffsetOfResult(which int64) int64 { 97 return int64(a.results[which].Offset) 98 } 99 100 // OffsetOfArg returns the SP offset of argument which (indexed 0, 1, etc). 101 func (a *AuxCall) OffsetOfArg(which int64) int64 { 102 return int64(a.args[which].Offset) 103 } 104 105 // TypeOfResult returns the type of result which (indexed 0, 1, etc). 106 func (a *AuxCall) TypeOfResult(which int64) *types.Type { 107 return a.results[which].Type 108 } 109 110 // TypeOfArg returns the type of argument which (indexed 0, 1, etc). 111 func (a *AuxCall) TypeOfArg(which int64) *types.Type { 112 return a.args[which].Type 113 } 114 115 // SizeOfResult returns the size of result which (indexed 0, 1, etc). 116 func (a *AuxCall) SizeOfResult(which int64) int64 { 117 return a.TypeOfResult(which).Width 118 } 119 120 // SizeOfArg returns the size of argument which (indexed 0, 1, etc). 121 func (a *AuxCall) SizeOfArg(which int64) int64 { 122 return a.TypeOfArg(which).Width 123 } 124 125 // NResults returns the number of results 126 func (a *AuxCall) NResults() int64 { 127 return int64(len(a.results)) 128 } 129 130 // LateExpansionResultType returns the result type (including trailing mem) 131 // for a call that will be expanded later in the SSA phase. 132 func (a *AuxCall) LateExpansionResultType() *types.Type { 133 var tys []*types.Type 134 for i := int64(0); i < a.NResults(); i++ { 135 tys = append(tys, a.TypeOfResult(i)) 136 } 137 tys = append(tys, types.TypeMem) 138 return types.NewResults(tys) 139 } 140 141 // NArgs returns the number of arguments 142 func (a *AuxCall) NArgs() int64 { 143 return int64(len(a.args)) 144 } 145 146 // String returns 147 // "AuxCall{<fn>(<args>)}" if len(results) == 0; 148 // "AuxCall{<fn>(<args>)<results[0]>}" if len(results) == 1; 149 // "AuxCall{<fn>(<args>)(<results>)}" otherwise. 150 func (a *AuxCall) String() string { 151 var fn string 152 if a.Fn == nil { 153 fn = "AuxCall{nil" // could be interface/closure etc. 154 } else { 155 fn = fmt.Sprintf("AuxCall{%v", a.Fn) 156 } 157 158 if len(a.args) == 0 { 159 fn += "()" 160 } else { 161 s := "(" 162 for _, arg := range a.args { 163 fn += fmt.Sprintf("%s[%v,%v]", s, arg.Type, arg.Offset) 164 s = "," 165 } 166 fn += ")" 167 } 168 169 if len(a.results) > 0 { // usual is zero or one; only some RT calls have more than one. 170 if len(a.results) == 1 { 171 fn += fmt.Sprintf("[%v,%v]", a.results[0].Type, a.results[0].Offset) 172 } else { 173 s := "(" 174 for _, result := range a.results { 175 fn += fmt.Sprintf("%s[%v,%v]", s, result.Type, result.Offset) 176 s = "," 177 } 178 fn += ")" 179 } 180 } 181 182 return fn + "}" 183 } 184 185 // StaticAuxCall returns an AuxCall for a static call. 186 func StaticAuxCall(sym *obj.LSym, args []Param, results []Param) *AuxCall { 187 return &AuxCall{Fn: sym, args: args, results: results} 188 } 189 190 // InterfaceAuxCall returns an AuxCall for an interface call. 191 func InterfaceAuxCall(args []Param, results []Param) *AuxCall { 192 return &AuxCall{Fn: nil, args: args, results: results} 193 } 194 195 // ClosureAuxCall returns an AuxCall for a closure call. 196 func ClosureAuxCall(args []Param, results []Param) *AuxCall { 197 return &AuxCall{Fn: nil, args: args, results: results} 198 } 199 200 const ( 201 auxNone auxType = iota 202 auxBool // auxInt is 0/1 for false/true 203 auxInt8 // auxInt is an 8-bit integer 204 auxInt16 // auxInt is a 16-bit integer 205 auxInt32 // auxInt is a 32-bit integer 206 auxInt64 // auxInt is a 64-bit integer 207 auxInt128 // auxInt represents a 128-bit integer. Always 0. 208 auxUInt8 // auxInt is an 8-bit unsigned integer 209 auxFloat32 // auxInt is a float32 (encoded with math.Float64bits) 210 auxFloat64 // auxInt is a float64 (encoded with math.Float64bits) 211 auxFlagConstant // auxInt is a flagConstant 212 auxString // aux is a string 213 auxSym // aux is a symbol (a *gc.Node for locals, an *obj.LSym for globals, or nil for none) 214 auxSymOff // aux is a symbol, auxInt is an offset 215 auxSymValAndOff // aux is a symbol, auxInt is a ValAndOff 216 auxTyp // aux is a type 217 auxTypSize // aux is a type, auxInt is a size, must have Aux.(Type).Size() == AuxInt 218 auxCCop // aux is a ssa.Op that represents a flags-to-bool conversion (e.g. LessThan) 219 auxCall // aux is a *ssa.AuxCall 220 auxCallOff // aux is a *ssa.AuxCall, AuxInt is int64 param (in+out) size 221 222 // architecture specific aux types 223 auxARM64BitField // aux is an arm64 bitfield lsb and width packed into auxInt 224 auxS390XRotateParams // aux is a s390x rotate parameters object encoding start bit, end bit and rotate amount 225 auxS390XCCMask // aux is a s390x 4-bit condition code mask 226 auxS390XCCMaskInt8 // aux is a s390x 4-bit condition code mask, auxInt is a int8 immediate 227 auxS390XCCMaskUint8 // aux is a s390x 4-bit condition code mask, auxInt is a uint8 immediate 228 ) 229 230 // A SymEffect describes the effect that an SSA Value has on the variable 231 // identified by the symbol in its Aux field. 232 type SymEffect int8 233 234 const ( 235 SymRead SymEffect = 1 << iota 236 SymWrite 237 SymAddr 238 239 SymRdWr = SymRead | SymWrite 240 241 SymNone SymEffect = 0 242 ) 243 244 // A Sym represents a symbolic offset from a base register. 245 // Currently a Sym can be one of 3 things: 246 // - a *gc.Node, for an offset from SP (the stack pointer) 247 // - a *obj.LSym, for an offset from SB (the global pointer) 248 // - nil, for no offset 249 type Sym interface { 250 String() string 251 CanBeAnSSASym() 252 } 253 254 // A ValAndOff is used by the several opcodes. It holds 255 // both a value and a pointer offset. 256 // A ValAndOff is intended to be encoded into an AuxInt field. 257 // The zero ValAndOff encodes a value of 0 and an offset of 0. 258 // The high 32 bits hold a value. 259 // The low 32 bits hold a pointer offset. 260 type ValAndOff int64 261 262 func (x ValAndOff) Val() int64 { return int64(x) >> 32 } 263 func (x ValAndOff) Val32() int32 { return int32(int64(x) >> 32) } 264 func (x ValAndOff) Val16() int16 { return int16(int64(x) >> 32) } 265 func (x ValAndOff) Val8() int8 { return int8(int64(x) >> 32) } 266 267 func (x ValAndOff) Off() int64 { return int64(int32(x)) } 268 func (x ValAndOff) Off32() int32 { return int32(x) } 269 270 func (x ValAndOff) String() string { 271 return fmt.Sprintf("val=%d,off=%d", x.Val(), x.Off()) 272 } 273 274 // validVal reports whether the value can be used 275 // as an argument to makeValAndOff. 276 func validVal(val int64) bool { 277 return val == int64(int32(val)) 278 } 279 280 // validOff reports whether the offset can be used 281 // as an argument to makeValAndOff. 282 func validOff(off int64) bool { 283 return off == int64(int32(off)) 284 } 285 286 // validValAndOff reports whether we can fit the value and offset into 287 // a ValAndOff value. 288 func validValAndOff(val, off int64) bool { 289 if !validVal(val) { 290 return false 291 } 292 if !validOff(off) { 293 return false 294 } 295 return true 296 } 297 298 func makeValAndOff32(val, off int32) ValAndOff { 299 return ValAndOff(int64(val)<<32 + int64(uint32(off))) 300 } 301 func makeValAndOff64(val, off int64) ValAndOff { 302 if !validValAndOff(val, off) { 303 panic("invalid makeValAndOff64") 304 } 305 return ValAndOff(val<<32 + int64(uint32(off))) 306 } 307 308 func (x ValAndOff) canAdd32(off int32) bool { 309 newoff := x.Off() + int64(off) 310 return newoff == int64(int32(newoff)) 311 } 312 func (x ValAndOff) canAdd64(off int64) bool { 313 newoff := x.Off() + off 314 return newoff == int64(int32(newoff)) 315 } 316 317 func (x ValAndOff) addOffset32(off int32) ValAndOff { 318 if !x.canAdd32(off) { 319 panic("invalid ValAndOff.addOffset32") 320 } 321 return makeValAndOff64(x.Val(), x.Off()+int64(off)) 322 } 323 func (x ValAndOff) addOffset64(off int64) ValAndOff { 324 if !x.canAdd64(off) { 325 panic("invalid ValAndOff.addOffset64") 326 } 327 return makeValAndOff64(x.Val(), x.Off()+off) 328 } 329 330 // int128 is a type that stores a 128-bit constant. 331 // The only allowed constant right now is 0, so we can cheat quite a bit. 332 type int128 int64 333 334 type BoundsKind uint8 335 336 const ( 337 BoundsIndex BoundsKind = iota // indexing operation, 0 <= idx < len failed 338 BoundsIndexU // ... with unsigned idx 339 BoundsSliceAlen // 2-arg slicing operation, 0 <= high <= len failed 340 BoundsSliceAlenU // ... with unsigned high 341 BoundsSliceAcap // 2-arg slicing operation, 0 <= high <= cap failed 342 BoundsSliceAcapU // ... with unsigned high 343 BoundsSliceB // 2-arg slicing operation, 0 <= low <= high failed 344 BoundsSliceBU // ... with unsigned low 345 BoundsSlice3Alen // 3-arg slicing operation, 0 <= max <= len failed 346 BoundsSlice3AlenU // ... with unsigned max 347 BoundsSlice3Acap // 3-arg slicing operation, 0 <= max <= cap failed 348 BoundsSlice3AcapU // ... with unsigned max 349 BoundsSlice3B // 3-arg slicing operation, 0 <= high <= max failed 350 BoundsSlice3BU // ... with unsigned high 351 BoundsSlice3C // 3-arg slicing operation, 0 <= low <= high failed 352 BoundsSlice3CU // ... with unsigned low 353 BoundsKindCount 354 ) 355 356 // boundsAPI determines which register arguments a bounds check call should use. For an [a:b:c] slice, we do: 357 // CMPQ c, cap 358 // JA fail1 359 // CMPQ b, c 360 // JA fail2 361 // CMPQ a, b 362 // JA fail3 363 // 364 // fail1: CALL panicSlice3Acap (c, cap) 365 // fail2: CALL panicSlice3B (b, c) 366 // fail3: CALL panicSlice3C (a, b) 367 // 368 // When we register allocate that code, we want the same register to be used for 369 // the first arg of panicSlice3Acap and the second arg to panicSlice3B. That way, 370 // initializing that register once will satisfy both calls. 371 // That desire ends up dividing the set of bounds check calls into 3 sets. This function 372 // determines which set to use for a given panic call. 373 // The first arg for set 0 should be the second arg for set 1. 374 // The first arg for set 1 should be the second arg for set 2. 375 func boundsABI(b int64) int { 376 switch BoundsKind(b) { 377 case BoundsSlice3Alen, 378 BoundsSlice3AlenU, 379 BoundsSlice3Acap, 380 BoundsSlice3AcapU: 381 return 0 382 case BoundsSliceAlen, 383 BoundsSliceAlenU, 384 BoundsSliceAcap, 385 BoundsSliceAcapU, 386 BoundsSlice3B, 387 BoundsSlice3BU: 388 return 1 389 case BoundsIndex, 390 BoundsIndexU, 391 BoundsSliceB, 392 BoundsSliceBU, 393 BoundsSlice3C, 394 BoundsSlice3CU: 395 return 2 396 default: 397 panic("bad BoundsKind") 398 } 399 } 400 401 // arm64BitFileld is the GO type of ARM64BitField auxInt. 402 // if x is an ARM64BitField, then width=x&0xff, lsb=(x>>8)&0xff, and 403 // width+lsb<64 for 64-bit variant, width+lsb<32 for 32-bit variant. 404 // the meaning of width and lsb are instruction-dependent. 405 type arm64BitField int16 406