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] Vector types, comparison operators #420

Closed
wants to merge 1,352 commits into from

Conversation

dkolsen-pgi
Copy link
Collaborator

This is part 3 of implementing vector types and vector operations in ClangIR, issue #284.

Create new operation cir.vec.cmp which implements the relational comparison operators (== != < > <= >=) on vector types. A new operation was created rather than reusing cir.cmp because the result is a vector of a signed intergral type, not a bool.

Add CodeGen and Lowering tests for vector comparisons.

Fix the floating-point comparison predicate when lowering to LLVM. To handle NaN values correctly, the comparisons need to be ordered rather than unordered. (Except for !=, which needs to be unordered.) For example, "ueq" was changed to "oeq".

bcardosolopes and others added 30 commits December 19, 2023 22:37
There is no really good way to test this since `-verify` does not really
care how many times the same diagnostics shows up (somehow it already dedups).

This improves user experience a tiny bit :)
Before this fix foo(1, 1) returned 255 in
  int foo(int a, int b) {
    return a && b;
  }
Whenever a variable declaration is found, it is created as a global
variable in the module. In C, these variables must be instantiated with
the CIRGenBuilder::createVersionedGlobal to prevent naming conflicts
when multiple static variables are declared across the compilation unit.
In C++, name mangling is used to prevent naming conflicts.
Remove boilerplate code for replacing a CIR global op with a region
initialized LLVM global op and mark variables as constant whenever
possible.

ghstack-source-id: dcf7ff7183426700a780f08cc2e29268b10a50c5
Pull Request resolved: llvm#199
At its core, this patch fixes an issue where the ConstStruct parser
would infer its type from the list of elements and override its explicit
attribute type that had already been parsed. This caused type mismatches
since the name of the StructType would be dropped.

Since `cir.typeinfo` depended on this broken parsing method to work, it
was also fixed to use a `ArrayAttr` instead of a `cir.const_struct` to
store its values.

To simplify parsing and printing struct member on both `cir.typeinfo`
and `cir.const_struct`, the custom `ConstStructMembers` parser/printer
was renamed to `StructMembers` and is now used on both attributes. It
was also refactored to patch malformed spacing between members and to
simplify the code.

ghstack-source-id: 1c913c6c00b2826fd0dac3dd18e7cb56bfe2e737
Pull Request resolved: llvm#200
Adds support for CIR globals with basic struct initializers. This patch
tackes only primitive, pointers, and simple nested structs. Arrays,
char pointers, and other more complex types are not supported yet.

ghstack-source-id: a077f04ca9ddb2649bcba0e986280ab19eaabe79
Pull Request resolved: llvm#201
Lowers the particular case where an array of structs is constant-initialized.

ghstack-source-id: f8389d899e07f73485658d86469d7595a4373901
Pull Request resolved: llvm#202
Updates the lowering of cir.const to support #cir.const_struct as a
initializer, allowing the initialization of local structs with said
attribute.

ghstack-source-id: cc6c1378775bd2239c821c357e281bbc8cc3b0a7
Pull Request resolved: llvm#203
Global addresses are constant, so we can initialize them at compile time
using CIR's global_view attribute. This patch adds codegen support for
the initialization of variables with constant global addresses.

Since a builder method was added for global_view, the patch also updates
the codegen of global variables to use it wherever possible.

ghstack-source-id: 513365c52ac1ca603a81fcf3ff124b5da39f6f14
Pull Request resolved: llvm#204
Adds lowering logic for CIR's global_view attributes with no indexes.
This is done by converting the global to a region-initialized LLVM
global operation, where the region returns the address of the global
used in the gloval_view initializer attribute.

ghstack-source-id: a9452eddbd516553273461a8187afcebc211e4d3
Pull Request resolved: llvm#205
Constant initialization of static local arrays would fail due to a
mismatch between the variable and the initializer type size. This patch
fixes the data layout interface implementation for the cir.array type.

A complete array in C/C++ should have its type size in bits equal to
the size of the array times the size of the element type.

ghstack-source-id: 56f3f2918b23309210ad026017bafa37ca03b2d4
Pull Request resolved: llvm#206
ghstack-source-id: 1f793b2abcb144ab10b1ddbd99f12d1dcc6c8707
Pull Request resolved: llvm#207
PR llvm#200 broke the vtableAttr usage, as it was not properly refactored
to use ArrayAttr instead of ConstStructAttr to store its members.
This change does the CIR generation for globals initialized by a
constructor call. It currently only covers C++ to CIR generation. The
corresponding LLVM lowering will be in a follow-up commit.

A motivating example is

```
class Init {
  friend class ios_base;

public:
  Init(bool);
  ~Init();

private:
  static bool _S_synced_with_stdio;
};

static Init ioinit(true);
```

Unlike what the default Clang codegen generates LLVM that detaches the
initialization code from the global var definition (like below), we are
taking a different approach that keeps them together, which we think
will make the later dataflow analysis/transform easier.

```
@_ZL8ioinit = internal global %class.Init zeroinitializer, align 1, !dbg !0

define internal void @cxx_global_var_init() #0 section ".text.startup" !dbg !23 {
entry:
  call void @_ZN4InitC2Ev(ptr noundef nonnull align 1 dereferenceable(1) @_ZL8ioinit), !dbg !27
  %0 = call i32 @cxa_atexit(ptr @_ZN4InitD1Ev, ptr @_ZL8ioinit, ptr @dso_handle) llvm#3, !dbg !29
  ret void, !dbg !27
}
```

So on CIR, we have something like:

```
  cir.global "private" internal @_ZL8__ioinit = ctor : !ty_22class2EInit22 {
    %0 = cir.get_global @_ZL8__ioinit : cir.ptr <!ty_22class2EInit22> loc(#loc8)
    %1 = cir.const(#true) : !cir.bool loc(#loc5)
    cir.call @_ZN4InitC1Eb(%0, %1) : (!cir.ptr<!ty_22class2EInit22>, !cir.bool) -> () loc(#loc6)
}
```

The destructor support will also be in a separate change.
This PR fixes lowering for `break/continue`  in loops.
The idea is to replace `cir.yield break` and `cir.yield continue` with
the branch operations to the corresponding blocks. Note, that we need to
ignore nesting loops and don't touch `break` in switch operations. Also,
`yield` from `if` need to be considered only when it's not the loop
`yield` and `continue` in switch is ignored since it's processed in the
loops lowering.

Fixes llvm#160
Essentially emits an LValue for the struct and then passes it as a call
argument.
Enabling IR printing with --mlir-print-ir-after=passName1, passName2.
This requires all CIR passes to be registered at startup time.
Summary:
Setting the Optnone attribute for CIR functions and progating it all the
way down to LLVM IR for those not supposed to be optimized.
gitoleg and others added 24 commits January 16, 2024 10:44
The next step for inline assembly. Sorry, maybe it looks too big on the
first glance. And it's kind of hard to extract something well-grained
from the code and introduce it as a separate PR, but I try.

Actually there is nothing really interesting done here, and the next
will (I hope :) ) simplify your review process.

1) In this PR we introduce operand's constraints and the task is to
collect them (and maybe transform a little)
2) There are two big functions copy-pasted from the traditional
`Codegen` and I doubt they need to be reviewed.
3) We still don't do anything CIR-specific. Basically, we just work with
strings in the same way like traditional `Codegen` does.
4) We just iterate over the input and output operands and collect the
constraints
5) We still follow to the traditional `CodeGen` and don't do anything
new, except a separate function that collects constraints infos in the
very beginning of the `buildStmt`.


Also, I renamed `AsmDialect` to `AsmFlavor` as you asked in llvm#326
This PR fixes CIR lowering for the next case.

```
void foo() {
    struct {
        int a;
        int b;
    } a[1] = {{0,1}}; 
}
```
Note, we don't create attribute here and lower such const arrays as
values.
When introducing attribute `#cir.int`, the constant type verification is
not updated. If a `cir.const` operation produces an integral constant
from a `#cir.int` attribute, the integer's type is not verified:

```mlir
%1 = cir.const(#cir.int<0> : !cir.int<s, 8>) : !cir.int<u, 8>
// Not verified: !cir.int<s, 8> differs from !cir.int<u, 8>
```

The corresponding test is also wrong but fail to be detected.

This patch fixes this issue.
This is part 2 of implementing vector types and vector operations in
ClangIR, issue llvm#284.

Create new operation `cir.vec.insert`, which changes one element of an
existing vector object and returns the modified vector object. The input
and output vectors are prvalues; this operation does not touch memory.
The assembly format and the order of the arguments match that of
llvm.insertelement in the LLVM dialect, since the operations have
identical semantics.

Implement vector element lvalues in class `LValue`, adding member
functions `getVectorAddress()`, `getVectorPointer()`, `getVectorIdx()`,
and `MakeVectorElt(...)`.

The assembly format for operation `cir.vec.extract` was changed to match
that of llvm.extractelement in the LLVM dialect, since the operations
have identical semantics.

These two features, `cir.vec.insert` and vector element lvalues, are
used to implement `v[n] = e`, where `v` is a vector. This is a little
tricky, because `v[n]` isn't really an lvalue, as its address cannot be
taken. The only place it can be used as an lvalue is on the left-hand
side of an assignment.

Implement unary operators on vector objects (except for logical not on a
vector mask, which will be covered in a future commit for boolean
vectors). The code for lowering cir.unary for all types, in
`CIRUnaryOpLowering::matchAndRewrite`, was largely rewritten. Support
for unary `+` on non-vector pointer types was added. (It was already
supported and tested in AST->ClangIR CodeGen, but was missing from
ClangIR->LLVM Dialect lowering.)

Add tests for all binary vector arithmetic operations other than
relational operators and shift operators. There were all working after
the previous vector types commit, but only addition had beet tested at
the time.

Co-authored-by: Bruno Cardoso Lopes <bcardosolopes@users.noreply.github.com>
Detaches the representation of the C/C++ `continue` statement into a
separate operation. This simplifies mostly lowering and verifications
related to `continue` statements, as well as the definition and lowering
of the `cir.yield` operation.

A few checks regarding region terminators were also removed from the
lowering stage, since they are already enforced by MLIR.

ghstack-source-id: 1810a48ada88fe7ef5638b0758a2298d9cfbdb8b
Pull Request resolved: llvm#394
Same rationale as `cir.continue`, it detaches the representation of the
C/C++ `break` statement into a separate operation. This simplifies
lowering and verifications related to `break` statements, as well as the
definition and lowering of the `cir.yield` operation.

ghstack-source-id: 929cf96c3abe51d717c2fa6ca9e0073e42e770c6
Pull Request resolved: llvm#395
This changes the `cir.await` operation to expect a `cir.condition` as
the terminator for the ready region. This simplifies the `cir.await`
while also simplifying the `cir.yield`. If `cir.condition` holds a
true value, then the `cir.await` will continue the coroutine, otherwise,
it will suspend its execution.

The `cir.condition` op was also updated to allow `cir.await` as its
parent operation.

ghstack-source-id: 1ebeb2cfbdeff6f289936d16354cba534e093ea7
Pull Request resolved: llvm#396
Instead of having a `cir.yield fallthrough` operation, the default
branching behavior of the parent operation is denoted by `cir.yield`.
In other words, a `cir.yield` operation in a switch case region
represents the default branching behavior of the switch operation, which
is a fallthrough.

The `cir.yield` operation now represents the default branching behavior
of the parent operation's region. For example, in a if-else region, a
`cir.yield` operation represents a branch to the exit block.

ghstack-source-id: 713c457dfb2228fbdf63ba72dd6396665512bb9d
Pull Request resolved: llvm#397
…tcase

- Add cir.try_call parsing.
- Add block destinations and hookup exception info type.
- Properly implement interface methods.

Printer is still missing, but coming next.
After some discussions with @sitio-couto, it might be better if we use a
simplified version that doesn't take the labels into account just yet.
`cir.try_call` should have the same semantics as `cir.break`, in the sense that
it needs further expansion when getting rid of structured control flow. Early
lowering here would complicate CIR generated code and make it harder to
analyse. Further CIR to CIR passes will properly expand this at some point
prior to LLVM lowering.
We can now handle more of EHScope::Catch and lay out the skeleton for CIR's
version of that, adding tons of asserts for cases not currently handled. As
part of this we're able to build the clause list as part of CatchOp based on
the handlers, and create allocation for the exception_info type.

In the next part (where we currently hit an assert) of this work, the CatchOp
will then get its regions populated.

Incremental steps into getting basic exceptions to work, not enough for a
testcase just yet.
Doesn't do a lot of things compared to LLVM traditional codegen, one
more step towards basic exception support. No testcase possible just yet.
- Add an extra CatchOp region to hold fallback (where EH usually resumes or
  rethrows as part of try/catch).
- Emit `cir.resume` on the fallback region.

Incremental step into the next assertion, still missing pieces before adding
the first testcase.
This commit supports the codegen of wide string literals, including
`wchar_t` string literals, `char16_t` string literals, and `char32_t`
string literals.

I'm not following the proposal in llvm#374. The clang frontend doesn't
record the literal string. It only records the encoded code units for
wide string literals. So I believe that a dedicated string attribute
with an encoding tag as described in llvm#374 may not be that helpful as I
thought.
This patch introduces initial support for:
```
pragma omp parallel
```

This patch doesn't add support for any of the `parallel` clauses,
including variable privatization; thus, all variables are handled as
shared.

This PR fixes issue llvm#285.
Adds an interface to generically handle lowering and analysis of loop
operations in CIR. It can also perform verification of invariants common
to all loop operations.

ghstack-source-id: 0e413b14ea063a2b0d75aeaca0af88e547c15277
Pull Request resolved: llvm#405
Leverages the new LoopOpInterface for lowering instead of the LoopOp
operation. This is a step towards removing the LoopOp operation.

ghstack-source-id: 28c1294833a12669d222a293de76609d2cf19148
Pull Request resolved: llvm#406
Creates a separate C/C++ operation for do-while loops, while keeping the
LoopOpInterface to generically handle loops. This simplifies the IR
generation and printing/parsing of do-while loops. It also allows us to
define it regions in the order that they are executed, which is useful
for the lifetime analysis.

ghstack-source-id: b4d9517197b8f82ae677dc2684101fe5762b21b7
Pull Request resolved: llvm#407
Creates a separate C/C++ operation for while loops, while keeping the
LoopOpInterface to generically handle loops. This simplifies the IR
generation and printing/parsing of while loops.

ghstack-source-id: 29a6d7530263a4f96dbe6ce3052875831126005d
Pull Request resolved: llvm#408
This patch completes the deprecation of the generic `cir.loop` operation
by adding a new `cir.for` operation and removing the `cir.loop` op. The
new representation removes some bloat and places the regions in order of
execution.

ghstack-source-id: 886e0dacc632e5809015e2212810d690ef3ec294
Pull Request resolved: llvm#409
…rations

More machinery for exceptions.

This time around we finally emit a cir.catch and fix the order of emitting
operations. This allows a testcase to be added. I also added `CatchParamOp`,
which fetches the arguments for the clauses from the !cir.eh_info object.

Work coming next:
- Dtors.
- Use cir.try instead of cir.scope.
- Eesume.
- Documentation.`
This is part 3 of implementing vector types and vector operations in
ClangIR, issue llvm#284.

Create new operation `cir.vec.cmp` which implements the relational
comparison operators (`== != < > <= >=`) on vector types.  A new operation
was created rather than reusing `cir.cmp` because the result is a vector
of a signed intergral type, not a `bool`.

Add CodeGen and Lowering tests for vector comparisons.

Fix the floating-point comparison predicate when lowering to LLVM.  To
handle NaN values correctly, the comparisons need to be ordered rather
than unordered.  (Except for `!=`, which needs to be unordered.)  For
example, "ueq" was changed to "oeq".
@bcardosolopes
Copy link
Member

@lanza just rebased and this has the side effect of breaking GH PR workflow, making it impossible to review, apologies. Please rebase!

@dkolsen-pgi
Copy link
Collaborator Author

I couldn't figure out how to correctly update my branch after the rebase, and it was trivial to create a new branch with the correct contents. So I created a new PR, #432. I am abandoning this one.

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.

None yet