@@ -14,12 +14,15 @@ import io.computenode.cyfra.foton.GFunction
1414
1515enum UnaryFn :
1616 case Neg , Abs , Exp , Sqrt , Sin , Cos , Tan , Asin , Acos , Atan , Log
17+ end UnaryFn
1718
1819enum ScalarFn :
1920 case Add , Sub , Mul , Div , Pow
21+ end ScalarFn
2022
2123enum BinaryFn :
2224 case Add , Sub , Mul , Div
25+ end BinaryFn
2326
2427// ── Expression tree ────────────────────────────────────────
2528
@@ -60,8 +63,7 @@ sealed trait GExpr:
6063 // ── Materialize: compile AST → GPU dispatch → result ───
6164 def run (using CyfraRuntime ): Array [Float ] = GExprCompiler .run(this )
6265
63- /** Phase 1: Walk the AST and compute the dimension at each node.
64- * All ops are elementwise, so propagation is trivial.
66+ /** Phase 1: Walk the AST and compute the dimension at each node. All ops are elementwise, so propagation is trivial.
6567 * Catches dimension mismatches eagerly — no GPU work, no data movement.
6668 */
6769 def validateDimensions : Int = GExprCompiler .shapeAnalysis(this )
@@ -83,23 +85,23 @@ private[gpu] object GExprCompiler:
8385 private def transferLog (msg : => String ): Unit =
8486 if logGExprTransfers then System .err.println(s " [GExpr] $msg" )
8587
86- /** Phase 1: Walk the AST and compute the dimension at each node.
87- * All ops are elementwise, so propagation is trivial.
88+ /** Phase 1: Walk the AST and compute the dimension at each node. All ops are elementwise, so propagation is trivial.
8889 * Catches dimension mismatches eagerly — no GPU work, no data movement.
8990 */
9091 def shapeAnalysis (expr : GExpr ): Int =
9192 expr match
92- case GLeaf (data) => data.length
93- case GUnaryOp (input, _) => shapeAnalysis(input)
94- case GScalarOp (input, _, _) => shapeAnalysis(input)
95- case GClampOp (input, _, _) => shapeAnalysis(input)
96- case GBinaryOp (left, right, _) =>
93+ case GLeaf (data) => data.length
94+ case GUnaryOp (input, _) => shapeAnalysis(input)
95+ case GScalarOp (input, _, _) => shapeAnalysis(input)
96+ case GClampOp (input, _, _) => shapeAnalysis(input)
97+ case GBinaryOp (left, right, _) =>
9798 val ld = shapeAnalysis(left)
9899 val rd = shapeAnalysis(right)
99100 if ld != rd then
100101 throw new IllegalArgumentException (
101102 s " GExpr dimension mismatch: left has $ld elements, right has $rd"
102103 )
104+ end if
103105 ld
104106
105107 def run (expr : GExpr )(using CyfraRuntime ): Array [Float ] =
@@ -109,13 +111,15 @@ private[gpu] object GExprCompiler:
109111 leaves match
110112 case single :: Nil => runSingleInput(expr, single)
111113 case _ => runFused(expr, leaves)
114+ end match
115+ end run
112116
113117 private def collectLeaves (expr : GExpr ): List [GLeaf ] =
114118 expr match
115- case l : GLeaf => List (l)
116- case GUnaryOp (input, _) => collectLeaves(input)
117- case GScalarOp (input, _, _) => collectLeaves(input)
118- case GClampOp (input, _, _) => collectLeaves(input)
119+ case l : GLeaf => List (l)
120+ case GUnaryOp (input, _) => collectLeaves(input)
121+ case GScalarOp (input, _, _) => collectLeaves(input)
122+ case GClampOp (input, _, _) => collectLeaves(input)
119123 case GBinaryOp (left, right, _) =>
120124 val ll = collectLeaves(left)
121125 val rl = collectLeaves(right)
@@ -131,6 +135,7 @@ private[gpu] object GExprCompiler:
131135 val result : Array [Float ] = gf.run(leaf.data)
132136 transferLog(s " GPU→CPU download ${result.length} floats (runSingleInput) " )
133137 result
138+ end runSingleInput
134139
135140 private def compileSingle (expr : GExpr , leaf : GLeaf , x : Float32 )(using Source ): Float32 =
136141 expr match
@@ -200,10 +205,11 @@ private[gpu] object GExprCompiler:
200205 case class FusedLayout (inputs : GBuffer [Float32 ], output : GBuffer [Float32 ]) derives Layout
201206
202207 val program = GProgram .static[Int , FusedLayout ](
203- layout = _ => FusedLayout (
204- inputs = GBuffer [Float32 ](packedSize),
205- output = GBuffer [Float32 ](n)
206- ),
208+ layout = _ =>
209+ FusedLayout (
210+ inputs = GBuffer [Float32 ](packedSize),
211+ output = GBuffer [Float32 ](n)
212+ ),
207213 dispatchSize = identity
208214 ): layout =>
209215 val idx = GIO .invocationId
@@ -224,10 +230,10 @@ private[gpu] object GExprCompiler:
224230 )
225231 transferLog(s " GPU→CPU download $n floats (runFused) " )
226232 result
233+ end runFused
227234
228- /** Walk the AST emitting Cyfra DSL code that reads each leaf from
229- * the packed input buffer at offset `leafIndex * n + invocationId`.
230- * All operations are fused into a single GPU kernel.
235+ /** Walk the AST emitting Cyfra DSL code that reads each leaf from the packed input buffer at offset
236+ * `leafIndex * n + invocationId`. All operations are fused into a single GPU kernel.
231237 */
232238 private def compileFused (
233239 expr : GExpr ,
@@ -241,6 +247,7 @@ private[gpu] object GExprCompiler:
241247 val i = leafIndex(leaf.data.asInstanceOf [AnyRef ])
242248 if i == 0 then inputs.read(idx)
243249 else inputs.read(idx + i * n)
250+ end if
244251 case GUnaryOp (input, fn) =>
245252 applyUnary(fn, compileFused(input, leafIndex, inputs, idx, n))
246253 case GScalarOp (input, s, fn) =>
@@ -253,9 +260,9 @@ private[gpu] object GExprCompiler:
253260 applyBinary(fn, l, r)
254261
255262 private def exprLabel (e : GExpr ): String = e match
256- case _ : GLeaf => " Leaf"
257- case GUnaryOp (_, f) => s " Unary( $f) "
258- case GScalarOp (_, s, f) => s " Scalar( $f, $s) "
263+ case _ : GLeaf => " Leaf"
264+ case GUnaryOp (_, f) => s " Unary( $f) "
265+ case GScalarOp (_, s, f) => s " Scalar( $f, $s) "
259266 case GClampOp (_, lo, hi) => s " Clamp( $lo, $hi) "
260267 case GBinaryOp (_, _, f) => s " Binary( $f) "
261268
@@ -270,5 +277,5 @@ end GExprCompiler
270277
271278// ── Entry point: Array[Float] → GExpr ──────────────────────
272279
273- extension (arr : Array [Float ])
274- inline def gpu : GExpr = GLeaf (arr)
280+ extension (arr : Array [Float ]) inline def gpu : GExpr = GLeaf (arr)
281+ end extension
0 commit comments