struct_loong64.go raw

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