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] Add default alignment to createStore #1433

Draft
wants to merge 2,324 commits into
base: main
Choose a base branch
from

Conversation

FantasqueX
Copy link
Collaborator

@FantasqueX FantasqueX commented Mar 2, 2025

Currently, CIR's createStore doesn't add alignment by default while OG does. CIR adds alignment in lowering stage if missing alignment attribute by inferring size of type. However, this could be inaccurate. This change will break a lot of test cases. But I think we should do this as early as we can.

Resolves: #1204

ghehg and others added 30 commits January 27, 2025 12:39
follow llvm#1033
handle `LongDoubleType` with `FP80Type`.
)

As title, this patch refactors raw string literals for (module)
attribute names into static methods of `CIRDialect`, following the
convention of MLIR.
This PR handles calls with unions passed by value in the calling
convention pass.

#### Implementation
As one may know, data layout for unions in CIR and in LLVM differ one
from another. In CIR we track all the union members, while in LLVM IR
only the largest one.

And here we need to take this difference into account: we need to find a
type of the largest member and treat it as the first (and only) union
member in order to preserve all the logic from the original codegen.

There is a method `StructType::getLargestMember` - but looks like it
produces different results (with the one I implemented or better to say
copy-pasted). Maybe it's done intentionally, I don't know.

The LLVM IR produced has also some difference from the original one. In
the original IR `gep` is emitted - and we can not do the same. If we
create `getMemberOp` we may fail on type checking for unions - since the
first member type may differ from the largest type. This is why we
create `bitcast` instead. Relates to the issue llvm#1061
…enBuiltinAArch64.cpp (llvm#1124)

We are still seeing crash message like `NYI UNREACHABLE executed at
clang/lib/CIR/CodeGen/CIRGenBuiltinAArch64.cpp:3304`, which is not
convenient for triaging as our code base changes so fast, line number
doesn't help much.
So, here we replaced most of `llvm_unreachable("NYI")` with more
informative message.
…1125)

Currently, the final `target triple` in LLVM IR is set in
`CIRGenAction`, which is not executed by cir tools like `cir-translate`.
This PR delay its assignment to LLVM lowering, enabling sharing the
emitting of `target triple` between different invoking paths.
…ts (llvm#1074)

As the title says, this PR adds support for calls with struct types >
128 bits, building upon this
[PR](llvm#1068).

The idea is gotten from the original Codegen, and I have added a couple
of tests.
…bsOp to take vector input (llvm#1099)

Extend AbsOp to take vector of int input. With it, we can support
__builtin_elementwise_abs.
We should in the next PR extend FpUnaryOps to support vector type input
so we won't have blocker to implement all elementwise builtins
completely. Now just temporarily have missingFeature
`fpUnaryOPsSupportVectorType`.
Currently, int type UnaryOp support vector type.

FYI:
[clang's documentation about elementwise
builtins](https://clang.llvm.org/docs/LanguageExtensions.html#vector-builtins)
…vm#1102)

This is a NFC patch that moves declaration from  LowerToLLVM.cpp.

The motivation of the patch is, we hope we can use the abilities from
MLIR's standard dialects without lowering **ALL** clangir operation to
MLIR's standard dialects. For example, currently we have 86 operations
in LowerToLLVM.cpp but only 45 operations under though MLIR. It won't be
easy to add proper lowering for all operation to **different** dialects.

I think the solution may be to allow **mixed** IR. So that we can
lowering CIR to MLIR's standard dialects partially and we can use some
existing analysis and optimizations in MLIR and then we can lower all of
them (the MLIR dialects and unlowered clangir) to LLVM IR. The hybrid IR
is one of the goals of MLIR as far as I know.

NOTE: I completely understand that the DirectlyLLVM pipeline is the
tier-1 pipeline that we want to support. The idea above won't change
this. I just want to offer some oppotunities for the downstream projects
and finally some chances to improve the overall ecosystem.
This is going to be raised in follow up work, which is hard to
do in one go because createBaseClassAddr goes of the OG skeleton
and ideally we want ApplyNonVirtualAndVirtualOffset to work naturally.

This also doesn't handle null checks, coming next.
Now that we fixed the dep on VBase, clean up the rest of the function.
It was always the intention for `cir.cmp` operations to return bool
result. Due
to missing constraints, a bug in codegen has slipped in which created
`cir.cmp`
operations with result type that matches the original AST expression
type. In
C, as opposed to C++, boolean expression types are "int". This resulted
with
extra operations being codegened around boolean expressions and their
usage.

This commit both enforces `cir.cmp` in the op definition and fixes the
mentioned bug.
This is the first patch to support TBAA, following the discussion at
llvm#1076 (comment)

- add skeleton for CIRGen, utilizing `decorateOperationWithTBAA`
- add empty implementation in `CIRGenTBAA`
- introduce `CIR_TBAAAttr` with empty body
- attach `CIR_TBAAAttr` to `LoadOp` and `StoreOp`
- no handling of vtable pointer
- no LLVM lowering
)

The title describes the purpose of the PR. It adds initial support for
structures with padding to the call convention lowering for AArch64.

I have also _initial support_ for the missing feature
[FinishLayout](https://github.com/llvm/clangir/blob/5c5d58402bebdb1e851fb055f746662d4e7eb586/clang/lib/AST/RecordLayoutBuilder.cpp#L786)
for records, and the logic is gotten from the original codegen.

Finally, I added a test for verification.
…m#1152)

The function `populateCIRToLLVMConversionPatterns` contains a spaghetti
of LLVM dialect conversion patterns, which results in merge conflicts
very easily. Besides, a few patterns are even registered for more than
once, possibly due to careless resolution of merge conflicts.

This PR attempts to mitigate this problem. Pattern names now are sorted
in alphabetical order, and each source code line now only lists exactly
one pattern name to reduce potential merge conflicts.
…ltinExpr (llvm#1133)

This PR is a NFC as we just NYI every builtID of neon SISD. We will
implement them in subsequent PRs.
…CFG (llvm#1147)

This PR implements NYI in CIRScopeOpFlattening. It seems to me the best
way is to let results of ScopeOp forwarded as block arguments of the
last block split from the cir.scope block.
…der (llvm#1157)

With [llvm-project#116090](llvm/llvm-project#116090)
merged, we can get rid of `#include "../../../../Basic/Targets.h"` now.
AmrDeveloper and others added 14 commits February 26, 2025 11:00
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.
…#1412)

for example, lower 
`cir.alloca !cir.array<!s32i x N>, !cir.ptr<!cir.array<!s32i x N>>` 
to `memref.alloca() : memref<Nxi32>`

see llvm#1405
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?
Cleans up default linkage query implementations. Removes duplicities
from `extraClassDeclaration` that are now introduced through
`CIRGlobalValueInterface`. This makes it more consistent with the
`llvm::GlobalValue` methods.
… functions (llvm#1424)

This PR adds CIRGen and LLVM lowering support for base-to-derived and
derived-to-base cast operations on pointers to member functions.

This PR includes a new operation `cir.update_member` to help the LLVM
lowering procedure of such cast operations.

Resolve llvm#973 .
@FantasqueX FantasqueX marked this pull request as draft March 2, 2025 18:51
AdUhTkJm and others added 7 commits March 3, 2025 19:16
CIR didn't work on structs with destructor but without constructor. Now
it is fixed.

Moreover, CUDA kernels must be emitted if it was referred to in the
destructor of a non-device variable. It seems already working, so I just
unblocked the code path.
@FantasqueX FantasqueX force-pushed the createstore-default-alignment branch from eefe2d7 to 4a2d639 Compare March 4, 2025 19:26
@FantasqueX
Copy link
Collaborator Author

@bcardosolopes Hi, could you please take a look at this PR before it is ready? Currently, there are 140 test cases failing still. I want to make sure my direction is correct before continuing to work. Thanks.

@FantasqueX FantasqueX requested a review from Lancern March 12, 2025 13:34
Copy link
Member

@Lancern Lancern left a comment

Choose a reason for hiding this comment

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

This looks cool!

Comment on lines 353 to 360
bool _volatile = false, uint64_t alignment = {},
cir::MemOrderAttr order = {}) {
if (mlir::cast<cir::PointerType>(dst.getType()).getPointee() !=
val.getType())
dst = createPtrBitcast(dst, val.getType());
return create<cir::StoreOp>(loc, val, dst, _volatile, align, order,
const mlir::IntegerAttr alignmentAttr = mlir::IntegerAttr::get(
mlir::IntegerType::get(dst.getContext(), 64), alignment);
return create<cir::StoreOp>(loc, val, dst, _volatile, alignmentAttr, order,
Copy link
Member

Choose a reason for hiding this comment

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

Is this right? If alignment is not given an alignment of 0 will be added to the store operation, which looks a bit suspicious.

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.

inconsistency alignment of store instructions