@@ -385,6 +385,11 @@ object Translator {
385
385
386
386
val mangledVars = mutable.Map .empty[VarDef , String ]
387
387
388
+ val lastUsagesPost = mutable.Map .empty[Expr , Set [VarDef ]]
389
+ val lastUsagesPre = mutable.Map .empty[Expr , Set [VarDef ]]
390
+
391
+ val unusedVars = mutable.Set .empty[VarDef ]
392
+
388
393
private var resVarCounter = 0
389
394
390
395
def translateType (typ : TypeDef ): String = {
@@ -437,18 +442,33 @@ object Translator {
437
442
438
443
/** Returns code for the function's declaration and implementation */
439
444
def fnToC (fn : FnDef )(using bindings : Bindings ): (String , String ) = {
445
+ given Bindings = bindings.enterFn(fn)
446
+ lastUsagesPre.clear()
447
+ lastUsagesPost.clear()
448
+ unusedVars.clear()
449
+
450
+ val paramDefs = fn.params
451
+ .map(param => VarDef .Param (param, bindings.getType(param.typ))).toSet
452
+ val unusedParams = findLastUsages(fn.body, paramDefs).map(_.name)
453
+ assert(
454
+ unusedParams.subsetOf(paramDefs.map(_.name)),
455
+ s " Function: ${fn.name.value}, unused vars: $unusedParams"
456
+ )
457
+
458
+ // println(lastUsagesPre.view.mapValues(_.map(_.name)).toMap)
459
+ println(fn.name.value)
460
+ println(lastUsagesPost.view.mapValues(_.map(_.name)).toMap)
461
+
440
462
// param names don't need to be mangled because they're the first occurrence
441
463
val params = fn.params
442
464
.map(param => s " ${typeRefToC(param.typ.name)} ${param.name.value}" )
443
465
.mkString(" , " )
444
- val paramsSetup = fn.params
445
- .map(param => incrRc(param.name.value, bindings.getType(param.typ)))
446
- .mkString(" \n " )
447
- val paramsTeardown = fn.params
448
- .map(param => decrRc(param.name.value, bindings.getType(param.typ)))
449
- .mkString(" \n " )
450
- val (bodySetup, body, bodyTeardown) =
451
- exprToC(fn.body)(using bindings.enterFn(fn))
466
+ val paramsSetup = fn.params.map { param =>
467
+ if (unusedParams(param.name.value)) " "
468
+ else incrRc(param.name.value, bindings.getType(param.typ))
469
+ }.mkString(" \n " )
470
+
471
+ val (bodySetup, body, bodyTeardown) = exprToC(fn.body)
452
472
val resVar = newVar(" ret" )
453
473
val typeToC = typeRefToC(fn.returnType.name)
454
474
@@ -474,13 +494,99 @@ object Translator {
474
494
| ${indent(1 )(bodySetup)}
475
495
| $typeToC $resVar = $body;
476
496
| ${indent(1 )(bodyTeardown)}
477
- | ${indent(1 )(paramsTeardown)}
478
497
| $triggerGC
479
498
| return $resVar;
480
499
|} """ .stripMargin
481
500
(decl, impl)
482
501
}
483
502
503
+ /** Go through an expression backwards, finding the last usage of every
504
+ * variable. These last usages are added to `lastUsagesPre` and
505
+ * `lastUsagesPost`.
506
+ *
507
+ * @param vars
508
+ * Variables that so far have been unused ("so far" while moving
509
+ * backwards, not forwards)
510
+ * @return
511
+ * The remaining variables that weren't used in this expression
512
+ */
513
+ private def findLastUsages (expr : Expr , vars : Set [VarDef ])(using
514
+ bindings : Bindings
515
+ ): Set [VarDef ] = {
516
+ expr match {
517
+ case IntLiteral (_, _) | StringLiteral (_, _) => vars
518
+ case VarRef (name, span) =>
519
+ val varDef = bindings.vars(name)
520
+ if (vars.contains(varDef)) {
521
+ lastUsagesPost(expr) = Set (varDef)
522
+ vars - varDef
523
+ } else { vars }
524
+ case SetFieldExpr (lhsObj, lhsField, value, span) =>
525
+ val varDef = bindings.vars(lhsObj.value)
526
+ if (vars.contains(varDef)) {
527
+ lastUsagesPost(expr) = Set (varDef)
528
+ findLastUsages(value, vars - varDef)
529
+ } else { findLastUsages(value, vars) }
530
+ case FieldAccess (obj, field, typ) => findLastUsages(obj, vars)
531
+ case BinExpr (lhs, op, rhs, typ) =>
532
+ val remVars = findLastUsages(rhs, vars)
533
+ findLastUsages(lhs, remVars)
534
+ case FnCall (fnName, args, resolvedFn, typ, span) => args
535
+ .foldRight(vars)(findLastUsages(_, _))
536
+ case CtorCall (ctorName, values, span) => values
537
+ .foldRight(vars) { case ((_, expr), vars) =>
538
+ findLastUsages(expr, vars)
539
+ }
540
+ case expr @ LetExpr (name, value, body, span) =>
541
+ val varType = typer.types(value)
542
+ val varDef = VarDef .Let (expr, varType)
543
+ val newBindings = bindings.withVar(name.value, varDef)
544
+ var bodyRemVars =
545
+ findLastUsages(body, vars + varDef)(using newBindings)
546
+ if (bodyRemVars.contains(varDef)) {
547
+ unusedVars += varDef
548
+ bodyRemVars -= varDef
549
+ }
550
+ findLastUsages(value, bodyRemVars)
551
+ case IfExpr (cond, thenBody, elseBody, span) =>
552
+ val thenRemVars = findLastUsages(thenBody, vars)
553
+ val elseRemVars = findLastUsages(elseBody, vars)
554
+ lastUsagesPre(thenBody) = thenRemVars -- elseRemVars
555
+ lastUsagesPre(elseBody) = elseRemVars -- thenRemVars
556
+ findLastUsages(cond, thenRemVars & elseRemVars)
557
+ case matchExpr @ MatchExpr (obj, arms, armsSpan) =>
558
+ val objType = typer.types(obj).asInstanceOf [TypeDef ]
559
+
560
+ val armVars = arms.map {
561
+ case arm @ MatchArm (
562
+ pat @ MatchPattern (ctorName, patBindings),
563
+ body,
564
+ _
565
+ ) =>
566
+ val newBindings = bindings.enterPattern(matchExpr, pat, objType)
567
+ val newVars = patBindings.map { case (_, varName) =>
568
+ newBindings.vars(varName.value)
569
+ }.toSet
570
+ val remVars =
571
+ findLastUsages(body, vars | newVars)(using newBindings)
572
+
573
+ unusedVars ++= newVars -- remVars
574
+
575
+ arm -> remVars
576
+ }.toMap
577
+
578
+ for (arm <- arms) {
579
+ val remVars = armVars(arm)
580
+ val otherArms = arms.filter(_ != arm)
581
+ val otherVars = otherArms.map(armVars).reduce(_ | _)
582
+ lastUsagesPre(arm.body) = remVars -- otherVars
583
+ }
584
+
585
+ val commonUnused = armVars.values.reduce(_ | _)
586
+ findLastUsages(obj, commonUnused)
587
+ }
588
+ }
589
+
484
590
/** @return
485
591
* A tuple containing the C code for setup, the C code for the actual
486
592
* expression, and the C code for teardown (decrementing reference
@@ -491,7 +597,7 @@ object Translator {
491
597
private def exprToC (
492
598
expr : Expr
493
599
)(using bindings : Bindings ): (String , String , String ) = {
494
- expr match {
600
+ val (setup, toC, teardown) = expr match {
495
601
case IntLiteral (value, _) => (" " , value.toString, " " )
496
602
case StringLiteral (value, _) =>
497
603
(" " , s " \" ${value.replace(" \" " , " \\\" " )}\" " , " " )
@@ -543,8 +649,8 @@ object Translator {
543
649
val (valueSetup, valueToC, valueTeardown) = exprToC(value)
544
650
val typ = typer.types(value)
545
651
val shouldMangle = bindings.vars.contains(name.value)
546
- val newBindings = bindings
547
- .withVar(name.value, VarDef . Let (letExpr, typ) )
652
+ val varDef = VarDef . Let (letExpr, typ)
653
+ val newBindings = bindings .withVar(name.value, varDef )
548
654
val mangledName =
549
655
if (shouldMangle) newMangledVar(name.value) else name.value
550
656
if (shouldMangle) {
@@ -554,8 +660,10 @@ object Translator {
554
660
val (bodySetup, bodyToC, bodyTeardown) =
555
661
exprToC(body)(using newBindings)
556
662
663
+ val decr =
664
+ if (unusedVars.contains(varDef)) decrRc(mangledName, typ) else " "
557
665
(
558
- s " $valueSetup\n $letSetup\n $valueTeardown\n $bodySetup" ,
666
+ s " $valueSetup\n $letSetup\n $valueTeardown\n $decr \n $ bodySetup" ,
559
667
bodyToC,
560
668
s " $bodyTeardown\n $letTeardown"
561
669
)
@@ -615,6 +723,7 @@ object Translator {
615
723
616
724
val armsToC = arms.map {
617
725
case MatchArm (pat @ MatchPattern (ctorName, patBindings), body, _) =>
726
+ // TODO decrement unused variables immediately
618
727
val variant = objType.cases.find(_.name.value == ctorName.value)
619
728
.get
620
729
val oldBindings = bindings
@@ -665,6 +774,27 @@ object Translator {
665
774
666
775
(setup, resVar, teardown)
667
776
}
777
+
778
+ val setupDecrs = lastUsagesPre.getOrElse(expr, Set .empty).map { varDef =>
779
+ decrRc(mangledVars.getOrElse(varDef, varDef.name), varDef.typ)
780
+ }.mkString(" " , " \n " , " \n " )
781
+ val teardownDecrs = lastUsagesPost.getOrElse(expr, Set .empty)
782
+ .map { varDef =>
783
+ decrRc(mangledVars.getOrElse(varDef, varDef.name), varDef.typ)
784
+ }.mkString(" \n " , " \n " , " " )
785
+
786
+ if (lastUsagesPre.getOrElse(expr, Set .empty).nonEmpty) {
787
+ println(
788
+ s " last usage pre: ${lastUsagesPre(expr).map(_.name)}, expr: $expr"
789
+ )
790
+ }
791
+ if (lastUsagesPost.getOrElse(expr, Set .empty).nonEmpty) {
792
+ println(
793
+ s " last usage post: ${lastUsagesPost(expr).map(_.name)}, expr: $expr"
794
+ )
795
+ }
796
+
797
+ (setupDecrs + setup, toC, teardown + teardownDecrs)
668
798
}
669
799
670
800
private def newMangledVar (baseName : String ): String = {
@@ -678,8 +808,7 @@ object Translator {
678
808
): (String , String ) = {
679
809
val setup = s """ | ${typeRefToC(typ.name)} ${varName} = $value;
680
810
| ${incrRc(varName, typ)}""" .stripMargin
681
- val teardown = decrRc(varName, typ)
682
- (setup, teardown)
811
+ (setup, " " )
683
812
}
684
813
685
814
/** Create a variable name that hopefully won't conflict with any other
0 commit comments