Skip to content

[NFC] Factor out common parts of ArraySections into its own class #89639

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Apr 25, 2024

Conversation

erichkeane
Copy link
Collaborator

OpenACC is going to need an array sections implementation that is a simpler version/more restrictive version of the OpenMP version. This patch extracts the things that should be common between the two into a template base class. This will allow a followup patch to implement an OpenACC array sections expression type.

OpenACC is going to need an array sections implementation that is a
simpler version/more restrictive version of the OpenMP version.  This
patch extracts the things that should be common between the two into a
template base class. This will allow a followup patch to implement an
OpenACC array sections expression type.
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" clang:openmp OpenMP related changes to Clang labels Apr 22, 2024
@llvmbot
Copy link
Member

llvmbot commented Apr 22, 2024

@llvm/pr-subscribers-clang-codegen
@llvm/pr-subscribers-clang-static-analyzer-1
@llvm/pr-subscribers-clang-modules

@llvm/pr-subscribers-clang

Author: Erich Keane (erichkeane)

Changes

OpenACC is going to need an array sections implementation that is a simpler version/more restrictive version of the OpenMP version. This patch extracts the things that should be common between the two into a template base class. This will allow a followup patch to implement an OpenACC array sections expression type.


Full diff: https://github.com/llvm/llvm-project/pull/89639.diff

2 Files Affected:

  • (modified) clang/include/clang/AST/Expr.h (+105)
  • (modified) clang/include/clang/AST/ExprOpenMP.h (+14-60)
diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h
index 2bfefeabc348be..3fc0170d1a0d8e 100644
--- a/clang/include/clang/AST/Expr.h
+++ b/clang/include/clang/AST/Expr.h
@@ -6610,6 +6610,111 @@ class TypoExpr : public Expr {
 
 };
 
+// This is a sub-class for OpenMP and OpenACC array sections. OpenACC uses a
+// very small subset of the functionality, so this class only exposes the things
+// the two have in common. This type is not expected to be used directly,
+// instead it is inherited from by the OMP and OpenACC variants.
+//
+// This type does not capture a 'stride', only a lower-bound and length (plus
+// the base expression), but provides room via a template argument to get
+// additional ones.
+template<unsigned NumSubExprs, bool AllowNullExprs>
+class ArraySectionExprBase : public Expr {
+  enum {BASE, LOWER_BOUND, LENGTH, END_EXPR = NumSubExprs};
+  Stmt *SubExprs[END_EXPR];
+  SourceLocation ColonLocFirst;
+  SourceLocation RBracketLoc;
+
+  protected:
+    ArraySectionExprBase(StmtClass SC, Expr *Base, Expr *LowerBound,
+                         Expr *Length, QualType Type, ExprValueKind VK,
+                         ExprObjectKind OK, SourceLocation ColonLocFirst,
+                         SourceLocation RBracketLoc)
+        : Expr(SC, Type, VK, OK), ColonLocFirst(ColonLocFirst),
+          RBracketLoc(RBracketLoc) {
+      setBase(Base);
+      setLowerBound(LowerBound);
+      setLength(Length);
+    }
+
+    explicit ArraySectionExprBase(StmtClass SC, EmptyShell Shell)
+        : Expr(SC, Shell) {}
+
+    void setSubExpr(unsigned Idx, Expr *SubExpr) {
+      assert(Idx > LENGTH &&
+             "setting sub expression owned by ArraySectionExprBase: Should be "
+             "using the direct 'setter' functions");
+      assert((SubExpr || AllowNullExprs) && "Null expression when not allowed");
+      SubExprs[Idx] = SubExpr;
+    }
+
+    Expr *getSubExpr(unsigned Idx) {
+      assert(Idx > LENGTH &&
+             "getting sub expression owned by ArraySectionExprBase: Should be "
+             "using the direct 'getter' functions");
+      return cast_or_null<Expr>(SubExprs[Idx]);
+    }
+    const Expr *getSubExpr(unsigned Idx) const {
+      assert(Idx > LENGTH &&
+             "getting sub expression owned by ArraySectionExprBase: Should be "
+             "using the direct 'getter' functions");
+      return cast_or_null<Expr>(SubExprs[Idx]);
+    }
+
+  public:
+  /// Get base of the array section.
+  Expr *getBase() { return cast<Expr>(SubExprs[BASE]); }
+  const Expr *getBase() const { return cast<Expr>(SubExprs[BASE]); }
+  /// Set base of the array section.
+  void setBase(Expr *E) {
+    assert((E || AllowNullExprs) && "Null expression when not allowed");
+    SubExprs[BASE] = E;
+  }
+
+  /// Get lower bound of array section.
+  Expr *getLowerBound() { return cast_or_null<Expr>(SubExprs[LOWER_BOUND]); }
+  const Expr *getLowerBound() const {
+    return cast_or_null<Expr>(SubExprs[LOWER_BOUND]);
+  }
+  /// Set lower bound of the array section.
+  void setLowerBound(Expr *E) {
+    assert((E || AllowNullExprs) && "Null expression when not allowed");
+    SubExprs[LOWER_BOUND] = E;
+  }
+
+  /// Get length of array section.
+  Expr *getLength() { return cast_or_null<Expr>(SubExprs[LENGTH]); }
+  const Expr *getLength() const { return cast_or_null<Expr>(SubExprs[LENGTH]); }
+  /// Set length of the array section.
+  void setLength(Expr *E) {
+    assert((E || AllowNullExprs) && "Null expression when not allowed");
+    SubExprs[LENGTH] = E;
+  }
+
+  SourceLocation getBeginLoc() const LLVM_READONLY {
+    return getBase()->getBeginLoc();
+  }
+
+  SourceLocation getEndLoc() const LLVM_READONLY { return RBracketLoc; }
+
+  SourceLocation getColonLocFirst() const { return ColonLocFirst; }
+  void setColonLocFirst(SourceLocation L) { ColonLocFirst = L; }
+
+  SourceLocation getRBracketLoc() const { return RBracketLoc; }
+  void setRBracketLoc(SourceLocation L) { RBracketLoc = L; }
+
+  SourceLocation getExprLoc() const LLVM_READONLY {
+    return getBase()->getExprLoc();
+  }
+    child_range children() {
+      return child_range(&SubExprs[BASE], &SubExprs[END_EXPR]);
+    }
+
+    const_child_range children() const {
+      return const_child_range(&SubExprs[BASE], &SubExprs[END_EXPR]);
+    }
+};
+
 /// Frontend produces RecoveryExprs on semantic errors that prevent creating
 /// other well-formed expressions. E.g. when type-checking of a binary operator
 /// fails, we cannot produce a BinaryOperator expression. Instead, we can choose
diff --git a/clang/include/clang/AST/ExprOpenMP.h b/clang/include/clang/AST/ExprOpenMP.h
index be5b1f3fdd112f..c410c82d9469a3 100644
--- a/clang/include/clang/AST/ExprOpenMP.h
+++ b/clang/include/clang/AST/ExprOpenMP.h
@@ -53,92 +53,46 @@ namespace clang {
 /// When the length is absent it defaults to ⌈(size − lower-bound)/stride⌉,
 /// where size is the size of the array dimension. When the lower-bound is
 /// absent it defaults to 0.
-class OMPArraySectionExpr : public Expr {
+namespace OMPArraySectionIndices {
   enum { BASE, LOWER_BOUND, LENGTH, STRIDE, END_EXPR };
-  Stmt *SubExprs[END_EXPR];
-  SourceLocation ColonLocFirst;
+}
+class OMPArraySectionExpr
+    : public ArraySectionExprBase<OMPArraySectionIndices::END_EXPR,
+                                  /*AllowNullExprs=*/true> {
   SourceLocation ColonLocSecond;
-  SourceLocation RBracketLoc;
 
 public:
   OMPArraySectionExpr(Expr *Base, Expr *LowerBound, Expr *Length, Expr *Stride,
                       QualType Type, ExprValueKind VK, ExprObjectKind OK,
                       SourceLocation ColonLocFirst,
                       SourceLocation ColonLocSecond, SourceLocation RBracketLoc)
-      : Expr(OMPArraySectionExprClass, Type, VK, OK),
-        ColonLocFirst(ColonLocFirst), ColonLocSecond(ColonLocSecond),
-        RBracketLoc(RBracketLoc) {
-    SubExprs[BASE] = Base;
-    SubExprs[LOWER_BOUND] = LowerBound;
-    SubExprs[LENGTH] = Length;
-    SubExprs[STRIDE] = Stride;
+      : ArraySectionExprBase(OMPArraySectionExprClass, Base, LowerBound, Length,
+                             Type, VK, OK, ColonLocFirst, RBracketLoc) {
+    setSubExpr(OMPArraySectionIndices::STRIDE, Stride);
     setDependence(computeDependence(this));
   }
 
   /// Create an empty array section expression.
   explicit OMPArraySectionExpr(EmptyShell Shell)
-      : Expr(OMPArraySectionExprClass, Shell) {}
-
-  /// An array section can be written only as Base[LowerBound:Length].
-
-  /// Get base of the array section.
-  Expr *getBase() { return cast<Expr>(SubExprs[BASE]); }
-  const Expr *getBase() const { return cast<Expr>(SubExprs[BASE]); }
-  /// Set base of the array section.
-  void setBase(Expr *E) { SubExprs[BASE] = E; }
+      : ArraySectionExprBase(OMPArraySectionExprClass, Shell) {}
 
   /// Return original type of the base expression for array section.
   static QualType getBaseOriginalType(const Expr *Base);
 
-  /// Get lower bound of array section.
-  Expr *getLowerBound() { return cast_or_null<Expr>(SubExprs[LOWER_BOUND]); }
-  const Expr *getLowerBound() const {
-    return cast_or_null<Expr>(SubExprs[LOWER_BOUND]);
-  }
-  /// Set lower bound of the array section.
-  void setLowerBound(Expr *E) { SubExprs[LOWER_BOUND] = E; }
-
-  /// Get length of array section.
-  Expr *getLength() { return cast_or_null<Expr>(SubExprs[LENGTH]); }
-  const Expr *getLength() const { return cast_or_null<Expr>(SubExprs[LENGTH]); }
-  /// Set length of the array section.
-  void setLength(Expr *E) { SubExprs[LENGTH] = E; }
-
   /// Get stride of array section.
-  Expr *getStride() { return cast_or_null<Expr>(SubExprs[STRIDE]); }
-  const Expr *getStride() const { return cast_or_null<Expr>(SubExprs[STRIDE]); }
-  /// Set length of the array section.
-  void setStride(Expr *E) { SubExprs[STRIDE] = E; }
-
-  SourceLocation getBeginLoc() const LLVM_READONLY {
-    return getBase()->getBeginLoc();
+  Expr *getStride() { return getSubExpr(OMPArraySectionIndices::STRIDE); }
+  const Expr *getStride() const {
+    return getSubExpr(OMPArraySectionIndices::STRIDE);
   }
-  SourceLocation getEndLoc() const LLVM_READONLY { return RBracketLoc; }
-
-  SourceLocation getColonLocFirst() const { return ColonLocFirst; }
-  void setColonLocFirst(SourceLocation L) { ColonLocFirst = L; }
+  /// Set length of the array section.
+  void setStride(Expr *E) { setSubExpr(OMPArraySectionIndices::STRIDE, E); }
 
   SourceLocation getColonLocSecond() const { return ColonLocSecond; }
   void setColonLocSecond(SourceLocation L) { ColonLocSecond = L; }
 
-  SourceLocation getRBracketLoc() const { return RBracketLoc; }
-  void setRBracketLoc(SourceLocation L) { RBracketLoc = L; }
-
-  SourceLocation getExprLoc() const LLVM_READONLY {
-    return getBase()->getExprLoc();
-  }
-
   static bool classof(const Stmt *T) {
     return T->getStmtClass() == OMPArraySectionExprClass;
   }
-
-  child_range children() {
-    return child_range(&SubExprs[BASE], &SubExprs[END_EXPR]);
-  }
-
-  const_child_range children() const {
-    return const_child_range(&SubExprs[BASE], &SubExprs[END_EXPR]);
-  }
 };
 
 /// An explicit cast in C or a C-style cast in C++, which uses the syntax

@erichkeane
Copy link
Collaborator Author

Note: this is one of my approaches. This ends up touching the OpenMP work 'the least' (in that this is the entirety of the OpenMP changes as best I can tell), at the expense of more work/a separate AST Node for OpenACC.

The OTHER approach I have is to move/rename OpenMPArraySectionExpr to just ArraySectionExpr, and give it an IsOpenACC or IsOpenMP type function, and change everywhere it is used to check/assert on that.

I'm totally open to other ideas though, I'd love to have a good solution for this.

Copy link

github-actions bot commented Apr 22, 2024

✅ With the latest revision this PR passed the C/C++ code formatter.

Comment on lines 6668 to 6673
/// Set base of the array section.
void setBase(Expr *E) {
assert((E || AllowNullExprs) && "Null expression when not allowed");
SubExprs[BASE] = E;
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you make it private or protected?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made it protected (since I want the constructors of the inheritors to use it) and had to make ASTStmtReader a friend.

Comment on lines 6679 to 6683
/// Set lower bound of the array section.
void setLowerBound(Expr *E) {
assert((E || AllowNullExprs) && "Null expression when not allowed");
SubExprs[LOWER_BOUND] = E;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same

Comment on lines 6688 to 6692
/// Set length of the array section.
void setLength(Expr *E) {
assert((E || AllowNullExprs) && "Null expression when not allowed");
SubExprs[LENGTH] = E;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same

Copy link
Member

@alexey-bataev alexey-bataev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LG

@erichkeane
Copy link
Collaborator Author

@njames93 : My test failure has to do with the "NodeIntrospection" stuff that you seemed to review ~3 years ago: https://reviews.llvm.org/D93164

At the moment, I cannot even figure out how to get it to build, but I suspect it is getting confused in some way by having a non-AST-node base class of a type? Any help you could give would be appreciated.

@erichkeane
Copy link
Collaborator Author

@alexey-bataev
Not having this new 'base' type as a member of the StmtNodes.td results in one of the Clang tools failing (see the CI). AND, since it is a template, it can't do that.

I can't make it a Mixin, because it needs to provide some of the Expr-type functions regarding source location/etc.

So I don't have a great idea on how to have reuse between the two while still having them be separate AST nodes (as there IS a bit of a difference in behaviors).

I COULD just move the OMP type to Expr.h (renamed as just ArraySectionExpr), add a flag for OMP vs OpenACC, then make the logic of each function check it, but I'm tentative as to whether that is better.

@alexey-bataev
Copy link
Member

@alexey-bataev Not having this new 'base' type as a member of the StmtNodes.td results in one of the Clang tools failing (see the CI). AND, since it is a template, it can't do that.

I can't make it a Mixin, because it needs to provide some of the Expr-type functions regarding source location/etc.

So I don't have a great idea on how to have reuse between the two while still having them be separate AST nodes (as there IS a bit of a difference in behaviors).

I COULD just move the OMP type to Expr.h (renamed as just ArraySectionExpr), add a flag for OMP vs OpenACC, then make the logic of each function check it, but I'm tentative as to whether that is better.

I'm fine with having just a single AST node

@erichkeane
Copy link
Collaborator Author

@alexey-bataev Not having this new 'base' type as a member of the StmtNodes.td results in one of the Clang tools failing (see the CI). AND, since it is a template, it can't do that.
I can't make it a Mixin, because it needs to provide some of the Expr-type functions regarding source location/etc.
So I don't have a great idea on how to have reuse between the two while still having them be separate AST nodes (as there IS a bit of a difference in behaviors).
I COULD just move the OMP type to Expr.h (renamed as just ArraySectionExpr), add a flag for OMP vs OpenACC, then make the logic of each function check it, but I'm tentative as to whether that is better.

I'm fine with having just a single AST node

Ok, I'll start on the work to make this happen.

This version of the patch generializes the OMPArraySectionExpr to
be more generic, and renames it ArraySectionExpr. There is a LOT of work
involved obviously, but it is pretty rote. I've also added stubs for the
OpenACC implementation/use of this, but there is currently no Sema for
it implemented.

Note that I've found a few small 'bugs' in the OpenMP implementation
while auditing hte code, which I'll mention inline.
@llvmbot llvmbot added clang:modules C++20 modules and Clang Header Modules clang:codegen IR generation bugs: mangling, exceptions, etc. clang:static analyzer labels Apr 24, 2024
Copy link
Collaborator Author

@erichkeane erichkeane left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I opted to leave the type of both array sections the same as it seems like it will save a bunch of work and complexity, at the expense of 1 'print', and potentially 1 diagnostic. Let me know if you think this is an important distinction!

@@ -11163,7 +11163,7 @@ def err_omp_declare_mapper_redefinition : Error<
"redefinition of user-defined mapper for type %0 with name %1">;
def err_omp_invalid_mapper: Error<
"cannot find a valid user-defined mapper for type %0 with name %1">;
def err_omp_array_section_use : Error<"OpenMP array section is not allowed here">;
def err_array_section_use : Error<"array section is not allowed here">;
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I generalized this diagnostic as well, though I see I could PERHAPS get this right based on the type of the Expression above. WDYT?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be good to select proper extension type here in the diagnostic, at least for OpenMP

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright, will do!

auto D = E->getBase()->getDependence();
if (auto *LB = E->getLowerBound())
D |= LB->getDependence();
if (auto *Len = E->getLength())
D |= Len->getDependence();

if (E->isOMPArraySection()) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that this is a 'bug fix' here, we weren't computing dependence based on stride.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice catch.

case OMPArraySection:
return "<OpenMP array section type>";
case ArraySection:
return "<array section type>";
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generalized again, but this is one I couldn't really do anything about, all I have is the 'type', not the associated expression.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok

LowerBound.get() == E->getLowerBound() && Length.get() == E->getLength())
LowerBound.get() == E->getLowerBound() &&
Length.get() == E->getLength() &&
(E->isOpenACCArraySection() || Stride.get() == E->getStride()))
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is another bug fix, we previously weren't checking this correctly here.

@erichkeane
Copy link
Collaborator Author

I think I've got everything done you were concerned about. Let me know if you see anything else!

Copy link
Member

@alexey-bataev alexey-bataev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LG

@erichkeane erichkeane merged commit 39adc8f into llvm:main Apr 25, 2024
5 of 6 checks passed
Comment on lines 75 to 76
// FIXME: Once we have a new array-section type to represent OpenACC as
// well, change this error message.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This FIXME was resolved by this commit, consider removing it in a trivial follow-up change.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, done in bb1a8bb

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:codegen IR generation bugs: mangling, exceptions, etc. clang:frontend Language frontend issues, e.g. anything involving "Sema" clang:modules C++20 modules and Clang Header Modules clang:openmp OpenMP related changes to Clang clang:static analyzer clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants