1// Copyright 2013 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// MakeFunc amd64 implementation. 6 7package reflect 8 9import "unsafe" 10 11// The assembler stub will pass a pointer to this structure. 12// This will come in holding all the registers that might hold 13// function parameters. On return we will set the registers that 14// might hold result values. 15type amd64Regs struct { 16 rax uint64 17 rdi uint64 18 rsi uint64 19 rdx uint64 20 rcx uint64 21 r8 uint64 22 r9 uint64 23 rsp uint64 24 xmm0 [2]uint64 25 xmm1 [2]uint64 26 xmm2 [2]uint64 27 xmm3 [2]uint64 28 xmm4 [2]uint64 29 xmm5 [2]uint64 30 xmm6 [2]uint64 31 xmm7 [2]uint64 32} 33 34// Argument classifications. The amd64 ELF ABI uses several more, but 35// these are the only ones that arise for Go types. 36type amd64Class int 37 38const ( 39 amd64Integer amd64Class = iota 40 amd64SSE 41 amd64NoClass 42 amd64Memory 43) 44 45// amd64Classify returns the one or two register classes needed to 46// pass the value of type. Go types never need more than two 47// registers. amd64Memory means the value is stored in memory. 48// amd64NoClass means the register is not used. 49func amd64Classify(typ *rtype) (amd64Class, amd64Class) { 50 switch typ.Kind() { 51 default: 52 panic("internal error--unknown kind in amd64Classify") 53 54 case Bool, Int, Int8, Int16, Int32, Int64, 55 Uint, Uint8, Uint16, Uint32, Uint64, 56 Uintptr, Chan, Func, Map, Ptr, UnsafePointer: 57 58 return amd64Integer, amd64NoClass 59 60 case Float32, Float64, Complex64: 61 return amd64SSE, amd64NoClass 62 63 case Complex128: 64 return amd64SSE, amd64SSE 65 66 case Array: 67 if typ.size == 0 { 68 return amd64NoClass, amd64NoClass 69 } else if typ.size > 16 { 70 return amd64Memory, amd64NoClass 71 } 72 atyp := (*arrayType)(unsafe.Pointer(typ)) 73 eclass1, eclass2 := amd64Classify(atyp.elem) 74 if eclass1 == amd64Memory { 75 return amd64Memory, amd64NoClass 76 } 77 if eclass2 == amd64NoClass && typ.size > 8 { 78 eclass2 = eclass1 79 } 80 return eclass1, eclass2 81 82 case Interface: 83 return amd64Integer, amd64Integer 84 85 case Slice: 86 return amd64Memory, amd64NoClass 87 88 case String: 89 return amd64Integer, amd64Integer 90 91 case Struct: 92 if typ.size == 0 { 93 return amd64NoClass, amd64NoClass 94 } else if typ.size > 16 { 95 return amd64Memory, amd64NoClass 96 } 97 var first, second amd64Class 98 f := amd64NoClass 99 onFirst := true 100 styp := (*structType)(unsafe.Pointer(typ)) 101 for _, field := range styp.fields { 102 if onFirst && field.offset >= 8 { 103 first = f 104 f = amd64NoClass 105 onFirst = false 106 } 107 fclass1, fclass2 := amd64Classify(field.typ) 108 f = amd64MergeClasses(f, fclass1) 109 if fclass2 != amd64NoClass { 110 if !onFirst { 111 panic("amd64Classify inconsistent") 112 } 113 first = f 114 f = fclass2 115 onFirst = false 116 } 117 } 118 if onFirst { 119 first = f 120 second = amd64NoClass 121 } else { 122 second = f 123 } 124 if first == amd64Memory || second == amd64Memory { 125 return amd64Memory, amd64NoClass 126 } 127 return first, second 128 } 129} 130 131// amd64MergeClasses merges two register classes as described in the 132// amd64 ELF ABI. 133func amd64MergeClasses(c1, c2 amd64Class) amd64Class { 134 switch { 135 case c1 == c2: 136 return c1 137 case c1 == amd64NoClass: 138 return c2 139 case c2 == amd64NoClass: 140 return c1 141 case c1 == amd64Memory || c2 == amd64Memory: 142 return amd64Memory 143 case c1 == amd64Integer || c2 == amd64Integer: 144 return amd64Integer 145 default: 146 return amd64SSE 147 } 148} 149 150// MakeFuncStubGo implements the amd64 calling convention for 151// MakeFunc. This should not be called. It is exported so that 152// assembly code can call it. 153 154func MakeFuncStubGo(regs *amd64Regs, c *makeFuncImpl) { 155 ftyp := c.typ 156 157 // See if the result requires a struct. If it does, the first 158 // parameter is a pointer to the struct. 159 var ret1, ret2 amd64Class 160 switch len(ftyp.out) { 161 case 0: 162 ret1, ret2 = amd64NoClass, amd64NoClass 163 case 1: 164 ret1, ret2 = amd64Classify(ftyp.out[0]) 165 default: 166 off := uintptr(0) 167 f := amd64NoClass 168 onFirst := true 169 for _, rt := range ftyp.out { 170 off = align(off, uintptr(rt.fieldAlign)) 171 172 if onFirst && off >= 8 { 173 ret1 = f 174 f = amd64NoClass 175 onFirst = false 176 } 177 178 off += rt.size 179 if off > 16 { 180 break 181 } 182 183 fclass1, fclass2 := amd64Classify(rt) 184 f = amd64MergeClasses(f, fclass1) 185 if fclass2 != amd64NoClass { 186 if !onFirst { 187 panic("amd64Classify inconsistent") 188 } 189 ret1 = f 190 f = fclass2 191 onFirst = false 192 } 193 } 194 if off > 16 { 195 ret1, ret2 = amd64Memory, amd64NoClass 196 } else { 197 if onFirst { 198 ret1, ret2 = f, amd64NoClass 199 } else { 200 ret2 = f 201 } 202 } 203 if ret1 == amd64Memory || ret2 == amd64Memory { 204 ret1, ret2 = amd64Memory, amd64NoClass 205 } 206 } 207 208 in := make([]Value, 0, len(ftyp.in)) 209 intreg := 0 210 ssereg := 0 211 ap := uintptr(regs.rsp) 212 213 maxIntregs := 6 // When we support Windows, this would be 4. 214 maxSSEregs := 8 215 216 if ret1 == amd64Memory { 217 // We are returning a value in memory, which means 218 // that the first argument is a hidden parameter 219 // pointing to that return area. 220 intreg++ 221 } 222 223argloop: 224 for _, rt := range ftyp.in { 225 c1, c2 := amd64Classify(rt) 226 227 fl := flag(rt.Kind()) << flagKindShift 228 if c2 == amd64NoClass { 229 230 // Argument is passed in a single register or 231 // in memory. 232 233 switch c1 { 234 case amd64NoClass: 235 v := Value{rt, nil, fl | flagIndir} 236 in = append(in, v) 237 continue argloop 238 case amd64Integer: 239 if intreg < maxIntregs { 240 reg := amd64IntregVal(regs, intreg) 241 iw := unsafe.Pointer(reg) 242 if k := rt.Kind(); k != Ptr && k != UnsafePointer { 243 iw = unsafe.Pointer(®) 244 fl |= flagIndir 245 } 246 v := Value{rt, iw, fl} 247 in = append(in, v) 248 intreg++ 249 continue argloop 250 } 251 case amd64SSE: 252 if ssereg < maxSSEregs { 253 reg := amd64SSEregVal(regs, ssereg) 254 v := Value{rt, unsafe.Pointer(®), fl | flagIndir} 255 in = append(in, v) 256 ssereg++ 257 continue argloop 258 } 259 } 260 261 in, ap = amd64Memarg(in, ap, rt) 262 continue argloop 263 } 264 265 // Argument is passed in two registers. 266 267 nintregs := 0 268 nsseregs := 0 269 switch c1 { 270 case amd64Integer: 271 nintregs++ 272 case amd64SSE: 273 nsseregs++ 274 default: 275 panic("inconsistent") 276 } 277 switch c2 { 278 case amd64Integer: 279 nintregs++ 280 case amd64SSE: 281 nsseregs++ 282 default: 283 panic("inconsistent") 284 } 285 286 // If the whole argument does not fit in registers, it 287 // is passed in memory. 288 289 if intreg+nintregs > maxIntregs || ssereg+nsseregs > maxSSEregs { 290 in, ap = amd64Memarg(in, ap, rt) 291 continue argloop 292 } 293 294 var word1, word2 uintptr 295 switch c1 { 296 case amd64Integer: 297 word1 = amd64IntregVal(regs, intreg) 298 intreg++ 299 case amd64SSE: 300 word1 = amd64SSEregVal(regs, ssereg) 301 ssereg++ 302 } 303 switch c2 { 304 case amd64Integer: 305 word2 = amd64IntregVal(regs, intreg) 306 intreg++ 307 case amd64SSE: 308 word2 = amd64SSEregVal(regs, ssereg) 309 ssereg++ 310 } 311 312 p := unsafe_New(rt) 313 *(*uintptr)(p) = word1 314 *(*uintptr)(unsafe.Pointer(uintptr(p) + ptrSize)) = word2 315 v := Value{rt, p, fl | flagIndir} 316 in = append(in, v) 317 } 318 319 // All the real arguments have been found and turned into 320 // Value's. Call the real function. 321 322 out := c.call(in) 323 324 if len(out) != len(ftyp.out) { 325 panic("reflect: wrong return count from function created by MakeFunc") 326 } 327 328 for i, typ := range ftyp.out { 329 v := out[i] 330 if v.typ != typ { 331 panic("reflect: function created by MakeFunc using " + funcName(c.fn) + 332 " returned wrong type: have " + 333 out[i].typ.String() + " for " + typ.String()) 334 } 335 if v.flag&flagRO != 0 { 336 panic("reflect: function created by MakeFunc using " + funcName(c.fn) + 337 " returned value obtained from unexported field") 338 } 339 } 340 341 if ret1 == amd64NoClass { 342 return 343 } 344 345 if ret1 == amd64Memory { 346 // The address of the memory area was passed as a 347 // hidden parameter in %rdi. 348 ptr := unsafe.Pointer(uintptr(regs.rdi)) 349 off := uintptr(0) 350 for i, typ := range ftyp.out { 351 v := out[i] 352 off = align(off, uintptr(typ.fieldAlign)) 353 addr := unsafe.Pointer(uintptr(ptr) + off) 354 if v.flag&flagIndir == 0 && (v.kind() == Ptr || v.kind() == UnsafePointer) { 355 storeIword(addr, iword(v.val), typ.size) 356 } else { 357 memmove(addr, v.val, typ.size) 358 } 359 off += typ.size 360 } 361 return 362 } 363 364 if len(out) == 1 && ret2 == amd64NoClass { 365 v := out[0] 366 w := v.iword() 367 if v.Kind() != Ptr && v.Kind() != UnsafePointer { 368 w = loadIword(unsafe.Pointer(w), v.typ.size) 369 } 370 switch ret1 { 371 case amd64Integer: 372 regs.rax = uint64(uintptr(w)) 373 case amd64SSE: 374 regs.xmm0[0] = uint64(uintptr(w)) 375 regs.xmm0[1] = 0 376 default: 377 panic("inconsistency") 378 } 379 return 380 } 381 382 var buf [2]unsafe.Pointer 383 ptr := unsafe.Pointer(&buf[0]) 384 off := uintptr(0) 385 for i, typ := range ftyp.out { 386 v := out[i] 387 off = align(off, uintptr(typ.fieldAlign)) 388 addr := unsafe.Pointer(uintptr(ptr) + off) 389 if v.flag&flagIndir == 0 && (v.kind() == Ptr || v.kind() == UnsafePointer) { 390 storeIword(addr, iword(v.val), typ.size) 391 } else { 392 memmove(addr, v.val, typ.size) 393 } 394 off += uintptr(typ.size) 395 } 396 397 switch ret1 { 398 case amd64Integer: 399 regs.rax = *(*uint64)(unsafe.Pointer(&buf[0])) 400 case amd64SSE: 401 regs.xmm0[0] = *(*uint64)(unsafe.Pointer(&buf[0])) 402 regs.xmm0[1] = 0 403 default: 404 panic("inconsistency") 405 } 406 407 switch ret2 { 408 case amd64Integer: 409 reg := *(*uint64)(unsafe.Pointer(&buf[1])) 410 if ret1 == amd64Integer { 411 regs.rdx = reg 412 } else { 413 regs.rax = reg 414 } 415 case amd64SSE: 416 reg := *(*uint64)(unsafe.Pointer(&buf[1])) 417 if ret1 == amd64Integer { 418 regs.xmm0[0] = reg 419 regs.xmm0[1] = 0 420 } else { 421 regs.xmm1[0] = reg 422 regs.xmm1[1] = 0 423 } 424 case amd64NoClass: 425 default: 426 panic("inconsistency") 427 } 428} 429 430// The amd64Memarg function adds an argument passed in memory. 431func amd64Memarg(in []Value, ap uintptr, rt *rtype) ([]Value, uintptr) { 432 ap = align(ap, ptrSize) 433 ap = align(ap, uintptr(rt.align)) 434 435 // We have to copy the argument onto the heap in case the 436 // function hangs onto the reflect.Value we pass it. 437 p := unsafe_New(rt) 438 memmove(p, unsafe.Pointer(ap), rt.size) 439 440 v := Value{rt, p, flag(rt.Kind()<<flagKindShift) | flagIndir} 441 in = append(in, v) 442 ap += rt.size 443 return in, ap 444} 445 446// The amd64IntregVal function returns the value of integer register i. 447func amd64IntregVal(regs *amd64Regs, i int) uintptr { 448 var r uint64 449 switch i { 450 case 0: 451 r = regs.rdi 452 case 1: 453 r = regs.rsi 454 case 2: 455 r = regs.rdx 456 case 3: 457 r = regs.rcx 458 case 4: 459 r = regs.r8 460 case 5: 461 r = regs.r9 462 default: 463 panic("amd64IntregVal: bad index") 464 } 465 return uintptr(r) 466} 467 468// The amd64SSEregVal function returns the value of SSE register i. 469// Note that although SSE registers can hold two uinptr's, for the 470// types we use in Go we only ever use the least significant one. The 471// most significant one would only be used for 128 bit types. 472func amd64SSEregVal(regs *amd64Regs, i int) uintptr { 473 var r uint64 474 switch i { 475 case 0: 476 r = regs.xmm0[0] 477 case 1: 478 r = regs.xmm1[0] 479 case 2: 480 r = regs.xmm2[0] 481 case 3: 482 r = regs.xmm3[0] 483 case 4: 484 r = regs.xmm4[0] 485 case 5: 486 r = regs.xmm5[0] 487 case 6: 488 r = regs.xmm6[0] 489 case 7: 490 r = regs.xmm7[0] 491 } 492 return uintptr(r) 493} 494