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
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import java.util.concurrent.TimeUnit
import scala.util.Random
import scala.annotation.nowarn
import fr.hammons.slinc.modules.LibModule
import org.openjdk.jmh.infra.Blackhole

case class div_t(quot: Int, rem: Int)

Expand Down Expand Up @@ -61,6 +62,17 @@ trait BindingsBenchmarkShape(val s: Slinc):
)
}

val path = Random.nextBoolean()

@Benchmark
def ifmark(blackhole: Blackhole) =
if path then blackhole.consume(1 + 2)
else blackhole.consume(2 + 3)

@Benchmark
def noifmark(blackhole: Blackhole) =
blackhole.consume(1 + 2)

@Benchmark
def abs =
Cstd.abs(6)
Expand Down
256 changes: 109 additions & 147 deletions core/src/fr/hammons/slinc/CFunctionBindingGenerator.scala
Original file line number Diff line number Diff line change
@@ -1,60 +1,113 @@
package fr.hammons.slinc

import java.lang.invoke.MethodHandle
import scala.quoted.*
import scala.annotation.nowarn
import fr.hammons.slinc.CFunctionRuntimeInformation.{
InputTransition,
ReturnTransition
}
import fr.hammons.slinc.CFunctionBindingGenerator.VariadicTransition

type InputTransition = (Allocator, Any) => Any
type OutputTransition = (Object | Null) => AnyRef
type VariadicInputTransition = (TypeDescriptor, Allocator, Any) => Any
trait CFunctionBindingGenerator:
def generate(
methodHandler: MethodHandler,
inputTransitions: IArray[InputTransition],
outputTransition: OutputTransition,
scope: Scope
): AnyRef

def generateVariadic(
methodHandler: MethodHandler,
inputTransitions: IArray[InputTransition],
variadicTransition: VariadicInputTransition,
outputTransition: OutputTransition,
transitionSet: CFunctionRuntimeInformation,
variadicTransition: VariadicTransition,
scope: Scope
): AnyRef

object CFunctionBindingGenerator:
private def getVariadicExprs(
s: Seq[Variadic],
variadicTransition: VariadicInputTransition,
allocator: Allocator
): Seq[Any] =
s.map: vararg =>
vararg.use[DescriptorOf](dc ?=>
d => variadicTransition(dc.descriptor, allocator, d)
)
type VariadicTransition = (Allocator, Seq[Variadic]) => Seq[Any]

private enum LambdaInputs:
case Standard(args: List[Expr[Any]])
case VariadicInputs(args: List[Expr[Any]], varArgs: Expr[Seq[Variadic]])

private object LambdaInputs:
def choose(args: List[Expr[Any]], isVariadic: Boolean)(
variadicInput: => Expr[Seq[Variadic]]
) = if isVariadic then
LambdaInputs.VariadicInputs(args, varArgs = variadicInput)
else LambdaInputs.Standard(args)

@nowarn("msg=unused implicit parameter")
private def invokeVariadicArguments(
mhGen: Expr[Seq[Variadic] => MethodHandle],
inputs: Expr[Seq[Any]],
varArgs: Expr[Seq[Variadic]],
variadicTransition: Expr[VariadicInputTransition],
alloc: Expr[Allocator]
private def invokation(
variadicTransition: Expr[VariadicTransition],
mh: Expr[MethodHandler]
)(using Quotes) =
'{
MethodHandleFacade.callVariadic(
$mhGen($varArgs),
$inputs ++ getVariadicExprs($varArgs, $variadicTransition, $alloc)*
)
}
(alloc: Expr[Allocator], inputs: LambdaInputs) =>
inputs match
case LambdaInputs.Standard(args) =>
MethodHandleTools.invokeArguments(
'{ $mh.nonVariadic },
args
)
case LambdaInputs.VariadicInputs(args, varArgs) =>
'{
MethodHandleFacade.callVariadic(
$mh.variadic($varArgs),
${ Expr.ofList(args) } ++ $variadicTransition($alloc, $varArgs)*
)
}

inline def apply[L](
name: String
): CFunctionBindingGenerator = ${
applyImpl[L]('name)
}

@nowarn("msg=unused implicit parameter")
private def lambda(
argNumbers: Int,
scope: Expr[Scope],
inputTransitions: Expr[IArray[InputTransition]],
outputTransition: Expr[ReturnTransition],
allocatingReturn: Boolean,
varArg: Boolean
)(
invocationExpr: Quotes ?=> (
Expr[Allocator],
LambdaInputs
) => Expr[Object | Null]
)(using Quotes) =
import quotes.reflect.*

val names = List.fill(argNumbers)("a")
val argTypes =
if varArg then
List.fill(argNumbers - 1)(TypeRepr.of[Object]) :+ TypeRepr
.of[Seq[Variadic]]
else List.fill(argNumbers)(TypeRepr.of[Object])

val methodType =
MethodType(names)(_ => argTypes, _ => TypeRepr.of[Object])

Lambda(
Symbol.spliceOwner,
methodType,
(sym, inputs) =>
def inputExprs(alloc: Expr[Allocator])(using q: Quotes) =
val prefix = if allocatingReturn then List(alloc.asTerm) else Nil
val toTransform = if varArg then inputs.init else inputs
LambdaInputs.choose(
prefix
.concat(toTransform)
.map(_.asExpr)
.zipWithIndex
.map: (exp, i) =>
'{ $inputTransitions(${ Expr(i) })($alloc, $exp) },
varArg
)(inputs.last.asExprOf[Seq[Variadic]])

'{
$scope { alloc ?=>
$outputTransition(
${ invocationExpr('alloc, inputExprs('alloc)) }
)
}
}.asTerm.changeOwner(sym)
).asExprOf[AnyRef]

@nowarn("msg=unused implicit parameter")
private def applyImpl[L](name: Expr[String])(using
Quotes,
Expand All @@ -69,124 +122,33 @@ object CFunctionBindingGenerator:
.declaredMethod(name.valueOrAbort)
.head

val argNumbers = methodSymbol.paramSymss.map(_.size).sum
val names = List.fill(argNumbers)("a")

def lambda(
methodHandle: Expr[MethodHandle],
inputTransitions: Expr[IArray[InputTransition]],
outputTransition: Expr[OutputTransition],
scope: Expr[Scope]
)(using Quotes): Expr[AnyRef] =
import quotes.reflect.*

val argsTypes = List.fill(argNumbers)(TypeRepr.of[Object])

val methodType =
MethodType(names)(_ => argsTypes, _ => TypeRepr.of[Object])
Lambda(
Symbol.spliceOwner,
methodType,
(sym, inputs) =>
def inputExprs(using q: Quotes) = (alloc: Expr[Allocator]) =>
inputs
.map(i => i.asExpr)
.zipWithIndex
.map((exp, i) =>
'{ $inputTransitions(${ Expr(i) })($alloc, $exp) }
)
'{
$scope { alloc ?=>
$outputTransition(
${
MethodHandleTools.invokeArguments(
methodHandle,
inputExprs('alloc)
)
}
)
}
}.asTerm.changeOwner(sym)
).asExprOf[AnyRef]

def lambdaVariadic(
methodHandleGen: Expr[Seq[Variadic] => MethodHandle],
inputTransitions: Expr[IArray[InputTransition]],
outputTransition: Expr[OutputTransition],
variadicTransition: Expr[
VariadicInputTransition
],
scope: Expr[Scope]
)(using Quotes): Expr[AnyRef] =
import quotes.reflect.*

val argTypes = List.fill(argNumbers - 1)(TypeRepr.of[Object]) :+ TypeRepr
.of[Seq[Variadic]]

val methodType =
MethodType(names)(_ => argTypes, _ => TypeRepr.of[Object])

Lambda(
Symbol.spliceOwner,
methodType,
(sym, inputs) =>
def inputExprs(using q: Quotes) = (alloc: Expr[Allocator]) =>
Expr.ofList(
inputs.init
.map(i => i.asExpr)
.zipWithIndex
.map((exp, i) =>
'{ $inputTransitions(${ Expr(i) })($alloc, $exp) }
)
)

'{
$scope { alloc ?=>
$outputTransition(
${
invokeVariadicArguments(
methodHandleGen,
inputExprs('alloc),
inputs.last.asExprOf[Seq[Variadic]],
variadicTransition,
'{ alloc }
)
}
)
}
}.asTerm.changeOwner(sym)
).asExprOf[AnyRef]

'{
new CFunctionBindingGenerator:
def generate(
methodHandler: MethodHandler,
inputTransitions: IArray[InputTransition],
outputTransition: OutputTransition,
functionInformation: CFunctionRuntimeInformation,
variadicTransition: VariadicTransition,
scope: Scope
): AnyRef =
${
lambda(
'{ methodHandler.nonVariadic },
'inputTransitions,
'outputTransition,
'scope
)
def lambdaGen(allocatingReturn: Boolean, variadic: Boolean) =
lambda(
methodSymbol.paramSymss.map(_.size).sum,
'scope,
'{ functionInformation.inputTransitions },
'{ functionInformation.returnTransition },
allocatingReturn,
variadic
)(invokation('variadicTransition, 'methodHandler))

'{
if functionInformation.isVariadic && functionInformation.returnAllocates
then ${ lambdaGen(allocatingReturn = true, variadic = true) }
else if functionInformation.isVariadic then
${ lambdaGen(allocatingReturn = false, variadic = true) }
else if functionInformation.returnAllocates then
${ lambdaGen(allocatingReturn = true, variadic = false) }
else ${ lambdaGen(allocatingReturn = false, variadic = false) }
}
}

def generateVariadic(
methodHandler: MethodHandler,
inputTransitions: IArray[InputTransition],
variadicTransitions: VariadicInputTransition,
outputTransition: OutputTransition,
scope: Scope
): AnyRef = ${
lambdaVariadic(
'{ methodHandler.variadic },
'inputTransitions,
'outputTransition,
'variadicTransitions,
'scope
)
}
}
56 changes: 56 additions & 0 deletions core/src/fr/hammons/slinc/CFunctionInformation.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package fr.hammons.slinc

import fr.hammons.slinc.modules.TransitionModule
import fr.hammons.slinc.CFunctionRuntimeInformation.InputTransition
import fr.hammons.slinc.CFunctionRuntimeInformation.ReturnTransition

final case class CFunctionRuntimeInformation(
name: String,
inputDescriptors: Seq[TypeDescriptor],
inputTransitions: IArray[InputTransition],
returnDescriptor: Option[TypeDescriptor],
returnTransition: ReturnTransition,
isVariadic: Boolean,
returnAllocates: Boolean
)

object CFunctionRuntimeInformation:
def apply(functionDescriptor: CFunctionDescriptor)(using
transitionModule: TransitionModule
) =
val allocatingReturn = functionDescriptor.returnDescriptor
.map:
case ad: AliasDescriptor[?] => ad.real
case a => a
.exists:
case _: StructDescriptor => true
case _ => false

val allocationTransition: Seq[InputTransition] =
if allocatingReturn then
Seq((allocator, _) => transitionModule.methodArgument(allocator))
else Seq.empty

val inputTransitions: Seq[InputTransition] =
functionDescriptor.inputDescriptors.map: typeDescriptor =>
(allocator, input) =>
transitionModule.methodArgument(typeDescriptor, input, allocator)
val outputTransition: ReturnTransition =
functionDescriptor.returnDescriptor match
case None => _ => ().asInstanceOf[Object]
case Some(descriptor) =>
returnValue =>
transitionModule.methodReturn[Object](descriptor, returnValue.nn)

new CFunctionRuntimeInformation(
functionDescriptor.name,
functionDescriptor.inputDescriptors,
IArray.from(allocationTransition ++ inputTransitions),
functionDescriptor.returnDescriptor,
outputTransition,
functionDescriptor.isVariadic,
allocatingReturn
)

type InputTransition = (Allocator, Any) => Any
type ReturnTransition = (Object | Null) => AnyRef
12 changes: 12 additions & 0 deletions core/test/src/fr/hammons/slinc/LibSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,18 @@ trait LibSpec(val slinc: Slinc) extends ScalaCheckSuite {
}
}

test("allocating returns") {
import slinc.given
case class div_t(quot: Int, rem: Int) derives Struct

trait AllocatingLib derives Lib:
def div(i: Int, r: Int): div_t

val allocatingLib = Lib.instance[AllocatingLib]

assertEquals(allocatingLib.div(5, 2), div_t(2, 1))
}

test("platform dependent types") {
val maybeError = compileErrors("""
trait PlatformLib derives Lib:
Expand Down
Loading