struct_loong64.go 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. // SPDX-License-Identifier: Apache-2.0
  2. // SPDX-FileCopyrightText: 2025 The Ebitengine Authors
  3. package purego
  4. import (
  5. "math"
  6. "reflect"
  7. "unsafe"
  8. )
  9. func getStruct(outType reflect.Type, syscall syscall15Args) (v reflect.Value) {
  10. outSize := outType.Size()
  11. switch {
  12. case outSize == 0:
  13. return reflect.New(outType).Elem()
  14. case outSize <= 8:
  15. r1 := syscall.a1
  16. if isAllFloats, numFields := isAllSameFloat(outType); isAllFloats {
  17. r1 = syscall.f1
  18. if numFields == 2 {
  19. r1 = syscall.f2<<32 | syscall.f1
  20. }
  21. }
  22. return reflect.NewAt(outType, unsafe.Pointer(&struct{ a uintptr }{r1})).Elem()
  23. case outSize <= 16:
  24. r1, r2 := syscall.a1, syscall.a2
  25. if isAllFloats, numFields := isAllSameFloat(outType); isAllFloats {
  26. switch numFields {
  27. case 4:
  28. r1 = syscall.f2<<32 | syscall.f1
  29. r2 = syscall.f4<<32 | syscall.f3
  30. case 3:
  31. r1 = syscall.f2<<32 | syscall.f1
  32. r2 = syscall.f3
  33. case 2:
  34. r1 = syscall.f1
  35. r2 = syscall.f2
  36. default:
  37. panic("unreachable")
  38. }
  39. }
  40. return reflect.NewAt(outType, unsafe.Pointer(&struct{ a, b uintptr }{r1, r2})).Elem()
  41. default:
  42. // create struct from the Go pointer created above
  43. // weird pointer dereference to circumvent go vet
  44. return reflect.NewAt(outType, *(*unsafe.Pointer)(unsafe.Pointer(&syscall.a1))).Elem()
  45. }
  46. }
  47. const (
  48. _NO_CLASS = 0b00
  49. _FLOAT = 0b01
  50. _INT = 0b11
  51. )
  52. func addStruct(v reflect.Value, numInts, numFloats, numStack *int, addInt, addFloat, addStack func(uintptr), keepAlive []any) []any {
  53. if v.Type().Size() == 0 {
  54. return keepAlive
  55. }
  56. if size := v.Type().Size(); size <= 16 {
  57. placeRegisters(v, addFloat, addInt)
  58. } else {
  59. keepAlive = placeStack(v, keepAlive, addInt)
  60. }
  61. return keepAlive // the struct was allocated so don't panic
  62. }
  63. func placeRegisters(v reflect.Value, addFloat func(uintptr), addInt func(uintptr)) {
  64. var val uint64
  65. var shift byte
  66. var flushed bool
  67. class := _NO_CLASS
  68. var place func(v reflect.Value)
  69. place = func(v reflect.Value) {
  70. var numFields int
  71. if v.Kind() == reflect.Struct {
  72. numFields = v.Type().NumField()
  73. } else {
  74. numFields = v.Type().Len()
  75. }
  76. for k := 0; k < numFields; k++ {
  77. flushed = false
  78. var f reflect.Value
  79. if v.Kind() == reflect.Struct {
  80. f = v.Field(k)
  81. } else {
  82. f = v.Index(k)
  83. }
  84. align := byte(f.Type().Align()*8 - 1)
  85. shift = (shift + align) &^ align
  86. if shift >= 64 {
  87. shift = 0
  88. flushed = true
  89. if class == _FLOAT {
  90. addFloat(uintptr(val))
  91. } else {
  92. addInt(uintptr(val))
  93. }
  94. }
  95. switch f.Type().Kind() {
  96. case reflect.Struct:
  97. place(f)
  98. case reflect.Bool:
  99. if f.Bool() {
  100. val |= 1 << shift
  101. }
  102. shift += 8
  103. class |= _INT
  104. case reflect.Uint8:
  105. val |= f.Uint() << shift
  106. shift += 8
  107. class |= _INT
  108. case reflect.Uint16:
  109. val |= f.Uint() << shift
  110. shift += 16
  111. class |= _INT
  112. case reflect.Uint32:
  113. val |= f.Uint() << shift
  114. shift += 32
  115. class |= _INT
  116. case reflect.Uint64, reflect.Uint, reflect.Uintptr:
  117. addInt(uintptr(f.Uint()))
  118. shift = 0
  119. flushed = true
  120. class = _NO_CLASS
  121. case reflect.Int8:
  122. val |= uint64(f.Int()&0xFF) << shift
  123. shift += 8
  124. class |= _INT
  125. case reflect.Int16:
  126. val |= uint64(f.Int()&0xFFFF) << shift
  127. shift += 16
  128. class |= _INT
  129. case reflect.Int32:
  130. val |= uint64(f.Int()&0xFFFF_FFFF) << shift
  131. shift += 32
  132. class |= _INT
  133. case reflect.Int64, reflect.Int:
  134. addInt(uintptr(f.Int()))
  135. shift = 0
  136. flushed = true
  137. class = _NO_CLASS
  138. case reflect.Float32:
  139. if class == _FLOAT {
  140. addFloat(uintptr(val))
  141. val = 0
  142. shift = 0
  143. }
  144. val |= uint64(math.Float32bits(float32(f.Float()))) << shift
  145. shift += 32
  146. class |= _FLOAT
  147. case reflect.Float64:
  148. addFloat(uintptr(math.Float64bits(float64(f.Float()))))
  149. shift = 0
  150. flushed = true
  151. class = _NO_CLASS
  152. case reflect.Ptr, reflect.UnsafePointer:
  153. addInt(f.Pointer())
  154. shift = 0
  155. flushed = true
  156. class = _NO_CLASS
  157. case reflect.Array:
  158. place(f)
  159. default:
  160. panic("purego: unsupported kind " + f.Kind().String())
  161. }
  162. }
  163. }
  164. place(v)
  165. if !flushed {
  166. if class == _FLOAT {
  167. addFloat(uintptr(val))
  168. } else {
  169. addInt(uintptr(val))
  170. }
  171. }
  172. }
  173. func placeStack(v reflect.Value, keepAlive []any, addInt func(uintptr)) []any {
  174. // Struct is too big to be placed in registers.
  175. // Copy to heap and place the pointer in register
  176. ptrStruct := reflect.New(v.Type())
  177. ptrStruct.Elem().Set(v)
  178. ptr := ptrStruct.Elem().Addr().UnsafePointer()
  179. keepAlive = append(keepAlive, ptr)
  180. addInt(uintptr(ptr))
  181. return keepAlive
  182. }
  183. // shouldBundleStackArgs always returns false on loong64
  184. // since C-style stack argument bundling is only needed on Darwin ARM64.
  185. func shouldBundleStackArgs(v reflect.Value, numInts, numFloats int) bool {
  186. return false
  187. }
  188. // structFitsInRegisters is not used on loong64.
  189. func structFitsInRegisters(val reflect.Value, tempNumInts, tempNumFloats int) (bool, int, int) {
  190. panic("purego: structFitsInRegisters should not be called on loong64")
  191. }
  192. // collectStackArgs is not used on loong64.
  193. func collectStackArgs(args []reflect.Value, startIdx int, numInts, numFloats int,
  194. keepAlive []any, addInt, addFloat, addStack func(uintptr),
  195. pNumInts, pNumFloats, pNumStack *int) ([]reflect.Value, []any) {
  196. panic("purego: collectStackArgs should not be called on loong64")
  197. }
  198. // bundleStackArgs is not used on loong64.
  199. func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) {
  200. panic("purego: bundleStackArgs should not be called on loong64")
  201. }