-
Notifications
You must be signed in to change notification settings - Fork 156
[CIR] Correctly handle annotate attribute on C++ constructors/destructors #1699
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
base: main
Are you sure you want to change the base?
Conversation
…lvm#1385) Lower builtin_neon_vqshld_n s64 and u64
This PR adds support for LLVM lowering of pointers to member functions. The lowering is ABI-specific and this patch only considers Itanium ABI. Itanium ABI lowers pointers to member functions to a struct with two fields of type `ptrdiff_t`. To extract fields from such aggregate values, this PR includes a new operation `cir.extract_member` to accomplish this.
This PR adds CIRGen and LLVM lowering support for the `__builtin_bitreverse` family of builtin functions.
Lower neon_vrnd32x
This deals with some x86 aggregate types for CallConvLowering pass. Suppose we have a simple struct like this. ```cpp struct dim3 { int x, y, z; }; ``` It can be coerced into ```cpp struct dim3_ { uint64_t xy; int z; }; ``` And for a function that receives it as an argument, OG does the following transformation for x86: ```cpp void f(dim3 arg) { /* Before */ } void f(uint64_t xy, int z) { /* After */ } ``` Now this transformation is implemented in the CallConvLowering pass of CIR.
I checked https://github.com/llvm/clangir/blob/main/clang/test/CIR/CodeGen/globals.cpp and thought code works as expected. Although, test results need to be adjusted a bit. Resolves: llvm#1252
Lower vrnd32z and vrnd32zq
This PR adds support for returns inside of a TryOp, for example: ``` void foo() { int r = 1; try { return; ++r; } catch (...) { } } ``` Currently, it fails during the CodeGen with: ``` error: 'cir.return' op expects parent op to be one of 'cir.func, cir.scope, cir.if, cir.switch, cir.do, cir.while, cir.for, cir.case' ``` were TryOp's omitted on purpose?
Change the assembly format for `cir::FuncType` from ``` !cir.func<!returnType (!argType)> ``` to ``` !cir.func<(!argType) -> !returnType> ``` This change (1) is easier to parse because it doesn't require lookahead, (2) is consistent with the format of ClangIR `FuncOp` assembly, and (3) is consistent with function types in other MLIR dialects. Change all the tests to use or to expect the new format for function types. The contents and the semantics of `cir::FuncType` are unchanged. Only the assembly format is being changed. Functions that return `void` in C or C++ are still represented in MLIR as having no return type. Most of the changes are in `parseFuncType` and `printFuncType` and the functions they call in `CIRTypes.cpp`. A `FuncType::verify` function was added to check that an explicit return type is not `cir::VoidType`. `FuncType::isVoid()` was renamed to `FuncType::hasVoidReturn()` Some comments for `FuncType` were improved. An `llvm_unreachable` was added to `StructType::getKindAsStr` to suppress a compiler warning and to catch a memory error that corrupts the `RecordKind` field. (This was a drive-by fix and has nothing to do with the rest of this change.)
Includes function name while mangling C functions to avoid link errors. [This is the same way OG handles it](https://github.com/llvm/clangir/blob/c301b4a0d3d2d79b26c9c809c11b8a1137c0a9ec/clang/lib/CodeGen/CodeGenModule.cpp#L1896).
Commit the .clang-tidy files for ClangIR. The biggest different between these and the Clang files is the capitalization of variable names. Most ClangIR code follows the MLIR conventions instead of the Clang conventions.
llvm#1390) The CIRGen support is already there. This PR adds LLVM lowering support for comparisons between pointers to member functions. Note that pointers to member functions could only be compared for equality.
Run clang-tidy on `CIRGenFunction.cpp` and fix all the issues that were identified. The vast majority of issues were the case of parameter and variable names. Some of the issues that didn't have to do with names had to be resolved manually. There should be no behavior changes.
Lower vrnd64x and vrnd64xq
To give LoweringPrepare type information from `CIRGenTypeCache`, this PR adds two attributes to ModuleOp: ```mlir module attributes { cir.int_size = #cir.int_size<32>, cir.size_type_size = #cir.size_type_size<64>, ... } {} ``` The `CIRDataLayout` class is also extended to have `getPtrDiffTy` and so on. Some tests that only expects `cir.lang` and `cir.sob` are also changed to take this into account.
If type of operand is not integer, it can be handled like what I do in `__builtin_elementwise_exp`.
This patch adds support for simple cast operations on pointers to member functions, including: 1) casting pointers to member function values to boolean values; 2) reinterpret casts between pointers to member functions.
This uses the assembly format for the optional return type and keeps a custom printer/parser only for function parameters, which still require a custom form for ellipses.
Lower vrnd64z and vrnd64zq
Get rid of the function `FuncOp::verifyType`. The function performed three checks: 1. Check that `isa<cir::FuncType>(getFunctionType())`. This is a tautology that is always true, since the return type of `getFunctionType()` is already `cir::FuncType`. 2. Report an error if `type.isVarArg() && type.getNumInputs() == 0`, i.e. when a variadic function has no named parameters. That check is incorrect. In C++, variadic functions don't need to have any named parameters. `void f(...) { }` is legal in C++ and ClangIR needs to be able to compile it. 3. Report an error when the return type is `void`. This check is correct (`void` return is represented as no return in MLIR), but it is redundant. This is already checked in `FuncType::verify`. Since `FuncOp::verifyType` serves no useful purpose, delete it, along with the test for `int variadic(...)` that was in `clang/test/CIR/IR/invalid.cir`.
) Currently, the following code snippet fails during CIR codegen with exceptions enabled: ``` #include <string> void foo(const char *path) { std::string str = path; str = path; str = path; } ``` using `bin/clang++ tmp.cpp -fclangir -Xclang -emit-cir -S`, the error: ``` error: empty block: expect at least a terminator ``` Relevant part of the CIR before verification looks like: ``` %118 = "cir.load"(%114) : (!cir.ptr<!cir.ptr<!cir.int<s, 8>>>) -> !cir.ptr<!cir.int<s, 8>> "cir.try"() <{catch_types = [#cir.unwind], cleanup, synthetic}> ({ %123 = "cir.call"(%115, %118) <{ast = #cir.call.expr.ast, callee = @_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEaSEPKc, calling_conv = 1 : i32, exception, extra_attrs = #cir<extra({})>, side_effect = 1 : i32}> ({ "cir.call"(%115) <{callee = @_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEED1Ev, calling_conv = 1 : i32, extra_attrs = #cir<extra({nothrow = #cir.nothrow})>, side_effect = 1 : i32}> ({ }) : (!cir.ptr<!cir.struct<class "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>" {!cir.struct<struct "std::__cxx11::basic_string<char>::_Alloc_hider" {!cir.ptr<!cir.int<s, 8>>} #cir.record.decl.ast>, !cir.int<u, 64>, !cir.struct<union "anon.0" padded {!cir.array<!cir.int<s, 8> x 16>, !cir.int<u, 64>, !cir.array<!cir.int<u, 8> x 8>} #cir.record.decl.ast>} #cir.record.decl.ast>>) -> () "cir.yield"() : () -> () }) : (!cir.ptr<!cir.struct<class "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>" {!cir.struct<struct "std::__cxx11::basic_string<char>::_Alloc_hider" {!cir.ptr<!cir.int<s, 8>>} #cir.record.decl.ast>, !cir.int<u, 64>, !cir.struct<union "anon.0" padded {!cir.array<!cir.int<s, 8> x 16>, !cir.int<u, 64>, !cir.array<!cir.int<u, 8> x 8>} #cir.record.decl.ast>} #cir.record.decl.ast>>, !cir.ptr<!cir.int<s, 8>>) -> !cir.ptr<!cir.struct<class "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>" {!cir.struct<struct "std::__cxx11::basic_string<char>::_Alloc_hider" {!cir.ptr<!cir.int<s, 8>>} #cir.record.decl.ast>, !cir.int<u, 64>, !cir.struct<union "anon.0" padded {!cir.array<!cir.int<s, 8> x 16>, !cir.int<u, 64>, !cir.array<!cir.int<u, 8> x 8>} #cir.record.decl.ast>} #cir.record.decl.ast>> "cir.store"(%123, %116) : (!cir.ptr<!cir.struct<class "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>" {!cir.struct<struct "std::__cxx11::basic_string<char>::_Alloc_hider" {!cir.ptr<!cir.int<s, 8>>} #cir.record.decl.ast>, !cir.int<u, 64>, !cir.struct<union "anon.0" padded {!cir.array<!cir.int<s, 8> x 16>, !cir.int<u, 64>, !cir.array<!cir.int<u, 8> x 8>} #cir.record.decl.ast>} #cir.record.decl.ast>>, !cir.ptr<!cir.ptr<!cir.struct<class "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>" {!cir.struct<struct "std::__cxx11::basic_string<char>::_Alloc_hider" {!cir.ptr<!cir.int<s, 8>>} #cir.record.decl.ast>, !cir.int<u, 64>, !cir.struct<union "anon.0" padded {!cir.array<!cir.int<s, 8> x 16>, !cir.int<u, 64>, !cir.array<!cir.int<u, 8> x 8>} #cir.record.decl.ast>} #cir.record.decl.ast>>>) -> () "cir.yield"() : () -> () }, { ^bb0: <--- EMPTY BLOCK }) : () -> () ``` There is an empty block! If you extend the snippet with more `str = path;`, you get more empty blocks... The issue is the `cir.resume` ops which should be in those empty blocks from synthetic TryOp's aren't linked properly during the cleanup. My suggestion: We should explicitly add `cir.resume` for synthetic tryOp's, because we already know they have just an [unwind handler](https://github.com/llvm/clangir/blob/8746bd4bbe777352c2935e9937449637a8943767/clang/lib/CIR/CodeGen/CIRGenCall.cpp#L506). So, during [CIRGenCleanup](https://github.com/llvm/clangir/blob/8746bd4bbe777352c2935e9937449637a8943767/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp#L667) we don't need to add `cir.resume` for synthetic TryOp's. This PR adds this and a test.
Run clang-tidy on `clang/lib/CIR/CodeGen/CIRGenModule.cpp`. Accept all of the recommended fixes, except for one suggestion to use `std::any_of`. The vast majority of the changes had to do with the case of identifiers, changing variables and parameters from `VarName` to `varName`.
I noticed that `AtomicFenceOp` doesn't use `OptionalAttr` like mlir llvmir. As a result, `getLLVMSyncScope` does't return `std::optional`. Should I use `Arg` instead?
…lvm#1666) This adds missing print of `dso_local` to FuncOp. Attribute `dsolocal` was renamed in both `FuncOp` and `GlobalOp` to align with LLVM naming.
Implement atan2 intrinsic as part of llvm#1192
…llvm#1660) Fixes llvm#1405 ## Improving eraseIfSafe: as far as I understand it eraseIfSafe should intuativly check if all memref load/store ops are created, which obtain offsets from the memref.reinterpret_cast in the eraseList. If so the operations in the eraseList are erased, otherwise they are kept until all cir.load/store ops relying on them are lowered. One challenge here is that we can't actually do this by checking the uses of memref.reinterpret_cast operations, as their results aren't actually used in the created memref load/store ops (the base alloca result found via findBaseAndIndices is used). Because of this, this base alloca result is passed as the newAddr Value to eraseIfSafe in the [cir.load](https://github.com/llvm/clangir/blob/6e5fa09550c98f84d017873ed3e5667fd5fd909c/clang/lib/CIR/Lowering/ThroughMLIR/LowerCIRToMLIR.cpp#L236C5-L242C6)/[cir.store](https://github.com/llvm/clangir/blob/6e5fa09550c98f84d017873ed3e5667fd5fd909c/clang/lib/CIR/Lowering/ThroughMLIR/LowerCIRToMLIR.cpp#L266C1-L271C6) lowerings. Currently the eraseIfSafe function counts all memref.load/store values that use this base address: https://github.com/llvm/clangir/blob/6e5fa09550c98f84d017873ed3e5667fd5fd909c/clang/lib/CIR/Lowering/ThroughMLIR/LowerCIRToMLIR.cpp#L215-L218 The problem here is that this also counts all the other memref.load/store ops, which store/load to/from the base address, but don't use the memref.reinterpret_cast ops to obtain the offsets. Because of this the lowering fails if multiple store/loads to/from the same array are performed in the original C code as in the example of issue llvm#1405. Because we are erroneously counting all the other memref.load/store ops, the newUsedNum is (for the later stores) larger than oldUsedNum (only the uses of the cir.ptr_stride op) and the memref.reinterpret_cast ops are not removed. This PR contains a first attempt to fix this (i.e only count the memref.load/store ops, which obtain offsets from the memref.reinterpret_cast in the eraseList). I only count memref.load/store ops, if the first offset value, corresponds to the offset value in the last memref.reinterpret_cast. Limitations of this PR: This fixes the indirect lowering of the example in issue llvm#1405 and also works for other test I made where multiple store/loads to/from the same array, but assumes two thing to be the case: 1. The cir.const used as the stride in the cir.ptr_str is not reused in other cir.ptr_stride ops 2. Only the last cir.ptr_stride can have multiple uses (for multidim arrays) Both of these assumptions seem to be true for the C-Code I testet (for the translation of accesses to C/C++ Arrays to cir ops). But the eraseIfSafe function might need to be changed/further improved in the future to support cases, where those assumptions fail. For example if an optimization is run on cir where the cir.const ops with the same value are reused for the different cir.ptr_stride ops, the indirect lowering would still fail. Or if in a multidimensional array a subarray is accessed, e.g. ```c int arr[3][4]; int *row = arr[1]; ``` (Note: I pretty sure for this it isn't suffiicient to just extend the function to check if all offset value, corresponds to the offset value in the all memref.reinterpret_cast, but we would probably need to seperatly check for each memref.reinterpret_cast if it can be removed (instead of removing all or non in the eraseList)) ## Improving the lowering of canonical ForOps: While debugging issue llvm#1405 I noticed a few thing that I think could be improved in the canonical ForOp lowering: 1. There is one edge case, where the forOp should not be marked as canonical in my opinion: ```c int i; for (i = 0; i < 100; i++); i += 10; ``` (with the current lowering this for is marked canonical but since i is replaced by the induction var of the scf.for op and the actual memory representing i is not updated i has a wrong value after the for. This is avoided when we lower this for as a non canonical for.) 2. I think we can directly replace the loads to the CIR.IV with the scf.IV and not create the dummy arith.add IV, 0 op (I think this might be a relic from previous MLIR version where the replaceOp only worked with operations (not values). This make the IR more readable and easier to understand. If I'm missing somethink here and the arith.add IV, 0 has a purpose I'm not seeing let me know. 3. When implementing the change in 1, we know that in a canonical for the induction variable is definied inside the for and is only valid here. Because of this and since we replace the loads of the cir IV with the scf.IV we can remove the unneccessary alloca and store op created for the cir.IV (These changes only show up in an non-optimized binary, but aren't relevant when running clang with optimations, I still think they improve the readability + understandability of the core ir) ## Running SCFPreparePass cir pass always when lowering throughMLIR: I also noticed, that we are currently only running the SCFPreparePass when we are printing the result of the cir to core dialect translation. https://github.com/llvm/clangir/blob/6e5fa09550c98f84d017873ed3e5667fd5fd909c/clang/lib/CIR/CodeGen/CIRPasses.cpp#L84-L85 Because of this compiling to an object file (or llvm IR) with the indirect lowering path fails, if the code contains a canonical for. I suggest always running this pass, when were going throughMLIR. ## Passing through is_nontemporal in loads/store lowerings: Since the corresponding memref ops also have this attribute it's basically just passing through a boolean (and doesn't need any special handling, I think). Even tho there is probably no practical application now I think this might avoid bugs/confusion in the future. If there is any reason not to do this let me know. I also added a new test case for arrays, adjusted the canonical forOp test to reflect the made changes and combined the non canonical forOp tests into one file and added a test case for the edge case describe before. (Note: if I find the time I will try to run the SingleSource test suite with the throughMLIR lowering in the next week to get a better idea, where we are with this pipeline. In general I agree with everything discussed in issue llvm#1219, but I think we probably can already add more support in regard to arrays (and maybe pointers) with the existing mlir core constructs)
) This PR introduces [`TryMarkNoThrow`](https://github.com/llvm/clangir/blob/6e5fa09550c98f84d017873ed3e5667fd5fd909c/clang/lib/CodeGen/CodeGenFunction.cpp#L1394). [`isInterposable`](https://github.com/llvm/clangir/blob/6e5fa09550c98f84d017873ed3e5667fd5fd909c/clang/lib/CodeGen/CodeGenFunction.cpp#L1397C10-L1397C26) isn't fully implemented and I'm not quite sure we need it? Anyway, I have introduced a missing feature `getSemanticInterposition` relevant for its completion. I have also updated an old test -- [`foo()`](https://github.com/llvm/clangir/blob/6e5fa09550c98f84d017873ed3e5667fd5fd909c/clang/test/CIR/CodeGen/try-catch-dtors.cpp#L313) should be marked as unwind/nothrow. I have compared with the original CodeGen and attached the llvm output for verification. One concern I have is if the cases I have to mimic [`mayThrow`](https://github.com/llvm/clangir/blob/6e5fa09550c98f84d017873ed3e5667fd5fd909c/llvm/lib/IR/Instruction.cpp#L1158) from the OG are enough, please let me know your thoughts.
This PR adds support for the `-fdump-record-layouts` flag. It enables printing both the `CIRGenRecordLayout` and the `ASTRecordLayout`, similar to what is done in CodeGen.
Backport support for Complex value initialization from the empty InitList. Backported from llvm/llvm-project#143192
) Currently we can't handle continues nested under `IfOp`, because if we replace it with a yield, then it only breaks out of that `if`-statement, rather than continuing the whole loop. Perhaps that should be done by changing the whole structure of the while loop. Co-authored-by: Yue Huang <[email protected]>
…llvm#1670) Backport the VecShuffleOp verifier to catch invalid index Implemented in llvm/llvm-project#143262
…llvm#1673) When we process a completed Enum type, we were checking to see if the completed type was in the type cache and clearing the cache if the completed and converted underlying type for the enum doesn't pass an `isInteger(32)` check. Unfortunately, this checks to see if the type is the MLIR builtin 32-bit integer type, whereas it will always be a CIR integer type, so the check always fails. I don't believe there can ever be a case where the forward declared type for the enum doesn't match the completed type, so we should never need to clear the cache. This change replaces the previous check with an assert that compares the actual completed type to the cached type.
…vm#1672) This removes unnecessary parens from the assembly format of BaseClassAddrOp, DerivedClassAddrOp, BaseDataMemberOp, DerivedDataMemberOp, BaseMethodOp, and DerivedMethodOp to bring them into conformance with the CIR ASM Style Guide. The is no function change beyond the assembly format change.
- Replace std::map with llvm::StringMap and std::vector with llvm::SmallVector for improved performance. - Preserve the behavior - Remove unused headers
Backport creating Array type with ComplexType as element type
As the scf dialect does not support early exits, it might be necessary to change the body of WhileOp to implement the semantics of ContinueOp. I choose to add a guard `if (!cond)` for everything following the `continue`. Co-authored-by: Yue Huang <[email protected]>
This PR is related to llvm#1685 and adds some basic support for the printf function. Limitations: 1. It only works if all variadic params are of basic interger/float type (for more info why memref type operands don't work see llvm#1685) 2. Only works if the format string is definied directly inside the printf function The downside of this PR is also that the handling this edge case adds significant code bloat and reduces readability for the cir.call op lowering (I tried to insert some meanigful comments to improve the readability), but I think its worth to do this so we have some basic printf support (without adding an extra cir operation) until upstream support for variadic functions is added to the func dialect. Also a few more test (which use such a basic form of printf) in the llvm Single Source test suite are working with this PR: before this PR: Testing Time: 4.00s Total Discovered Tests: 1833 Passed : 420 (22.91%) Failed : 10 (0.55%) Executable Missing: 1403 (76.54%) with this PR: Testing Time: 10.29s Total Discovered Tests: 1833 Passed : 458 (24.99%) Failed : 6 (0.33%) Executable Missing: 1369 (74.69%)
This PR addresses the feedback from llvm/llvm-project#142041 (comment). Our algorithm for accumulating bitfields has diverged from CodeGen since Clang 19. There is one key difference: in CIR, we use the function `getBitfieldStorageType`, which checks whether the bit width of the current accumulation run is a valid fundamental width (i.e., a power of two: 8, 16, 32, 64). If it is, it returns a CIR type of that size otherwise, it returns an array with the closest alignment. For example, given the following struct: ```c struct S { int a : 4; int b : 27; int c : 17; int d : 2; int e : 15; unsigned f; }; ``` The CodeGen output is: ```llvm %struct.S = type { i64, i16, i32 } ``` Whereas the new CIR algorithm produces: ```mlir !cir.record<struct "S" {!cir.array<!u8i x 7>, !u16i, !u32i}> ``` In CIR, the algorithm accumulates up to field `d`, resulting in 50 accumulated bits. Since 50 is not a fundamental width, the closest alignment is 56 bits, which leads to the type `!cir.array<!u8i x 7>`. The algorithm stops before accumulating field `e` because including it would exceed the register size (64), which is not ideal. At this point, it's unclear whether this divergence from CodeGen represents an improvement. If we wanted to match CodeGen exactly, we would need to replace the use of `getBitfieldStorageType` with `getUIntNType`. The difference is that `getUIntNType` always returns the closest power-of-two integer type instead of falling back to an array when the size is not a fundamental width. With this change, CIR would match CodeGen's layout exactly. It would require the following small code change: ```diff diff --git a/clang/lib/CIR/CodeGen/CIRRecordLayoutBuilder.cpp b/clang/lib/CIR/CodeGen/CIRRecordLayoutBuilder.cpp index 7c1802b..17538b191738 100644 --- a/clang/lib/CIR/CodeGen/CIRRecordLayoutBuilder.cpp +++ b/clang/lib/CIR/CodeGen/CIRRecordLayoutBuilder.cpp @@ -616,7 +616,7 @@ CIRRecordLowering::accumulateBitFields(RecordDecl::field_iterator Field, if (!InstallBest) { // Determine if accumulating the just-seen span will create an expensive // access unit or not. - mlir::Type Type = getBitfieldStorageType(astContext.toBits(AccessSize)); + mlir::Type Type = getUIntNType(astContext.toBits(AccessSize)); if (!astContext.getTargetInfo().hasCheapUnalignedBitFieldAccess()) llvm_unreachable("NYI"); @@ -674,12 +674,12 @@ CIRRecordLowering::accumulateBitFields(RecordDecl::field_iterator Field, // remain there after a stable sort. mlir::Type Type; if (BestClipped) { - assert(getSize(getBitfieldStorageType( + assert(getSize(getUIntNType( astContext.toBits(AccessSize))) > AccessSize && "Clipped access need not be clipped"); Type = getByteArrayType(AccessSize); } else { - Type = getBitfieldStorageType(astContext.toBits(AccessSize)); + Type = getUIntNType(astContext.toBits(AccessSize)); assert(getSize(Type) == AccessSize && "Unclipped access must be clipped"); } ``` You can see a comparison between the two functions https://godbolt.org/z/qjx1MaEWG. I'm currently unsure whether using one function over the other has performance implications. Regarding the **ARM error I mentioned in the meeting: it was an `assert` I had forgotten to update. It's now fixed sorry for the confusion.**
- Create CIR specific EnumAttr bases and prefix enum attributes with `CIR_` that automatically puts enum to `cir` namespace - Removes unnecessary enum case definitions - Unifies naming of enum values to use capitals consistently and make enumerations to start from 0 - Remove now unnecessary printers/parsers that gets to be generated automatically
Implement base-2 exponential intrinsic as part of llvm#1192
…lvm#1671) Hi, This is my first here! Tried to mirror some of the patterns already presented in both the codegen lib and its tests I'm very excited to start contributing and potentially making an impact in this project! feedback is much appreciated.
convert from codegen ```c++ assert(!Base.isVirtual() && "should not see vbases here"); auto *BaseRD = Base.getType()->getAsCXXRecordDecl(); Address V = CGF.GetAddressOfDirectBaseInCompleteClass( Dest.getAddress(), CXXRD, BaseRD, /*isBaseVirtual*/ false); AggValueSlot AggSlot = AggValueSlot::forAddr( V, Qualifiers(), AggValueSlot::IsDestructed, AggValueSlot::DoesNotNeedGCBarriers, AggValueSlot::IsNotAliased, CGF.getOverlapForBaseInit(CXXRD, BaseRD, Base.isVirtual())); CGF.EmitAggExpr(InitExprs[curInitIndex++], AggSlot); if (QualType::DestructionKind dtorKind = Base.getType().isDestructedType()) CGF.pushDestroyAndDeferDeactivation(dtorKind, V, Base.getType()); ```
You can test this locally with the following command:git-clang-format --diff 32e3971d31452620eb102f122259b429733b3ef0 67e6ac3525ffff8e95fdefd8d4cc0861a8234c8c --extensions cpp -- clang/lib/CIR/CodeGen/CIRGenModule.cpp clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp View the diff from clang-format here.diff --git a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
index 756416302b..069233d27b 100644
--- a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
@@ -1610,7 +1610,7 @@ void LoweringPreparePass::runOnOperation() {
buildCXXGlobalInitFunc();
buildGlobalCtorDtorList();
buildGlobalAnnotationValues();
-
+
if (theModule && theModule->hasAttr("cir.global_annotations")) {
theModule->removeAttr("cir.global_annotations");
}
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the detailed explanation, some comments but this mostly ready to go
@@ -1610,6 +1610,10 @@ void LoweringPreparePass::runOnOperation() { | |||
buildCXXGlobalInitFunc(); | |||
buildGlobalCtorDtorList(); | |||
buildGlobalAnnotationValues(); | |||
|
|||
if (theModule && theModule->hasAttr("cir.global_annotations")) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove the curly braces!
@@ -2190,6 +2190,11 @@ mlir::LogicalResult CIRToLLVMFuncOpLowering::matchAndRewrite( | |||
cir::FuncOp op, OpAdaptor adaptor, | |||
mlir::ConversionPatternRewriter &rewriter) const { | |||
|
|||
mlir::ModuleOp module = op->getParentOfType<mlir::ModuleOp>(); | |||
if (module->hasAttr("cir.global_annotations")) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here!
@@ -2190,6 +2190,11 @@ mlir::LogicalResult CIRToLLVMFuncOpLowering::matchAndRewrite( | |||
cir::FuncOp op, OpAdaptor adaptor, | |||
mlir::ConversionPatternRewriter &rewriter) const { | |||
|
|||
mlir::ModuleOp module = op->getParentOfType<mlir::ModuleOp>(); | |||
if (module->hasAttr("cir.global_annotations")) { | |||
op->removeAttr("annotations"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this necessary? If cir.global_annotations
have been removed in lowering prepare, how come it might show up here again?
Seems like some clang-format is needed |
This PR fixes an issue where the
[[clang::annotate]]
attribute on C++ constructors (CXXConstructorDecl
) was not correctly lowered, and resolves a subsequent crash in thecir-to-llvm
pipeline caused by the initial fix.1. The Problem
There were two distinct problems:
Initial Bug: The
[[clang::annotate]]
attribute was completely ignored when applied to a C++ constructor. While it worked for regular functions, the specific code path for handlingCXXConstructorDecl
inCIRGenModule
did not process this attribute.Downstream Crash: After fixing the initial bug to generate the correct
annotations
andcir.global_annotations
in the CIR dialect, thecir-translate
tool would crash with a "redefinition of symbol" error for the annotation string (e.g.,.str.annotation
).2. Analysis and Root Cause
Cause of Initial Bug: In
CIRGenModule::emitGlobalDefinition
, the code path for constructors and destructors branches toABI->emitCXXStructor
. This path was missing the logic to check for anAnnotateAttr
and add it to thedeferredAnnotations
map, which is correctly handled for regularFunctionDecl
s.Cause of Downstream Crash: The
cir-to-llvm
lowering pipeline has two mechanisms for handling annotations:LoweringPrepare
pass processes thecir.global_annotations
attribute on theModuleOp
and generates the corresponding@llvm.global.annotations
array and its associated global string constants.cir-translate
binary also attempts to process the samecir.global_annotations
attribute.The issue was that
LoweringPreparePass
did not consume (remove) thecir.global_annotations
attribute after processing it. This left the attribute on the module, causing the later stage incir-translate
to re-process it, leading to the symbol redefinition crash.3. Implementation
This PR addresses both issues with two key changes:
In
CIRGenModule.cpp
:AnnotateAttr
has been added to theCXXConstructorDecl
/CXXDestructorDecl
path withinemitGlobalDefinition
. This ensures that constructor annotations are correctly identified and deferred for processing, just like regular functions.In
LoweringPreparePass
:buildGlobalAnnotationValues()
function successfully processes thecir.global_annotations
attribute and generates the necessary LLVM globals, the pass now removes thecir.global_annotations
attribute from theModuleOp
. This "consumes" the attribute, preventing any subsequent pass from redundantly processing it.4. Verification
With these changes, a C++ constructor with a
[[clang::annotate]]
attribute is now correctly lowered through the entire pipeline:cir
dialect) correctly contains both the localannotations
on thecir.func
and the module-levelcir.global_annotations
.cir-opt -cir-to-llvm
pass successfully lowers this to the LLVM dialect.cir-translate
tool successfully converts the LLVM dialect to LLVM IR text without crashing.@llvm.global.annotations
metadata for the constructor.This fix ensures that annotation metadata is preserved correctly and robustly for C++ constructors.