Skip to content

Commit c304ff9

Browse files
author
Oron Port
committed
feat: Add Exact1 support for DFIf and DFMatch and Exact0 for DFIf transformations
This commit extends the plugin's transformation capabilities by adding support for Exact0 and Exact1 types in DFIf and DFMatch. Key changes include: - Introducing fromBranchesExact0 and fromBranchesExact1 methods in DFIf - Adding fromCasesExact method in DFMatch - Updating CustomControlPhase to handle Exact type transformations - Extending test cases to demonstrate Exact type support in conditionals
1 parent 56d36c2 commit c304ff9

File tree

6 files changed

+196
-37
lines changed

6 files changed

+196
-37
lines changed

core/src/main/scala/dfhdl/core/DFIf.scala

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,57 @@ object DFIf:
3131
dfc.exitOwner()
3232
(dfType, block)
3333
end singleBranch
34+
35+
class Exact0TCDummy[From] extends Exact0.TC[From, DFC]:
36+
type Out = DFValAny
37+
def conv(from: From)(using DFC): Out = ???
38+
end Exact0TCDummy
39+
object Exact0TCDummy extends Exact0TCDummy[DFValAny]
40+
def fromBranchesExact0[R](
41+
branches: List[(DFValOf[DFBoolOrBit], () => Exact0[DFC, ?])],
42+
elseOption: Option[() => Exact0[DFC, ?]]
43+
)(using DFC): R =
44+
val ifFunc: () => DFValAny = () =>
45+
fromBranches(
46+
branches.map { case (cond, run) =>
47+
(
48+
cond,
49+
() =>
50+
val e = run.apply()
51+
e.apply().asInstanceOf[DFValAny]
52+
)
53+
},
54+
elseOption.map { run => () =>
55+
val e = run.apply()
56+
e.apply().asInstanceOf[DFValAny]
57+
}
58+
)
59+
val exact = new Exact0[DFC, Exact0TCDummy]:
60+
type ExactFrom = DFValAny
61+
val exactFrom: ExactFrom = ifFunc()
62+
type ExactTC = Exact0TCDummy.type
63+
val tc: ExactTC = Exact0TCDummy
64+
def apply()(using ctx: DFC): tc.Out = exactFrom.asInstanceOf[tc.Out]
65+
exact.asInstanceOf[R]
66+
end fromBranchesExact0
67+
68+
def fromBranchesExact1[R](
69+
branches: List[(DFValOf[DFBoolOrBit], () => DFVal.TC.Exact[DFTypeAny])],
70+
elseOption: Option[() => DFVal.TC.Exact[DFTypeAny]]
71+
)(using DFC): R =
72+
val ifFunc: DFTypeAny => DFValOf[DFTypeAny] = dfType =>
73+
fromBranches(
74+
branches.map { case (cond, run) => (cond, () => run.apply().apply(dfType)) },
75+
elseOption.map { run => () => run.apply().apply(dfType) }
76+
)
77+
val exact = new DFVal.TC.Exact[DFTypeAny]:
78+
type ExactFrom = DFValOf[DFTypeAny]
79+
type ExactTC = DFVal.TCDummy.type
80+
val tc: ExactTC = DFVal.TCDummy
81+
def apply(dfType: DFTypeAny)(using ctx: DFC): tc.Out = ifFunc(dfType).asInstanceOf[tc.Out]
82+
exact.asInstanceOf[R]
83+
end fromBranchesExact1
84+
3485
def fromBranches[R](
3586
branches: List[(DFValOf[DFBoolOrBit], () => R)],
3687
elseOption: Option[() => R]

core/src/main/scala/dfhdl/core/DFMatch.scala

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,27 @@ object DFMatch:
2727
(dfType, block)
2828
end singleCase
2929

30+
def fromCasesExact[R](
31+
selector: DFValAny,
32+
cases: List[(Pattern, Option[DFValOf[DFBool]], () => DFVal.TC.Exact[DFTypeAny])],
33+
forceAnonymous: Boolean
34+
)(using DFC): R =
35+
val ifFunc: DFTypeAny => DFValOf[DFTypeAny] = dfType =>
36+
fromCases(
37+
selector,
38+
cases.map { case (pattern, guardOption, run) =>
39+
(pattern, guardOption, () => run.apply().apply(dfType))
40+
},
41+
forceAnonymous
42+
)
43+
val exact = new DFVal.TC.Exact[DFTypeAny]:
44+
type ExactFrom = DFValOf[DFTypeAny]
45+
type ExactTC = DFVal.TCDummy.type
46+
val tc: ExactTC = DFVal.TCDummy
47+
def apply(dfType: DFTypeAny)(using ctx: DFC): tc.Out = ifFunc(dfType).asInstanceOf[tc.Out]
48+
exact.asInstanceOf[R]
49+
end fromCasesExact
50+
3051
def fromCases[R](
3152
selector: DFValAny,
3253
cases: List[(Pattern, Option[DFValOf[DFBool]], () => R)],

core/src/main/scala/dfhdl/core/DFVal.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -883,6 +883,12 @@ object DFVal extends DFValLP:
883883
final def apply(dfType: T, value: R)(using DFC): Out = trydf:
884884
conv(dfType, value)
885885

886+
// This is a dummy instance for DFIf and DFMatch specialized Exact1 extractions
887+
object TCDummy extends TC[DFTypeAny, DFValOf[DFTypeAny]]:
888+
type OutP = NOTCONST
889+
def conv(dfType: DFTypeAny, value: DFValOf[DFTypeAny])(using dfc: DFC): DFValOf[DFTypeAny] =
890+
???
891+
886892
trait TCLPLP:
887893
// Reject OPEN with a dedicated message
888894
transparent inline given fromOPEN[T <: DFTypeAny]: TC[T, OPEN] =

core/src/test/scala/CoreSpec/DFIfSpec.scala

Lines changed: 68 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -81,12 +81,19 @@ class DFIfSpec extends DFSpec:
8181
| if (i) d"8'1"
8282
| else if (!i) x.bits.uint
8383
| else d"8'2"
84-
|""".stripMargin
84+
|val res2 = UInt(8) <> VAR
85+
|res2 := ((
86+
| if (i) d"8'1"
87+
| else if (!i) x.bits.uint
88+
| else d"8'2"
89+
|): UInt[8] <> VAL)""".stripMargin
8590
) {
8691
val res: UInt[8] <> VAL =
8792
if (i) 1
8893
else if (!i) x.bits.uint
8994
else 2
95+
val res2 = UInt(8) <> VAR
96+
res2 := (if (i) 1 else if (!i) x.bits.uint else 2)
9097
}
9198
assertCodeString(
9299
"""|val res: UInt[8] <> VAL =
@@ -96,14 +103,29 @@ class DFIfSpec extends DFSpec:
96103
| else if (!i) x.bits.uint
97104
| else d"8'3"
98105
| end if
99-
|""".stripMargin
106+
|val res2 = UInt(8) <> VAR
107+
|res2 := ((
108+
| if (i)
109+
| if (i) d"8'1"
110+
| else d"8'2"
111+
| else if (!i) x.bits.uint
112+
| else d"8'3"
113+
| end if
114+
|): UInt[8] <> VAL)""".stripMargin
100115
) {
101116
val res: UInt[8] <> VAL =
102117
if (i)
103118
if (i) 1
104119
else 2
105120
else if (!i) x.bits.uint
106121
else 3
122+
val res2 = UInt(8) <> VAR
123+
res2 :=
124+
(if (i)
125+
if (i) 1
126+
else 2
127+
else if (!i) x.bits.uint
128+
else 3)
107129
}
108130
assertCodeString(
109131
"""|val res: UInt[8] <> VAL =
@@ -115,7 +137,17 @@ class DFIfSpec extends DFSpec:
115137
| else if (!i) x.bits.uint
116138
| else d"8'3"
117139
| end if
118-
|""".stripMargin
140+
|val res2 = UInt(8) <> VAR
141+
|res2 := ((
142+
| if (i)
143+
| val internal: UInt[8] <> VAL =
144+
| if (i) d"8'1"
145+
| else d"8'2"
146+
| internal
147+
| else if (!i) x.bits.uint
148+
| else d"8'3"
149+
| end if
150+
|): UInt[8] <> VAL)""".stripMargin
119151
) {
120152
val res: UInt[8] <> VAL =
121153
if (i)
@@ -125,6 +157,16 @@ class DFIfSpec extends DFSpec:
125157
internal
126158
else if (!i) x.bits.uint
127159
else 3
160+
val res2 = UInt(8) <> VAR
161+
res2 := (
162+
if i then
163+
val internal: UInt[8] <> VAL =
164+
if (i) 1
165+
else 2
166+
internal
167+
else if (!i) x.bits.uint
168+
else 3
169+
)
128170
}
129171
assertCodeString(
130172
"""|val res =
@@ -141,21 +183,29 @@ class DFIfSpec extends DFSpec:
141183
((if (i) i else i): Boolean <> VAL) ||
142184
((if (!i) !i else !i): Boolean <> VAL)
143185
}
144-
// TODO: make this work! We have Exact casting to DFVal.
145-
// assertCodeString(
146-
// """|val res =
147-
// | ((
148-
// | if (i) i
149-
// | else i
150-
// | ): Boolean <> VAL) || ((
151-
// | if (!i) !i
152-
// | else !i
153-
// | ): Boolean <> VAL)
154-
// |""".stripMargin
155-
// ) {
156-
// val res: Boolean <> VAL =
157-
// (if (i) i else i) || (if (!i) !i else !i)
158-
// }
186+
assertCodeString(
187+
"""|val res =
188+
| ((
189+
| if (i) i
190+
| else i
191+
| ): Boolean <> VAL) || ((
192+
| if (!i) !i
193+
| else !i
194+
| ): Boolean <> VAL)
195+
|val res2 = Boolean <> VAR
196+
|res2 := ((
197+
| if (i) i
198+
| else i
199+
|): Boolean <> VAL) || ((
200+
| if (!i) !i
201+
| else !i
202+
|): Boolean <> VAL)""".stripMargin
203+
) {
204+
val res: Boolean <> VAL =
205+
(if (i) i else i) || (if (!i) !i else !i)
206+
val res2 = Boolean <> VAR
207+
res2 := (if (i) i else i) || (if (!i) !i else !i)
208+
}
159209
assertCodeString(
160210
"""|val rs0: Boolean <> VAL =
161211
| if (!i) !i

core/src/test/scala/CoreSpec/DFMatchSpec.scala

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,12 +132,22 @@ class DFMatchSpec extends DFSpec:
132132
| case d"8'0" | d"8'1" | d"8'2" | d"8'3" => d"8'77"
133133
| case _ => d"8'22"
134134
| end match
135-
|""".stripMargin
135+
|val res2 = UInt(8) <> VAR
136+
|res2 := ((
137+
| x match
138+
| case d"8'0" | d"8'1" | d"8'2" | d"8'3" => d"8'77"
139+
| case _ => d"8'22"
140+
| end match
141+
|): UInt[8] <> VAL)""".stripMargin
136142
) {
137143
val res: UInt[8] <> VAL =
138144
x match
139145
case 0 | 1 | 2 | 3 => 77
140146
case _ => 22
147+
val res2 = UInt(8) <> VAR
148+
res2 := x match
149+
case 0 | 1 | 2 | 3 => 77
150+
case _ => 22
141151
}
142152
}
143153

plugin/src/main/scala/plugin/CustomControlPhase.scala

Lines changed: 39 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,16 @@ class CustomControlPhase(setting: Setting) extends CommonPhase:
4343
var fromBooleanSym: Symbol = uninitialized
4444
var toFunc1Sym: Symbol = uninitialized
4545
var fromBranchesSym: Symbol = uninitialized
46+
var fromBranchesExact0Sym: Symbol = uninitialized
47+
var fromBranchesExact1Sym: Symbol = uninitialized
4648
var fromCasesSym: Symbol = uninitialized
49+
var fromCasesExactSym: Symbol = uninitialized
4750
var dfValClsRef: TypeRef = uninitialized
4851
var dfEncodingRef: TypeRef = uninitialized
4952
var enumHackedUnapply: Symbol = uninitialized
53+
var exact0Sym: Symbol = uninitialized
54+
var exact1Sym: Symbol = uninitialized
55+
var exactApplySym: Symbol = uninitialized
5056
var dfcStack: List[Tree] = Nil
5157

5258
override def prepareForDefDef(tree: DefDef)(using Context): Context =
@@ -148,27 +154,34 @@ class CustomControlPhase(setting: Setting) extends CommonPhase:
148154
(pairs.reverse, someBlock)
149155
end transformIfRecur
150156

157+
private def isExact0(tpe: Type)(using Context): Boolean =
158+
tpe.dealias match
159+
case OrType(t1, t2) => isExact0(t1) || isExact0(t2)
160+
case tpe if tpe.typeSymbol == exact0Sym => true
161+
case _ => false
162+
163+
private def isExact1(tpe: Type)(using Context): Boolean =
164+
tpe.dealias match
165+
case OrType(t1, t2) => isExact1(t1) || isExact1(t2)
166+
case tpe if tpe.typeSymbol == exact1Sym => true
167+
case _ => false
168+
151169
override def transformIf(tree: If)(using Context): Tree =
152170
if (replaceIfs.contains(tree.srcPos.show))
153171
// debug("=======================")
154172
val dfcTree = dfcStack.head
155-
def buildIf(combinedTpe: Type): Tree =
156-
val (branchesVarArgs, elseOption) =
157-
transformIfRecur(tree, combinedTpe, dfcTree, Nil)
158-
val branches = mkList(branchesVarArgs)
159-
ref(fromBranchesSym)
160-
.appliedToType(combinedTpe)
161-
.appliedTo(branches, elseOption)
162-
.appliedTo(dfcTree)
163-
tree.tpe match
164-
case AppliedType(tycon, List(tpe))
165-
if tycon.dealias.typeSymbol == requiredClass("dfhdl.internals.Exact") =>
166-
val combinedTpe = tpe.widen
167-
val ifTree = buildIf(combinedTpe)
168-
ref(requiredMethod("dfhdl.internals.Exact.apply"))
169-
.appliedToType(combinedTpe)
170-
.appliedTo(ifTree.withType(combinedTpe))
171-
case combinedTpe => buildIf(combinedTpe)
173+
val combinedTpe = tree.tpe.widen
174+
val (branchesVarArgs, elseOption) =
175+
transformIfRecur(tree, combinedTpe, dfcTree, Nil)
176+
val branches = mkList(branchesVarArgs)
177+
val sym =
178+
if (isExact1(combinedTpe)) fromBranchesExact1Sym
179+
else if (isExact0(combinedTpe)) fromBranchesExact0Sym
180+
else fromBranchesSym
181+
ref(sym)
182+
.appliedToType(combinedTpe)
183+
.appliedTo(branches, elseOption)
184+
.appliedTo(dfcTree)
172185
else tree
173186

174187
object DFType:
@@ -550,7 +563,8 @@ class CustomControlPhase(setting: Setting) extends CommonPhase:
550563
caseTupleTrees: List[Tree],
551564
forceAnonymous: Boolean
552565
)(using Context): Tree =
553-
ref(fromCasesSym)
566+
val sym = if (isExact1(retTpe)) fromCasesExactSym else fromCasesSym
567+
ref(sym)
554568
.appliedToType(retTpe)
555569
.appliedTo(
556570
selectorTree,
@@ -954,9 +968,16 @@ class CustomControlPhase(setting: Setting) extends CommonPhase:
954968
fromBooleanSym = requiredMethod("dfhdl.core.r__For_Plugin.fromBoolean")
955969
toFunc1Sym = requiredMethod("dfhdl.core.r__For_Plugin.toFunc1")
956970
fromBranchesSym = requiredMethod("dfhdl.core.DFIf.fromBranches")
971+
fromBranchesExact0Sym = requiredMethod("dfhdl.core.DFIf.fromBranchesExact0")
972+
fromBranchesExact1Sym = requiredMethod("dfhdl.core.DFIf.fromBranchesExact1")
957973
fromCasesSym = requiredMethod("dfhdl.core.DFMatch.fromCases")
974+
fromCasesExactSym = requiredMethod("dfhdl.core.DFMatch.fromCasesExact")
958975
dfValClsRef = requiredClassRef("dfhdl.core.DFVal")
959976
dfEncodingRef = requiredClassRef("dfhdl.core.DFEncoding")
960977
enumHackedUnapply = requiredMethod("dfhdl.unapply")
978+
exact0Sym = requiredClass("dfhdl.internals.Exact0")
979+
exact1Sym = requiredClass("dfhdl.internals.Exact1")
980+
exactApplySym = requiredMethod("dfhdl.internals.Exact.apply")
961981
ctx
982+
end prepareForUnit
962983
end CustomControlPhase

0 commit comments

Comments
 (0)