Skip to content
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

[CIR] Refactor StructType with TableGen #1504

Open
wants to merge 19 commits into
base: main
Choose a base branch
from

Conversation

el-ev
Copy link
Contributor

@el-ev el-ev commented Mar 19, 2025

Closes #1367

@el-ev
Copy link
Contributor Author

el-ev commented Mar 19, 2025

This patch currently causes the test CIR/CodeGen/pointer-to-member-func.cpp to fail, specifically this one:

struct Foo {
    void m1(int);
    virtual void m2(int);
    virtual void m3(int);
};

void call(Foo* obj, void (Foo::*func)(int), int arg)
{
    (obj->*func)(arg);
}

I have been debugging for some time, but haven't been able to resolve this issue. Any help would be appreciated.

The backtrace is attached below:

clang: llvm-project/llvm/include/llvm/Support/Casting.h:566: decltype(auto) llvm::cast(const From &) [To = mlir::detail::TypedValue<cir::StructType>, From = mlir::Value]: Assertion `isa<To>(Val) && "cast<Ty>() argument of incompatible type!"' failed.
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: /tmp/cirbin/clang -cc1 -std=c++17 -fclangir -emit-llvm ptr2func.cpp
1.      <eof> parser at end of file
 #0 0x00005559f55555b6 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (/tmp/cirbin/clang+0x240b5b6)
 #1 0x00005559f5552ece llvm::sys::RunSignalHandlers() (/tmp/cirbin/clang+0x2408ece)
 #2 0x00005559f5555df4 SignalHandler(int, siginfo_t*, void*) Signals.cpp:0:0
 #3 0x00007ffbf611bcd0 (/usr/lib/libc.so.6+0x3dcd0)
 #4 0x00007ffbf6175624 (/usr/lib/libc.so.6+0x97624)
 #5 0x00007ffbf611bba0 raise (/usr/lib/libc.so.6+0x3dba0)
 #6 0x00007ffbf6103582 abort (/usr/lib/libc.so.6+0x25582)
 #7 0x00007ffbf61034eb __assert_perror_fail (/usr/lib/libc.so.6+0x254eb)
 #8 0x00005559f686d3df cir::direct::CIRToLLVMExtractMemberOpLowering::matchAndRewrite(cir::ExtractMemberOp, cir::ExtractMemberOpAdaptor, mlir::ConversionPatternRewriter&) const (/tmp/cirbin/clang+0x37233df)
 #9 0x00005559f690855c mlir::OpConversionPattern<cir::ExtractMemberOp>::matchAndRewrite(cir::ExtractMemberOp, cir::ExtractMemberOpGenericAdaptor<llvm::ArrayRef<mlir::ValueRange>>, mlir::ConversionPatternRewriter&) const (/tmp/cirbin/clang+0x37be55c)
#10 0x00005559f68a66b1 mlir::OpConversionPattern<cir::ExtractMemberOp>::matchAndRewrite(mlir::Operation*, llvm::ArrayRef<mlir::ValueRange>, mlir::ConversionPatternRewriter&) const (/tmp/cirbin/clang+0x375c6b1)
#11 0x00005559f819b6b6 mlir::ConversionPattern::matchAndRewrite(mlir::Operation*, mlir::PatternRewriter&) const (/tmp/cirbin/clang+0x50516b6)
#12 0x00005559f81dbda0 void llvm::function_ref<void ()>::callback_fn<mlir::PatternApplicator::matchAndRewrite(mlir::Operation*, mlir::PatternRewriter&, llvm::function_ref<bool (mlir::Pattern const&)>, llvm::function_ref<void (mlir::Pattern const&)>, llvm::function_ref<llvm::LogicalResult (mlir::Pattern const&)>)::$_0>(long) PatternApplicator.cpp:0:0
#13 0x00005559f81d86ed mlir::PatternApplicator::matchAndRewrite(mlir::Operation*, mlir::PatternRewriter&, llvm::function_ref<bool (mlir::Pattern const&)>, llvm::function_ref<void (mlir::Pattern const&)>, llvm::function_ref<llvm::LogicalResult (mlir::Pattern const&)>) (/tmp/cirbin/clang+0x508e6ed)
#14 0x00005559f819c820 (anonymous namespace)::OperationLegalizer::legalize(mlir::Operation*, mlir::ConversionPatternRewriter&) DialectConversion.cpp:0:0
#15 0x00005559f81a838f llvm::LogicalResult llvm::function_ref<llvm::LogicalResult (mlir::Pattern const&)>::callback_fn<(anonymous namespace)::OperationLegalizer::legalizeWithPattern(mlir::Operation*, mlir::ConversionPatternRewriter&)::$_2>(long, mlir::Pattern const&) DialectConversion.cpp:0:0
#16 0x00005559f81dbeca void llvm::function_ref<void ()>::callback_fn<mlir::PatternApplicator::matchAndRewrite(mlir::Operation*, mlir::PatternRewriter&, llvm::function_ref<bool (mlir::Pattern const&)>, llvm::function_ref<void (mlir::Pattern const&)>, llvm::function_ref<llvm::LogicalResult (mlir::Pattern const&)>)::$_0>(long) PatternApplicator.cpp:0:0
#17 0x00005559f81d86ed mlir::PatternApplicator::matchAndRewrite(mlir::Operation*, mlir::PatternRewriter&, llvm::function_ref<bool (mlir::Pattern const&)>, llvm::function_ref<void (mlir::Pattern const&)>, llvm::function_ref<llvm::LogicalResult (mlir::Pattern const&)>) (/tmp/cirbin/clang+0x508e6ed)
#18 0x00005559f819c820 (anonymous namespace)::OperationLegalizer::legalize(mlir::Operation*, mlir::ConversionPatternRewriter&) DialectConversion.cpp:0:0
#19 0x00005559f819b807 mlir::OperationConverter::convert(mlir::ConversionPatternRewriter&, mlir::Operation*) (/tmp/cirbin/clang+0x5051807)
#20 0x00005559f819ca4f mlir::OperationConverter::convertOperations(llvm::ArrayRef<mlir::Operation*>) (/tmp/cirbin/clang+0x5052a4f)
#21 0x00005559f81a1b52 mlir::applyPartialConversion(llvm::ArrayRef<mlir::Operation*>, mlir::ConversionTarget const&, mlir::FrozenRewritePatternSet const&, mlir::ConversionConfig) (/tmp/cirbin/clang+0x5057b52)
#22 0x00005559f687b0f3 cir::direct::ConvertCIRToLLVMPass::runOnOperation() (/tmp/cirbin/clang+0x37310f3)
#23 0x00005559f82e23ef mlir::detail::OpToOpPassAdaptor::run(mlir::Pass*, mlir::Operation*, mlir::AnalysisManager, bool, unsigned int) (/tmp/cirbin/clang+0x51983ef)
#24 0x00005559f82e2cb0 mlir::detail::OpToOpPassAdaptor::runPipeline(mlir::OpPassManager&, mlir::Operation*, mlir::AnalysisManager, bool, unsigned int, mlir::PassInstrumentor*, mlir::PassInstrumentation::PipelineParentInfo const*) (/tmp/cirbin/clang+0x5198cb0)
#25 0x00005559f82e5657 mlir::PassManager::run(mlir::Operation*) (/tmp/cirbin/clang+0x519b657)
#26 0x00005559f687bc92 cir::direct::lowerDirectlyFromCIRToLLVMDialect(mlir::ModuleOp, bool, bool, bool) (/tmp/cirbin/clang+0x3731c92)
#27 0x00005559f687bd6d cir::direct::lowerDirectlyFromCIRToLLVMIR(mlir::ModuleOp, llvm::LLVMContext&, bool, bool, bool) (/tmp/cirbin/clang+0x3731d6d)
#28 0x00005559f684be64 cir::CIRGenConsumer::HandleTranslationUnit(clang::ASTContext&) (/tmp/cirbin/clang+0x3701e64)
#29 0x00005559f88f61d9 clang::ParseAST(clang::Sema&, bool, bool) (/tmp/cirbin/clang+0x57ac1d9)
#30 0x00005559f6033224 clang::FrontendAction::Execute() (/tmp/cirbin/clang+0x2ee9224)
#31 0x00005559f5f9967d clang::CompilerInstance::ExecuteAction(clang::FrontendAction&) (/tmp/cirbin/clang+0x2e4f67d)
#32 0x00005559f6119bc5 clang::ExecuteCompilerInvocation(clang::CompilerInstance*) (/tmp/cirbin/clang+0x2fcfbc5)
#33 0x00005559f428ab0e cc1_main(llvm::ArrayRef<char const*>, char const*, void*) (/tmp/cirbin/clang+0x1140b0e)
#34 0x00005559f4286bb9 ExecuteCC1Tool(llvm::SmallVectorImpl<char const*>&, llvm::ToolContext const&) driver.cpp:0:0
#35 0x00005559f4285c3d clang_main(int, char**, llvm::ToolContext const&) (/tmp/cirbin/clang+0x113bc3d)
#36 0x00005559f42959b6 main (/tmp/cirbin/clang+0x114b9b6)
#37 0x00007ffbf6105488 (/usr/lib/libc.so.6+0x27488)
#38 0x00007ffbf610554c __libc_start_main (/usr/lib/libc.so.6+0x2754c)
#39 0x00005559f4284555 _start (/tmp/cirbin/clang+0x113a555)

@Lancern
Copy link
Member

Lancern commented Mar 20, 2025

This should be considered a bug in the ExtractMemberOp operation, which is defined in TableGen as:

def ExtractMemberOp : CIR_Op<"extract_member", [Pure]> {
let summary = "Extract the value of a member of a struct value";
let description = [{
The `cir.extract_member` operation extracts the value of a particular member
from the input record. Unlike `cir.get_member` which derives pointers, this
operation operates on values. It takes a value of record type, and extract
the value of the specified record member from the input record value.
Currently `cir.extract_member` does not work on unions.
Example:
```mlir
// Suppose we have a struct with multiple members.
!s32i = !cir.int<s, 32>
!s8i = !cir.int<s, 32>
!struct_ty = !cir.struct<"struct.Bar" {!s32i, !s8i}>
// And suppose we have a value of the struct type.
%0 = cir.const #cir.const_struct<{#cir.int<1> : !s32i, #cir.int<2> : !s8i}> : !struct_ty
// Extract the value of the second member of the struct.
%1 = cir.extract_member %0[1] : !struct_ty -> !s8i
```
}];
let arguments = (ins CIR_StructType:$record, IndexAttr:$index_attr);
let results = (outs CIR_AnyType:$result);
let assemblyFormat = [{
$record `[` $index_attr `]` attr-dict
`:` qualified(type($record)) `->` qualified(type($result))
}];
let builders = [
OpBuilder<(ins "mlir::Type":$type, "mlir::Value":$record, "uint64_t":$index), [{
mlir::APInt fieldIdx(64, index);
build($_builder, $_state, type, record, fieldIdx);
}]>,
OpBuilder<(ins "mlir::Value":$record, "uint64_t":$index), [{
auto recordTy = mlir::cast<cir::StructType>(record.getType());
mlir::Type memberTy = recordTy.getMembers()[index];
build($_builder, $_state, memberTy, record, index);
}]>
];
let extraClassDeclaration = [{
/// Get the index of the struct member being accessed.
uint64_t getIndex() { return getIndexAttr().getZExtValue(); }
}];
let hasVerifier = 1;
}

Simply put, the introduction of CIR_StructType in TableGen changes the type constraint of the first argument (namely $record) of ExtractMemberOp. This further changes the code of ExtractMemberOp::getRecord, effectively making it an assertion failure if the type of $record is not a CIR struct type. This could happen during LLVM lowering when such an operation is created as a temporary intermediary representation.

To mitigate this problem, preserve the old definition of CIR_StructType, but give it a different name:

- def CIR_StructType : Type<CPred<"::mlir::isa<::cir::StructType>($_self)">,
-                           "CIR struct type">;
+ def Struct : Type<CPred<"::mlir::isa<::cir::StructType>($_self)">, "CIR struct type">;

And then update the definition of ExtractMemberOp and change the type constraint of the argument $record to the renamed constraint class.

- let arguments = (ins CIR_StructType:$record, IndexAttr:$index_attr);
+ let arguments = (ins Struct:$record, IndexAttr:$index_attr);

Btw, you could build the code with -DCMAKE_BUILD_TYPE=Debug to include debug information in your stack dump, which could make your life easier when encountered with problems.

@el-ev
Copy link
Contributor Author

el-ev commented Mar 20, 2025

And then update the definition of ExtractMemberOp and change the type constraint of the argument $record to the renamed constraint class.

Thank you for your assistance. I noticed that InsertMemberOp also references CIR_StructType:$record as its argument and return value, should I modify them for the same reason?

@Lancern
Copy link
Member

Lancern commented Mar 20, 2025

I noticed that InsertMemberOp also references CIR_StructType:$record as its argument and return value, should I modify them for the same reason?

Yes.

@el-ev el-ev marked this pull request as ready for review March 20, 2025 04:24
@bcardosolopes
Copy link
Member

Amazing catch @Lancern

@bcardosolopes
Copy link
Member

To mitigate this problem, preserve the old definition of CIR_StructType, but give it a different name:

Before renaming to Struct, try out CIRStructType or StructType and be sure to mention in a comment next to it that the name is different from the pattern because limitations discussed.

@bcardosolopes
Copy link
Member

I see unrelated changes in this PR (e.g. changes to STD ops), perhaps some bad merging on your end? You need to fix your branch somehow so that the diff we see only has the relevant bits.

Lancern and others added 15 commits March 25, 2025 07:58
This PR adds an insertion guard for the try body scope for try-catch.
Currently, the following code snippet fails during CodeGen:

```
void foo() {
  int r = 1;
  try {
    ++r;
    return;
  } catch (...) {
  }
}
```

The insertion point doesn't get reset properly and the cleanup is being
ran for a wrong/deleted block causing a segmentation fault. I also added
a test.
The comments suggested that we should use TableGen to generate the
recognizing functions. However, I think templates might be more suitable
for generating them -- and I can't find any existing TableGen backends
that let us generate arbitrary functions.

My choice of design is to offer a template to match standard library
functions:
```cpp
// matches std::find with 3 arguments, and raise it into StdFindOp
StdRecognizer<3, StdFindOp, StdFuncsID::Find>
```
I have to use a TableGen'd enum to map names to IDs, as we can't pass
string literals to template arguments easily in C++17.

This also constraints design of future `StdXXXOp`s: they must take
operands the same way of StdFindOp, where the first one is the original
function, and the rest are function arguments.

I'm not sure if this approach is the best way. Please tell me if you
have concerns or any alternative ways.
…was set explicitly (llvm#1482)

This is backported from a change made in
llvm/llvm-project#131181

---------

Co-authored-by: Morris Hafner <mhafner@nvidia.com>
…R attribute. (llvm#1467)

Started decorating CUDA shadow variables with the shadow_name CIR
attribute which will be used for registering the globals.
… target was set explicitly" (llvm#1509)

Reverts llvm#1482

@mmha this is crashing on macos on asserts build:
```
FAIL: Clang :: CIR/Tools/cir-translate/warn-default-triple.cir (472 of 552)
******************** TEST 'Clang :: CIR/Tools/cir-translate/warn-default-triple.cir' FAILED ********************
Exit Code: 134

Command Output (stdout):
--
Assertion failed: (!DataLayoutString.empty() && "Uninitialized DataLayout!"), function getDataLayoutString, file TargetInfo.h, line 1282.
```

Perhaps besides picking a default you maybe need to do some missing
datalayout init?
@el-ev el-ev force-pushed the cir_structtype_td branch from b35d074 to fc3b818 Compare March 24, 2025 23:58
Copy link
Member

@bcardosolopes bcardosolopes left a comment

Choose a reason for hiding this comment

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

LGTM after nit

}

// Note CIRStructType is used instead of CIR_StructType because of tablegen conflicts
def CIRStructType : Type<CPred<"::mlir::isa<::cir::StructType>($_self)">, "CIR struct type">;
Copy link
Member

Choose a reason for hiding this comment

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

One last nit: we should be bound to 80-col in tablegen files.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed

Copy link
Member

Choose a reason for hiding this comment

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

Have you confirmed the rest of the diff is also bound? I see some suspect entries

Copy link
Contributor Author

@el-ev el-ev Mar 25, 2025

Choose a reason for hiding this comment

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

My apologies, I didn't check carefully enough... Everything should be in bounds now.

edit: oops, i forgot to push

@el-ev el-ev force-pushed the cir_structtype_td branch from 03f8fa3 to 0a7facc Compare March 25, 2025 03:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Miss cir::StructType documentation
9 participants