struct_riscv64.go raw

   1  // SPDX-License-Identifier: Apache-2.0
   2  // SPDX-FileCopyrightText: 2026 The Ebitengine Authors
   3  
   4  package purego
   5  
   6  import (
   7  	"reflect"
   8  	"unsafe"
   9  )
  10  
  11  func getStruct(outType reflect.Type, syscall syscall15Args) reflect.Value {
  12  	outSize := outType.Size()
  13  
  14  	switch {
  15  	case outSize == 0:
  16  		return reflect.New(outType).Elem()
  17  
  18  	case outSize <= 16:
  19  		// Reconstruct from registers by copying raw bytes
  20  		var buf [16]byte
  21  
  22  		// Integer registers
  23  		*(*uintptr)(unsafe.Pointer(&buf[0])) = syscall.a1
  24  		if outSize > 8 {
  25  			*(*uintptr)(unsafe.Pointer(&buf[8])) = syscall.a2
  26  		}
  27  
  28  		// Homogeneous float aggregates override integer regs
  29  		if isAllFloats, numFields := isAllSameFloat(outType); isAllFloats {
  30  			if outType.Field(0).Type.Kind() == reflect.Float32 {
  31  				// float32 values are NaN-boxed in FP regs; use low 32 bits only
  32  				f := []uintptr{syscall.f1, syscall.f2, syscall.f3, syscall.f4}
  33  				for i := 0; i < numFields; i++ {
  34  					*(*uint32)(unsafe.Pointer(&buf[i*4])) = uint32(f[i])
  35  				}
  36  			} else {
  37  				// float64: whole register value is valid
  38  				*(*uintptr)(unsafe.Pointer(&buf[0])) = syscall.f1
  39  				if outSize > 8 {
  40  					*(*uintptr)(unsafe.Pointer(&buf[8])) = syscall.f2
  41  				}
  42  			}
  43  		}
  44  
  45  		return reflect.NewAt(outType, unsafe.Pointer(&buf[0])).Elem()
  46  
  47  	default:
  48  		// Returned indirectly via pointer in a1
  49  		ptr := *(*unsafe.Pointer)(unsafe.Pointer(&syscall.a1))
  50  		return reflect.NewAt(outType, ptr).Elem()
  51  	}
  52  }
  53  
  54  func addStruct(
  55  	v reflect.Value,
  56  	numInts, numFloats, numStack *int,
  57  	addInt, addFloat, addStack func(uintptr),
  58  	keepAlive []any,
  59  ) []any {
  60  	size := v.Type().Size()
  61  	if size == 0 {
  62  		return keepAlive
  63  	}
  64  
  65  	if size <= 16 {
  66  		return placeSmallAggregateRISCV64(v, addFloat, addInt, keepAlive)
  67  	}
  68  
  69  	return placeStack(v, keepAlive, addInt)
  70  }
  71  
  72  func placeSmallAggregateRISCV64(
  73  	v reflect.Value,
  74  	addFloat, addInt func(uintptr),
  75  	keepAlive []any,
  76  ) []any {
  77  	size := v.Type().Size()
  78  
  79  	var ptr unsafe.Pointer
  80  	if v.CanAddr() {
  81  		ptr = v.Addr().UnsafePointer()
  82  	} else {
  83  		tmp := reflect.New(v.Type())
  84  		tmp.Elem().Set(v)
  85  		ptr = tmp.UnsafePointer()
  86  		keepAlive = append(keepAlive, tmp.Interface())
  87  	}
  88  
  89  	var buf [16]byte
  90  	src := unsafe.Slice((*byte)(ptr), size)
  91  	copy(buf[:], src)
  92  
  93  	w0 := *(*uintptr)(unsafe.Pointer(&buf[0]))
  94  	w1 := uintptr(0)
  95  	if size > 8 {
  96  		w1 = *(*uintptr)(unsafe.Pointer(&buf[8]))
  97  	}
  98  
  99  	if isFloats, _ := isAllSameFloat(v.Type()); isFloats {
 100  		addFloat(w0)
 101  		if size > 8 {
 102  			addFloat(w1)
 103  		}
 104  	} else {
 105  		addInt(w0)
 106  		if size > 8 {
 107  			addInt(w1)
 108  		}
 109  	}
 110  
 111  	return keepAlive
 112  }
 113  
 114  // placeStack is a fallback for structs that are too large to fit in registers
 115  func placeStack(v reflect.Value, keepAlive []any, addInt func(uintptr)) []any {
 116  	if v.CanAddr() {
 117  		addInt(v.Addr().Pointer())
 118  		return keepAlive
 119  	}
 120  	ptr := reflect.New(v.Type())
 121  	ptr.Elem().Set(v)
 122  	addInt(ptr.Pointer())
 123  	return append(keepAlive, ptr.Interface())
 124  }
 125  
 126  func shouldBundleStackArgs(v reflect.Value, numInts, numFloats int) bool {
 127  	// RISCV64 does not bundle stack args
 128  	return false
 129  }
 130  
 131  func collectStackArgs(
 132  	args []reflect.Value,
 133  	i, numInts, numFloats int,
 134  	keepAlive []any,
 135  	addInt, addFloat, addStack func(uintptr),
 136  	numIntsPtr, numFloatsPtr, numStackPtr *int,
 137  ) ([]reflect.Value, []any) {
 138  	return nil, keepAlive
 139  }
 140  
 141  func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) {
 142  	panic("bundleStackArgs not supported on RISCV64")
 143  }
 144