diff options
Diffstat (limited to 'fuzzer/src/main/scala/firrtl/ExprGen.scala')
| -rw-r--r-- | fuzzer/src/main/scala/firrtl/ExprGen.scala | 728 |
1 files changed, 728 insertions, 0 deletions
diff --git a/fuzzer/src/main/scala/firrtl/ExprGen.scala b/fuzzer/src/main/scala/firrtl/ExprGen.scala new file mode 100644 index 00000000..fe85799a --- /dev/null +++ b/fuzzer/src/main/scala/firrtl/ExprGen.scala @@ -0,0 +1,728 @@ +package firrtl.fuzzer + +import firrtl.ir._ +import firrtl.{PrimOps, Utils} + +import scala.language.higherKinds + +/** A generator that generates expressions of a certain type for a given IR type + */ +trait ExprGen[E <: Expression] { self => + type Width = BigInt + + /** The name of this generator, used for logging + */ + def name: String + + /** A [[StateGen]] that produces a one width UInt [[firrtl.ir.Expression Expression]] + */ + def boolUIntGen[S: ExprState, G[_]: GenMonad]: Option[StateGen[S, G, E]] + + /** Takes a width and returns a [[StateGen]] that produces a UInt [[firrtl.ir.Expression Expression]] with the given width + * + * The input width will be greater than 1 and less than or equal to the + * maximum width allowed specified by the input state + */ + def uintGen[S: ExprState, G[_]: GenMonad]: Option[Width => StateGen[S, G, E]] + + /** A [[StateGen]] that produces a one width SInt [[firrtl.ir.Expression Expression]] + */ + def boolSIntGen[S: ExprState, G[_]: GenMonad]: Option[StateGen[S, G, E]] + + /** Takes a width and returns a [[StateGen]] that produces a SInt [[firrtl.ir.Expression Expression]] with the given width + * + * The input width will be greater than 1 and less than or equal to the + * maximum width allowed by the input state + */ + def sintGen[S: ExprState, G[_]: GenMonad]: Option[Width => StateGen[S, G, E]] + + /** Returns a copy of this [[ExprGen]] that throws better exceptions + * + * Wraps this [[ExprGen]]'s functions with a try-catch that will wrap + * exceptions and record the name of this generator in an + * [[ExprGen.TraceException]]. + */ + def withTrace: ExprGen[E] = new ExprGen[E] { + import GenMonad.syntax._ + + def name = self.name + + private def wrap[S: ExprState, G[_]: GenMonad]( + name: String, + tpe: Type, + stateGen: StateGen[S, G, E]): StateGen[S, G, E] = { + StateGen { (s: S) => + GenMonad[G].choose(0, 1).map ( _ => + try { + GenMonad[G].generate(stateGen.run(s)) + } catch { + case e: ExprGen.TraceException if e.trace.size < 10 => + throw e.copy(trace = s"$name: ${tpe.serialize}" +: e.trace) + case e: IllegalArgumentException => + throw ExprGen.TraceException(Seq(s"$name: ${tpe.serialize}"), e) + } + ) + } + } + + /** Identical to self.boolUIntGen, but will wrap exceptions in a [[ExprGen.TraceException]] + */ + def boolUIntGen[S: ExprState, G[_]: GenMonad]: Option[StateGen[S, G, E]] = { + self.boolUIntGen.map(wrap(self.name, Utils.BoolType, _)) + } + + /** Identical to self.uintGen, but will wrap exceptions in a [[ExprGen.TraceException]] + */ + def uintGen[S: ExprState, G[_]: GenMonad]: Option[Width => StateGen[S, G, E]] = { + self.uintGen.map(fn => (width: Width) => wrap(self.name, UIntType(IntWidth(width)), fn(width))) + } + + /** Identical to self.boolSIntGen, but will wrap exceptions in a [[ExprGen.TraceException]] + */ + def boolSIntGen[S: ExprState, G[_]: GenMonad]: Option[StateGen[S, G, E]] = { + self.boolSIntGen.map(wrap(self.name, SIntType(IntWidth(1)), _)) + } + + /** Identical to self.sintGen, but will wrap exceptions in a [[ExprGen.TraceException]] + */ + def sintGen[S: ExprState, G[_]: GenMonad]: Option[Width => StateGen[S, G, E]] = { + self.sintGen.map(fn => (width: Width) => wrap(self.name, SIntType(IntWidth(width)), fn(width))) + } + } +} + +/** An Expression Generator that generates [[firrtl.ir.DoPrim DoPrim]]s of the given operator + */ +abstract class DoPrimGen(val primOp: PrimOp) extends ExprGen[DoPrim] { + def name = primOp.serialize +} + +object ExprGen { + type Width = BigInt + + import GenMonad.syntax._ + + private def printStack(e: Throwable): String = { + val sw = new java.io.StringWriter() + val pw = new java.io.PrintWriter(sw) + e.printStackTrace(pw) + pw.flush() + sw.toString + } + + case class TraceException(trace: Seq[String], cause: Throwable) extends Exception( + s"cause: ${printStack(cause)}\ntrace:\n${trace.reverse.mkString("\n")}\n" + ) + + def genWidth[G[_]: GenMonad](min: Int, max: Int): G[IntWidth] = GenMonad[G].choose(min, max).map(IntWidth(_)) + def genWidthMax[G[_]: GenMonad](max: Int): G[IntWidth] = genWidth(1, max) + + private def makeBinDoPrimStateGen[S: ExprState, G[_]: GenMonad]( + primOp: PrimOp, + typeGen: Int => G[(Type, Type, Type)]): StateGen[S, G, DoPrim] = { + for { + t <- StateGen.inspectG((s: S) => typeGen(ExprState[S].maxWidth(s))) + (tpe1, tpe2, exprTpe) = t + expr1 <- ExprState[S].exprGen(tpe1) + expr2 <- ExprState[S].exprGen(tpe2) + } yield { + DoPrim(primOp, Seq(expr1, expr2), Seq.empty, exprTpe) + } + } + + private def makeUnaryDoPrimStateGen[S: ExprState, G[_]: GenMonad]( + primOp: PrimOp, + typeGen: Int => G[(Type, Type)]): StateGen[S, G, DoPrim] = { + for { + p <- StateGen.inspectG((s: S) => typeGen(ExprState[S].maxWidth(s))) + (tpe1, exprTpe) = p + expr1 <- ExprState[S].exprGen(tpe1) + } yield { + DoPrim(primOp, Seq(expr1), Seq.empty, exprTpe) + } + } + + private def log2Floor(i: Int): Int = { + if (i > 0 && ((i & (i - 1)) == 0)) { + BigInt(i - 1).bitLength + } else { + BigInt(i - 1).bitLength - 1 + } + } + + def inspectMinWidth[S: ExprState, G[_]: GenMonad](min: Int): StateGen[S, G, IntWidth] = { + StateGen.inspectG { s => + val maxWidth = ExprState[S].maxWidth(s) + GenMonad[G].choose(min, maxWidth).map(IntWidth(_)) + } + } + + + object ReferenceGen extends ExprGen[Reference] { + def name = "Ref" + def boolUIntGen[S: ExprState, G[_]: GenMonad]: Option[StateGen[S, G, Reference]] = uintGen.map(_(1)) + def uintGen[S: ExprState, G[_]: GenMonad]: Option[Width => StateGen[S, G, Reference]] = Some { width => + referenceGenImp(UIntType(IntWidth(width))) + } + + def boolSIntGen[S: ExprState, G[_]: GenMonad]: Option[StateGen[S, G, Reference]] = sintGen.map(_(1)) + def sintGen[S: ExprState, G[_]: GenMonad]: Option[Width => StateGen[S, G, Reference]] = Some { width => + referenceGenImp(SIntType(IntWidth(width))) + } + + private def referenceGenImp[S: ExprState, G[_]: GenMonad](tpe: Type): StateGen[S, G, Reference] = { + for { + // don't bother randomizing name, because it usually does not help with coverage + tryName <- StateGen.pure("ref") + ref <- ExprState[S].withRef(Reference(tryName, tpe)) + } yield { + ref + } + } + } + + object LiteralGen extends ExprGen[Literal] { + def name = "Literal" + def boolUIntGen[S: ExprState, G[_]: GenMonad]: Option[StateGen[S, G, Literal]] = uintGen.map(_(1)) + def uintGen[S: ExprState, G[_]: GenMonad]: Option[Width => StateGen[S, G, Literal]] = Some { width => + uintLiteralGenImp(width) + } + + def boolSIntGen[S: ExprState, G[_]: GenMonad]: Option[StateGen[S, G, Literal]] = sintGen.map(_(1)) + def sintGen[S: ExprState, G[_]: GenMonad]: Option[Width => StateGen[S, G, Literal]] = Some { width => + sintLiteralGenImp(width) + } + + private def uintLiteralGenImp[S: ExprState, G[_]: GenMonad](width: Width): StateGen[S, G, Literal] = { + val maxValue = if (width < BigInt(31)) { + (1 << width.toInt) - 1 + } else { + Int.MaxValue + } + StateGen.liftG(for { + value <- GenMonad[G].choose(0, maxValue) + } yield { + UIntLiteral(value, IntWidth(width)) + }) + } + + private def sintLiteralGenImp[S: ExprState, G[_]: GenMonad](width: Width): StateGen[S, G, Literal] = { + StateGen.liftG( + if (width <= BigInt(32)) { + GenMonad[G].choose(-(1 << (width.toInt - 1)), (1 << (width.toInt - 1)) - 1).map { value => + SIntLiteral(value, IntWidth(width)) + } + } else { + GenMonad[G].choose(Int.MinValue, Int.MaxValue).map { value => + SIntLiteral(value, IntWidth(width)) + } + } + ) + } + } + + abstract class AddSubDoPrimGen(isAdd: Boolean) extends DoPrimGen(if (isAdd) PrimOps.Add else PrimOps.Sub) { + def boolUIntGen[S: ExprState, G[_]: GenMonad]: Option[StateGen[S, G, DoPrim]] = None + def uintGen[S: ExprState, G[_]: GenMonad]: Option[Width => StateGen[S, G, DoPrim]] = { + Some { width => + def typeGen(maxWidth: Int): G[(Type, Type, Type)] = for { + randWidth <- GenMonad[G].choose(1, width.toInt - 1) + flip <- GenMonad.bool + } yield { + if (flip) { + (UIntType(IntWidth(randWidth)), UIntType(IntWidth(width.toInt - 1)), UIntType(IntWidth(width))) + } else { + (UIntType(IntWidth(width.toInt - 1)), UIntType(IntWidth(randWidth)), UIntType(IntWidth(width))) + } + } + makeBinDoPrimStateGen(primOp, typeGen) + } + } + + def boolSIntGen[S: ExprState, G[_]: GenMonad]: Option[StateGen[S, G, DoPrim]] = None + def sintGen[S: ExprState, G[_]: GenMonad]: Option[Width => StateGen[S, G, DoPrim]] = { + Some { width => + def typeGen(maxWidth: Int): G[(Type, Type, Type)] = for { + randWidth <- GenMonad[G].choose(1, width.toInt - 1) + flip <- GenMonad.bool + } yield { + if (flip) { + (SIntType(IntWidth(randWidth)), SIntType(IntWidth(width.toInt - 1)), SIntType(IntWidth(width))) + } else { + (SIntType(IntWidth(width.toInt - 1)), SIntType(IntWidth(randWidth)), SIntType(IntWidth(width))) + } + } + makeBinDoPrimStateGen(primOp, typeGen) + } + } + } + + object AddDoPrimGen extends AddSubDoPrimGen(isAdd = true) + object SubDoPrimGen extends AddSubDoPrimGen(isAdd = false) + + object MulDoPrimGen extends DoPrimGen(PrimOps.Mul) { + private def imp[S: ExprState, G[_]: GenMonad](isUInt: Boolean): Option[Width => StateGen[S, G, DoPrim]] = { + val tpe = if (isUInt) UIntType(_) else SIntType(_) + Some { totalWidth => + def typeGen(maxWidth: Int): G[(Type, Type, Type)] = for { + width1 <- GenMonad[G].choose(1, totalWidth.toInt - 1) + flip <- GenMonad.bool + } yield { + val t1 = tpe(IntWidth(math.max(totalWidth.toInt - width1, 0))) + val t2 = tpe(IntWidth(width1)) + val t3 = tpe(IntWidth(totalWidth)) + if (flip) (t2, t1, t3) else (t1, t2, t3) + } + makeBinDoPrimStateGen(primOp, typeGen) + } + } + + def boolUIntGen[S: ExprState, G[_]: GenMonad]: Option[StateGen[S, G, DoPrim]] = None + def uintGen[S: ExprState, G[_]: GenMonad]: Option[Width => StateGen[S, G, DoPrim]] = imp(isUInt = true) + + def boolSIntGen[S: ExprState, G[_]: GenMonad]: Option[StateGen[S, G, DoPrim]] = None + def sintGen[S: ExprState, G[_]: GenMonad]: Option[Width => StateGen[S, G, DoPrim]] = imp(isUInt = false) + } + + object DivDoPrimGen extends DoPrimGen(PrimOps.Div) { + private def imp[S: ExprState, G[_]: GenMonad](isUInt: Boolean): Option[Width => StateGen[S, G, DoPrim]] = { + val tpe = if (isUInt) UIntType(_) else SIntType(_) + Some { resultWidth => + val numWidth = if (isUInt) resultWidth.toInt else (resultWidth.toInt - 1) + def typeGen(maxWidth: Int): G[(Type, Type, Type)] = for { + denWidth <- genWidthMax(maxWidth) + } yield { + (tpe(IntWidth(numWidth)), tpe(denWidth), tpe(IntWidth(resultWidth))) + } + makeBinDoPrimStateGen(primOp, typeGen) + } + } + + def boolUIntGen[S: ExprState, G[_]: GenMonad]: Option[StateGen[S, G, DoPrim]] = None + def uintGen[S: ExprState, G[_]: GenMonad]: Option[Width => StateGen[S, G, DoPrim]] = imp(isUInt = true) + + def boolSIntGen[S: ExprState, G[_]: GenMonad]: Option[StateGen[S, G, DoPrim]] = None + def sintGen[S: ExprState, G[_]: GenMonad]: Option[Width => StateGen[S, G, DoPrim]] = imp(isUInt = false) + } + + object RemDoPrimGen extends DoPrimGen(PrimOps.Rem) { + private def imp[S: ExprState, G[_]: GenMonad](isUInt: Boolean): Option[Width => StateGen[S, G, DoPrim]] = { + val tpe = if (isUInt) UIntType(_) else SIntType(_) + Some { resultWidth => + def typeGen(maxWidth: Int): G[(Type, Type, Type)] = for { + argWidth <- genWidth(resultWidth.toInt, maxWidth) + flip <- GenMonad.bool + } yield { + val t1 = UIntType(argWidth) + val t2 = UIntType(IntWidth(resultWidth)) + val t3 = t2 + if (flip) (t2, t1, t3) else (t1, t2, t3) + } + makeBinDoPrimStateGen(primOp, typeGen) + } + } + + def boolUIntGen[S: ExprState, G[_]: GenMonad]: Option[StateGen[S, G, DoPrim]] = uintGen.map(_(1)) + def uintGen[S: ExprState, G[_]: GenMonad]: Option[Width => StateGen[S, G, DoPrim]] = imp(isUInt = true) + + def boolSIntGen[S: ExprState, G[_]: GenMonad]: Option[StateGen[S, G, DoPrim]] = sintGen.map(_(1)) + def sintGen[S: ExprState, G[_]: GenMonad]: Option[Width => StateGen[S, G, DoPrim]] = imp(isUInt = false) + } + + abstract class CmpDoPrimGen(primOp: PrimOp) extends DoPrimGen(primOp) { + def boolUIntGen[S: ExprState, G[_]: GenMonad]: Option[StateGen[S, G, DoPrim]] = { + def typeGen(maxWidth: Int): G[(Type, Type, Type)] = for { + width1 <- genWidthMax(maxWidth) + width2 <- genWidthMax(maxWidth) + tpe <- GenMonad[G].oneOf(UIntType(_), SIntType(_)) + } yield { + (tpe(width1), tpe(width2), Utils.BoolType) + } + Some(makeBinDoPrimStateGen(primOp, typeGen)) + } + def uintGen[S: ExprState, G[_]: GenMonad]: Option[Width => StateGen[S, G, DoPrim]] = None + + def boolSIntGen[S: ExprState, G[_]: GenMonad]: Option[StateGen[S, G, DoPrim]] = None + def sintGen[S: ExprState, G[_]: GenMonad]: Option[Width => StateGen[S, G, DoPrim]] = None + } + object LtDoPrimGen extends CmpDoPrimGen(PrimOps.Lt) + object LeqDoPrimGen extends CmpDoPrimGen(PrimOps.Leq) + object GtDoPrimGen extends CmpDoPrimGen(PrimOps.Gt) + object GeqDoPrimGen extends CmpDoPrimGen(PrimOps.Geq) + object EqDoPrimGen extends CmpDoPrimGen(PrimOps.Eq) + object NeqDoPrimGen extends CmpDoPrimGen(PrimOps.Neq) + + object PadDoPrimGen extends DoPrimGen(PrimOps.Pad) { + private def imp[S: ExprState, G[_]: GenMonad](isUInt: Boolean): Option[Width => StateGen[S, G, DoPrim]] = { + val tpe = if (isUInt) UIntType(_) else SIntType(_) + Some { resultWidth => + for { + width1 <- StateGen.liftG(genWidthMax(resultWidth.toInt)) + flip <- StateGen.liftG(GenMonad.bool) + expr <- ExprState[S].exprGen(tpe(if (flip) IntWidth(resultWidth) else width1)) + } yield { + DoPrim(primOp, Seq(expr), Seq(if (flip) width1.width else resultWidth), tpe(IntWidth(resultWidth))) + } + } + } + + def boolUIntGen[S: ExprState, G[_]: GenMonad]: Option[StateGen[S, G, DoPrim]] = uintGen.map(_(1)) + def uintGen[S: ExprState, G[_]: GenMonad]: Option[Width => StateGen[S, G, DoPrim]] = imp(isUInt = true) + + def boolSIntGen[S: ExprState, G[_]: GenMonad]: Option[StateGen[S, G, DoPrim]] = sintGen.map(_(1)) + def sintGen[S: ExprState, G[_]: GenMonad]: Option[Width => StateGen[S, G, DoPrim]] = imp(isUInt = false) + } + + object ShlDoPrimGen extends DoPrimGen(PrimOps.Shl) { + private def imp[S: ExprState, G[_]: GenMonad](isUInt: Boolean): Option[Width => StateGen[S, G, DoPrim]] = { + val tpe = if (isUInt) UIntType(_) else SIntType(_) + Some { totalWidth => + for { + width1 <- StateGen.liftG(genWidthMax(totalWidth.toInt)) + expr <- ExprState[S].exprGen(tpe(width1)) + } yield { + val width2 = totalWidth.toInt - width1.width.toInt + DoPrim(primOp, Seq(expr), Seq(width2), tpe(IntWidth(totalWidth))) + } + } + } + + def boolUIntGen[S: ExprState, G[_]: GenMonad]: Option[StateGen[S, G, DoPrim]] = None + def uintGen[S: ExprState, G[_]: GenMonad]: Option[Width => StateGen[S, G, DoPrim]] = imp(isUInt = true) + + def boolSIntGen[S: ExprState, G[_]: GenMonad]: Option[StateGen[S, G, DoPrim]] = None + def sintGen[S: ExprState, G[_]: GenMonad]: Option[Width => StateGen[S, G, DoPrim]] = imp(isUInt = false) + } + + object ShrDoPrimGen extends DoPrimGen(PrimOps.Shr) { + private def imp[S: ExprState, G[_]: GenMonad](isUInt: Boolean): Option[Width => StateGen[S, G, DoPrim]] = { + val tpe = if (isUInt) UIntType(_) else SIntType(_) + Some { minWidth => + for { + exprWidth <- inspectMinWidth(minWidth.toInt) + expr <- ExprState[S].exprGen(tpe(exprWidth)) + shamt <- StateGen.liftG(if (minWidth == BigInt(1)) { + GenMonad[G].choose(exprWidth.width.toInt - minWidth.toInt, Int.MaxValue) + } else { + GenMonad[G].const(exprWidth.width.toInt - minWidth.toInt) + }) + } yield { + DoPrim(primOp, Seq(expr), Seq(shamt), tpe(IntWidth(minWidth))) + } + } + } + + def boolUIntGen[S: ExprState, G[_]: GenMonad]: Option[StateGen[S, G, DoPrim]] = uintGen.map(_(1)) + def uintGen[S: ExprState, G[_]: GenMonad]: Option[Width => StateGen[S, G, DoPrim]] = imp(isUInt = true) + + def boolSIntGen[S: ExprState, G[_]: GenMonad]: Option[StateGen[S, G, DoPrim]] = sintGen.map(_(1)) + def sintGen[S: ExprState, G[_]: GenMonad]: Option[Width => StateGen[S, G, DoPrim]] = imp(isUInt = false) + } + + object DshlDoPrimGen extends DoPrimGen(PrimOps.Dshl) { + private def imp[S: ExprState, G[_]: GenMonad](isUInt: Boolean): Option[Width => StateGen[S, G, DoPrim]] = { + val tpe = if (isUInt) UIntType(_) else SIntType(_) + Some { totalWidth => + val shWidthMax = log2Floor(totalWidth.toInt) + def typeGen(maxWidth: Int): G[(Type, Type, Type)] = for { + shWidth <- genWidthMax(shWidthMax) + } yield { + val w1 = totalWidth.toInt - ((1 << shWidth.width.toInt) - 1) + // println(s"TOTALWIDTH: $totalWidth, SHWIDHTMAX: ${shWidthMax}, SHWIDTH: $shWidth, ARGWITH: $w1") + (tpe(IntWidth(w1)), UIntType(shWidth), tpe(IntWidth(totalWidth))) + } + makeBinDoPrimStateGen(primOp, typeGen) + } + } + + def boolUIntGen[S: ExprState, G[_]: GenMonad]: Option[StateGen[S, G, DoPrim]] = None + def uintGen[S: ExprState, G[_]: GenMonad]: Option[Width => StateGen[S, G, DoPrim]] = imp(isUInt = true) + + def boolSIntGen[S: ExprState, G[_]: GenMonad]: Option[StateGen[S, G, DoPrim]] = None + def sintGen[S: ExprState, G[_]: GenMonad]: Option[Width => StateGen[S, G, DoPrim]] = imp(isUInt = false) + } + + object DshrDoPrimGen extends DoPrimGen(PrimOps.Dshr) { + private def imp[S: ExprState, G[_]: GenMonad](isUInt: Boolean): Option[Width => StateGen[S, G, DoPrim]] = { + val tpe = if (isUInt) UIntType(_) else SIntType(_) + Some { width => + def typeGen(maxWidth: Int): G[(Type, Type, Type)] = for { + w2 <- genWidthMax(maxWidth) + } yield { + (tpe(IntWidth(width)), tpe(w2), tpe(IntWidth(width))) + } + makeBinDoPrimStateGen(primOp, typeGen) + } + } + + def boolUIntGen[S: ExprState, G[_]: GenMonad]: Option[StateGen[S, G, DoPrim]] = uintGen.map(_(1)) + def uintGen[S: ExprState, G[_]: GenMonad]: Option[Width => StateGen[S, G, DoPrim]] = imp(isUInt = true) + + def boolSIntGen[S: ExprState, G[_]: GenMonad]: Option[StateGen[S, G, DoPrim]] = sintGen.map(_(1)) + def sintGen[S: ExprState, G[_]: GenMonad]: Option[Width => StateGen[S, G, DoPrim]] = imp(isUInt = false) + } + + object CvtDoPrimGen extends DoPrimGen(PrimOps.Cvt) { + def boolUIntGen[S: ExprState, G[_]: GenMonad]: Option[StateGen[S, G, DoPrim]] = None + def uintGen[S: ExprState, G[_]: GenMonad]: Option[Width => StateGen[S, G, DoPrim]] = None + + def boolSIntGen[S: ExprState, G[_]: GenMonad]: Option[StateGen[S, G, DoPrim]] = { + Some { + def typeGen(maxWidth: Int): G[(Type, Type)] = { + val resultType = SIntType(IntWidth(1)) + GenMonad[G].const(resultType -> resultType) + } + makeUnaryDoPrimStateGen(primOp, typeGen) + } + } + def sintGen[S: ExprState, G[_]: GenMonad]: Option[Width => StateGen[S, G, DoPrim]] = { + Some { width => + def typeGen(maxWidth: Int): G[(Type, Type)] = for { + isUInt <- GenMonad[G].oneOf(true, false) + } yield { + val resultType = SIntType(IntWidth(width)) + if (isUInt) { + UIntType(IntWidth(width.toInt - 1)) -> resultType + } else { + resultType -> resultType + } + } + makeUnaryDoPrimStateGen(primOp, typeGen) + } + } + } + + object NegDoPrimGen extends DoPrimGen(PrimOps.Neg) { + def boolUIntGen[S: ExprState, G[_]: GenMonad]: Option[StateGen[S, G, DoPrim]] = None + def uintGen[S: ExprState, G[_]: GenMonad]: Option[Width => StateGen[S, G, DoPrim]] = None + + def boolSIntGen[S: ExprState, G[_]: GenMonad]: Option[StateGen[S, G, DoPrim]] = None + def sintGen[S: ExprState, G[_]: GenMonad]: Option[Width => StateGen[S, G, DoPrim]] = { + Some { width => + def typeGen(maxWidth: Int): G[(Type, Type)] = for { + isUInt <- GenMonad[G].oneOf(true, false) + } yield { + val resultType = SIntType(IntWidth(width)) + if (isUInt) { + UIntType(IntWidth(width.toInt - 1)) -> resultType + } else { + SIntType(IntWidth(width.toInt - 1)) -> resultType + } + } + makeUnaryDoPrimStateGen(primOp, typeGen) + } + } + } + + object NotDoPrimGen extends DoPrimGen(PrimOps.Not) { + def boolUIntGen[S: ExprState, G[_]: GenMonad]: Option[StateGen[S, G, DoPrim]] = uintGen.map(_(1)) + def uintGen[S: ExprState, G[_]: GenMonad]: Option[Width => StateGen[S, G, DoPrim]] = { + Some { width => + def typeGen(maxWidth: Int): G[(Type, Type)] = for { + isUInt <- GenMonad[G].oneOf(true, false) + } yield { + val resultType = UIntType(IntWidth(width)) + if (isUInt) { + resultType -> resultType + } else { + SIntType(IntWidth(width)) -> resultType + } + } + makeUnaryDoPrimStateGen(primOp, typeGen) + } + } + + def boolSIntGen[S: ExprState, G[_]: GenMonad]: Option[StateGen[S, G, DoPrim]] = None + def sintGen[S: ExprState, G[_]: GenMonad]: Option[Width => StateGen[S, G, DoPrim]] = None + } + + abstract class BitwiseDoPrimGen(primOp: PrimOp) extends DoPrimGen(primOp) { + def boolUIntGen[S: ExprState, G[_]: GenMonad]: Option[StateGen[S, G, DoPrim]] = uintGen.map(_(1)) + def uintGen[S: ExprState, G[_]: GenMonad]: Option[Width => StateGen[S, G, DoPrim]] = { + Some { width => + def typeGen(maxWidth: Int): G[(Type, Type, Type)] = for { + width1 <- genWidthMax(width.toInt) + flip <- GenMonad.bool + tpe <- GenMonad[G].oneOf(UIntType(_), SIntType(_)) + } yield { + val t1 = tpe(width1) + val t2 = tpe(IntWidth(width)) + val t3 = UIntType(IntWidth(width)) + if (flip) (t2, t1, t3) else (t1, t2, t3) + } + makeBinDoPrimStateGen(primOp, typeGen) + } + } + + def boolSIntGen[S: ExprState, G[_]: GenMonad]: Option[StateGen[S, G, DoPrim]] = None + def sintGen[S: ExprState, G[_]: GenMonad]: Option[Width => StateGen[S, G, DoPrim]] = None + } + object AndDoPrimGen extends BitwiseDoPrimGen(PrimOps.And) + object OrDoPrimGen extends BitwiseDoPrimGen(PrimOps.Or) + object XorDoPrimGen extends BitwiseDoPrimGen(PrimOps.Xor) + + abstract class ReductionDoPrimGen(primOp: PrimOp) extends DoPrimGen(primOp) { + def boolUIntGen[S: ExprState, G[_]: GenMonad]: Option[StateGen[S, G, DoPrim]] = { + Some { + def typeGen(maxWidth: Int): G[(Type, Type)] = for { + width1 <- genWidthMax(maxWidth) + tpeFn <- GenMonad[G].oneOf(UIntType(_), SIntType(_)) + } yield { + (tpeFn(width1), Utils.BoolType) + } + makeUnaryDoPrimStateGen(primOp, typeGen) + } + } + def uintGen[S: ExprState, G[_]: GenMonad]: Option[Width => StateGen[S, G, DoPrim]] = None + + def boolSIntGen[S: ExprState, G[_]: GenMonad]: Option[StateGen[S, G, DoPrim]] = None + def sintGen[S: ExprState, G[_]: GenMonad]: Option[Width => StateGen[S, G, DoPrim]] = None + } + object AndrDoPrimGen extends ReductionDoPrimGen(PrimOps.Andr) + object OrrDoPrimGen extends ReductionDoPrimGen(PrimOps.Orr) + object XorrDoPrimGen extends ReductionDoPrimGen(PrimOps.Xorr) + + object CatDoPrimGen extends DoPrimGen(PrimOps.Cat) { + def boolUIntGen[S: ExprState, G[_]: GenMonad]: Option[StateGen[S, G, DoPrim]] = None + def uintGen[S: ExprState, G[_]: GenMonad]: Option[Width => StateGen[S, G, DoPrim]] = { + Some { totalWidth => + def typeGen(maxWidth: Int): G[(Type, Type, Type)] = for { + width1 <- GenMonad[G].choose(1, totalWidth.toInt - 1) + flip <- GenMonad.bool + tpe <- GenMonad[G].oneOf(UIntType(_), SIntType(_)) + } yield { + val t1 = tpe(IntWidth(totalWidth.toInt - width1)) + val t2 = tpe(IntWidth(width1)) + val t3 = UIntType(IntWidth(totalWidth)) + if (flip) (t2, t1, t3) else (t1, t2, t3) + } + makeBinDoPrimStateGen(primOp, typeGen) + } + } + + def boolSIntGen[S: ExprState, G[_]: GenMonad]: Option[StateGen[S, G, DoPrim]] = None + def sintGen[S: ExprState, G[_]: GenMonad]: Option[Width => StateGen[S, G, DoPrim]] = None + } + + object BitsDoPrimGen extends DoPrimGen(PrimOps.Bits) { + def boolUIntGen[S: ExprState, G[_]: GenMonad]: Option[StateGen[S, G, DoPrim]] = uintGen.map(_(1)) + def uintGen[S: ExprState, G[_]: GenMonad]: Option[Width => StateGen[S, G, DoPrim]] = { + Some { resultWidth => + for { + argWidth <- inspectMinWidth(resultWidth.toInt) + lo <- StateGen.liftG(GenMonad[G].choose(0, argWidth.width.toInt - resultWidth.toInt)) + tpe <- StateGen.liftG(GenMonad[G].oneOf(UIntType(_), SIntType(_))) + arg <- ExprState[S].exprGen(tpe(argWidth)) + } yield { + DoPrim(primOp, Seq(arg), Seq(lo + resultWidth - 1, lo), UIntType(IntWidth(resultWidth))) + } + } + } + + def boolSIntGen[S: ExprState, G[_]: GenMonad]: Option[StateGen[S, G, DoPrim]] = None + def sintGen[S: ExprState, G[_]: GenMonad]: Option[Width => StateGen[S, G, DoPrim]] = None + } + + object HeadDoPrimGen extends DoPrimGen(PrimOps.Head) { + def boolUIntGen[S: ExprState, G[_]: GenMonad]: Option[StateGen[S, G, DoPrim]] = uintGen.map(_(1)) + def uintGen[S: ExprState, G[_]: GenMonad]: Option[Width => StateGen[S, G, DoPrim]] = { + Some { resultWidth => + for { + argWidth <- inspectMinWidth(resultWidth.toInt) + tpe <- StateGen.liftG(GenMonad[G].oneOf(UIntType(_), SIntType(_))) + arg <- ExprState[S].exprGen(tpe(argWidth)) + } yield { + DoPrim(primOp, Seq(arg), Seq(resultWidth), UIntType(IntWidth(resultWidth))) + } + } + } + + def boolSIntGen[S: ExprState, G[_]: GenMonad]: Option[StateGen[S, G, DoPrim]] = None + def sintGen[S: ExprState, G[_]: GenMonad]: Option[Width => StateGen[S, G, DoPrim]] = None + } + + object TailDoPrimGen extends DoPrimGen(PrimOps.Tail) { + def boolUIntGen[S: ExprState, G[_]: GenMonad]: Option[StateGen[S, G, DoPrim]] = uintGen.map(_(1)) + def uintGen[S: ExprState, G[_]: GenMonad]: Option[Width => StateGen[S, G, DoPrim]] = { + Some { totalWidth => + for { + maxWidth <- StateGen.inspect((s: S) => ExprState[S].maxWidth(s)) + tailNum <- if (totalWidth < BigInt(maxWidth)) { + StateGen.liftG[S, G, Int](GenMonad[G].choose(1, maxWidth - totalWidth.toInt)) + } else { + StateGen.pure[S, G, Int](0) + } + tpe <- StateGen.liftG(GenMonad[G].oneOf(UIntType(_), SIntType(_))) + arg <- ExprState[S].exprGen(tpe(IntWidth(totalWidth + tailNum))) + } yield { + DoPrim(primOp, Seq(arg), Seq(tailNum), UIntType(IntWidth(totalWidth))) + } + } + } + + def boolSIntGen[S: ExprState, G[_]: GenMonad]: Option[StateGen[S, G, DoPrim]] = None + def sintGen[S: ExprState, G[_]: GenMonad]: Option[Width => StateGen[S, G, DoPrim]] = None + } + + object AsUIntDoPrimGen extends DoPrimGen(PrimOps.AsUInt) { + def boolUIntGen[S: ExprState, G[_]: GenMonad]: Option[StateGen[S, G, DoPrim]] = uintGen.map(_(1)) + def uintGen[S: ExprState, G[_]: GenMonad]: Option[Width => StateGen[S, G, DoPrim]] = { + Some { width => + val intWidth = IntWidth(width) + for { + tpe <- StateGen.liftG(GenMonad[G].oneOf(UIntType(_), SIntType(_))) + arg <- ExprState[S].exprGen(tpe(intWidth)) + } yield { + DoPrim(primOp, Seq(arg), Seq(), UIntType(intWidth)) + } + } + } + + def boolSIntGen[S: ExprState, G[_]: GenMonad]: Option[StateGen[S, G, DoPrim]] = None + def sintGen[S: ExprState, G[_]: GenMonad]: Option[Width => StateGen[S, G, DoPrim]] = None + } + + object AsSIntDoPrimGen extends DoPrimGen(PrimOps.AsSInt) { + def boolUIntGen[S: ExprState, G[_]: GenMonad]: Option[StateGen[S, G, DoPrim]] = None + def uintGen[S: ExprState, G[_]: GenMonad]: Option[Width => StateGen[S, G, DoPrim]] = None + + def boolSIntGen[S: ExprState, G[_]: GenMonad]: Option[StateGen[S, G, DoPrim]] = sintGen.map(_(1)) + def sintGen[S: ExprState, G[_]: GenMonad]: Option[Width => StateGen[S, G, DoPrim]] = { + Some { width => + val intWidth = IntWidth(width) + for { + tpe <- StateGen.liftG(GenMonad[G].oneOf(UIntType(_), SIntType(_))) + arg <- ExprState[S].exprGen(tpe(intWidth)) + } yield { + DoPrim(primOp, Seq(arg), Seq(), SIntType(intWidth)) + } + } + } + } + + object MuxGen extends ExprGen[Mux] { + def name = "mux" + private def imp[S: ExprState, G[_]: GenMonad](isUInt: Boolean)(width: BigInt): StateGen[S, G, Mux] = { + val tpe = if (isUInt) UIntType(_) else SIntType(_) + // spec states that types must be equivalent, but in practice we allow differing widths + for { + cond <- ExprState[S].exprGen(Utils.BoolType) + flip <- StateGen.liftG(GenMonad.bool) + expr1 <- ExprState[S].exprGen(tpe(IntWidth(width))) + width2 <- StateGen.liftG(genWidthMax(width.toInt)) + expr2 <- ExprState[S].exprGen(tpe(width2)) + } yield { + Mux(cond, expr1, expr2, tpe(IntWidth(width))) + } + } + def boolUIntGen[S: ExprState, G[_]: GenMonad]: Option[StateGen[S, G, Mux]] = uintGen.map(_(1)) + def uintGen[S: ExprState, G[_]: GenMonad]: Option[Width => StateGen[S, G, Mux]] = { + Some { imp(isUInt = true) } + } + + def boolSIntGen[S: ExprState, G[_]: GenMonad]: Option[StateGen[S, G, Mux]] = sintGen.map(_(1)) + def sintGen[S: ExprState, G[_]: GenMonad]: Option[Width => StateGen[S, G, Mux]] = { + Some { imp(isUInt = false) } + } + } +} |
