[llvm] [RISCV][NFC] Allow SchedVar to be a def inside our scheduler model fi… (PR #82634)

Michael Maitland via llvm-commits llvm-commits at lists.llvm.org
Thu Feb 22 07:35:38 PST 2024


https://github.com/michaelmaitland created https://github.com/llvm/llvm-project/pull/82634

…les.

All SchedModel files have a line that looks like:

```
def SomeModel : SchedMachineModel;
let SchedModel = SomeModel in {
  ...
}
```

TableGen requires that all records defined within the top level `let` must have a field `SchedModel` somewhere in their nested record hierarchy (i.e. the record has a field `SchedModel : SchedMachineModel` or recursively, one of its members has a field `SchedModel : SchedMachineModel`.

Classes such as `SchedPredicate` have added a field `SchedModel : SchedMachineModel`, even though the field is never used, just to supress **errors** (not warnings) caused from having the top level let in the model files. This decision was made to avoid having hundreds of the same `let` statement littered in every scheduler model file.

The reason we have never seen an error for `SchedVar` before is because `SchedVar` is never instantiated with a `def`. Instead, it is only created as a value that is consumed by `SchedWriteVariant`:

```
... : SchedWriteVariant<[SchedVar<...>, SchedVar<...>]>;
```

There is a problem with this style of instantiation. In particular, the problem arises as we try to take a class based approach to building scheduler models. I will describe the problem from the bottom up.

The `LMULWriteResMXVariant` multiclass takes in a `SchedPredicateBase Pred`. Today, the RISCVSchedSiFive7.td file defines `VLDSX0Pred` outside the scope of any class. That means that `VLDSX0Pred` exists before `LMULWriteResMXVariant` multiclass is instantiated. With this approach, there is no error since the predicate is instantated in entirety before the variant multiclass is instantiated. However, I have the intention to move the definition of both the predicate and the variant multiclass records inside a multiclass to factor out common parts between multiple scheduler models.

I plan to have something like:

```
multiclass SiFive7Base<SiFive7BaseConfig c> {
  def VLDSX0Pred : ...;
  // Need defvar since record is prefixed with NAME.
  defvar VLDSX0Pred = !cast<...>(NAME # VLDSX0Pred);
  defm SiFive7 : LMULWriteResMXVariant<VLDSX0Pred>;
}

defm "" :SiFive7Version1<SiFive7BaseConfig<...>>;
defm "" :SiFive7Version2<SiFive7BaseConfig<...>>;
```

In this scheme, VLDSX0Pred is defined within the same multiclass transaction that the `LMULWriteResMXVariant` is defined in. For some reason, TableGen does not allow `Values` to reference records that were created in the same parent record construction. If the `SchedVar` is not a `def`, then it will not be able to find the record `NAME # VLDSX0Pred`. Making it a def, allows TableGen to find `NAME # VLDSX0Pred` in scope.

The simplest example of this is:

```
class A {}
class B<A a> { A x = a;}
class C<B b> { B y = b;}
multiclass D {
  def MyA : A;
  defvar aa = !cast<A>(NAME # MyA);
  // This works
  def : B<aa>;
  // This does not work because constructing B by value cannot find `NAME # MyA`
  // error: Undefined reference to record: 'MyA'
  def : C<B<aa>>;
  // To fix it, define it like such:
  def MyB : B<aa>;
  defvar bb = !cast<B>(NAME # MyB);
  def : C<bb>;
}
defm "" : D;
```

In summary, in order to use a class based approach to creating scheduler resources to promote resusability, `SchedVar`s must be created using defs instead of being instantiated by value so that it can resolve records that were part of the instantiation of the parent record being created. In order to do this without refactoring the top level `let` statement that all scheduler model files use, we add an unused field `SchedModel : SchedMachineModel` to `SchedVar`, similiar to what has been done in `SchedPredicate`.

>From d7a3b4842add39472983bb6a6e8f9e60a597cea4 Mon Sep 17 00:00:00 2001
From: Michael Maitland <michaeltmaitland at gmail.com>
Date: Thu, 22 Feb 2024 06:47:42 -0800
Subject: [PATCH] [RISCV][NFC] Allow SchedVar to be a def inside our scheduler
 model files.

All SchedModel files have a line that looks like:

```
def SomeModel : SchedMachineModel;
let SchedModel = SomeModel in {
  ...
}
```

TableGen requires that all records defined within the top level `let` must
have a field `SchedModel` somewhere in their nested record hierarchy
(i.e. the record has a field `SchedModel : SchedMachineModel` or
recursively, one of its members has a field `SchedModel : SchedMachineModel`.

Classes such as `SchedPredicate` have added a field `SchedModel :
SchedMachineModel`, even though the field is never used, just to supress
**errors** (not warnings) caused from having the top level let in the model
files. This decision was made to avoid having hundreds of the same `let`
statement littered in every scheduler model file.

The reason we have never seen an error for `SchedVar` before is because
`SchedVar` is never instantiated with a `def`. Instead, it is only
created as a value that is consumed by `SchedWriteVariant`:

```
... : SchedWriteVariant<[SchedVar<...>, SchedVar<...>]>;
```

There is a problem with this style of instantiation. In particular, the
problem arises as we try to take a class based approach to building
scheduler models. I will describe the problem from the bottom up.

The `LMULWriteResMXVariant` multiclass takes in a `SchedPredicateBase
Pred`. Today, the RISCVSchedSiFive7.td file defines `VLDSX0Pred` outside
the scope of any class. That means that `VLDSX0Pred` exists before
`LMULWriteResMXVariant` multiclass is instantiated. With this approach,
there is no error since the predicate is instantated in entirety before
the variant multiclass is instantiated. However, I have the
intention to move the definition of both the predicate and the variant
multiclass records inside a multiclass to factor out common parts between
multiple scheduler models.

I plan to have something like:

```
multiclass SiFive7Base<SiFive7BaseConfig c> {
  def VLDSX0Pred : ...;
  // Need defvar since record is prefixed with NAME.
  defvar VLDSX0Pred = !cast<...>(NAME # VLDSX0Pred);
  defm SiFive7 : LMULWriteResMXVariant<VLDSX0Pred>;
}

defm "" :SiFive7Version1<SiFive7BaseConfig<...>>;
defm "" :SiFive7Version2<SiFive7BaseConfig<...>>;
```

In this scheme, VLDSX0Pred is defined within the same multiclass
transaction that the `LMULWriteResMXVariant` is defined in. For some
reason, TableGen does not allow `Values` to reference records that were
created in the same parent record construction. If the `SchedVar` is not a `def`,
then it will not be able to find the record `NAME # VLDSX0Pred`. Making
it a def, allows TableGen to find `NAME # VLDSX0Pred` in scope.

The simplest example of this is:

```
class A {}
class B<A a> { A x = a;}
class C<B b> { B y = b;}
multiclass D {
  def MyA : A;
  defvar aa = !cast<A>(NAME # MyA);
  // This works
  def : B<aa>;
  // This does not work because constructing B by value cannot find `NAME # MyA`
  // error: Undefined reference to record: 'MyA'
  def : C<B<aa>>;
  // To fix it, define it like such:
  def MyB : B<aa>;
  defvar bb = !cast<B>(NAME # MyB);
  def : C<bb>;
}
defm "" : D;
```

In summary, in order to use a class based approach to creating scheduler
resources to promote resusability, `SchedVar`s must be created using
defs instead of being instantiated by value so that it can resolve
records that were part of the instantiation of the parent record
being created. In order to do this without refactoring the top
level `let` statement that all scheduler model files use, we add an
unused field `SchedModel : SchedMachineModel` to `SchedVar`, similiar to
what has been done in `SchedPredicate`.
---
 llvm/include/llvm/Target/TargetSchedule.td |  2 ++
 llvm/lib/Target/RISCV/RISCVScheduleV.td    | 21 +++++++++++++--------
 2 files changed, 15 insertions(+), 8 deletions(-)

diff --git a/llvm/include/llvm/Target/TargetSchedule.td b/llvm/include/llvm/Target/TargetSchedule.td
index e2781a5d1ea54a..48c9387977c075 100644
--- a/llvm/include/llvm/Target/TargetSchedule.td
+++ b/llvm/include/llvm/Target/TargetSchedule.td
@@ -399,6 +399,8 @@ def NoSchedPred : MCSchedPredicate<TruePred>;
 class SchedVar<SchedPredicateBase pred, list<SchedReadWrite> selected> {
   SchedPredicateBase Predicate = pred;
   list<SchedReadWrite> Selected = selected;
+  // SchedModel silences warnings but is ignored.
+  SchedMachineModel SchedModel = ?;
 }
 
 // SchedModel silences warnings but is ignored.
diff --git a/llvm/lib/Target/RISCV/RISCVScheduleV.td b/llvm/lib/Target/RISCV/RISCVScheduleV.td
index d15cb611ae665e..0be681de3daf69 100644
--- a/llvm/lib/Target/RISCV/RISCVScheduleV.td
+++ b/llvm/lib/Target/RISCV/RISCVScheduleV.td
@@ -88,20 +88,25 @@ multiclass LMULWriteResMXVariant<string name, SchedPredicateBase Pred,
     let ReleaseAtCycles = noPredReleaseCycles;
   }
 
+  // Define SchedVars
+  def nameMX # PredSchedVar
+      : SchedVar<Pred, [!cast<SchedWriteRes>(NAME # nameMX # "_Pred")]>;
+  def nameMX # NoPredSchedVar
+      : SchedVar<NoSchedPred, [!cast<SchedWriteRes>(NAME # nameMX #"_NoPred")]>;
+  // Allow multiclass to refer to SchedVars -- need to have NAME prefix.
+  defvar PredSchedVar = !cast<SchedVar>(NAME # nameMX # PredSchedVar);
+  defvar NoPredSchedVar = !cast<SchedVar>(NAME # nameMX # NoPredSchedVar);
+
   // Tie behavior to predicate
-  def NAME # nameMX # "_Variant" : SchedWriteVariant<[
-    SchedVar<Pred, [!cast<SchedWriteRes>(NAME # nameMX # "_Pred")]>,
-    SchedVar<NoSchedPred, [!cast<SchedWriteRes>(NAME # nameMX # "_NoPred")]>
-  ]>;
+  def NAME # nameMX # "_Variant"
+      : SchedWriteVariant<[PredSchedVar, NoPredSchedVar]>;
   def : SchedAlias<
     !cast<SchedReadWrite>(nameMX),
     !cast<SchedReadWrite>(NAME # nameMX # "_Variant")>;
 
   if IsWorstCase then {
-    def NAME # name # "_WorstCase_Variant" : SchedWriteVariant<[
-      SchedVar<Pred, [!cast<SchedWriteRes>(NAME # nameMX # "_Pred")]>,
-      SchedVar<NoSchedPred, [!cast<SchedWriteRes>(NAME # nameMX # "_NoPred")]>
-    ]>;
+    def NAME # name # "_WorstCase_Variant"
+      : SchedWriteVariant<[PredSchedVar, NoPredSchedVar]>;
     def : SchedAlias<
       !cast<SchedReadWrite>(name # "_WorstCase"),
       !cast<SchedReadWrite>(NAME # name # "_WorstCase_Variant")>;



More information about the llvm-commits mailing list