Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions core/src/fr/hammons/slinc/Mem.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ trait Mem:
def asBase: Object
def asAddress: Object
def asVarArgs: VarArgs
def copyFrom(other: Mem): Unit

def writeFloat(v: Float, offset: Bytes): Unit
def writeLong(v: Long, offset: Bytes): Unit
Expand Down
7 changes: 4 additions & 3 deletions core/src/fr/hammons/slinc/TypeDescriptor.scala
Original file line number Diff line number Diff line change
Expand Up @@ -201,16 +201,17 @@ case class CUnionDescriptor(possibleTypes: Set[TypeDescriptor])
type Inner = CUnion[? <: NonEmptyTuple]

override val reader: (ReadWriteModule, DescriptorModule) ?=> Reader[Inner] =
???
summon[ReadWriteModule].unionReader(this)

override val returnTransition
: (TransitionModule, ReadWriteModule) ?=> ReturnTransition[Inner] = obj =>
summon[TransitionModule].cUnionReturn(this, obj).asInstanceOf[Inner]
summon[ReadWriteModule]
.unionReader(this)(summon[TransitionModule].memReturn(obj), Bytes(0))

override val argumentTransition
: (TransitionModule, ReadWriteModule, Allocator) ?=> ArgumentTransition[
Inner
] = (i: Inner) => i.mem.asBase

override val writer: (ReadWriteModule, DescriptorModule) ?=> Writer[Inner] =
???
summon[ReadWriteModule].unionWriter(this)
3 changes: 3 additions & 0 deletions core/src/fr/hammons/slinc/modules/ReadWriteModule.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package fr.hammons.slinc.modules
import fr.hammons.slinc.*
import java.lang.invoke.MethodHandle
import scala.reflect.ClassTag
import scala.NonEmptyTuple

type Reader[A] = (Mem, Bytes) => A
type Writer[A] = (Mem, Bytes, A) => Unit
Expand All @@ -27,6 +28,8 @@ trait ReadWriteModule:

val memReader: Reader[Mem]
val memWriter: Writer[Mem]
def unionReader(td: TypeDescriptor): Reader[CUnion[? <: NonEmptyTuple]]
def unionWriter(td: TypeDescriptor): Writer[CUnion[? <: NonEmptyTuple]]

def write(
memory: Mem,
Expand Down
2 changes: 0 additions & 2 deletions core/src/fr/hammons/slinc/modules/TransitionModule.scala
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,6 @@ trait TransitionModule:

def memReturn(value: Object): Mem

def cUnionReturn(td: TypeDescriptor, value: Object): CUnion[?]

def functionArgument[A](td: TypeDescriptor, value: Object): A =
methodReturn[A](td, value)

Expand Down
17 changes: 16 additions & 1 deletion core/test/resources/native/test.c
Original file line number Diff line number Diff line change
Expand Up @@ -123,4 +123,19 @@ EXPORTED union_b_issue_176 i176_test(union_a_issue_176 a, char is_left) {
}

return b;
}
}

typedef struct {
union {
long x;
double y;
} my_union;
} struct_issue_175;
EXPORTED struct_issue_175 i175_test(struct_issue_175 a, char left) {
if(left) {
a.my_union.x = a.my_union.x * 2;
} else {
a.my_union.y = a.my_union.y / 2;
}
return a;
}
24 changes: 24 additions & 0 deletions core/test/src/fr/hammons/slinc/BindingSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ trait BindingSpec(val slinc: Slinc) extends ScalaCheckSuite:
case class I36Outer(inner: Ptr[I36Inner]) derives Struct

case class I30Struct(list: Ptr[VarArgs]) derives Struct
case class I175_Struct(union: CUnion[(CInt, CDouble)]) derives Struct

@NeedsResource("test")
trait TestLib derives FSet:
Expand Down Expand Up @@ -53,6 +54,11 @@ trait BindingSpec(val slinc: Slinc) extends ScalaCheckSuite:
is_left: CChar
): CUnion[(CLong, CDouble)]

def i175_test(
input: I175_Struct,
left: CChar
): I175_Struct

test("int_identity") {
val test = FSet.instance[TestLib]

Expand Down Expand Up @@ -162,3 +168,21 @@ trait BindingSpec(val slinc: Slinc) extends ScalaCheckSuite:
union.set(int)
val res = test.i176_test(union, 0).get[CLong]
assertEquals(res, CLong(int))

property(
"issue 175 - can send and receive structs with union types to C functions"
):
val test = FSet.instance[TestLib]
forAll: (int: CInt, double: CDouble, left: Boolean) =>
val union = CUnion[(CInt, CDouble)]
if left then
union.set(int)
val res = test.i175_test(I175_Struct(union), 1)
assertEquals(
res.union.get[CInt],
int * 2
)
else
union.set(double)
val res = test.i175_test(I175_Struct(union), 0)
assertEquals(res.union.get[CDouble], double / 2)
22 changes: 22 additions & 0 deletions core/test/src/fr/hammons/slinc/TransferSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ trait TransferSpec[ThreadException <: Throwable](val slinc: Slinc)(using

case class E(list: VarArgs) derives Struct

case class F(u: CUnion[(CInt, CFloat)]) derives Struct

test("can read and write jvm ints") {
Scope.global {
val mem = Ptr.blank[Int]
Expand Down Expand Up @@ -331,3 +333,23 @@ trait TransferSpec[ThreadException <: Throwable](val slinc: Slinc)(using

assertEquals(deallocated, false)
assertEquals(union.get[CInt], 0)

property("can create F ptrs"):
val union = CUnion[(CInt, CFloat)]
forAll: (a: CInt, b: CFloat, left: Boolean) =>
if left then
union.set(a)
val fReturn = Scope.confined {
val f = Ptr.copy(F(union))
!f
}

assertEquals(fReturn.u.get[CInt], union.get[CInt])
else
union.set(b)
val fReturn = Scope.confined {
val f = Ptr.copy(F(union))
!f
}

assertEquals(fReturn.u.get[CFloat], union.get[CFloat])
1 change: 0 additions & 1 deletion j17/src/fr/hammons/slinc/Allocator17.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import jdk.incubator.foreign.{
}, CLinker.{C_POINTER, C_INT, C_LONG_LONG, C_DOUBLE, VaList}
import fr.hammons.slinc.modules.{descriptorModule17, transitionModule17}
import fr.hammons.slinc.modules.LinkageModule17
import java.lang.ref.Cleaner

class Allocator17(
segmentAllocator: SegmentAllocator,
Expand Down
4 changes: 4 additions & 0 deletions j17/src/fr/hammons/slinc/Mem17.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ class Mem17(private[slinc] val mem: MemorySegment) extends Mem:
VaList.ofAddress(mem.address().nn).nn
)

override def copyFrom(other: Mem): Unit =
other match
case oMem: Mem17 => mem.copyFrom(oMem.mem)

override def readLong(offset: Bytes): Long =
MemoryAccess.getLongAtOffset(mem, offset.toLong)

Expand Down
13 changes: 6 additions & 7 deletions j17/src/fr/hammons/slinc/Scope17.scala
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
package fr.hammons.slinc

import jdk.incubator.foreign.CLinker
import jdk.incubator.foreign.ResourceScope
import jdk.incubator.foreign.SegmentAllocator
import jdk.incubator.foreign.MemoryAddress

class Scope17(linker: CLinker) extends ScopeI.PlatformSpecific:
object Scope17 extends ScopeI.PlatformSpecific:
private val baseNull = Ptr[Nothing](
Mem17(MemoryAddress.NULL.nn.asSegment(1, ResourceScope.globalScope).nn),
Bytes(0)
Expand All @@ -17,14 +16,14 @@ class Scope17(linker: CLinker) extends ScopeI.PlatformSpecific:
def apply[A](fn: (Allocator) ?=> A): A =
val rs = ResourceScope.globalScope().nn
given Allocator =
Allocator17(SegmentAllocator.arenaAllocator(rs).nn, rs, linker)
Allocator17(SegmentAllocator.arenaAllocator(rs).nn, rs, Slinc17.linker)
fn

def createConfinedScope: ConfinedScope = new ConfinedScope:
def apply[A](fn: Allocator ?=> A): A =
val rs = ResourceScope.newConfinedScope().nn
given Allocator =
Allocator17(SegmentAllocator.arenaAllocator(rs).nn, rs, linker)
Allocator17(SegmentAllocator.arenaAllocator(rs).nn, rs, Slinc17.linker)
val res = fn
rs.close()
res
Expand All @@ -33,7 +32,7 @@ class Scope17(linker: CLinker) extends ScopeI.PlatformSpecific:
def apply[A](fn: Allocator ?=> A): A =
val rs = ResourceScope.newSharedScope().nn
given Allocator =
Allocator17(SegmentAllocator.arenaAllocator(rs).nn, rs, linker)
Allocator17(SegmentAllocator.arenaAllocator(rs).nn, rs, Slinc17.linker)
val res = fn
rs.close()
res
Expand All @@ -46,7 +45,7 @@ class Scope17(linker: CLinker) extends ScopeI.PlatformSpecific:
given Allocator = Allocator17(
segmentAllocator,
ResourceScope.globalScope().nn,
linker
Slinc17.linker
)
val res = fn
allocator.reset()
Expand All @@ -55,5 +54,5 @@ class Scope17(linker: CLinker) extends ScopeI.PlatformSpecific:
def createInferredScope: InferredScope = new InferredScope:
def apply[A](fn: Allocator ?=> A): A =
val scope = ResourceScope.newSharedScope().nn
given Allocator = InferredAllocator17(scope, linker)
given Allocator = InferredAllocator17(scope, Slinc17.linker)
fn
14 changes: 7 additions & 7 deletions j17/src/fr/hammons/slinc/Slinc17.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,21 @@ import fr.hammons.slinc.modules.{
}
import fr.hammons.slinc.modules.given

class Slinc17(_jitManager: JitManager, linker: CLinker)(using
class Slinc17(_jitManager: JitManager)(using
val dm: DescriptorModule,
val tm: TransitionModule,
val rwm: ReadWriteModule,
val lm: FSetModule
) extends Slinc:
val version: Int = 17
protected def jitManager = _jitManager
protected def scopePlatformSpecific = Scope17(linker)
protected def scopePlatformSpecific = Scope17

@SlincImpl(17)
object Slinc17:
private val compiler =
private lazy val compiler =
scala.quoted.staging.Compiler.make(getClass().getClassLoader().nn)
private[slinc] val linker = CLinker.getInstance().nn
val default = Slinc17(JitManagerImpl(compiler), linker)
val noJit = Slinc17(NoJitManager, linker)
val immediateJit = Slinc17(InstantJitManager(compiler), linker)
private[slinc] lazy val linker = CLinker.getInstance().nn
val default = Slinc17(JitManagerImpl(compiler))
val noJit = Slinc17(NoJitManager)
val immediateJit = Slinc17(InstantJitManager(compiler))
6 changes: 2 additions & 4 deletions j17/src/fr/hammons/slinc/modules/LinkageModule17.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,8 @@ import java.lang.invoke.MethodType
object LinkageModule17 extends LinkageModule:
import descriptorModule17.*
override type CSymbol = Addressable
private val linker = CLinker.getInstance().nn
private val lookup = CLinker.systemLookup().nn
private val lookupLoader = SymbolLookup.loaderLookup().nn
private val ts = Scope17(linker)

override def defaultLookup(name: String): Option[CSymbol] =
lookup.lookup(name).nn.toScala
Expand Down Expand Up @@ -59,7 +57,7 @@ object LinkageModule17 extends LinkageModule:
.toSeq
)

linker.downcallHandle(mt, fd).nn
Slinc17.linker.downcallHandle(mt, fd).nn
end getDowncall

lazy val tempScope: Scope = ts.createTempScope
lazy val tempScope: Scope = Scope17.createTempScope
16 changes: 16 additions & 0 deletions j17/src/fr/hammons/slinc/modules/ReadWriteModule17.scala
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,22 @@ given readWriteModule17: ReadWriteModule with

val memReader = (mem, offset) => mem.readAddress(offset)

def unionReader(
typeDescriptor: TypeDescriptor
): Reader[CUnion[? <: NonEmptyTuple]] =
val size = descriptorModule17.sizeOf(typeDescriptor)
(mem, offset) =>
Scope17.createInferredScope(alloc ?=>
val newMem = alloc.allocate(typeDescriptor, 1)
newMem.copyFrom(mem.offset(offset).resize(size))

new CUnion(newMem)
)

def unionWriter(td: TypeDescriptor): Writer[CUnion[? <: NonEmptyTuple]] =
val size = descriptorModule17.sizeOf(td)
(mem, offset, value) => mem.offset(offset).resize(size).copyFrom(value.mem)

arrayWriterCache
.addOne(
ByteDescriptor,
Expand Down
14 changes: 1 addition & 13 deletions j17/src/fr/hammons/slinc/modules/TransitionModule17.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,14 @@ import fr.hammons.slinc.TypeDescriptor
import scala.collection.concurrent.TrieMap
import fr.hammons.slinc.*

import jdk.incubator.foreign.{
MemoryAddress,
MemorySegment,
ResourceScope,
SegmentAllocator
}
import jdk.incubator.foreign.{MemoryAddress, MemorySegment}

given transitionModule17: TransitionModule with

override def memReturn(value: Object): Mem = Mem17(
value.asInstanceOf[MemorySegment]
)

override def cUnionReturn(td: TypeDescriptor, value: Object): CUnion[?] =
val scope = ResourceScope.newImplicitScope().nn
val alloc = SegmentAllocator.arenaAllocator(scope).nn
val segment = alloc.allocate(descriptorModule17.toMemoryLayout(td)).nn
segment.copyFrom(value.asInstanceOf[MemorySegment])
new CUnion(Mem17(segment))

override def addressReturn(value: Object): Mem = Mem17(
MemorySegment
.globalNativeSegment()
Expand Down
3 changes: 3 additions & 0 deletions j19/src/fr/hammons/slinc/Mem19.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ object Mem19:
class Mem19(private[slinc] val mem: MemorySegment) extends Mem:
import Mem19.*

override def copyFrom(other: Mem): Unit = other match
case oMem: Mem19 => mem.copyFrom(oMem.mem).nn

override def writeByteArray(v: Array[Byte], offset: Bytes): Unit =
mem.asSlice(offset.toLong).nn.copyFrom(MemorySegment.ofArray(v))

Expand Down
13 changes: 6 additions & 7 deletions j19/src/fr/hammons/slinc/Scope19.scala
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
package fr.hammons.slinc

import java.lang.foreign.Linker
import java.lang.foreign.MemorySession
import java.lang.foreign.SegmentAllocator
import java.lang.foreign.MemorySegment
import java.lang.foreign.MemoryAddress

class Scope19(linker: Linker) extends ScopeI.PlatformSpecific:
object Scope19 extends ScopeI.PlatformSpecific:

val baseNull: Ptr[Any] = Ptr(
Mem19(
Expand All @@ -20,7 +19,7 @@ class Scope19(linker: Linker) extends ScopeI.PlatformSpecific:
given Allocator = Allocator19(
TempAllocator19.localAllocator,
MemorySession.global().nn,
linker
Slinc19.linker
)
def apply[A](fn: Allocator ?=> A): A =
val res = fn
Expand All @@ -31,14 +30,14 @@ class Scope19(linker: Linker) extends ScopeI.PlatformSpecific:
def apply[A](fn: Allocator ?=> A): A =
val rs = MemorySession.global().nn
given Allocator =
Allocator19(SegmentAllocator.newNativeArena(rs).nn, rs, linker)
Allocator19(SegmentAllocator.newNativeArena(rs).nn, rs, Slinc19.linker)
fn

override def createConfinedScope: ConfinedScope = new ConfinedScope:
def apply[A](fn: Allocator ?=> A): A =
val rs = MemorySession.openConfined().nn
given Allocator =
Allocator19(SegmentAllocator.newNativeArena(rs).nn, rs, linker)
Allocator19(SegmentAllocator.newNativeArena(rs).nn, rs, Slinc19.linker)
val res = fn
rs.close()
res
Expand All @@ -47,7 +46,7 @@ class Scope19(linker: Linker) extends ScopeI.PlatformSpecific:
def apply[A](fn: Allocator ?=> A): A =
val rs = MemorySession.openShared().nn
given Allocator =
Allocator19(SegmentAllocator.newNativeArena(rs).nn, rs, linker)
Allocator19(SegmentAllocator.newNativeArena(rs).nn, rs, Slinc19.linker)
val res = fn
rs.close()
res
Expand All @@ -57,5 +56,5 @@ class Scope19(linker: Linker) extends ScopeI.PlatformSpecific:
val rs = MemorySession.openImplicit().nn

given Allocator =
Allocator19(SegmentAllocator.newNativeArena(rs).nn, rs, linker)
Allocator19(SegmentAllocator.newNativeArena(rs).nn, rs, Slinc19.linker)
fn
Loading