Skip to content

[Clang] Generate the GEP instead of adding AST nodes #73730

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

Closed
wants to merge 35 commits into from

Conversation

bwendling
Copy link
Collaborator

This is an alternative to #73465. It generates the GEP directly. It's not tested well, but it might be a nicer solution rather than adding AST nodes.

PTAL

@llvmbot llvmbot added clang Clang issues not falling into any other category clang:codegen IR generation bugs: mangling, exceptions, etc. labels Nov 29, 2023
@llvmbot
Copy link
Member

llvmbot commented Nov 29, 2023

@llvm/pr-subscribers-clang-codegen

@llvm/pr-subscribers-clang

Author: Bill Wendling (bwendling)

Changes

This is an alternative to #73465. It generates the GEP directly. It's not tested well, but it might be a nicer solution rather than adding AST nodes.

PTAL


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

3 Files Affected:

  • (modified) clang/lib/CodeGen/CGBuiltin.cpp (+1-2)
  • (modified) clang/lib/CodeGen/CGExpr.cpp (+67-32)
  • (modified) clang/lib/CodeGen/CodeGenFunction.h (+2-2)
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index c83ea966fdeadc6..487bd14244e531c 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -916,8 +916,7 @@ CodeGenFunction::emitFlexibleArrayMemberSize(const Expr *E, unsigned Type,
 
   // Build a load of the counted_by field.
   bool IsSigned = CountedByFD->getType()->isSignedIntegerType();
-  const Expr *CountedByExpr = BuildCountedByFieldExpr(Base, CountedByFD);
-  Value *CountedByInst = EmitAnyExprToTemp(CountedByExpr).getScalarVal();
+  Value *CountedByInst = BuildCountedByFieldExpr(Base, CountedByFD);
   llvm::Type *CountedByTy = CountedByInst->getType();
 
   // Build a load of the index and subtract it from the count.
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 9fe8f1d7da780c8..b77d9d8904dfc2d 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -26,6 +26,7 @@
 #include "clang/AST/Attr.h"
 #include "clang/AST/DeclObjC.h"
 #include "clang/AST/NSAPI.h"
+#include "clang/AST/StmtVisitor.h"
 #include "clang/Basic/Builtins.h"
 #include "clang/Basic/CodeGenOptions.h"
 #include "clang/Basic/SourceManager.h"
@@ -940,8 +941,7 @@ static llvm::Value *getArrayIndexingBound(CodeGenFunction &CGF,
 
     if (const ValueDecl *VD = CGF.FindCountedByField(Base)) {
       IndexedType = Base->getType();
-      const Expr *E = CGF.BuildCountedByFieldExpr(Base, VD);
-      return CGF.EmitAnyExprToTemp(E).getScalarVal();
+      return CGF.BuildCountedByFieldExpr(Base, VD);
     }
   }
 
@@ -956,42 +956,77 @@ static llvm::Value *getArrayIndexingBound(CodeGenFunction &CGF,
   return nullptr;
 }
 
-const Expr *
+namespace  {
+
+struct MemberExprBaseVisitor
+    : public StmtVisitor<MemberExprBaseVisitor, Expr *> {
+  MemberExprBaseVisitor() = default;
+
+  //===--------------------------------------------------------------------===//
+  //                            Visitor Methods
+  //===--------------------------------------------------------------------===//
+
+  Expr *Visit(Expr *E) {
+    return StmtVisitor<MemberExprBaseVisitor, Expr *>::Visit(E);
+  }
+
+  Expr *VisitArraySubscriptExpr(ArraySubscriptExpr *E) {
+    return Visit(E->getBase());
+  }
+  Expr *VisitCastExpr(CastExpr *E) {
+    return Visit(E->getSubExpr());
+  }
+  Expr *VisitDeclRefExpr(DeclRefExpr *E) {
+    return E;
+  }
+  Expr *VisitMemberExpr(MemberExpr *E) {
+    return Visit(E->getBase());
+  }
+  Expr *VisitParenExpr(ParenExpr *E) {
+    return Visit(E->getSubExpr());
+  }
+  Expr *VisitUnaryOperator(UnaryOperator *E) {
+    return Visit(E->getSubExpr());
+  }
+};
+
+} // end anonymous namespace
+
+llvm::Value *
 CodeGenFunction::BuildCountedByFieldExpr(const Expr *Base,
                                          const ValueDecl *CountedByVD) {
   // Find the outer struct expr (i.e. p in p->a.b.c.d).
-  Expr *CountedByExpr = const_cast<Expr *>(Base)->IgnoreParenImpCasts();
-
-  // Work our way up the expression until we reach the DeclRefExpr.
-  while (!isa<DeclRefExpr>(CountedByExpr))
-    if (const auto *ME = dyn_cast<MemberExpr>(CountedByExpr))
-      CountedByExpr = ME->getBase()->IgnoreParenImpCasts();
-
-  // Add back an implicit cast to create the required pr-value.
-  CountedByExpr = ImplicitCastExpr::Create(
-      getContext(), CountedByExpr->getType(), CK_LValueToRValue, CountedByExpr,
-      nullptr, VK_PRValue, FPOptionsOverride());
-
-  if (const auto *IFD = dyn_cast<IndirectFieldDecl>(CountedByVD)) {
-    // The counted_by field is inside an anonymous struct / union. The
-    // IndirectFieldDecl has the correct order of FieldDecls to build this
-    // easily. (Yay!)
-    for (NamedDecl *ND : IFD->chain()) {
-      auto *VD = cast<ValueDecl>(ND);
-      CountedByExpr =
-          MemberExpr::CreateImplicit(getContext(), CountedByExpr,
-                                     CountedByExpr->getType()->isPointerType(),
-                                     VD, VD->getType(), VK_LValue, OK_Ordinary);
+  Expr *CountedByExpr = MemberExprBaseVisitor().Visit(const_cast<Expr *>(Base));
+
+  llvm::Value *Res =
+      CountedByExpr->getType()->isPointerType()
+          ? EmitPointerWithAlignment(CountedByExpr).getPointer()
+          : EmitDeclRefLValue(cast<DeclRefExpr>(CountedByExpr)).getPointer(*this);
+
+  auto *Zero = llvm::ConstantInt::get(Int32Ty, 0);
+  SmallVector<llvm::Value *, 4> Indices{Zero};
+  if (const auto *FD = dyn_cast<FieldDecl>(CountedByVD)) {
+    Indices.emplace_back(llvm::ConstantInt::get(Int32Ty, FD->getFieldIndex()));
+  } else if (const auto *I = dyn_cast<IndirectFieldDecl>(CountedByVD)) {
+    for (auto *ND : I->chain()) {
+      if (auto *FD = dyn_cast<FieldDecl>(ND)) {
+        Indices.emplace_back(llvm::ConstantInt::get(Int32Ty, FD->getFieldIndex()));
+      }
     }
-  } else {
-    CountedByExpr = MemberExpr::CreateImplicit(
-        getContext(), const_cast<Expr *>(CountedByExpr),
-        CountedByExpr->getType()->isPointerType(),
-        const_cast<ValueDecl *>(CountedByVD), CountedByVD->getType(), VK_LValue,
-        OK_Ordinary);
   }
 
-  return CountedByExpr;
+  const DeclContext *DC = CountedByVD->getLexicalDeclContext();
+  const auto *CountedByRD = cast<RecordDecl>(DC);
+
+  llvm::Type *Ty = CGM.getTypes().ConvertType(QualType(CountedByRD->getTypeForDecl(), 0));
+  Res = Builder.CreateGEP(Ty, Res, Indices, "bork");
+
+  QualType CountedByTy(CountedByVD->getType());
+  TypeInfo TI = getContext().getTypeInfo(CountedByTy);
+  Ty = CGM.getTypes().ConvertType(CountedByTy);
+  Address Addr(Res, Ty, CharUnits::fromQuantity(TI.Align / getContext().getCharWidth()));
+
+  return Builder.CreateLoad(Addr, Res, "fork");
 }
 
 const ValueDecl *
diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index 618e78809db408b..e3137eb65dbad23 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -3032,8 +3032,8 @@ class CodeGenFunction : public CodeGenTypeCache {
   const ValueDecl *FindCountedByField(const Expr *Base);
 
   /// Build an expression accessing the "counted_by" field.
-  const Expr *BuildCountedByFieldExpr(const Expr *Base,
-                                      const ValueDecl *CountedByVD);
+  llvm::Value *BuildCountedByFieldExpr(const Expr *Base,
+                                       const ValueDecl *CountedByVD);
 
   llvm::Value *EmitScalarPrePostIncDec(const UnaryOperator *E, LValue LV,
                                        bool isInc, bool isPre);

Copy link

github-actions bot commented Nov 29, 2023

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

Copy link
Member

@nickdesaulniers nickdesaulniers left a comment

Choose a reason for hiding this comment

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

Hey, that looks pretty good! I'm surprised it wasn't too much more work to convert to generating the GEP directly. Nice job!

Does this change result in codegen differences in IR? (was expecting test changes that used "fork" loads and "bork" GEPs)

@bwendling
Copy link
Collaborator Author

Hey, that looks pretty good! I'm surprised it wasn't too much more work to convert to generating the GEP directly. Nice job!

Does this change result in codegen differences in IR? (was expecting test changes that used "fork" loads and "bork" GEPs)

Thanks. There will be. There's an issue I need to hammer out with the "union_of_fams". I wanted to get everyone's opinion on this method though before I invested a ton more time on it first. :-)

Copy link
Collaborator

@AaronBallman AaronBallman left a comment

Choose a reason for hiding this comment

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

I added the codegen code owners for opinions, but personally, I prefer the direction this patch is heading over the one in #73465

@bwendling
Copy link
Collaborator Author

I added the codegen code owners for opinions, but personally, I prefer the direction this patch is heading over the one in #73465

This does seem to be a cleaner way of doing things. Thanks, @nickdesaulniers, for the suggestion. :-)

@bwendling
Copy link
Collaborator Author

bwendling commented Nov 29, 2023

@AaronBallman It's not letting me comment on your comment. While a compound literal doesn't have a DRE, it's also not a flexible array, so it's not supposed to get this far. Note that this method should only be executed if the flexible array member exists and has the counted_by attribute.

@AaronBallman
Copy link
Collaborator

@AaronBallman It's not letting me comment on your comment. While a compound literal doesn't have a DRE, it's also not a flexible array, so it's not supposed to get this far. Note that this method should only be executed if the flexible array member exists and has the counted_by attribute.

Sorry, I was being lazy with my examples and leaving stuff off.

struct S {
  int x, y;
  int blah[] __counted_by(x);
};

int main() {
  (struct S){ 1, 2 }.blah[0]; // incorrect code
}

@bwendling
Copy link
Collaborator Author

@AaronBallman It's not letting me comment on your comment. While a compound literal doesn't have a DRE, it's also not a flexible array, so it's not supposed to get this far. Note that this method should only be executed if the flexible array member exists and has the counted_by attribute.

Sorry, I was being lazy with my examples and leaving stuff off.

struct S {
  int x, y;
  int blah[] __counted_by(x);
};

int main() {
  (struct S){ 1, 2 }.blah[0]; // incorrect code
}

Ah! I see what you mean now. I'll make sure we can catch this.

@bwendling
Copy link
Collaborator Author

This should be ready for a final review now.

@efriedma-quic
Copy link
Collaborator

efriedma-quic commented Nov 29, 2023

CodeGenFunction::FindCountedByField finds a field with a corresponding base expression. Currently, it throws away the base expression. And the code you've just written tries to recompute the base. Instead of doing this dance, can we just make CodeGenFunction::FindCountedByField return both the field and the base expression?

I'm extremely suspicious of the way MemberExprBaseVisitor works; it's basically never right in CodeGen to treat all CastExprs the same way, or handle MemberExprs without checking isArrow().

@bwendling
Copy link
Collaborator Author

CodeGenFunction::FindCountedByField finds a field with a corresponding base expression. Currently, it throws away the base expression. And the code you've just written tries to recompute the base. Instead of doing this dance, can we just make CodeGenFunction::FindCountedByField return both the field and the base expression?

Maybe... CodeGenFunction::FindCountedByField isn't super concerned with finding the DeclRefExpr. It just goes down the RecordDecl to find the Decl with the counted_by attribute. It jumps through some hoops to see if the FAM is in a sub-structure. I'll look into it though.

I'm extremely suspicious of the way MemberExprBaseVisitor works; it's basically never right in CodeGen to treat all CastExprs the same way, or handle MemberExprs without checking isArrow().

I initially had a check to see if a cast had the same RecordDecl type as the one we're looking for; if so return that expression. Would that help in this case? I'm also wondering about the ParenExpr. Should they get the same treatment?

For the MemberExprs, I could check the isArrow(), but in that case we'd be dealing with a pointer into another object, which ... ick. We should probably return nullptr in that instance. At this point though, we should be dealing with a more-or-less well structured MemberExpr to DeclRefExpr expression. I.e. it'll be a list of MemberExprs with potential casts and maybe a UnaryOperand thrown in for good measure until we get to a DeclRefExpr or CompoundLiteralExpr. Of course, it's hard to state anything for sure with C, but I'll need examples to help harden this code.

@efriedma-quic
Copy link
Collaborator

I'd like to see a few tests involving multiple arrows in the same expression. (If my understanding is correct, you want to cut the recursion when you see an arrow member.)

Looking at the code again, I guess FindCountedByField doesn't explicitly compute the base expression, so maybe that doesn't work as-is. But tying the recursion over the expression to the recursion over the record would be a good way to ensure the base expression is actually the expression you want. The way the code is currently written seems to strongly assume that if there's a relevant counted_by field, it's usable no matter how the expression is actually structured.

@bwendling
Copy link
Collaborator Author

I'd like to see a few tests involving multiple arrows in the same expression. (If my understanding is correct, you want to cut the recursion when you see an arrow member.)

Correct. I'll add the code and some testcases.

Looking at the code again, I guess FindCountedByField doesn't explicitly compute the base expression, so maybe that doesn't work as-is. But tying the recursion over the expression to the recursion over the record would be a good way to ensure the base expression is actually the expression you want. The way the code is currently written seems to strongly assume that if there's a relevant counted_by field, it's usable no matter how the expression is actually structured.

There are Sema checks to ensure that counted_by is applied only to flexible array members. I am crossing my fingers here that the Base expression is well behaved. But I agree that tying the two will make for a stronger check here. Let me work on that.

return E;
return Visit(E->getBase());
}
Expr *VisitCastExpr(CastExpr *E) { return Visit(E->getSubExpr()); }
Copy link
Contributor

Choose a reason for hiding this comment

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

So this seems to look through cast expressions. Does it mean casts to/from flexible base would work as below? Are these intended?

struct foo {
  int c;
  int fam[__counted_by(c)];
};

void bar(void) {
  char buf[100];
  __bdos((struct foo *)buf, 0); // the base foo will not be identified. is it intended?
};

struct not_foo {
  int c;
};

void baz(struct foo *p) {
  __bdos((struct not_foo*)p, 0); // the base will be identified as `struct foo *`. is it intended?
};

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

In a way, yes. I'm being extra careful because this is a security feature. Perhaps I'm being too cautious?

auto *Idx = EmitIdxAfterBase(/*Promote*/true);

if (SanOpts.has(SanitizerKind::ArrayBounds)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Would it make it easier if you use CodeGenFunction::EmitLValueForField instead to get the fields?

auto *Idx = EmitIdxAfterBase(/*Promote*/true);

if (SanOpts.has(SanitizerKind::ArrayBounds)) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Recursively skipping over all GEPs seems, at best, extremely fragile; there could be GEPs for reasons other than the member expressions you care about.

@bwendling
Copy link
Collaborator Author

@efriedma-quic, @nickdesaulniers, @rapidsna:

This is the latest version of this patch. I believe it's ready to submit. I really tried to implement @efriedma-quic's idea, but there were too corner cases that made it much trickier than first imagined. I know that it's not impossible to implement it, but the further I went along the more I realized that the infrastructure just isn't sufficient to deal with it. I would much rather use EmitLValueForField, but it's not able to handle fields deeper than the current record level. A comment in the method even admits that it can't handle anonymous structs and unions.

It would be beneficial to rework EmitLValueForField to handle arbitrarily deep FieldDecls, but that's frankly beyond the scope of this patch.

This patch takes advantage of the fact that the Clang front-end appends the "source element type" to the GEPs it generates. So it's therefore possible to find the pointer to the correct struct base.

PTAL.

ME->getMemberDecl()->hasAttr<CountedByAttr>()) {
RecordDecl *RD = ME->getMemberDecl()
->getDeclContext()
->getOuterLexicalRecordContext();
Copy link
Contributor

@rjmccall rjmccall Dec 15, 2023

Choose a reason for hiding this comment

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

Hmm. Is the use of getOuterLexicalRecordContext() just trying to look through anonymous records? It makes sense that counted_by paths for members of anonymous records would be specified starting from the containing non-anonymous type, so we need to find that type. But I think getOuterLexicalRecordContext() does too much here: it's actively wrong whenever you have a record defined lexically within some other record, which doesn't always look like the anonymous record pattern. For example:

// C++
struct A { // <- the outer lexical record context
  struct B {
    int member[] __counted_by(...);
  };
};

// C
struct A { // <- the outer lexical record context, I'm pretty sure
  struct {
    int member[] __counted_by(...);
  } *ptr;
};

I think getNonTransparentContext() would be right, but you should also be able to get the base type of the counted_by path from the attribute itself.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The first example is why I'm using getOuterLexicalRecordContext, because we want to start looking for the count field from the beginning of the most-enclosing struct.

The second example isn't currently allowed (you'll get an error unless the count field is in the outer structure). It's probably a bug when sema checking the attribute. I'll need to address that, but I'd like to do it as a separate patch. If the second example is an anonymous struct, then it's iffy if you can define that as a flexible array on its own. If it's a "named" struct, then it should be quite possible to do that.

Copy link
Contributor

@rjmccall rjmccall Dec 15, 2023

Choose a reason for hiding this comment

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

How is the first example supposed to work? Is the expectation that __counted_by will only be enforced if the access expression happens to previously pass through some instance of A, e.g. someA->x.y.z.someB->member, and it better be the instance we have in mind? What happens if I just do someB->member?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Basically, yes. If you have something like:

void foo(struct B *b) {
  bar(b->member[12]);`
}

Then there won't be a check, because we won't be able to "see" the count in struct A. I know it's restrictive, but it's the best we can do given that someB may not be allocated within an enclosing struct A.

auto *Idx = EmitIdxAfterBase(/*Promote*/true);

if (SanOpts.has(SanitizerKind::ArrayBounds)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we not just reverse the member access to get back to the root of the counted_by path? Presumably the root of the counted_by path for a field is always the non-transparent context of the field. This should always be at a constant offset from the address point of the field — there's no such thing as an anonymous array. Or am I misunderstanding something important about the constraints on counted_by paths?

… This change also restricts the 'counted_by' attribute to a top-level flexible array member.

or:

"Hal, have you replaced that lightbulb in the kitchen?"
"WHAT DOES IT LOOK LIKE I'M DOING?!"
@nathanchance
Copy link
Member

The latest version of this change causes test failures for me:

$ cmake \
	-B build \
	-G Ninja \
	-S llvm \
	--log-level=NOTICE \
	-Wno-dev \
	-DCMAKE_BUILD_TYPE=Release \
	-DCMAKE_C_COMPILER=clang \
	-DCMAKE_CXX_COMPILER=clang++ \
	-DLLVM_ENABLE_ASSERTIONS=true \
	-DLLVM_ENABLE_PROJECTS=clang \
	-DLLVM_USE_LINKER=lld

$ ninja -C build check-clang
...
FAIL: Clang :: OpenMP/distribute_parallel_for_simd_misc_messages.c (12637 of 20466)
******************** TEST 'Clang :: OpenMP/distribute_parallel_for_simd_misc_messages.c' FAILED ********************
Exit Code: 139

Command Output (stderr):
--
RUN: at line 1: /mnt/nvme/tmp/build/llvm-bisect/bin/clang -cc1 -internal-isystem /mnt/nvme/tmp/build/llvm-bisect/lib/clang/18/include -nostdsysteminc -fsyntax-only -fopenmp -fopenmp-version=45 -verify=expected,omp45 /home/nathan/cbl/src/llvm-project/clang/test/OpenMP/distribute_parallel_for_simd_misc_messages.c -Wuninitialized
+ /mnt/nvme/tmp/build/llvm-bisect/bin/clang -cc1 -internal-isystem /mnt/nvme/tmp/build/llvm-bisect/lib/clang/18/include -nostdsysteminc -fsyntax-only -fopenmp -fopenmp-version=45 -verify=expected,omp45 /home/nathan/cbl/src/llvm-project/clang/test/OpenMP/distribute_parallel_for_simd_misc_messages.c -Wuninitialized
PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace, preprocessed source, and associated run script.
Stack dump:
0.      Program arguments: /mnt/nvme/tmp/build/llvm-bisect/bin/clang -cc1 -internal-isystem /mnt/nvme/tmp/build/llvm-bisect/lib/clang/18/include -nostdsysteminc -fsyntax-only -fopenmp -fopenmp-version=45 -verify=expected,omp45 /home/nathan/cbl/src/llvm-project/clang/test/OpenMP/distribute_parallel_for_simd_misc_messages.c -Wuninitialized
1.      /home/nathan/cbl/src/llvm-project/clang/test/OpenMP/distribute_parallel_for_simd_misc_messages.c:359:1: at annotation token
2.      /home/nathan/cbl/src/llvm-project/clang/test/OpenMP/distribute_parallel_for_simd_misc_messages.c:350:33: parsing function body 'test_safelen_simdlen'
3.      /home/nathan/cbl/src/llvm-project/clang/test/OpenMP/distribute_parallel_for_simd_misc_messages.c:350:33: in compound statement ('{}')
 #0 0x0000564839563fb8 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (/mnt/nvme/tmp/build/llvm-bisect/bin/clang+0x7787fb8)
 #1 0x0000564839561b7e llvm::sys::RunSignalHandlers() (/mnt/nvme/tmp/build/llvm-bisect/bin/clang+0x7785b7e)
 #2 0x0000564839564668 SignalHandler(int) Signals.cpp:0:0
 #3 0x00007f1976577710 (/usr/lib/libc.so.6+0x3e710)
 #4 0x000056483bd312a1 clang::Sema::ActOnFields(clang::Scope*, clang::SourceLocation, clang::Decl*, llvm::ArrayRef<clang::Decl*>, clang::SourceLocation, clang::SourceLocation, clang::ParsedAttributesView const&) (/mnt/nvme/tmp/build/llvm-bisect/bin/clang+0x9f552a1)
 #5 0x000056483c304887 clang::Sema::ActOnCapturedRegionError() (/mnt/nvme/tmp/build/llvm-bisect/bin/clang+0xa528887)
 #6 0x000056483c1cdb48 clang::Sema::ActOnOpenMPRegionEnd(clang::ActionResult<clang::Stmt*, true>, llvm::ArrayRef<clang::OMPClause*>) (/mnt/nvme/tmp/build/llvm-bisect/bin/clang+0xa3f1b48)
 #7 0x000056483bac79ec clang::Parser::ParseOpenMPDeclarativeOrExecutableDirective(clang::Parser::ParsedStmtContext, bool) (/mnt/nvme/tmp/build/llvm-bisect/bin/clang+0x9ceb9ec)
 #8 0x000056483ba955ba clang::Parser::ParseStatementOrDeclarationAfterAttributes(llvm::SmallVector<clang::Stmt*, 32u>&, clang::Parser::ParsedStmtContext, clang::SourceLocation*, clang::ParsedAttributes&, clang::ParsedAttributes&) (/mnt/nvme/tmp/build/llvm-bisect/bin/clang+0x9cb95ba)
 #9 0x000056483ba93972 clang::Parser::ParseStatementOrDeclaration(llvm::SmallVector<clang::Stmt*, 32u>&, clang::Parser::ParsedStmtContext, clang::SourceLocation*) (/mnt/nvme/tmp/build/llvm-bisect/bin/clang+0x9cb7972)
#10 0x000056483ba93810 clang::Parser::ParseStatement(clang::SourceLocation*, clang::Parser::ParsedStmtContext) (/mnt/nvme/tmp/build/llvm-bisect/bin/clang+0x9cb7810)
#11 0x000056483bac7999 clang::Parser::ParseOpenMPDeclarativeOrExecutableDirective(clang::Parser::ParsedStmtContext, bool) (/mnt/nvme/tmp/build/llvm-bisect/bin/clang+0x9ceb999)
#12 0x000056483ba955ba clang::Parser::ParseStatementOrDeclarationAfterAttributes(llvm::SmallVector<clang::Stmt*, 32u>&, clang::Parser::ParsedStmtContext, clang::SourceLocation*, clang::ParsedAttributes&, clang::ParsedAttributes&) (/mnt/nvme/tmp/build/llvm-bisect/bin/clang+0x9cb95ba)
#13 0x000056483ba93972 clang::Parser::ParseStatementOrDeclaration(llvm::SmallVector<clang::Stmt*, 32u>&, clang::Parser::ParsedStmtContext, clang::SourceLocation*) (/mnt/nvme/tmp/build/llvm-bisect/bin/clang+0x9cb7972)
#14 0x000056483ba9dbc1 clang::Parser::ParseCompoundStatementBody(bool) (/mnt/nvme/tmp/build/llvm-bisect/bin/clang+0x9cc1bc1)
#15 0x000056483ba9ec62 clang::Parser::ParseFunctionStatementBody(clang::Decl*, clang::Parser::ParseScope&) (/mnt/nvme/tmp/build/llvm-bisect/bin/clang+0x9cc2c62)
#16 0x000056483b9f2ded clang::Parser::ParseFunctionDefinition(clang::ParsingDeclarator&, clang::Parser::ParsedTemplateInfo const&, clang::Parser::LateParsedAttrList*) (/mnt/nvme/tmp/build/llvm-bisect/bin/clang+0x9c16ded)
#17 0x000056483ba63a3e clang::Parser::ParseDeclGroup(clang::ParsingDeclSpec&, clang::DeclaratorContext, clang::ParsedAttributes&, clang::SourceLocation*, clang::Parser::ForRangeInit*) (/mnt/nvme/tmp/build/llvm-bisect/bin/clang+0x9c87a3e)
#18 0x000056483b9f1a5f clang::Parser::ParseDeclOrFunctionDefInternal(clang::ParsedAttributes&, clang::ParsedAttributes&, clang::ParsingDeclSpec&, clang::AccessSpecifier) (/mnt/nvme/tmp/build/llvm-bisect/bin/clang+0x9c15a5f)
#19 0x000056483b9f12e6 clang::Parser::ParseDeclarationOrFunctionDefinition(clang::ParsedAttributes&, clang::ParsedAttributes&, clang::ParsingDeclSpec*, clang::AccessSpecifier) (/mnt/nvme/tmp/build/llvm-bisect/bin/clang+0x9c152e6)
#20 0x000056483b9f01bd clang::Parser::ParseExternalDeclaration(clang::ParsedAttributes&, clang::ParsedAttributes&, clang::ParsingDeclSpec*) (/mnt/nvme/tmp/build/llvm-bisect/bin/clang+0x9c141bd)
#21 0x000056483b9ee226 clang::Parser::ParseTopLevelDecl(clang::OpaquePtr<clang::DeclGroupRef>&, clang::Sema::ModuleImportState&) (/mnt/nvme/tmp/build/llvm-bisect/bin/clang+0x9c12226)
#22 0x000056483b9e8c1e clang::ParseAST(clang::Sema&, bool, bool) (/mnt/nvme/tmp/build/llvm-bisect/bin/clang+0x9c0cc1e)
#23 0x000056483a139d0f clang::FrontendAction::Execute() (/mnt/nvme/tmp/build/llvm-bisect/bin/clang+0x835dd0f)
#24 0x000056483a0a972d clang::CompilerInstance::ExecuteAction(clang::FrontendAction&) (/mnt/nvme/tmp/build/llvm-bisect/bin/clang+0x82cd72d)
#25 0x000056483a21c52e clang::ExecuteCompilerInvocation(clang::CompilerInstance*) (/mnt/nvme/tmp/build/llvm-bisect/bin/clang+0x844052e)
#26 0x0000564836f72202 cc1_main(llvm::ArrayRef<char const*>, char const*, void*) (/mnt/nvme/tmp/build/llvm-bisect/bin/clang+0x5196202)
#27 0x0000564836f6e5ad ExecuteCC1Tool(llvm::SmallVectorImpl<char const*>&, llvm::ToolContext const&) driver.cpp:0:0
#28 0x0000564836f6d299 clang_main(int, char**, llvm::ToolContext const&) (/mnt/nvme/tmp/build/llvm-bisect/bin/clang+0x5191299)
#29 0x0000564836f7ea31 main (/mnt/nvme/tmp/build/llvm-bisect/bin/clang+0x51a2a31)
#30 0x00007f1976560cd0 (/usr/lib/libc.so.6+0x27cd0)
#31 0x00007f1976560d8a __libc_start_main (/usr/lib/libc.so.6+0x27d8a)
#32 0x0000564836f6aaa5 _start (/mnt/nvme/tmp/build/llvm-bisect/bin/clang+0x518eaa5)
...
********************
********************
Failed Tests (17):
  Clang :: OpenMP/distribute_parallel_for_simd_misc_messages.c
  Clang :: OpenMP/distribute_simd_misc_messages.c
  Clang :: OpenMP/for_misc_messages.c
  Clang :: OpenMP/for_simd_misc_messages.c
  Clang :: OpenMP/master_taskloop_misc_messages.c
  Clang :: OpenMP/master_taskloop_simd_misc_messages.c
  Clang :: OpenMP/parallel_for_misc_messages.c
  Clang :: OpenMP/parallel_for_simd_misc_messages.c
  Clang :: OpenMP/parallel_master_taskloop_misc_messages.c
  Clang :: OpenMP/parallel_master_taskloop_simd_misc_messages.c
  Clang :: OpenMP/simd_misc_messages.c
  Clang :: OpenMP/target_parallel_for_misc_messages.c
  Clang :: OpenMP/target_parallel_for_simd_misc_messages.c
  Clang :: OpenMP/target_teams_distribute_parallel_for_simd_misc_messages.c
  Clang :: OpenMP/target_teams_distribute_simd_misc_messages.c
  Clang :: OpenMP/taskloop_misc_messages.c
  Clang :: OpenMP/taskloop_simd_misc_messages.c
...

I don't mean for this next bit to sound as aggressive or irritated as it might but #73168 has been open for almost a month now and it is actively preventing us from testing the Linux kernel against tip of tree LLVM because (for better or worse...) the kernel has started deploying __attribute__((__counted_by__(...))) annotations (and other developers are hitting this too: #75173). Can we please come to some consensus so that issue can be resolved?

@bwendling
Copy link
Collaborator Author

I don't mean for this next bit to sound as aggressive or irritated as it might but #73168 has been open for almost a month now and it is actively preventing us from testing the Linux kernel against tip of tree LLVM because (for better or worse...) the kernel has started deploying __attribute__((__counted_by__(...))) annotations (and other developers are hitting this too: #75173).

Apparently, the Scope variable can be NULL? Fix.ed

Can we please come to some consensus so that issue can be resolved?

This supposed bug fix snowballed out of control. I'd be happy to revert to a previous version and then submit the newer work as a improvements.

@nickdesaulniers
Copy link
Member

Can we please come to some consensus so that issue can be resolved?

This supposed bug fix snowballed out of control. I'd be happy to revert to a previous version and then submit the newer work as a improvements.

Linux kernel builds have been broken since the initial feature landed. With timing around the holidays, I wonder if we should consider backing everything out.

If the base commit ships in clang-18, then the linux kernel feature test macros will have to be converted to version test macros to work around issues with the initial implementation. Either this fix needs to land BEFORE release/18.x branches, or the entire feature backed out.

@bwendling
Copy link
Collaborator Author

Can we please come to some consensus so that issue can be resolved?

This supposed bug fix snowballed out of control. I'd be happy to revert to a previous version and then submit the newer work as a improvements.

Linux kernel builds have been broken since the initial feature landed. With timing around the holidays, I wonder if we should consider backing everything out.

If the base commit ships in clang-18, then the linux kernel feature test macros will have to be converted to version test macros to work around issues with the initial implementation. Either this fix needs to land BEFORE release/18.x branches, or the entire feature backed out.

That's another option. I'll prepare a revert.

@bwendling
Copy link
Collaborator Author

Here's a revert of the feature.

#75857

!isa<RecordDecl>(RD->getLexicalParent()))
break;
DC = RD->getLexicalParent();
}
Copy link
Contributor

Choose a reason for hiding this comment

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

while (true) {
  const auto *RD = dyn_cast<RecordDecl>(DC);
  if (!RD || !RD->isAnonymousStructOrUnion())
    break;
  DC = RD->getLexicalParent();
}

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'll do it, but could you explain why this is better? Is it a style guide issue?

Copy link
Contributor

Choose a reason for hiding this comment

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

It's computing and casting things once instead of twice. It's just generally much clearer.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Not really. I'm checking to make sure that the RecordDecl's lexical parent is also a RecordDecl. I need to do this, otherwise DC will end up as TranslationUnit in some cases.

DC = RD->getLexicalParent();
}

if (DC != FD->getDeclContext()->getOuterLexicalRecordContext()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

We need to figure out this thing about getOuterLexicalRecordContext() before we can go forward here. It sounds like you might be concerned about the established behavior of some existing attribute that isn't the -fbounds-safety __counted_by, but which people are trying to merge with __counted_by on general principle?

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 added this to ensure that we're not dealing with FAMs in sub-structures. I need to work with @rapidsna's work to make sure that what they do is what this attribute will do. It's not really a perfect check, but I removed the code in CodeGen that handles FAMs in substructs. So I think we can table this discussion if this patch goes on.


if (isa<FieldDecl>(D))
++FieldNo;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

I am deeply confused by the code in this function. Why are we visiting every member of the record?

It looks like the caller has the exact field path you need, but it's throwing that away and doing a brute-force recursive search. Just take a NamedDecl here and require it to be either a FieldDecl or an IndirectFieldDecl. Or better yet, add that as a method to ASTRecordLayout.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

If you call getFieldIndex for a FieldDecl that's not in a top-level of the RecordDecl, it will assert. So I need to go through each field one at a time, because the method is broken.

I was planning on adding that to the record layout contexts, but because this patch has been through the wringer, I didn't want to throw yet another reason for people to hate it.

Copy link
Contributor

Choose a reason for hiding this comment

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

Let's start by breaking that out as a separate patch. Just make a method on ASTRecordLayout that takes an IndirectFieldDecl and recursively adds up the offsets for every step in the path.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Could I please just do that as a follow-up patch? I don't have a great understanding of ASTRecordLayout and don't want to get bogged down in yet another endless review cycle.

bwendling added a commit that referenced this pull request Dec 18, 2023
There are many issues that popped up with the counted_by feature. The
patch #73730 has grown too large and approval is blocking Linux testing.

Includes reverts of:
commit 769bc11 ("[Clang] Implement the 'counted_by' attribute
(#68750)")
commit bc09ec6 ("[CodeGen] Revamp counted_by calculations
(#70606)")
commit 1a09cfb ("[Clang] counted_by attr can apply only to C99
flexible array members (#72347)")
commit a76adfb ("[NFC][Clang] Refactor code to calculate flexible
array member size (#72790)")
commit d8447c7 ("[Clang] Correct handling of negative and
out-of-bounds indices (#71877)")
Partial commit b31cd07 ("[Clang] Regenerate test checks (NFC)")

Closes #73168
Closes #75173
@bwendling
Copy link
Collaborator Author

#75857 has been merged in. This PR is no longer valid.

@bwendling bwendling closed this Dec 18, 2023
@h-vetinari
Copy link
Contributor

#75857 has been merged in. This PR is no longer valid.

Couldn't you just rebase it like:

It's a bit of a matter of taste obviously how to deal with such situations. I tend to prefer not to split the context across several PRs...

@bwendling
Copy link
Collaborator Author

#75857 has been merged in. This PR is no longer valid.

Couldn't you just rebase it like:

It's a bit of a matter of taste obviously how to deal with such situations. I tend to prefer not to split the context across several PRs...

Since the entire feature has been reverted, I don't think the discussion here is particularly useful.

@bwendling bwendling deleted the counted-by-fix-with-gep-pr73168 branch April 10, 2024 23:04
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 Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

10 participants