110 Commits

Author SHA1 Message Date
Markus Böck
a7865228b3 [mlir][NFC] Fix codestyle issues introduced in D121988
These were noticed in the post commit review of https://reviews.llvm.org/D121988
2022-03-28 16:55:01 +02:00
Chia-hung Duan
14ecafd0bd [mlir] Make OpBuilder::createOperation to accept raw inputs
This provides a way to create an operation without manipulating
OperationState directly. This is useful for creating unregistered ops.

Reviewed By: rriddle, mehdi_amini

Differential Revision: https://reviews.llvm.org/D120787
2022-03-23 22:13:48 +00:00
Markus Böck
e13d23bc6c [mlir] Rename OpAsmParser::OperandType to OpAsmParser::UnresolvedOperand
I am not sure about the meaning of Type in the name (was it meant be interpreted as Kind?), and given the importance and meaning of Type in the context of MLIR, its probably better to rename it. Given the comment in the source code, the suggestion in the GitHub issue and the final discussions in the review, this patch renames the OperandType to UnresolvedOperand.

Fixes https://github.com/llvm/llvm-project/issues/54446

Differential Revision: https://reviews.llvm.org/D122142
2022-03-21 21:42:13 +01:00
Markus Böck
c14ba3c4be [mlir] Fix block merging with the result of a terminator
When the current implementation merges two blocks that have operands defined outside of their block respectively, it will merge these by adding a block argument in the resulting merged block and adding successor arguments to the predecessors.
There is a special case where this is incorrect however: If one of predecessors terminator produce the operand, inserting the block argument and updating the predecessor would lead to the terminator using its own result as successor argument.
IR Example:
```
  %0 = "test.producing_br"()[^bb1, ^bb2] {
        operand_segment_sizes = dense<0> : vector<2 x i32>
	} : () -> i32

^bb1:
  "test.br"(%0)[^bb4] : (i32) -> ()
```
where `^bb1` is then merged with another block would lead to:
 ```
  %0 = "test.producing_br"(%0)[^bb1, ^bb2]
```

This patch fixes that issue during clustering by making sure that if the operand is from an outside block, that it is not produced by the terminator of a predecessor.

Differential Revision: https://reviews.llvm.org/D121988
2022-03-21 13:26:35 +01:00
River Riddle
50f82e6847 [mlir] Fix missing verification after running an OpToOpAdaptorPass
The current decision of when to run the verifier is running on the
assumption that nested passes can't affect the validity of the parent
operation, which isn't true. Parent operations may attach any number
of constraints on nested operations, which may not necessarily be
captured (or shouldn't be captured) at a smaller granularity.

This commit rectifies this by properly running the verifier after an
OpToOpAdaptor pass. To avoid an explosive increase in compile time,
we only run verification on the parent operation itself. To do this, a
flag to mlir::verify is added to avoid recursive verification if it isn't
desired.

Fixes #54288

Differential Revision: https://reviews.llvm.org/D121836
2022-03-16 14:53:41 -07:00
Andrzej Warzynski
1b2e35e4d4 Revert "[mlir] Add extensible dialects"
This reverts commit dbe9f0914fcfd8444fd9656821af0f1a34a27e7a.

The flang-x86_64-windows buildbot has been failing since this has been merged:
* https://lab.llvm.org/buildbot/#/builders/172/builds/9124
Similar failure was reported by the pre-commit CI.
2022-03-03 10:30:50 +00:00
Mathieu Fehr
dbe9f0914f [mlir] Add extensible dialects
Add support for extensible dialects, which are dialects that can be
extended at runtime with new operations and types.

These operations and types cannot at the moment implement traits
or interfaces.

Differential Revision: https://reviews.llvm.org/D104554
2022-03-02 12:42:59 -08:00
River Riddle
23aa5a7446 [mlir] Rename the Standard dialect to the Func dialect
The last remaining operations in the standard dialect all revolve around
FuncOp/function related constructs. This patch simply handles the initial
renaming (which by itself is already huge), but there are a large number
of cleanups unlocked/necessary afterwards:

* Removing a bunch of unnecessary dependencies on Func
* Cleaning up the From/ToStandard conversion passes
* Preparing for the move of FuncOp to the Func dialect

See the discussion at https://discourse.llvm.org/t/standard-dialect-the-final-chapter/6061

Differential Revision: https://reviews.llvm.org/D120624
2022-03-01 12:10:04 -08:00
Shraiysh Vaishay
5bec1ea7a7 [mlir] Added oilist primitive
This patch attempts to add the `oilist` primitive proposed in the [[ https://llvm.discourse.group/t/rfc-extending-declarative-assembly-format-to-support-order-independent-variadic-segments/4388 | RFC: Extending Declarative Assembly Format to support order-independent variadic segments ]].

This element supports optional order-independent variadic segments for operations. This will allow OpenACC and OpenMP Dialects to have similar and relaxed requirements while encouraging the use of Declarative Assembly Format and avoiding code duplication.

An oilist element parses grammar of the form:
```
clause-list := clause clause-list | empty
clause := `keyword` clause1 | `otherKeyword` clause2
clause1 := <assembly-format element>
clause2 := <assembly-format element>
```

AssemblyFormat specification:
```
let assemblyFormat = [{
  oilist( `keyword` clause1
        | `otherkeyword` clause2
        ...
        )
}];
```

Example:
```
oilist( `private` `(` $arg0 `:` type($arg0) `)`
      | `nowait`
      | `reduction` custom<ReductionClause>($arg1, type($arg1)))

oilist( `private` `=` $arg0 `:` type($arg0)
      | `reduction` `=` $arg1 `:` type($arg1)
      | `firstprivate` `=` $arg3 `:` type($arg2))
```

Reviewed By: Mogball, rriddle

Differential Revision: https://reviews.llvm.org/D115215
2022-02-17 11:10:24 +05:30
River Riddle
d7f0083dca [mlir:ODS] Deprecate Op parser/printer fields in favor of a new hasCustomAssemblyFormat field
Currently if an operation wants a C++ implemented parser/printer, it specifies inline
code blocks. This is quite problematic for various reasons, e.g. it requires defining
C++ inside of Tablegen which is discouraged when possible, but mainly because
nearly all usages simply forward to static functions (e.g. `static void parseSomeOp(...)`)
with users devising their own standards for how these are defined.

This commit adds support for a `hasCustomAssemblyFormat` bit field that specifies if
a C++ parser/printer is needed, and when set to 1 declares the parse/print methods for
operations to override. For migration purposes, the existing behavior is untouched. Upstream
usages will be replaced in a followup to keep this patch focused on the new implementation.

Differential Revision: https://reviews.llvm.org/D119054
2022-02-07 19:03:57 -08:00
River Riddle
42e5f1d97b [mlir] Refactor how additional verification is specified in ODS
Currently if an operation requires additional verification, it specifies an inline
code block (`let verifier = "blah"`). This is quite problematic for various reasons, e.g.
it requires defining C++ inside of Tablegen which is discouraged when possible, but mainly because
nearly all usages simply forward to a static function `static LogicalResult verify(SomeOp op)`.
This commit adds support for a `hasVerifier` bit field that specifies if an additional verifier
is needed, and when set to `1` declares a `LogicalResult verify()` method for operations to
override. For migration purposes, the existing behavior is untouched. Upstream usages will
be replaced in a followup to keep this patch focused on the hasVerifier implementation.

One main user facing change is that what was one `MyOp::verify` is now `MyOp::verifyInvariants`.
This better matches the name this method is called everywhere else, and also frees up `verify` for
the user defined additional verification. The `verify` function when generated now (for additional
verification) is private to the operation class, which should also help avoid accidental usages after
this switch.

Differential Revision: https://reviews.llvm.org/D118742
2022-02-02 13:34:28 -08:00
River Riddle
6842ec42f6 [mlir][NFC] Add a using for llvm::SMLoc/llvm::SMRange to LLVM.h
These are used pervasively during parsing.

Differential Revision: https://reviews.llvm.org/D118291
2022-01-26 21:37:23 -08:00
Mogball
572fa9642c [mlir] Add a ControlFlowSink pass.
Control-Flow Sink moves operations whose only uses are in conditionally-executed regions into those regions so that paths in which their results are not needed do not perform unnecessary computation.

Depends on D115087

Reviewed By: jpienaar, rriddle, bondhugula

Differential Revision: https://reviews.llvm.org/D115088
2022-01-24 23:08:34 +00:00
Mehdi Amini
9006bf4248 Remove obsolete getAsmResultNames from OpAsmDialectInterface
This is superseded by the same method on OpAsmOpInterface, which is
available on the Dialect through the Fallback mechanism,

Reviewed By: rriddle

Differential Revision: https://reviews.llvm.org/D117750
2022-01-21 05:58:32 +00:00
Dominik Grewe
1e09f0a972 Preserve function argument locations.
Previously the optional locations of function arguments were dropped in
`parseFunctionArgumentList`. This CL adds another output argument to the
function through which they are now returned. The values are then plumbed
through as an array of optional locations in the various places.

Reviewed By: rriddle

Differential Revision: https://reviews.llvm.org/D117604
2022-01-20 00:01:12 +00:00
Mogball
5c36ee8d57 [mlir] Drop the leading space when printing regions
The leading space that is always printed at the beginning of regions is not consistent with other parts of the printing API. Moreover, this leading space can lead to undesirable assembly formats:

```
attr-dict-with-keyword $region
```

Prints as:

```
// Two spaces between `}` and `{`
attributes {foo}  { ... }
```

Moreover, the leading space results in the odd generic op format:

```
"test.op"() ( {...}) : () -> ()
```

Reviewed By: rriddle, mehdi_amini

Differential Revision: https://reviews.llvm.org/D117411
2022-01-18 16:52:34 +00:00
Jacques Pienaar
05594de2d7 [mlir][ods] Handle DeclareOpInterfaceMethods in formatgen
Previously it would not consider ops with
DeclareOpInterfaceMethods<InferTypeOpInterface> as having the
InferTypeOpInterface interfaces added. The OpInterface nested inside
DeclareOpInterfaceMethods is not retained so that one could query it, so
check for the the C++ class directly (a bit raw/low level - will be
addressed in follow up).

Differential Revision: https://reviews.llvm.org/D116572
2022-01-04 08:28:59 -08:00
Mehdi Amini
02b6fb218e Fix clang-tidy issues in mlir/ (NFC)
Reviewed By: ftynse

Differential Revision: https://reviews.llvm.org/D115956
2021-12-20 20:25:01 +00:00
Mehdi Amini
7f9e9c7fc3 Move getAsmBlockArgumentNames from OpAsmDialectInterface to OpAsmOpInterface
This method is more suitable as an opinterface: it seems intrinsic to
individual instances of the operation instead of the dialect.
Also remove the restriction on the interface being applicable to the entry block only.

Differential Revision: https://reviews.llvm.org/D116018
2021-12-20 07:18:01 +00:00
Alexander Belyaev
f77e9f8768 [mlir] Extend tensor.from_elements to support N-D case.
RFC: https://llvm.discourse.group/t/rfc-extend-tensor-fromelementsop-to-n-d/4715

Differential Revision: https://reviews.llvm.org/D115821
2021-12-16 14:52:41 +01:00
Mogball
0845635eda [mlir][ir] Custom ops' parse/print fall back to dialect hooks
Custom ops that have no parser or printer should fall back to the dialect's parser and/or printer hooks. This avoids the need to define parsers and printers that simply dispatch to the dialect hook.

Reviewed By: mehdi_amini, rriddle

Differential Revision: https://reviews.llvm.org/D115481
2021-12-10 19:34:25 +00:00
Mehdi Amini
be0a7e9f27 Adjust "end namespace" comment in MLIR to match new agree'd coding style
See D115115 and this mailing list discussion:
https://lists.llvm.org/pipermail/llvm-dev/2021-December/154199.html

Differential Revision: https://reviews.llvm.org/D115309
2021-12-08 06:05:26 +00:00
Sandeep Dasgupta
e5a8c8c883 [mlir] Refactoring a few Parser APIs
Refactored two new parser APIs parseGenericOperationAfterOperands and
 parseCustomOperationName out of parseGenericOperation and parseCustomOperation.

Motivation: Sometimes an op can be printed in a special way if certain criteria
is met. While parsing, we need to handle all the versions.
`parseGenericOperationAfterOperands` is handy in situation where we already
parsed the operands and decide to fall back to default parsing.

`parseCustomOperationName` is useful when we need to know details (dialect,
operation name etc.) about a parsed token meant to be an mlir operation.

Reviewed By: rriddle

Differential Revision: https://reviews.llvm.org/D113719
2021-11-23 06:11:01 +00:00
River Riddle
0c7890c844 [mlir] Convert NamedAttribute to be a class
NamedAttribute is currently represented as an std::pair, but this
creates an extremely clunky .first/.second API. This commit
converts it to a class, with better accessors (getName/getValue)
and also opens the door for more convenient API in the future.

Differential Revision: https://reviews.llvm.org/D113956
2021-11-18 05:39:29 +00:00
River Riddle
195730a650 [mlir][NFC] Replace references to Identifier with StringAttr
This is part of the replacement of Identifier with StringAttr.

Differential Revision: https://reviews.llvm.org/D113953
2021-11-16 17:36:26 +00:00
Jacques Pienaar
6a99423390 [mlir] Expand prefixing to OpFormatGen
Follow up to also use the prefixed emitters in OpFormatGen (moved
getGetterName(s) and getSetterName(s) to Operator as that is most
convenient usage wise even though it just depends on Dialect). Prefix
accessors in Test dialect and follow up on missed changes in
OpDefinitionsGen.

Differential Revision: https://reviews.llvm.org/D112118
2021-10-20 07:08:37 -07:00
Jacques Pienaar
62bf850910 [mlir] Flipping Test dialect to prefixed form _Both
Starting with a mostly NFC change to be able to differentiate between
mechanical changes from ones that require more detailed review.

This will be used to flush out flow before flipping dialects used
outside local testing. As this dialect is not intended to be used
generally rather than in tests in core, I will not be following 2 week
staging approach here.
2021-10-18 10:00:37 -07:00
Mogball
a54f4eae0e [MLIR] Replace std ops with arith dialect ops
Precursor: https://reviews.llvm.org/D110200

Removed redundant ops from the standard dialect that were moved to the
`arith` or `math` dialects.

Renamed all instances of operations in the codebase and in tests.

Reviewed By: rriddle, jpienaar

Differential Revision: https://reviews.llvm.org/D110797
2021-10-13 03:07:03 +00:00
Vladislav Vinogradov
c6390f19f2 [mlir] Fix AsmPrinter for types with sub elements
Call `printType(subElemType)` instead of `os << subElemType` for them.
It allows to handle type aliases inside complex types.

As a side effect, fixed `test.int` parsing.

Reviewed By: rriddle

Differential Revision: https://reviews.llvm.org/D111536
2021-10-12 12:08:16 +03:00
Chris Lattner
fb093c8314 [ODS/AsmParser] Don't pass MLIRContext with DialectAsmParser.
The former is redundant because the later carries it as part of
its builder.  Add a getContext() helper method to DialectAsmParser
to make this more convenient, and stop passing the context around
explicitly.  This simplifies ODS generated parser hooks for attrs
and types.

This resolves PR51985

Recommit 4b32f8bac4 after fixing a dependency.

Differential Revision: https://reviews.llvm.org/D110796
2021-09-30 05:10:28 +00:00
Mehdi Amini
3310e0020c Revert "[ODS/AsmParser] Don't pass MLIRContext with DialectAsmParser."
This reverts commit 4b32f8bac40dcd1535bfe95757c3de0911bf6d1a.

Seems like the build is broken with -DDBUILD_SHARED_LIBS=ON
2021-09-30 05:01:17 +00:00
Chris Lattner
4b32f8bac4 [ODS/AsmParser] Don't pass MLIRContext with DialectAsmParser.
The former is redundant because the later carries it as part of
its builder.  Add a getContext() helper method to DialectAsmParser
to make this more convenient, and stop passing the context around
explicitly.  This simplifies ODS generated parser hooks for attrs
and types.

This resolves PR51985

Differential Revision: https://reviews.llvm.org/D110796
2021-09-29 21:36:05 -07:00
Mehdi Amini
b3891f28a3 Fix ClangTidyLegacy warning: "'virtual' is redundant since the function is already declared 'final' " (NFC) 2021-09-26 22:02:23 +00:00
Mehdi Amini
c41b16c26b Change ASM Op printer to print the operation name in the framework instead of leaving it up to each individual operation
This aligns the printer with the parser contract: the operation isn't part of the user-controllable part of the syntax.

Differential Revision: https://reviews.llvm.org/D108804
2021-08-31 17:52:40 +00:00
Mehdi Amini
fd87963eee Change dialect printOperation() hook to getOperationPrinter()
This makes the hook return a printer if available, instead of using LogicalResult  to
indicate if a printer was available (and invoked). This allows the caller to detect that
the dialect has a printer for a given operation without actually invoking the printer.
It'll be leveraged in a future revision to move printing the op name itself under control
of the ASMPrinter.

Differential Revision: https://reviews.llvm.org/D108803
2021-08-31 17:52:39 +00:00
Stephen Neuendorffer
7776b19eed [MLIR] Move TestDialect to ::test namespace
While the changes are extensive, they basically fall into a few
categories:
1) Moving the TestDialect itself.
2) Updating C++ code in tablegen to explicitly use ::mlir, since it
will be put in a headers that shouldn't expect a 'using'.
3) Updating some generic MLIR Interface definitions to do the same thing.
4) Updating the Tablegen generator in a few places to be explicit about
namespaces
5) Doing the same thing for llvm references, since we no longer pick
up the definitions from mlir/Support/LLVM.h

Differential Revision: https://reviews.llvm.org/D88251
2021-08-14 13:24:41 -07:00
Vladislav Vinogradov
59f59d1c62 [mlir] Allow to override type/attr aliases from various hooks
Use new return type for `OpAsmDialectInterface::getAlias`:

* `AliasResult::NoAlias` if an alias was not provided.
* `AliasResult::OverridableAlias` if an alias was provided, but it might be overriden by other hook.
* `AliasResult::FinalAlias` if an alias was provided and it should be used (no other hooks will be checked).

In that case `AsmPrinter` will use either the first alias with `FinalAlias` result or
the last alias with `OverridableAlias` result (it depends on dialect array order).

Used `OverridableAlias` result for `BuiltinOpAsmDialectInterface`.

Use case: provide more informative alias for built-in attributes like `AffineMapAttr`
instead of generic "map<N>".

Reviewed By: rriddle

Differential Revision: https://reviews.llvm.org/D107437
2021-08-06 12:05:31 +03:00
Jacques Pienaar
d425f58939 [mlir] Make ValueShapeRange a new class
Retaining old interface and should be constructable as previous, change would have been NFC except it this doesn't implicitly work with OpAdaptor generated in C++14.

Differential Revision: https://reviews.llvm.org/D106772
2021-07-26 17:08:32 -07:00
Vladislav Vinogradov
eb6c63cb0b [mlir] Fix RankedTensorType::walkImmediateSubElements method
Add 'enconding' attribute visitor.
Without it ASM printer doesn't use attribute aliases for 'enconding'.

Reviewed By: rriddle

Differential Revision: https://reviews.llvm.org/D105554
2021-07-26 11:49:25 +03:00
Jacques Pienaar
ee7242c662 [mlir] Update to use ValueShapeRange (NFC)
Update to use alias in preparation for changing it to not just be a pure alias.
2021-07-22 12:24:49 -07:00
MaheshRavishankar
9afc065743 Split InferShapedTypeOpInterface to create ReifyRankedShapedTypeInterface.
The `reifyReturnTypeShapesPerResultDim` method supports shape
inference for rsults that are ranked types. These are used lower in
the codegeneration stack than its counter part `reifyReturnTypeShapes`
which also supports unranked types, and is more suited for use higher
up the compilation stack. To have separation of concerns, this method
is split into its own interface.
See discussion : https://llvm.discourse.group/t/better-layering-for-infershapedtypeopinterface/3823

Differential Revision: https://reviews.llvm.org/D106133
2021-07-19 14:44:52 -07:00
Matthias Springer
c0a6318d96 [mlir][tensor] Add tensor.dim operation
* Split memref.dim into two operations: memref.dim and tensor.dim. Both ops have the same builder interface and op argument names, so that they can be used with templates in patterns that apply to both tensors and memrefs (e.g., some patterns in Linalg).
* Add constant materializer to TensorDialect (needed for folding in affine.apply etc.).
* Remove some MemRefDialect dependencies, make some explicit.

Differential Revision: https://reviews.llvm.org/D105165
2021-07-01 10:00:19 +09:00
Stella Laurenzo
485cc55edf [mlir] Generare .cpp.inc files for dialects.
* Previously, we were only generating .h.inc files. We foresee the need to also generate implementations and this is a step towards that.
* Discussed in https://llvm.discourse.group/t/generating-cpp-inc-files-for-dialects/3732/2
* Deviates from the discussion above by generating a default constructor in the .cpp.inc file (and adding a tablegen bit that disables this in case if this is user provided).
* Generating the destructor started as a way to flush out the missing includes (produces a link error), but it is a strict improvement on its own that is worth doing (i.e. by emitting key methods in the .cpp file, we root vtables in one translation unit, which is a non-controversial improvement).

Differential Revision: https://reviews.llvm.org/D105070
2021-06-29 20:10:30 +00:00
MaheshRavishankar
3ed3e438a7 [mlir] Move memref.dim canonicalization using InferShapedTypeOpInterface to a separate pass.
Based on dicussion in
[this](https://llvm.discourse.group/t/remove-canonicalizer-for-memref-dim-via-shapedtypeopinterface/3641)
thread the pattern to resolve the `memref.dim` of a value that is a
result of an operation that implements the
`InferShapedTypeOpInterface` is moved to a separate pass instead of
running it as a canonicalization pass. This allows shape resolution to
happen when explicitly required, instead of automatically through a
canonicalization.

Differential Revision: https://reviews.llvm.org/D104321
2021-06-16 22:13:11 -07:00
Jacques Pienaar
0e760a0870 Add hook for dialect specializing processing blocks post inlining calls
This allows for dialects to do different post-processing depending on operations with the inliner (my use case requires different attribute propagation rules depending on call op). This hook runs before the regular processInlinedBlocks method.

Differential Revision: https://reviews.llvm.org/D104399
2021-06-16 12:53:21 -07:00
River Riddle
0289a2692e [mlir] Add support for filtering patterns based on debug names and labels
This revision allows for attaching "debug labels" to patterns, and provides to FrozenRewritePatternSet for  filtering patterns based on these labels (in addition to the debug name of the pattern). This will greatly simplify the ability to write tests targeted towards specific patterns (in cases where many patterns may interact),  will also simplify debugging pattern application by observing how application changes when enabling/disabling specific patterns.

To enable better reuse of pattern rewrite options between passes, this revision also adds a new PassUtil.td file to the Rewrite/ library that will allow for passes to easily hook into a common interface for pattern debugging. Two options are used to seed this utility, `disable-patterns` and `enable-patterns`, which are used to enable the filtering behavior indicated above.

Differential Revision: https://reviews.llvm.org/D102441
2021-06-02 12:05:25 -07:00
Chia-hung Duan
c484c7dd9d [mlir-reduce] Reducer refactor.
* A Reducer is a kind of RewritePattern, so it's just the same as
writing graph rewrite.
* ReductionTreePass operates on Operation rather than ModuleOp, so that
* we are able to reduce a nested structure(e.g., module in module) by
* self-nesting.

Reviewed By: jpienaar, rriddle

Differential Revision: https://reviews.llvm.org/D101046
2021-06-02 07:45:00 +08:00
Lei Zhang
4694097dab [mlir] Don't elide the last op if there is no terminator
Reviewed By: mehdi_amini

Differential Revision: https://reviews.llvm.org/D103264
2021-05-28 07:25:49 -04:00
Matthias Springer
108ca7a7e7 [mlir] Support dialect-wide canonicalization pattern registration
* Add `hasCanonicalizer` option to Dialect.
* Initialize canonicalizer with dialect-wide canonicalization patterns.
* Add test case to TestDialect.

Dialect-wide canonicalization patterns are useful if a canonicalization pattern does not conceptually associate with any single operation, i.e., it should not be registered as part of an operation's `getCanonicalizationPatterns` function. E.g., this is the case for canonicalization patterns that match an op interface.

Differential Revision: https://reviews.llvm.org/D103226
2021-05-27 17:35:21 +09:00
Wenyi Zhao
851d02f61e Enhance InferShapedTypeOpInterface to make it accessible during dialect conversion
Original interfaces are not safe to be called during dialect conversion.
This is because some ops (e.g. `dynamic_reshape(input, target_shape)`)
depend on the values of their operands to calculate the output shape.
However the operands may be out of reach during dialect conversion (e.g.
converting from tensor world to buffer world). This patch provides a new
kind of interface which accpets user-provided operands to solve this
problem.

Reviewed By: herhut

Differential Revision: https://reviews.llvm.org/D102317
2021-05-19 02:51:14 +00:00