profile
viewpoint
Oleksandr "Alex" Zinenko ftynse @google Paris, France https://ozinenko.com Compilers and optimization. Polyhedral model. TensorComprehensions. MLIR.

ftynse/cs373 49

Homework for Course cs373: Programming a Robotic Car

ftynse/clint 13

Chunky Loop Interaction

ftynse/clan 1

Chunky Loop Analyzer: A Polyhedral Representation Extraction Tool for High Level Programs

ftynse/one-ovz-driver 1

OpenNebula driver for OpenVZ hypervisor

ftynse/candl 0

Data Dependence Analyzer in the Polyhedral Model

ftynse/chlore 0

chlore: chunky loop optimization recoverer

ftynse/clay 0

Clay, the Chunky Loop Alteration wizardrY

ftynse/cloog 0

The CLooG Code Generator in the Polytope Model

ftynse/clope 0

Chunky LOop Parallelism Extractor

push eventllvm/llvm-project

Alex Zinenko

commit sha 64c0c9f01511dc300b29e7a20a13958c5932e314

[mlir] Expose Dialect class and registration/loading to C API - Add a minimalist C API for mlir::Dialect. - Allow one to query the context about registered and loaded dialects. - Add API for loading dialects. - Provide functions to register the Standard dialect. When used naively, this will require to separately register each dialect. When we have more than one exposed, we can add variadic macros that expand to individual calls. Reviewed By: mehdi_amini Differential Revision: https://reviews.llvm.org/D88162

view details

push time in a day

push eventllvm/llvm-project

Alex Zinenko

commit sha c538169ee99516c178ecc00a5ec5187d78941fac

[mlir] Add insert before/after to list-like constructs in C API Blocks in a region and operations in a block are organized in a linked list. The C API only provides functions to append or to insert elements at the specified numeric position in the list. The latter is expensive since it requires to traverse the list. Add insert before/after functionality with low cost that relies on the iplist elements being convertible to iterators. Reviewed By: stellaraccident Differential Revision: https://reviews.llvm.org/D88148

view details

push time in 7 days

push eventllvm/llvm-project

Alex Zinenko

commit sha 9691806840606d48139b13516e9576902ba98923

[mlir] Fix typos in Dialect.h. NFC.

view details

push time in 7 days

push eventllvm/llvm-project

Alex Zinenko

commit sha 68cfb02668550e3398c8ee8915732daf132f2652

[mlir] turn clang-format back on in C API test C API test uses FileCheck comments inside C code and needs to temporarily switch off clang-format to prevent it from messing with FileCheck directives. A recently landed commit forgot to turn it back on after a block of FileCheck comments. Fix that.

view details

push time in 14 days

push eventllvm/llvm-project

Alex Zinenko

commit sha 855ec517a300daee6acb48474b6d3304c0914c60

[mlir] Model StringRef in C API Numerous MLIR functions return instances of `StringRef` to refer to a non-owning fragment of a string (usually owned by the context). This is a relatively simple class that is defined in LLVM. Provide a simple wrapper in the MLIR C API that contains the pointer and length of the string fragment and use it for Standard attribute functions that return StringRef instead of the previous, callback-based mechanism. Reviewed By: stellaraccident Differential Revision: https://reviews.llvm.org/D87677

view details

push time in 14 days

push eventllvm/llvm-project

Alex Zinenko

commit sha 967c7b6936a66878919568b94643c942cc7de69e

[mlir] check for failures when packing function sigunatures in std->llvm conversion When packing function results into a structure during the standard-to-llvm dialect conversion, do not assume the conversion was successful and propagate nullptr as error state. Fixes PR45184. Reviewed By: nicolasvasilache Differential Revision: https://reviews.llvm.org/D87605

view details

push time in 16 days

push eventllvm/llvm-project

Alex Zinenko

commit sha 5cac85c931d95f3c94f79837a3bf406eb68edaeb

[mlir] Check for type conversion success in std->llvm function conversion Type converter may fail and return nullptr on unconvertible types. The function conversion did not include a check and was attempting to use a nullptr type to construct an LLVM function, leading to a crash. Add a check and return early. The rest of the call stack propagates errors properly. Fixes PR47403. Reviewed By: mehdi_amini Differential Revision: https://reviews.llvm.org/D87075

view details

push time in 17 days

push eventftynse/llvm-project

Jonas Devlieghere

commit sha 9390b346fc207c3edabbca9665e77260b030cfe0

[lldb] Move ScriptCommand and RegexCommand under Commands (NFC) Move the CommandObjectScript and CommandObjectRegexCommand under Commands where all the other CommandObject implementations live. Although neither implementations currently use the TableGen-generated CommandOptions.inc, this move would have been necessary anyway if they were to in the future.

view details

Alina Sbirlea

commit sha 1ccfb52a6174816e450074f65e5f0929a9f046a5

[MemCpyOptimizer] Preserve analyses and replace use of lambdas to get them. Summary: Analyses are preserved in MemCpyOptimizer. Get analyses before running the pass and store the pointers, instead of using lambdas and getting them every time on demand. Reviewers: lenary, deadalnix, mehdi_amini, nikic, efriedma Subscribers: hiraditya, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D74494

view details

Xing GUO

commit sha 369f9169a52350365c9dcdcab55a7a5fe2fe6dbf

[DebugInfo] Simplify string table dumpers. This patch adds a helper function DumpStrSection to simplify codes. Besides, nonprintable chars in debug_str and debug_str.dwo sections are printed as escaped chars. Reviewed By: jhenderson Differential Revision: https://reviews.llvm.org/D86918

view details

Richard Smith

commit sha 0ffbbce78de60f4f4d03d6ef97fe2f3bb4275e08

Don't take the expression range into account when looking for widening of a unary - expression. This fixes an issue where we'd produce bogus diagnostics, and also should recover ~0.3% compile time.

view details

Eric Fiselier

commit sha 057028ed391f8b4bc029792fcca7771185b9da98

Revert switch based variant temporarily. There are currently some failures caused by this change internally. I'm working to debug them and hopefully these series of patches should be recommitted by the end of the week. Thank you to Micheal Park for the contributions, and for allowing the temporary rollback. The commits reverted by this change are: 7d15ece79c16dc3237fc514ff56a69e3d58fbd39 e0ec7a02064968c7df11713689107148b4efb993 02197f7e50b938f8167b17b89bdf7c55feff4339 a175a96517c5d9dc05ba13a6481b1b031a53a22f

view details

Lang Hames

commit sha 7ff335a25f80b8be3a1d8dd08b30453201dac132

[ORC] Fix MachOPlatform's synthetic symbol dependence registration. A think-o in the existing code meant that dependencies were never registered. This failure could lead to crashes rather than orderly error propagation if initialization dependencies failed to materialize. No test case: The bug was discovered in an out-of-tree code and requires pathalogically misconfigured JIT to generate the original error that lead to the crash.

view details

Lang Hames

commit sha c40ce0da7189bd2ada15725a79525da13a67f3e4

[ORC] Add an early out for MachOPlatform's init-scraper plugin setup. If there's no initializer symbol in the current MaterializationResponsibility then bail out without installing JITLink passes: they're going to be no-ops anyway.

view details

Alina Sbirlea

commit sha ce66089ac6f0cea15931db11d4cacfaa6f840f42

Fix build-bots. BasicAA can be freed (and it is not recomputed).

view details

Lang Hames

commit sha bd09e0dd741334682792cb7459aea261b4e3952b

[ORC] Remove stray debugging output.

view details

Fangrui Song

commit sha dc6734020844e92feef0ab3e3714f01d2bb98ea4

[CMake] Remove -Wl,-allow-shlib-undefined which was added in rL221530 In GNU ld, gold and LLD, --no-allow-shlib-undefined is the default when linking an executable. The option disallows unresolved symbols in shared objects. (gold and LLD catch fewer cases than GNU ld. See D57385 for details) See D57569 why it is bad idea to use --allow-shlib-undefined for executables [a]. GNU ld traditionally copied DT_NEEDED entries transitively. This was deemed not good, so GNU ld 2.22 defaulted to --no-copy-dt-needed-entries. gold and LLD always behave like --no-copy-dt-needed-entries. rL221530 added -Wl,-allow-shlib-undefined to make some old releases of GNU ld's --no-copy-dt-needed-entries to actually work. Due to [a] and [b], this patch drops -Wl,-allow-shlib-undefined. [b]: In a -DBUILD_SHARED_LIBS=on build, `--as-needed --allow-shlib-undefined` can unexpectedly suppress some .dynsym entries. The issue can cause mlir-cpu-runner to fail at runtime. Note, on Debian, gcc newer than (gcc-9-20190125-2) enable --as-needed by default. See https://sourceware.org/bugzilla/show_bug.cgi?id=26551 for a reduced example. Reviewed By: mehdi_amini, echristo Differential Revision: https://reviews.llvm.org/D86839

view details

Zi Xuan Wu

commit sha b21ddded8f04fee925bbf9e6458347104b5b99eb

[RFC][Target] Add a new triple called Triple::csky Before upstream a new target called CSKY, make a new triple of that called Triple::csky. For now, it's a 32-bit little endian target and the detail can be referred at D86269. This is the split part of D86269, which add a new target called CSKY. Differential Revision: https://reviews.llvm.org/D86505

view details

ZHANG Hongbin

commit sha 1d99472875100b230bac2d9ea70b5cd4b45e788b

[mlir] Add Complex Type, Vector Type and Tuple Type subclasses to python bindings Based on the PyType and PyConcreteType classes, this patch implements the bindings of Complex Type, Vector Type and Tuple Type subclasses. For the convenience of type checking, this patch defines a `mlirTypeIsAIntegerOrFloat` function to check whether the given type is an integer or float type. These three subclasses in this patch have similar binding strategy: - The function pointer `isaFunction` points to `mlirTypeIsA***`. - The `mlir***TypeGet` C API is bound with the `get_***` method in the python side. - The Complex Type and Vector Type check whether the given type is an integer or float type. Reviewed By: mehdi_amini Differential Revision: https://reviews.llvm.org/D86785

view details

Shinji Okumura

commit sha 7558e9e5a2a90ee58792e3b1327f400e63054dfe

[Attributor] Fix AANoUndef initialization When the associated value is undef, we immediately forced to indicate a pessimistic fixpoint so far. This patch changes the initialization to check the attribute given in IR at first and to indicate an optimistic fixpoint when it is given. This change will enable us to catch , for example, the following case in AAUB. ``` call void @foo(i32 noundef undef) ``` Reviewed By: jdoerfert Differential Revision: https://reviews.llvm.org/D86983

view details

Shinji Okumura

commit sha 5d13479574544f3fb4e7d704a1a6284b658c40d1

[Attributor] Make use of AANoUndef in AAUndefinedBehavior This patch makes it possible for AAUB to use information from AANoUndef. This is the next patch of D86983 Reviewed By: jdoerfert Differential Revision: https://reviews.llvm.org/D86984

view details

Raphael Isemann

commit sha 814242572731da240ff91b233a0bc8c7b2323434

Revert "[libc++] Workaround timespec_get not always being available in Apple SDKs" This reverts commit 99f3b231cb21abc567c93813650cd76cfa614325. It breaks libcxx/modules/stds_include.sh.cpp on macOS as the new include to sys/cdefs.h causes a dependency from __config to the Darwin module (which already has a dependency on __config). This cyclic dependency breaks compiling the std module which breaks compiling pretty much every program with ToT libc++ and enabled modules. I'll revert for now to get the bots green again. Sorry for the inconvenience.

view details

Simon Pilgrim

commit sha 21d02dc595797677a533c6b0508c0c9235bc4f13

[X86][SSE] SimplifyDemandedVectorEltsForTargetNode - add general shuffle combining support This patch uses partial DemandedElts masks to further simplify target shuffle chains and finally starts making target shuffle combining part of SimplifyDemandedBits/SimplifyDemandedVectorElts. We already manage this for Depth == 0 cases, where combineX86ShuffleChain would early-out if the shuffle combined to the same op, but the patch generalizes this by manipulating the depth handling of combineX86ShufflesRecursively - calling with a new Depth = 0 and reducing the maximum shuffle combine depth accordingly. Differential Revision: https://reviews.llvm.org/D66004

view details

Benjamin Kramer

commit sha 2bf491c7294c020d1754cddbf3a55e8e21c14bdc

[mlir][VectorOps] Fail fast when a strided memref is passed to vector_transfer Otherwise we'll silently miscompile things. Differential Revision: https://reviews.llvm.org/D86951

view details

Martin Storsjö

commit sha 4820af2bfc71f2a5bb7a335a7fdcc4ec395877e4

[X86] Remove superfluous trailing semicolons, fixing warnings. NFC.

view details

Igor Kudrin

commit sha 71eed4808fbc5e5baa016210f727683610139014

[DebugInfo] Remove Dwarf5AccelTableWriter::Header::UnitLength. NFC. The member is not in use; the unit length for the table is emitted as a difference between two labels. Moreover, the type of the member might be misleading, because for DWARF64 the field should be 64 bit long. Differential Revision: https://reviews.llvm.org/D86912

view details

Igor Kudrin

commit sha 3445ec9ba718035b27c0140dc1e892be843236f5

[DebugInfo] Emit a 1-byte value as a terminator of entries list in the name index. As stated in section 6.1.1.2, DWARFv5, p. 142, | The last entry for each name is followed by a zero byte that | terminates the list. There may be gaps between the lists. The patch changes emitting a 4-byte zero value to a 1-byte one, which effectively removes the gap between entry lists, and thus saves approximately 3 bytes per name; the calculation is not exact because the total size of the table is aligned to 4. Differential Revision: https://reviews.llvm.org/D86927

view details

push time in 19 days

push eventllvm/llvm-project

Alex Zinenko

commit sha 1e1a4a481987f77fe3e6debc015c1d07af249258

[mlir] Take ValueRange instead of ArrayRef<Value> in StructuredIndexed This was likely overlooked when ValueRange was first introduced. There is no reason why StructuredIndexed needs specifically an ArrayRef so use ValueRange for better type compatibility with the rest of the APIs. Reviewed By: nicolasvasilache, mehdi_amini Differential Revision: https://reviews.llvm.org/D87127

view details

push time in 23 days

push eventllvm/llvm-project

Alex Zinenko

commit sha aec9e20a3e9a4f25a5b1e07816c95f970300d918

[mlir] introduce type constraints for operands of LLVM dialect operations Historically, the operations in the MLIR's LLVM dialect only checked that the operand are of LLVM dialect type without more detailed constraints. This was due to LLVM dialect types wrapping LLVM IR types and having clunky verification methods. With the new first-class modeling, it is possible to define type constraints similarly to other dialects and use them to enforce some correctness rules in verifiers instead of having LLVM assert during translation to LLVM IR. This hardening discovered several issues where MLIR was producing LLVM dialect operations that cannot exist in LLVM IR. Depends On D85900 Reviewed By: rriddle Differential Revision: https://reviews.llvm.org/D85901

view details

push time in a month

push eventftynse/llvm-project

Alex Zinenko

commit sha 24c514dc8cd52b8fe4a69122503fc8a72b2ff94a

[mlir] use storage uniquer for side effects The Effect class hierarchy in the side effect infrastructure currently only supports non-parametric effects uniquely identified by their type. Only one instance of each effect is stored in method and returned by-pointer upon request. Hook the Effect class hierarchy to StorageUniquer instead. This makes Effect and derived classes value-types, similarly to types and attributes, and prepares the ground for parametric effets.

view details

Alex Zinenko

commit sha 0223e870863d9657bb08b8e7b827f2f2a75d74ff

Add SCF barrier op This op collects Write side effects from above and below.

view details

Alex Zinenko

commit sha 762a82d59407a871992e78ae34a691ee437bc4ca

Add integer set effects

view details

push time in a month

create barnchftynse/llvm-project

branch : sideeffect

created branch time in a month

Pull request review commentkumasento/polymer

Emitting OpenSCoP from MLIR affine

+#include "polymer/EmitOpenScop.h"+#include "polymer/OslScop.h"++#include "mlir/Analysis/AffineAnalysis.h"+#include "mlir/Analysis/AffineStructures.h"+#include "mlir/Analysis/LoopAnalysis.h"+#include "mlir/Analysis/Utils.h"+#include "mlir/Dialect/Affine/IR/AffineOps.h"+#include "mlir/Dialect/Affine/IR/AffineValueMap.h"+#include "mlir/Dialect/Affine/Passes.h"+#include "mlir/Dialect/StandardOps/IR/Ops.h"+#include "mlir/IR/BlockAndValueMapping.h"+#include "mlir/IR/Builders.h"+#include "mlir/IR/Function.h"+#include "mlir/IR/Module.h"+#include "mlir/IR/StandardTypes.h"+#include "mlir/Transforms/LoopUtils.h"+#include "mlir/Transforms/Utils.h"+#include "mlir/Translation.h"++#include "llvm/ADT/DenseMap.h"+#include "llvm/ADT/StringSet.h"+#include "llvm/Support/Debug.h"+#include "llvm/Support/Format.h"+#include "llvm/Support/FormatVariadic.h"+#include "llvm/Support/raw_ostream.h"++#include "osl/osl.h"++#include <memory>++using namespace mlir;+using namespace llvm;+using namespace polymer;++#define DEBUG_TYPE "emit-openscop"++namespace {++/// Extracted data from the domain constraints, will be used as the value of+/// paramMap when generating scop. lb and ub will be used to get the context+/// relation.+struct DomainParameter {+  unsigned pos; // Parameter column position in the constraints.+  llvm::Optional<int64_t> lb, ub; // Lower and upper bounds.++  DomainParameter() : pos(0), lb(llvm::None), ub(llvm::None) {}+  DomainParameter(unsigned pos, llvm::Optional<int64_t> lb,+                  llvm::Optional<int64_t> ub)+      : pos(pos), lb(lb), ub(ub) {}+};++/// Add parameters to the maintained parameter-ID map.+void addParamsToMap(FlatAffineConstraints &domain,+                    llvm::DenseMap<mlir::Value, DomainParameter> &paramMap) {+  // Get symbol values from the domain constraint.+  SmallVector<mlir::Value, 8> values;+  unsigned offset = domain.getNumDimIds();+  domain.getIdValues(offset, domain.getNumDimAndSymbolIds(), &values);++  // Insert the value into the map if there isn't one.+  for (int i = 0, e = values.size(); i < e; i++) {+    mlir::Value value = values[i];+    auto lb = domain.getConstantLowerBound(i + offset);+    auto ub = domain.getConstantUpperBound(i + offset);+    LLVM_DEBUG(llvm::dbgs() << "Bound for parameter i=" << i << " : [" << lb+                            << ", " << ub << ")\n");++    auto it = paramMap.find(value);+    if (it == paramMap.end()) {+      paramMap[value] = DomainParameter(paramMap.size(), lb, ub);+    }+  }+}++/// Build the mapping from position to parameters.+void buildPosToParamMap(llvm::DenseMap<mlir::Value, DomainParameter> &paramMap,+                        SmallVectorImpl<mlir::Value> &posToParam) {+  unsigned numParam = paramMap.size();+  posToParam.reserve(numParam);++  for (auto const &it : paramMap)+    posToParam.push_back(it.first);++  // TODO: Is there a more efficient way?+  llvm::sort(posToParam.begin(), posToParam.end(),+             [&](const mlir::Value &L, const mlir::Value &R) {+               return paramMap[L].pos < paramMap[R].pos;+             });+}++/// Create a context relation from parameters and add it to the scop.+void addContextToScop(llvm::DenseMap<mlir::Value, DomainParameter> &paramMap,+                      OslScop &scop) {+  unsigned numParams = paramMap.size();+  unsigned numCols = 2 + numParams;++  std::vector<std::vector<int64_t>> ctxEqs, ctxInEqs;++  // We assume the context relation is built on inequalities generated by the+  // lower and upper bound of each parameter.+  unsigned i = 0; // The parameter ID.+  ctxInEqs.reserve(numParams * 2);+  for (auto const &it : paramMap) {+    DomainParameter param = it.second;+    assert((param.lb != llvm::None || param.ub != llvm::None) &&+           "At least one of lb and ub should not be None.");++    // Set the lower bound+    if (param.lb != llvm::None) {+      std::vector<int64_t> inEq(numCols - 1, 0);+      inEq[i] = 1;                              // The pos of the parameter.+      inEq[numCols - 2] = -param.lb.getValue(); // The constant.+      ctxInEqs.push_back(inEq);+    }+    // Set the upper bound+    if (param.ub != llvm::None) {+      std::vector<int64_t> inEq(numCols - 1, 0);+      inEq[i] = -1;                            // The pos of the parameter.+      inEq[numCols - 2] = param.ub.getValue(); // The constant.+      ctxInEqs.push_back(inEq);+    }++    i++;+  }++  // Assuming no equality.+  unsigned numRows = ctxInEqs.size();+  scop.addRelation(0, OSL_TYPE_CONTEXT, numRows, numCols, 0, 0, 0, numParams,+                   ctxEqs, ctxInEqs);+}++/// Swap the posA^th identifier with the posB^th identifier.+/// TODO: This is from AffineStructure.h, should be turned into a publicly+/// available API.+void swapId(FlatAffineConstraints *A, unsigned posA, unsigned posB) {+  assert(posA < A->getNumIds() && "invalid position A");+  assert(posB < A->getNumIds() && "invalid position B");++  if (posA == posB)+    return;++  for (unsigned r = 0, e = A->getNumInequalities(); r < e; r++) {+    std::swap(A->atIneq(r, posA), A->atIneq(r, posB));+  }+  for (unsigned r = 0, e = A->getNumEqualities(); r < e; r++) {+    std::swap(A->atEq(r, posA), A->atEq(r, posB));+  }+  std::swap(A->getId(posA), A->getId(posB));+}++/// Update the parameter (symbol) section of the domain constraints. We need to+/// make sure that all domains share the same set of parameters and they are+/// located at the same positions.+void updateDomainParams(+    FlatAffineConstraints &domain, SmallVectorImpl<mlir::Value> &posToParam,+    llvm::DenseMap<mlir::Value, DomainParameter> &paramMap) {+  unsigned offset = domain.getNumDimIds();+  unsigned numParams = paramMap.size();++  for (unsigned pos = 0; pos < numParams; pos++) {+    mlir::Value param = posToParam[pos];++    unsigned posInDomain;+    if (domain.findId(param, &posInDomain)) {+      // If there is such param in the domain, we should check whether its+      // position is right.+      if (posInDomain != offset + pos) {+        // posInDomain should be larger than pos. It is because all parameters+        // located before pos should already be placed correctly.+        assert(posInDomain > pos &&+               "posInDomain should be larger than pos if not equal.");+        swapId(&domain, offset + pos, posInDomain);+      }+    } else {+      domain.addSymbolId(pos, /*id=*/param);+    }+  }+}++} // namespace++namespace {++/// Get a clone of the elements in the equalities or inequalities of+/// FlatAffineConstraints.+void getEqualities(FlatAffineConstraints &cst,+                   std::vector<std::vector<int64_t>> &eqs, bool isEq = true) {+  unsigned numEqualities =+      isEq ? cst.getNumEqualities() : cst.getNumInequalities();+  unsigned numDimIds = cst.getNumDimIds();+  unsigned numLocalIds = cst.getNumLocalIds();+  unsigned numSymbolIds = cst.getNumSymbolIds();++  eqs.resize(numEqualities);+  for (unsigned i = 0; i < numEqualities; i++) {+    auto eq = isEq ? cst.getEquality(i) : cst.getInequality(i);+    unsigned numCols = eq.size();+    eqs[i].resize(numCols);++    // Dims stay at the same positions.+    for (unsigned j = 0; j < numDimIds; j++)+      eqs[i][j] = eq[j];+    // Output local ids before symbols.+    for (unsigned j = 0; j < numLocalIds; j++)+      eqs[i][j + numDimIds] = eq[j + numDimIds + numSymbolIds];+    // Output symbols in the end.+    for (unsigned j = 0; j < numSymbolIds; j++)+      eqs[i][j + numDimIds + numLocalIds] = eq[j + numDimIds];+    eqs[i][numCols - 1] = eq[numCols - 1];+  }+}++/// Gather information from the domain FlatAffineConstraints and put them into+/// the scop as a DOMAIN relation. index gives the statement id.+void addDomainToScop(unsigned index, FlatAffineConstraints &domain,+                     OslScop &scop) {+  // First we clone the equalities and inequalities from the domain constraints.+  std::vector<std::vector<int64_t>> eqs, inEqs;+  getEqualities(domain, eqs);+  getEqualities(domain, inEqs, /*isEq=*/false);++  // Then put them into the scop as a DOMAIN relation.+  scop.addRelation(index + 1, OSL_TYPE_DOMAIN, domain.getNumConstraints(),+                   domain.getNumCols() + 1, domain.getNumDimIds(), 0,+                   domain.getNumLocalIds(), domain.getNumSymbolIds(), eqs,+                   inEqs);+}++} // namespace++namespace {+/// Tree that holds scattering information. This node can represent an induction+/// variable or a statement. A statement is constructed as a leaf node.+class ScatteringTreeNode {+public:+  ScatteringTreeNode(bool isLeaf = false) : isLeaf(isLeaf) {}+  ScatteringTreeNode(mlir::Value iv) : iv(iv), isLeaf(false) {}++  /// Children of the current node.+  std::vector<std::unique_ptr<ScatteringTreeNode>> children;++  /// Mapping from IV to child ID.+  llvm::DenseMap<mlir::Value, unsigned> valueIdMap;++  /// Induction variable.+  mlir::Value iv;++  /// If this node is a statement, then isLeaf is true.

Can't this be inferred from children.empty() ?

kumasento

comment created time in a month

Pull request review commentkumasento/polymer

Emitting OpenSCoP from MLIR affine

+#include "polymer/EmitOpenScop.h"+#include "polymer/OslScop.h"++#include "mlir/Analysis/AffineAnalysis.h"+#include "mlir/Analysis/AffineStructures.h"+#include "mlir/Analysis/LoopAnalysis.h"+#include "mlir/Analysis/Utils.h"+#include "mlir/Dialect/Affine/IR/AffineOps.h"+#include "mlir/Dialect/Affine/IR/AffineValueMap.h"+#include "mlir/Dialect/Affine/Passes.h"+#include "mlir/Dialect/StandardOps/IR/Ops.h"+#include "mlir/IR/BlockAndValueMapping.h"+#include "mlir/IR/Builders.h"+#include "mlir/IR/Function.h"+#include "mlir/IR/Module.h"+#include "mlir/IR/StandardTypes.h"+#include "mlir/Transforms/LoopUtils.h"+#include "mlir/Transforms/Utils.h"+#include "mlir/Translation.h"++#include "llvm/ADT/DenseMap.h"+#include "llvm/ADT/StringSet.h"+#include "llvm/Support/Debug.h"+#include "llvm/Support/Format.h"+#include "llvm/Support/FormatVariadic.h"+#include "llvm/Support/raw_ostream.h"++#include "osl/osl.h"++#include <memory>++using namespace mlir;+using namespace llvm;+using namespace polymer;++#define DEBUG_TYPE "emit-openscop"++namespace {++/// Extracted data from the domain constraints, will be used as the value of+/// paramMap when generating scop. lb and ub will be used to get the context+/// relation.+struct DomainParameter {+  unsigned pos; // Parameter column position in the constraints.+  llvm::Optional<int64_t> lb, ub; // Lower and upper bounds.++  DomainParameter() : pos(0), lb(llvm::None), ub(llvm::None) {}+  DomainParameter(unsigned pos, llvm::Optional<int64_t> lb,+                  llvm::Optional<int64_t> ub)+      : pos(pos), lb(lb), ub(ub) {}+};++/// Add parameters to the maintained parameter-ID map.+void addParamsToMap(FlatAffineConstraints &domain,+                    llvm::DenseMap<mlir::Value, DomainParameter> &paramMap) {+  // Get symbol values from the domain constraint.+  SmallVector<mlir::Value, 8> values;+  unsigned offset = domain.getNumDimIds();+  domain.getIdValues(offset, domain.getNumDimAndSymbolIds(), &values);++  // Insert the value into the map if there isn't one.+  for (int i = 0, e = values.size(); i < e; i++) {+    mlir::Value value = values[i];+    auto lb = domain.getConstantLowerBound(i + offset);+    auto ub = domain.getConstantUpperBound(i + offset);+    LLVM_DEBUG(llvm::dbgs() << "Bound for parameter i=" << i << " : [" << lb+                            << ", " << ub << ")\n");++    auto it = paramMap.find(value);+    if (it == paramMap.end()) {+      paramMap[value] = DomainParameter(paramMap.size(), lb, ub);+    }+  }+}++/// Build the mapping from position to parameters.+void buildPosToParamMap(llvm::DenseMap<mlir::Value, DomainParameter> &paramMap,+                        SmallVectorImpl<mlir::Value> &posToParam) {+  unsigned numParam = paramMap.size();+  posToParam.reserve(numParam);++  for (auto const &it : paramMap)+    posToParam.push_back(it.first);++  // TODO: Is there a more efficient way?+  llvm::sort(posToParam.begin(), posToParam.end(),+             [&](const mlir::Value &L, const mlir::Value &R) {+               return paramMap[L].pos < paramMap[R].pos;+             });+}++/// Create a context relation from parameters and add it to the scop.+void addContextToScop(llvm::DenseMap<mlir::Value, DomainParameter> &paramMap,+                      OslScop &scop) {+  unsigned numParams = paramMap.size();+  unsigned numCols = 2 + numParams;++  std::vector<std::vector<int64_t>> ctxEqs, ctxInEqs;++  // We assume the context relation is built on inequalities generated by the+  // lower and upper bound of each parameter.+  unsigned i = 0; // The parameter ID.+  ctxInEqs.reserve(numParams * 2);+  for (auto const &it : paramMap) {+    DomainParameter param = it.second;+    assert((param.lb != llvm::None || param.ub != llvm::None) &&+           "At least one of lb and ub should not be None.");++    // Set the lower bound+    if (param.lb != llvm::None) {+      std::vector<int64_t> inEq(numCols - 1, 0);+      inEq[i] = 1;                              // The pos of the parameter.+      inEq[numCols - 2] = -param.lb.getValue(); // The constant.+      ctxInEqs.push_back(inEq);+    }+    // Set the upper bound+    if (param.ub != llvm::None) {+      std::vector<int64_t> inEq(numCols - 1, 0);+      inEq[i] = -1;                            // The pos of the parameter.+      inEq[numCols - 2] = param.ub.getValue(); // The constant.+      ctxInEqs.push_back(inEq);+    }++    i++;+  }++  // Assuming no equality.+  unsigned numRows = ctxInEqs.size();+  scop.addRelation(0, OSL_TYPE_CONTEXT, numRows, numCols, 0, 0, 0, numParams,+                   ctxEqs, ctxInEqs);+}++/// Swap the posA^th identifier with the posB^th identifier.+/// TODO: This is from AffineStructure.h, should be turned into a publicly+/// available API.+void swapId(FlatAffineConstraints *A, unsigned posA, unsigned posB) {+  assert(posA < A->getNumIds() && "invalid position A");+  assert(posB < A->getNumIds() && "invalid position B");++  if (posA == posB)+    return;++  for (unsigned r = 0, e = A->getNumInequalities(); r < e; r++) {+    std::swap(A->atIneq(r, posA), A->atIneq(r, posB));+  }+  for (unsigned r = 0, e = A->getNumEqualities(); r < e; r++) {+    std::swap(A->atEq(r, posA), A->atEq(r, posB));+  }+  std::swap(A->getId(posA), A->getId(posB));+}++/// Update the parameter (symbol) section of the domain constraints. We need to+/// make sure that all domains share the same set of parameters and they are+/// located at the same positions.+void updateDomainParams(+    FlatAffineConstraints &domain, SmallVectorImpl<mlir::Value> &posToParam,+    llvm::DenseMap<mlir::Value, DomainParameter> &paramMap) {+  unsigned offset = domain.getNumDimIds();+  unsigned numParams = paramMap.size();++  for (unsigned pos = 0; pos < numParams; pos++) {+    mlir::Value param = posToParam[pos];++    unsigned posInDomain;+    if (domain.findId(param, &posInDomain)) {+      // If there is such param in the domain, we should check whether its+      // position is right.+      if (posInDomain != offset + pos) {+        // posInDomain should be larger than pos. It is because all parameters+        // located before pos should already be placed correctly.+        assert(posInDomain > pos &&+               "posInDomain should be larger than pos if not equal.");+        swapId(&domain, offset + pos, posInDomain);+      }+    } else {+      domain.addSymbolId(pos, /*id=*/param);+    }+  }+}++} // namespace++namespace {++/// Get a clone of the elements in the equalities or inequalities of+/// FlatAffineConstraints.+void getEqualities(FlatAffineConstraints &cst,+                   std::vector<std::vector<int64_t>> &eqs, bool isEq = true) {+  unsigned numEqualities =+      isEq ? cst.getNumEqualities() : cst.getNumInequalities();+  unsigned numDimIds = cst.getNumDimIds();+  unsigned numLocalIds = cst.getNumLocalIds();+  unsigned numSymbolIds = cst.getNumSymbolIds();++  eqs.resize(numEqualities);+  for (unsigned i = 0; i < numEqualities; i++) {+    auto eq = isEq ? cst.getEquality(i) : cst.getInequality(i);+    unsigned numCols = eq.size();+    eqs[i].resize(numCols);++    // Dims stay at the same positions.+    for (unsigned j = 0; j < numDimIds; j++)+      eqs[i][j] = eq[j];+    // Output local ids before symbols.+    for (unsigned j = 0; j < numLocalIds; j++)+      eqs[i][j + numDimIds] = eq[j + numDimIds + numSymbolIds];+    // Output symbols in the end.+    for (unsigned j = 0; j < numSymbolIds; j++)+      eqs[i][j + numDimIds + numLocalIds] = eq[j + numDimIds];+    eqs[i][numCols - 1] = eq[numCols - 1];+  }+}++/// Gather information from the domain FlatAffineConstraints and put them into+/// the scop as a DOMAIN relation. index gives the statement id.+void addDomainToScop(unsigned index, FlatAffineConstraints &domain,+                     OslScop &scop) {+  // First we clone the equalities and inequalities from the domain constraints.+  std::vector<std::vector<int64_t>> eqs, inEqs;+  getEqualities(domain, eqs);+  getEqualities(domain, inEqs, /*isEq=*/false);++  // Then put them into the scop as a DOMAIN relation.+  scop.addRelation(index + 1, OSL_TYPE_DOMAIN, domain.getNumConstraints(),+                   domain.getNumCols() + 1, domain.getNumDimIds(), 0,+                   domain.getNumLocalIds(), domain.getNumSymbolIds(), eqs,+                   inEqs);+}++} // namespace++namespace {+/// Tree that holds scattering information. This node can represent an induction+/// variable or a statement. A statement is constructed as a leaf node.+class ScatteringTreeNode {+public:+  ScatteringTreeNode(bool isLeaf = false) : isLeaf(isLeaf) {}+  ScatteringTreeNode(mlir::Value iv) : iv(iv), isLeaf(false) {}++  /// Children of the current node.+  std::vector<std::unique_ptr<ScatteringTreeNode>> children;++  /// Mapping from IV to child ID.+  llvm::DenseMap<mlir::Value, unsigned> valueIdMap;++  /// Induction variable.+  mlir::Value iv;++  /// If this node is a statement, then isLeaf is true.+  bool isLeaf;+};++/// Insert a statement characterized by its enclosing operations into a+/// "scattering tree". This is done by iterating through every enclosing for-op+/// from the outermost to the innermost, and we try to traverse the tree by the+/// IVs of these ops. If an IV does not exist, we will insert it into the tree.+/// After that, we insert the current load/store statement into the tree as a+/// leaf. In this progress, we keep track of all the IDs of each child we meet+/// and the final leaf node, which will be used as the scattering.+void insertStatement(ScatteringTreeNode *root,+                     SmallVectorImpl<Operation *> &enclosingOps,+                     SmallVectorImpl<unsigned> &scattering) {+  ScatteringTreeNode *curr = root;++  for (unsigned i = 0, e = enclosingOps.size(); i < e; i++) {+    Operation *op = enclosingOps[i];+    // We only handle for op here.+    // TODO: is it necessary to deal with if?+    if (auto forOp = dyn_cast<AffineForOp>(op)) {+      SmallVector<mlir::Value, 4> indices;+      extractForInductionVars(forOp, &indices);++      for (auto iv : indices) {+        auto it = curr->valueIdMap.find(iv);+        if (it != curr->valueIdMap.end()) {+          // Add a new element to the scattering.+          scattering.push_back(it->second);+          // move to the next IV along the tree.+          curr = curr->children[it->second].get();+        } else {+          // No existing node for such IV is found, create a new one.+          auto node = std::make_unique<ScatteringTreeNode>(iv);++          // Then insert the newly created node into the children set, update+          // the value to child ID map, and move the cursor to this new node.+          curr->children.push_back(std::move(node));+          unsigned valueId = curr->children.size() - 1;+          curr->valueIdMap[iv] = valueId;+          scattering.push_back(valueId);+          curr = curr->children.back().get();+        }+      }+    }+  }++  // Append the leaf node for statement+  auto leaf = std::make_unique<ScatteringTreeNode>(/*isLeaf=*/true);+  curr->children.push_back(std::move(leaf));+  scattering.push_back(curr->children.size() - 1);+}++void addScatteringToScop(unsigned index, ArrayRef<unsigned> scattering,+                         FlatAffineConstraints &domain, OslScop &scop) {+  // Elements (N of them) in `scattering` are constants, and there are IVs+  // interleaved them. Therefore, we have 2N - 1 number of scattering+  // equalities.+  unsigned numScatteringEqualities = scattering.size() * 2 - 1;+  // Columns include new scattering dimensions and those from the domain.+  unsigned numScatteringCols =+      numScatteringEqualities + domain.getNumCols() + 1;++  // Create equalities and inequalities.+  std::vector<std::vector<int64_t>> eqs, inEqs;++  // Initialize contents for equalities.+  eqs.resize(numScatteringEqualities);+  for (unsigned j = 0; j < numScatteringEqualities; j++) {+    eqs[j].resize(numScatteringCols - 1);++    // Initializing scattering dimensions by setting the diagonal to -1.+    for (unsigned k = 0; k < numScatteringEqualities; k++)+      eqs[j][k] = -static_cast<int64_t>(k == j);++    // Relating the loop IVs to the scattering dimensions. If it's the odd+    // equality, set its scattering dimension to the loop IV; otherwise, it's+    // scattering dimension will be set in the following constant section.+    for (unsigned k = 0; k < domain.getNumDimIds(); k++)+      eqs[j][k + numScatteringEqualities] = (j % 2) ? (k == (j / 2)) : 0;++    // TODO: consider the parameters that may appear in the scattering+    // dimension.+    for (unsigned k = 0; k < domain.getNumLocalIds() + domain.getNumSymbolIds();+         k++)+      eqs[j][k + numScatteringEqualities + domain.getNumDimIds()] = 0;++    // Relating the constants (the last column) to the scattering dimensions.+    eqs[j][numScatteringCols - 2] = (j % 2) ? 0 : scattering[j / 2];+  }++  // Then put them into the scop as a SCATTERING relation.+  scop.addRelation(index + 1, OSL_TYPE_SCATTERING, numScatteringEqualities,+                   numScatteringCols, numScatteringEqualities,+                   domain.getNumDimIds(), domain.getNumLocalIds(),+                   domain.getNumSymbolIds(), eqs, inEqs);+}+} // namespace++namespace {++/// Generate the access relation and add it to the scop.+void addAccessToScop(unsigned index, unsigned memrefId, bool isRead,+                     ArrayRef<SmallVector<int64_t, 8>> flatExprs,+                     FlatAffineConstraints &domain, OslScop &scop) {+  // Number of equalities equals to the number of enclosing loop indices+  // plus 1 (the array itself).+  unsigned numAccessEqualities = domain.getNumDimIds() + 1;+  unsigned numAccessCols = domain.getNumCols() + numAccessEqualities + 1;+  unsigned numDimIds = domain.getNumDimIds();+  unsigned numSymbolIds = domain.getNumSymbolIds();+  unsigned numLocalIds = domain.getNumLocalIds();++  // Create equalities and inequalities.+  std::vector<std::vector<int64_t>> eqs, inEqs;++  eqs.resize(numAccessEqualities);+  for (unsigned i = 0; i < numAccessEqualities; i++) {+    // We leave alone the first column that indicates whether it is an equality+    // or not.+    eqs[i].resize(numAccessCols - 1);++    // The first section of a diagonal square matrix that points which axis the+    // current access relation is working on.+    for (unsigned j = 0; j < numAccessEqualities; j++)+      eqs[i][j] = -static_cast<int64_t>(i == j);++    // Set up the relation betwene the memref access position and the loop IVs.+    if (i == 0) {+      // The first row sets the array ID to the memref ID.+      for (unsigned j = 0; j < domain.getNumCols() - 1; j++)+        eqs[i][j + numAccessEqualities] = 0;+      eqs[i][numAccessCols - 2] = memrefId;+    } else {+      unsigned numFlatExprCols = flatExprs[i - 1].size();+      // Put the coefficients in the flat exprs into the access relation.+      // Note that numFlatExprCols may be smaller than the number of columns in+      // the domain constraints, mainly because the flatExprs are not aligned+      // with the position in domain constraints. Therefore, for those cases+      // that numFlatExprCols equals to the number of columns in the domain+      // constraint, we take a special approach to handle the mis-placed local+      // IDs; otherwise, we just place what in the flatExpr into the access+      // equalities.+      // TODO: properly handle local vars in the access equalities.+      if (numFlatExprCols == numDimIds + numLocalIds + numSymbolIds + 1) {+        // Input dims.+        for (unsigned j = 0; j < numDimIds; j++)+          eqs[i][j + numAccessEqualities] = flatExprs[i - 1][j];+        // Local dims.+        for (unsigned j = 0; j < numLocalIds; j++)+          eqs[i][j + numAccessEqualities + numDimIds] =+              flatExprs[i - 1][j + numDimIds + numSymbolIds];+        // Parameters.+        for (unsigned j = 0; j < numSymbolIds; j++)+          eqs[i][j + numAccessEqualities + numDimIds + numLocalIds] =+              flatExprs[i - 1][j + numDimIds];+        // Constant.+        eqs[i][numAccessEqualities + numFlatExprCols - 1] =+            flatExprs[i - 1][numFlatExprCols - 1];+      } else {+        for (unsigned j = 0; j < numFlatExprCols; j++)+          eqs[i][j + numAccessEqualities] = flatExprs[i - 1][j];+      }+    }+  }++  // Then put them into the scop as a ACCESS relation.+  scop.addRelation(index + 1, isRead ? OSL_TYPE_READ : OSL_TYPE_WRITE,+                   numAccessEqualities, numAccessCols, numAccessEqualities,+                   domain.getNumDimIds(), domain.getNumLocalIds(),+                   domain.getNumSymbolIds(), eqs, inEqs);+}+} // namespace++namespace {+/// This class maintains the state of a working emitter.+class OpenScopEmitterState {+public:+  explicit OpenScopEmitterState(raw_ostream &os) : os(os) {}++  /// The stream to emit to.+  raw_ostream &os;++  bool encounteredError = false;+  unsigned currentIdent = 0; // TODO: may not need this.++private:+  OpenScopEmitterState(const OpenScopEmitterState &) = delete;+  void operator=(const OpenScopEmitterState &) = delete;+};++/// Base class for various OpenScop emitters.+class OpenScopEmitterBase {+public:+  explicit OpenScopEmitterBase(OpenScopEmitterState &state)+      : state(state), os(state.os) {}++  InFlightDiagnostic emitError(Operation *op, const Twine &message) {+    state.encounteredError = true;+    return op->emitError(message);+  }++  InFlightDiagnostic emitOpError(Operation *op, const Twine &message) {+    state.encounteredError = true;+    return op->emitOpError(message);+  }++  /// All of the mutable state we are maintaining.+  OpenScopEmitterState &state;++  /// The stream to emit to.+  raw_ostream &os;++private:+  OpenScopEmitterBase(const OpenScopEmitterBase &) = delete;+  void operator=(const OpenScopEmitterBase &) = delete;+};++/// Emit OpenScop representation from an MLIR module.+class ModuleEmitter : public OpenScopEmitterBase {+public:+  explicit ModuleEmitter(OpenScopEmitterState &state)+      : OpenScopEmitterBase(state) {}++  /// Emit OpenScop definitions for all functions in the given module.+  void emitMLIRModule(ModuleOp module);++private:+  /// Emit a OpenScop definition for a single function.+  LogicalResult emitFuncOp(FuncOp func);+};++LogicalResult ModuleEmitter::emitFuncOp(mlir::FuncOp func) {+  // Initialize ja new Scop per FuncOp.+  OslScop scop;++  // We iterate through every operations in the function and extrat load/store+  // operations out into loadAndStoreOps.+  SmallVector<Operation *, 8> loadAndStoreOps;+  func.getOperation()->walk([&](Operation *op) {+    if (isa<AffineReadOpInterface, AffineWriteOpInterface>(op))+      loadAndStoreOps.push_back(op);+  });++  LLVM_DEBUG(llvm::dbgs() << "Found " << Twine(loadAndStoreOps.size())+                          << " number of load/store operations.\n");++  // Total number of statements.+  unsigned numStatements = loadAndStoreOps.size();++  // Cache all the enclosing operations for all statements.+  std::vector<SmallVector<Operation *, 4>> enclosingOpsList(numStatements);+  // Store the domain constraints for all statements. We need to pre-calculate+  // them, such that we can derive the full set of parameters.+  std::vector<FlatAffineConstraints> domains(numStatements);++  // Create the root tree node.+  ScatteringTreeNode root;+  // Maintain the identifiers of memref objects+  llvm::DenseMap<mlir::Value, unsigned> memrefIdMap;+  // Maintain the mapping from the parameter Value and its numbering.+  llvm::DenseMap<mlir::Value, DomainParameter> paramMap;+  // Mapping the position of parameter to the parameter itself.+  SmallVector<mlir::Value, 8> posToParam;++  // Pre-calculate domains.+  for (unsigned i = 0; i < numStatements; i++) {+    Operation *op = loadAndStoreOps[i];+    LLVM_DEBUG(op->dump());++    // Each statement in the MLIR affine case is a load/store, which means+    // besides the domain and scattering relations, there will be only one+    // additional access relation. In together there will be 3 of them.++    // TODO: make getOpIndexSet publicly available+    getEnclosingAffineForAndIfOps(*op, &enclosingOpsList[i]);+    LLVM_DEBUG(llvm::dbgs() << "Number of enclosing operations: "+                            << enclosingOpsList[i].size() << "\n");++    // Get the domain first, which is structured as FlatAffineConstraints.+    if (failed(getIndexSet(enclosingOpsList[i], &domains[i])))+      return failure();++    LLVM_DEBUG(domains[i].dump());++    // Update the paramMap.+    addParamsToMap(domains[i], paramMap);+  }++  // Build the mapping from position to a specific parameter.+  buildPosToParamMap(paramMap, posToParam);++  for (unsigned i = 0; i < numStatements; i++) {+    Operation *op = loadAndStoreOps[i];+    FlatAffineConstraints domain = domains[i];+    auto ops = enclosingOpsList[i];++    // Update the parameters mapping for domain.+    updateDomainParams(domain, posToParam, paramMap);++    // Initialize a new statement in the scop. Maybe it is better to initialize+    // all statements at once?+    scop.createStatement();++    // Add the domain relation to the scop object.+    addDomainToScop(i, domain, scop);++    // Get the scattering. By using insertStatement, we create new nodes in the+    // scattering tree representation rooted at `root`, and get the result+    // scattering relation in the `scattering` vector.+    // TODO: consider strided loop indices.+    SmallVector<unsigned, 8> scattering;+    insertStatement(&root, ops, scattering);++    // Add the scattering relation to the scop object.+    addScatteringToScop(i, scattering, domain, scop);++    // TODO: can we wrap these calculation into a bigger data structure like+    // FlatAffineConstraints?+    // Elements (N of them) in `scattering` are constants, and there are IVs+    // interleaved them. Therefore, we have 2N - 1 number of scattering+    // equalities.+    unsigned numScatteringEqualities = scattering.size() * 2 - 1;+    // Columns include new scattering dimensions and those from the domain.+    unsigned numScatteringCols =+        numScatteringEqualities + domain.getNumCols() + 1;++    // Get the access+    MemRefAccess access(op);+    auto it = memrefIdMap.find(access.memref);+    if (it == memrefIdMap.end())+      memrefIdMap[access.memref] = memrefIdMap.size() + 1;+    auto memrefId = memrefIdMap[access.memref];++    AffineValueMap accessMap;+    access.getAccessMap(&accessMap);+    std::vector<SmallVector<int64_t, 8>> flatExprs;+    FlatAffineConstraints localVarCst;++    if (failed(getFlattenedAffineExprs(accessMap.getAffineMap(), &flatExprs,+                                       &localVarCst)))+      return failure();++    assert(flatExprs.size() > 0 &&+           "Number of flat expressions should be larger than 0.");++    LLVM_DEBUG(llvm::dbgs()+               << "Number of flat exprs: " << flatExprs.size() << "\n");+    LLVM_DEBUG(llvm::dbgs()+               << "Flat expr size: " << flatExprs[0].size() << "\n");++    // Insert the access relation into the scop.+    addAccessToScop(i, memrefId, !access.isStore(), flatExprs, domain, scop);+  }++  // Setup the context after iterated all statemenets.+  addContextToScop(paramMap, scop);++  assert(scop.validate() && "Scop created cannot be validated.");++  // Print the OpenScop representation to the ostream.+  // TODO: relate STDOUT with the os stream in the emitter.

llvm::outs() gives you a raw_ostream that prints to STDOUT

kumasento

comment created time in a month

Pull request review commentkumasento/polymer

Emitting OpenSCoP from MLIR affine

+#include "polymer/EmitOpenScop.h"+#include "polymer/OslScop.h"++#include "mlir/Analysis/AffineAnalysis.h"+#include "mlir/Analysis/AffineStructures.h"+#include "mlir/Analysis/LoopAnalysis.h"+#include "mlir/Analysis/Utils.h"+#include "mlir/Dialect/Affine/IR/AffineOps.h"+#include "mlir/Dialect/Affine/IR/AffineValueMap.h"+#include "mlir/Dialect/Affine/Passes.h"+#include "mlir/Dialect/StandardOps/IR/Ops.h"+#include "mlir/IR/BlockAndValueMapping.h"+#include "mlir/IR/Builders.h"+#include "mlir/IR/Function.h"+#include "mlir/IR/Module.h"+#include "mlir/IR/StandardTypes.h"+#include "mlir/Transforms/LoopUtils.h"+#include "mlir/Transforms/Utils.h"+#include "mlir/Translation.h"++#include "llvm/ADT/DenseMap.h"+#include "llvm/ADT/StringSet.h"+#include "llvm/Support/Debug.h"+#include "llvm/Support/Format.h"+#include "llvm/Support/FormatVariadic.h"+#include "llvm/Support/raw_ostream.h"++#include "osl/osl.h"++#include <memory>++using namespace mlir;+using namespace llvm;+using namespace polymer;++#define DEBUG_TYPE "emit-openscop"++namespace {++/// Extracted data from the domain constraints, will be used as the value of+/// paramMap when generating scop. lb and ub will be used to get the context+/// relation.+struct DomainParameter {+  unsigned pos; // Parameter column position in the constraints.+  llvm::Optional<int64_t> lb, ub; // Lower and upper bounds.++  DomainParameter() : pos(0), lb(llvm::None), ub(llvm::None) {}+  DomainParameter(unsigned pos, llvm::Optional<int64_t> lb,+                  llvm::Optional<int64_t> ub)+      : pos(pos), lb(lb), ub(ub) {}+};++/// Add parameters to the maintained parameter-ID map.+void addParamsToMap(FlatAffineConstraints &domain,+                    llvm::DenseMap<mlir::Value, DomainParameter> &paramMap) {+  // Get symbol values from the domain constraint.+  SmallVector<mlir::Value, 8> values;+  unsigned offset = domain.getNumDimIds();+  domain.getIdValues(offset, domain.getNumDimAndSymbolIds(), &values);++  // Insert the value into the map if there isn't one.+  for (int i = 0, e = values.size(); i < e; i++) {+    mlir::Value value = values[i];+    auto lb = domain.getConstantLowerBound(i + offset);+    auto ub = domain.getConstantUpperBound(i + offset);+    LLVM_DEBUG(llvm::dbgs() << "Bound for parameter i=" << i << " : [" << lb+                            << ", " << ub << ")\n");++    auto it = paramMap.find(value);+    if (it == paramMap.end()) {+      paramMap[value] = DomainParameter(paramMap.size(), lb, ub);+    }+  }+}++/// Build the mapping from position to parameters.+void buildPosToParamMap(llvm::DenseMap<mlir::Value, DomainParameter> &paramMap,+                        SmallVectorImpl<mlir::Value> &posToParam) {+  unsigned numParam = paramMap.size();+  posToParam.reserve(numParam);++  for (auto const &it : paramMap)+    posToParam.push_back(it.first);++  // TODO: Is there a more efficient way?+  llvm::sort(posToParam.begin(), posToParam.end(),+             [&](const mlir::Value &L, const mlir::Value &R) {+               return paramMap[L].pos < paramMap[R].pos;+             });+}++/// Create a context relation from parameters and add it to the scop.+void addContextToScop(llvm::DenseMap<mlir::Value, DomainParameter> &paramMap,+                      OslScop &scop) {+  unsigned numParams = paramMap.size();+  unsigned numCols = 2 + numParams;++  std::vector<std::vector<int64_t>> ctxEqs, ctxInEqs;++  // We assume the context relation is built on inequalities generated by the+  // lower and upper bound of each parameter.+  unsigned i = 0; // The parameter ID.+  ctxInEqs.reserve(numParams * 2);+  for (auto const &it : paramMap) {+    DomainParameter param = it.second;+    assert((param.lb != llvm::None || param.ub != llvm::None) &&+           "At least one of lb and ub should not be None.");++    // Set the lower bound+    if (param.lb != llvm::None) {+      std::vector<int64_t> inEq(numCols - 1, 0);+      inEq[i] = 1;                              // The pos of the parameter.+      inEq[numCols - 2] = -param.lb.getValue(); // The constant.+      ctxInEqs.push_back(inEq);+    }+    // Set the upper bound+    if (param.ub != llvm::None) {+      std::vector<int64_t> inEq(numCols - 1, 0);+      inEq[i] = -1;                            // The pos of the parameter.+      inEq[numCols - 2] = param.ub.getValue(); // The constant.+      ctxInEqs.push_back(inEq);+    }++    i++;+  }++  // Assuming no equality.+  unsigned numRows = ctxInEqs.size();+  scop.addRelation(0, OSL_TYPE_CONTEXT, numRows, numCols, 0, 0, 0, numParams,+                   ctxEqs, ctxInEqs);+}++/// Swap the posA^th identifier with the posB^th identifier.+/// TODO: This is from AffineStructure.h, should be turned into a publicly+/// available API.+void swapId(FlatAffineConstraints *A, unsigned posA, unsigned posB) {+  assert(posA < A->getNumIds() && "invalid position A");+  assert(posB < A->getNumIds() && "invalid position B");++  if (posA == posB)+    return;++  for (unsigned r = 0, e = A->getNumInequalities(); r < e; r++) {+    std::swap(A->atIneq(r, posA), A->atIneq(r, posB));+  }+  for (unsigned r = 0, e = A->getNumEqualities(); r < e; r++) {+    std::swap(A->atEq(r, posA), A->atEq(r, posB));+  }+  std::swap(A->getId(posA), A->getId(posB));+}++/// Update the parameter (symbol) section of the domain constraints. We need to+/// make sure that all domains share the same set of parameters and they are+/// located at the same positions.+void updateDomainParams(+    FlatAffineConstraints &domain, SmallVectorImpl<mlir::Value> &posToParam,+    llvm::DenseMap<mlir::Value, DomainParameter> &paramMap) {+  unsigned offset = domain.getNumDimIds();+  unsigned numParams = paramMap.size();++  for (unsigned pos = 0; pos < numParams; pos++) {+    mlir::Value param = posToParam[pos];++    unsigned posInDomain;+    if (domain.findId(param, &posInDomain)) {+      // If there is such param in the domain, we should check whether its+      // position is right.+      if (posInDomain != offset + pos) {+        // posInDomain should be larger than pos. It is because all parameters+        // located before pos should already be placed correctly.+        assert(posInDomain > pos &&+               "posInDomain should be larger than pos if not equal.");+        swapId(&domain, offset + pos, posInDomain);+      }+    } else {+      domain.addSymbolId(pos, /*id=*/param);+    }+  }+}++} // namespace++namespace {++/// Get a clone of the elements in the equalities or inequalities of+/// FlatAffineConstraints.+void getEqualities(FlatAffineConstraints &cst,+                   std::vector<std::vector<int64_t>> &eqs, bool isEq = true) {+  unsigned numEqualities =+      isEq ? cst.getNumEqualities() : cst.getNumInequalities();+  unsigned numDimIds = cst.getNumDimIds();+  unsigned numLocalIds = cst.getNumLocalIds();+  unsigned numSymbolIds = cst.getNumSymbolIds();++  eqs.resize(numEqualities);+  for (unsigned i = 0; i < numEqualities; i++) {+    auto eq = isEq ? cst.getEquality(i) : cst.getInequality(i);+    unsigned numCols = eq.size();+    eqs[i].resize(numCols);++    // Dims stay at the same positions.+    for (unsigned j = 0; j < numDimIds; j++)+      eqs[i][j] = eq[j];+    // Output local ids before symbols.+    for (unsigned j = 0; j < numLocalIds; j++)+      eqs[i][j + numDimIds] = eq[j + numDimIds + numSymbolIds];+    // Output symbols in the end.+    for (unsigned j = 0; j < numSymbolIds; j++)+      eqs[i][j + numDimIds + numLocalIds] = eq[j + numDimIds];+    eqs[i][numCols - 1] = eq[numCols - 1];+  }+}++/// Gather information from the domain FlatAffineConstraints and put them into+/// the scop as a DOMAIN relation. index gives the statement id.+void addDomainToScop(unsigned index, FlatAffineConstraints &domain,+                     OslScop &scop) {+  // First we clone the equalities and inequalities from the domain constraints.+  std::vector<std::vector<int64_t>> eqs, inEqs;+  getEqualities(domain, eqs);+  getEqualities(domain, inEqs, /*isEq=*/false);++  // Then put them into the scop as a DOMAIN relation.+  scop.addRelation(index + 1, OSL_TYPE_DOMAIN, domain.getNumConstraints(),+                   domain.getNumCols() + 1, domain.getNumDimIds(), 0,+                   domain.getNumLocalIds(), domain.getNumSymbolIds(), eqs,+                   inEqs);+}++} // namespace++namespace {+/// Tree that holds scattering information. This node can represent an induction+/// variable or a statement. A statement is constructed as a leaf node.+class ScatteringTreeNode {+public:+  ScatteringTreeNode(bool isLeaf = false) : isLeaf(isLeaf) {}+  ScatteringTreeNode(mlir::Value iv) : iv(iv), isLeaf(false) {}++  /// Children of the current node.+  std::vector<std::unique_ptr<ScatteringTreeNode>> children;++  /// Mapping from IV to child ID.+  llvm::DenseMap<mlir::Value, unsigned> valueIdMap;++  /// Induction variable.+  mlir::Value iv;++  /// If this node is a statement, then isLeaf is true.+  bool isLeaf;+};++/// Insert a statement characterized by its enclosing operations into a+/// "scattering tree". This is done by iterating through every enclosing for-op+/// from the outermost to the innermost, and we try to traverse the tree by the+/// IVs of these ops. If an IV does not exist, we will insert it into the tree.+/// After that, we insert the current load/store statement into the tree as a+/// leaf. In this progress, we keep track of all the IDs of each child we meet+/// and the final leaf node, which will be used as the scattering.+void insertStatement(ScatteringTreeNode *root,+                     SmallVectorImpl<Operation *> &enclosingOps,+                     SmallVectorImpl<unsigned> &scattering) {+  ScatteringTreeNode *curr = root;++  for (unsigned i = 0, e = enclosingOps.size(); i < e; i++) {+    Operation *op = enclosingOps[i];+    // We only handle for op here.+    // TODO: is it necessary to deal with if?+    if (auto forOp = dyn_cast<AffineForOp>(op)) {+      SmallVector<mlir::Value, 4> indices;+      extractForInductionVars(forOp, &indices);++      for (auto iv : indices) {+        auto it = curr->valueIdMap.find(iv);+        if (it != curr->valueIdMap.end()) {+          // Add a new element to the scattering.+          scattering.push_back(it->second);+          // move to the next IV along the tree.+          curr = curr->children[it->second].get();+        } else {+          // No existing node for such IV is found, create a new one.+          auto node = std::make_unique<ScatteringTreeNode>(iv);++          // Then insert the newly created node into the children set, update+          // the value to child ID map, and move the cursor to this new node.+          curr->children.push_back(std::move(node));+          unsigned valueId = curr->children.size() - 1;+          curr->valueIdMap[iv] = valueId;+          scattering.push_back(valueId);+          curr = curr->children.back().get();+        }+      }+    }+  }++  // Append the leaf node for statement+  auto leaf = std::make_unique<ScatteringTreeNode>(/*isLeaf=*/true);+  curr->children.push_back(std::move(leaf));+  scattering.push_back(curr->children.size() - 1);+}++void addScatteringToScop(unsigned index, ArrayRef<unsigned> scattering,+                         FlatAffineConstraints &domain, OslScop &scop) {+  // Elements (N of them) in `scattering` are constants, and there are IVs+  // interleaved them. Therefore, we have 2N - 1 number of scattering+  // equalities.+  unsigned numScatteringEqualities = scattering.size() * 2 - 1;+  // Columns include new scattering dimensions and those from the domain.+  unsigned numScatteringCols =+      numScatteringEqualities + domain.getNumCols() + 1;++  // Create equalities and inequalities.+  std::vector<std::vector<int64_t>> eqs, inEqs;++  // Initialize contents for equalities.+  eqs.resize(numScatteringEqualities);+  for (unsigned j = 0; j < numScatteringEqualities; j++) {+    eqs[j].resize(numScatteringCols - 1);++    // Initializing scattering dimensions by setting the diagonal to -1.+    for (unsigned k = 0; k < numScatteringEqualities; k++)+      eqs[j][k] = -static_cast<int64_t>(k == j);++    // Relating the loop IVs to the scattering dimensions. If it's the odd+    // equality, set its scattering dimension to the loop IV; otherwise, it's+    // scattering dimension will be set in the following constant section.+    for (unsigned k = 0; k < domain.getNumDimIds(); k++)+      eqs[j][k + numScatteringEqualities] = (j % 2) ? (k == (j / 2)) : 0;++    // TODO: consider the parameters that may appear in the scattering+    // dimension.+    for (unsigned k = 0; k < domain.getNumLocalIds() + domain.getNumSymbolIds();+         k++)+      eqs[j][k + numScatteringEqualities + domain.getNumDimIds()] = 0;++    // Relating the constants (the last column) to the scattering dimensions.+    eqs[j][numScatteringCols - 2] = (j % 2) ? 0 : scattering[j / 2];+  }++  // Then put them into the scop as a SCATTERING relation.+  scop.addRelation(index + 1, OSL_TYPE_SCATTERING, numScatteringEqualities,+                   numScatteringCols, numScatteringEqualities,+                   domain.getNumDimIds(), domain.getNumLocalIds(),+                   domain.getNumSymbolIds(), eqs, inEqs);+}+} // namespace++namespace {++/// Generate the access relation and add it to the scop.+void addAccessToScop(unsigned index, unsigned memrefId, bool isRead,+                     ArrayRef<SmallVector<int64_t, 8>> flatExprs,+                     FlatAffineConstraints &domain, OslScop &scop) {+  // Number of equalities equals to the number of enclosing loop indices+  // plus 1 (the array itself).+  unsigned numAccessEqualities = domain.getNumDimIds() + 1;+  unsigned numAccessCols = domain.getNumCols() + numAccessEqualities + 1;+  unsigned numDimIds = domain.getNumDimIds();+  unsigned numSymbolIds = domain.getNumSymbolIds();+  unsigned numLocalIds = domain.getNumLocalIds();++  // Create equalities and inequalities.+  std::vector<std::vector<int64_t>> eqs, inEqs;++  eqs.resize(numAccessEqualities);+  for (unsigned i = 0; i < numAccessEqualities; i++) {+    // We leave alone the first column that indicates whether it is an equality+    // or not.+    eqs[i].resize(numAccessCols - 1);++    // The first section of a diagonal square matrix that points which axis the+    // current access relation is working on.+    for (unsigned j = 0; j < numAccessEqualities; j++)+      eqs[i][j] = -static_cast<int64_t>(i == j);++    // Set up the relation betwene the memref access position and the loop IVs.+    if (i == 0) {+      // The first row sets the array ID to the memref ID.+      for (unsigned j = 0; j < domain.getNumCols() - 1; j++)+        eqs[i][j + numAccessEqualities] = 0;+      eqs[i][numAccessCols - 2] = memrefId;+    } else {+      unsigned numFlatExprCols = flatExprs[i - 1].size();+      // Put the coefficients in the flat exprs into the access relation.+      // Note that numFlatExprCols may be smaller than the number of columns in+      // the domain constraints, mainly because the flatExprs are not aligned+      // with the position in domain constraints. Therefore, for those cases+      // that numFlatExprCols equals to the number of columns in the domain+      // constraint, we take a special approach to handle the mis-placed local+      // IDs; otherwise, we just place what in the flatExpr into the access+      // equalities.+      // TODO: properly handle local vars in the access equalities.+      if (numFlatExprCols == numDimIds + numLocalIds + numSymbolIds + 1) {+        // Input dims.+        for (unsigned j = 0; j < numDimIds; j++)+          eqs[i][j + numAccessEqualities] = flatExprs[i - 1][j];+        // Local dims.+        for (unsigned j = 0; j < numLocalIds; j++)+          eqs[i][j + numAccessEqualities + numDimIds] =+              flatExprs[i - 1][j + numDimIds + numSymbolIds];+        // Parameters.+        for (unsigned j = 0; j < numSymbolIds; j++)+          eqs[i][j + numAccessEqualities + numDimIds + numLocalIds] =+              flatExprs[i - 1][j + numDimIds];+        // Constant.+        eqs[i][numAccessEqualities + numFlatExprCols - 1] =+            flatExprs[i - 1][numFlatExprCols - 1];+      } else {+        for (unsigned j = 0; j < numFlatExprCols; j++)+          eqs[i][j + numAccessEqualities] = flatExprs[i - 1][j];+      }+    }+  }++  // Then put them into the scop as a ACCESS relation.+  scop.addRelation(index + 1, isRead ? OSL_TYPE_READ : OSL_TYPE_WRITE,+                   numAccessEqualities, numAccessCols, numAccessEqualities,+                   domain.getNumDimIds(), domain.getNumLocalIds(),+                   domain.getNumSymbolIds(), eqs, inEqs);+}+} // namespace++namespace {+/// This class maintains the state of a working emitter.+class OpenScopEmitterState {+public:+  explicit OpenScopEmitterState(raw_ostream &os) : os(os) {}++  /// The stream to emit to.+  raw_ostream &os;++  bool encounteredError = false;+  unsigned currentIdent = 0; // TODO: may not need this.++private:+  OpenScopEmitterState(const OpenScopEmitterState &) = delete;+  void operator=(const OpenScopEmitterState &) = delete;+};++/// Base class for various OpenScop emitters.+class OpenScopEmitterBase {+public:+  explicit OpenScopEmitterBase(OpenScopEmitterState &state)+      : state(state), os(state.os) {}++  InFlightDiagnostic emitError(Operation *op, const Twine &message) {+    state.encounteredError = true;+    return op->emitError(message);+  }++  InFlightDiagnostic emitOpError(Operation *op, const Twine &message) {+    state.encounteredError = true;+    return op->emitOpError(message);+  }++  /// All of the mutable state we are maintaining.+  OpenScopEmitterState &state;++  /// The stream to emit to.+  raw_ostream &os;++private:+  OpenScopEmitterBase(const OpenScopEmitterBase &) = delete;+  void operator=(const OpenScopEmitterBase &) = delete;+};++/// Emit OpenScop representation from an MLIR module.+class ModuleEmitter : public OpenScopEmitterBase {+public:+  explicit ModuleEmitter(OpenScopEmitterState &state)+      : OpenScopEmitterBase(state) {}++  /// Emit OpenScop definitions for all functions in the given module.+  void emitMLIRModule(ModuleOp module);++private:+  /// Emit a OpenScop definition for a single function.+  LogicalResult emitFuncOp(FuncOp func);+};++LogicalResult ModuleEmitter::emitFuncOp(mlir::FuncOp func) {+  // Initialize ja new Scop per FuncOp.+  OslScop scop;++  // We iterate through every operations in the function and extrat load/store+  // operations out into loadAndStoreOps.+  SmallVector<Operation *, 8> loadAndStoreOps;+  func.getOperation()->walk([&](Operation *op) {+    if (isa<AffineReadOpInterface, AffineWriteOpInterface>(op))+      loadAndStoreOps.push_back(op);+  });++  LLVM_DEBUG(llvm::dbgs() << "Found " << Twine(loadAndStoreOps.size())

No need for Twine here, operator<< accepts integers just fine.

kumasento

comment created time in a month

Pull request review commentkumasento/polymer

Emitting OpenSCoP from MLIR affine

+#include "polymer/EmitOpenScop.h"+#include "polymer/OslScop.h"++#include "mlir/Analysis/AffineAnalysis.h"+#include "mlir/Analysis/AffineStructures.h"+#include "mlir/Analysis/LoopAnalysis.h"+#include "mlir/Analysis/Utils.h"+#include "mlir/Dialect/Affine/IR/AffineOps.h"+#include "mlir/Dialect/Affine/IR/AffineValueMap.h"+#include "mlir/Dialect/Affine/Passes.h"+#include "mlir/Dialect/StandardOps/IR/Ops.h"+#include "mlir/IR/BlockAndValueMapping.h"+#include "mlir/IR/Builders.h"+#include "mlir/IR/Function.h"+#include "mlir/IR/Module.h"+#include "mlir/IR/StandardTypes.h"+#include "mlir/Transforms/LoopUtils.h"+#include "mlir/Transforms/Utils.h"+#include "mlir/Translation.h"++#include "llvm/ADT/DenseMap.h"+#include "llvm/ADT/StringSet.h"+#include "llvm/Support/Debug.h"+#include "llvm/Support/Format.h"+#include "llvm/Support/FormatVariadic.h"+#include "llvm/Support/raw_ostream.h"++#include "osl/osl.h"++#include <memory>++using namespace mlir;+using namespace llvm;+using namespace polymer;++#define DEBUG_TYPE "emit-openscop"++namespace {++/// Extracted data from the domain constraints, will be used as the value of+/// paramMap when generating scop. lb and ub will be used to get the context+/// relation.+struct DomainParameter {+  unsigned pos; // Parameter column position in the constraints.+  llvm::Optional<int64_t> lb, ub; // Lower and upper bounds.++  DomainParameter() : pos(0), lb(llvm::None), ub(llvm::None) {}+  DomainParameter(unsigned pos, llvm::Optional<int64_t> lb,+                  llvm::Optional<int64_t> ub)+      : pos(pos), lb(lb), ub(ub) {}+};++/// Add parameters to the maintained parameter-ID map.+void addParamsToMap(FlatAffineConstraints &domain,+                    llvm::DenseMap<mlir::Value, DomainParameter> &paramMap) {+  // Get symbol values from the domain constraint.+  SmallVector<mlir::Value, 8> values;+  unsigned offset = domain.getNumDimIds();+  domain.getIdValues(offset, domain.getNumDimAndSymbolIds(), &values);++  // Insert the value into the map if there isn't one.+  for (int i = 0, e = values.size(); i < e; i++) {+    mlir::Value value = values[i];+    auto lb = domain.getConstantLowerBound(i + offset);+    auto ub = domain.getConstantUpperBound(i + offset);+    LLVM_DEBUG(llvm::dbgs() << "Bound for parameter i=" << i << " : [" << lb+                            << ", " << ub << ")\n");++    auto it = paramMap.find(value);+    if (it == paramMap.end()) {+      paramMap[value] = DomainParameter(paramMap.size(), lb, ub);+    }+  }+}++/// Build the mapping from position to parameters.+void buildPosToParamMap(llvm::DenseMap<mlir::Value, DomainParameter> &paramMap,+                        SmallVectorImpl<mlir::Value> &posToParam) {+  unsigned numParam = paramMap.size();+  posToParam.reserve(numParam);++  for (auto const &it : paramMap)+    posToParam.push_back(it.first);++  // TODO: Is there a more efficient way?+  llvm::sort(posToParam.begin(), posToParam.end(),+             [&](const mlir::Value &L, const mlir::Value &R) {+               return paramMap[L].pos < paramMap[R].pos;+             });+}++/// Create a context relation from parameters and add it to the scop.+void addContextToScop(llvm::DenseMap<mlir::Value, DomainParameter> &paramMap,+                      OslScop &scop) {+  unsigned numParams = paramMap.size();+  unsigned numCols = 2 + numParams;++  std::vector<std::vector<int64_t>> ctxEqs, ctxInEqs;++  // We assume the context relation is built on inequalities generated by the+  // lower and upper bound of each parameter.+  unsigned i = 0; // The parameter ID.+  ctxInEqs.reserve(numParams * 2);+  for (auto const &it : paramMap) {+    DomainParameter param = it.second;+    assert((param.lb != llvm::None || param.ub != llvm::None) &&+           "At least one of lb and ub should not be None.");++    // Set the lower bound+    if (param.lb != llvm::None) {+      std::vector<int64_t> inEq(numCols - 1, 0);+      inEq[i] = 1;                              // The pos of the parameter.+      inEq[numCols - 2] = -param.lb.getValue(); // The constant.+      ctxInEqs.push_back(inEq);+    }+    // Set the upper bound+    if (param.ub != llvm::None) {+      std::vector<int64_t> inEq(numCols - 1, 0);+      inEq[i] = -1;                            // The pos of the parameter.+      inEq[numCols - 2] = param.ub.getValue(); // The constant.+      ctxInEqs.push_back(inEq);+    }++    i++;+  }++  // Assuming no equality.+  unsigned numRows = ctxInEqs.size();+  scop.addRelation(0, OSL_TYPE_CONTEXT, numRows, numCols, 0, 0, 0, numParams,+                   ctxEqs, ctxInEqs);+}++/// Swap the posA^th identifier with the posB^th identifier.+/// TODO: This is from AffineStructure.h, should be turned into a publicly+/// available API.+void swapId(FlatAffineConstraints *A, unsigned posA, unsigned posB) {+  assert(posA < A->getNumIds() && "invalid position A");+  assert(posB < A->getNumIds() && "invalid position B");++  if (posA == posB)+    return;++  for (unsigned r = 0, e = A->getNumInequalities(); r < e; r++) {+    std::swap(A->atIneq(r, posA), A->atIneq(r, posB));+  }+  for (unsigned r = 0, e = A->getNumEqualities(); r < e; r++) {+    std::swap(A->atEq(r, posA), A->atEq(r, posB));+  }+  std::swap(A->getId(posA), A->getId(posB));+}++/// Update the parameter (symbol) section of the domain constraints. We need to+/// make sure that all domains share the same set of parameters and they are+/// located at the same positions.+void updateDomainParams(+    FlatAffineConstraints &domain, SmallVectorImpl<mlir::Value> &posToParam,+    llvm::DenseMap<mlir::Value, DomainParameter> &paramMap) {+  unsigned offset = domain.getNumDimIds();+  unsigned numParams = paramMap.size();++  for (unsigned pos = 0; pos < numParams; pos++) {+    mlir::Value param = posToParam[pos];++    unsigned posInDomain;+    if (domain.findId(param, &posInDomain)) {+      // If there is such param in the domain, we should check whether its+      // position is right.+      if (posInDomain != offset + pos) {+        // posInDomain should be larger than pos. It is because all parameters+        // located before pos should already be placed correctly.+        assert(posInDomain > pos &&+               "posInDomain should be larger than pos if not equal.");+        swapId(&domain, offset + pos, posInDomain);+      }+    } else {+      domain.addSymbolId(pos, /*id=*/param);+    }+  }+}++} // namespace++namespace {

No need to close a namespace just to reopen it immediately.

Independently, LLVM prefers static functions to functions-in-namespace.

kumasento

comment created time in a month

Pull request review commentkumasento/polymer

Emitting OpenSCoP from MLIR affine

+#include "polymer/EmitOpenScop.h"+#include "polymer/OslScop.h"++#include "mlir/Analysis/AffineAnalysis.h"+#include "mlir/Analysis/AffineStructures.h"+#include "mlir/Analysis/LoopAnalysis.h"+#include "mlir/Analysis/Utils.h"+#include "mlir/Dialect/Affine/IR/AffineOps.h"+#include "mlir/Dialect/Affine/IR/AffineValueMap.h"+#include "mlir/Dialect/Affine/Passes.h"+#include "mlir/Dialect/StandardOps/IR/Ops.h"+#include "mlir/IR/BlockAndValueMapping.h"+#include "mlir/IR/Builders.h"+#include "mlir/IR/Function.h"+#include "mlir/IR/Module.h"+#include "mlir/IR/StandardTypes.h"+#include "mlir/Transforms/LoopUtils.h"+#include "mlir/Transforms/Utils.h"+#include "mlir/Translation.h"++#include "llvm/ADT/DenseMap.h"+#include "llvm/ADT/StringSet.h"+#include "llvm/Support/Debug.h"+#include "llvm/Support/Format.h"+#include "llvm/Support/FormatVariadic.h"+#include "llvm/Support/raw_ostream.h"++#include "osl/osl.h"++#include <memory>++using namespace mlir;+using namespace llvm;+using namespace polymer;++#define DEBUG_TYPE "emit-openscop"++namespace {++/// Extracted data from the domain constraints, will be used as the value of+/// paramMap when generating scop. lb and ub will be used to get the context+/// relation.+struct DomainParameter {+  unsigned pos; // Parameter column position in the constraints.+  llvm::Optional<int64_t> lb, ub; // Lower and upper bounds.++  DomainParameter() : pos(0), lb(llvm::None), ub(llvm::None) {}+  DomainParameter(unsigned pos, llvm::Optional<int64_t> lb,+                  llvm::Optional<int64_t> ub)+      : pos(pos), lb(lb), ub(ub) {}+};++/// Add parameters to the maintained parameter-ID map.+void addParamsToMap(FlatAffineConstraints &domain,+                    llvm::DenseMap<mlir::Value, DomainParameter> &paramMap) {+  // Get symbol values from the domain constraint.+  SmallVector<mlir::Value, 8> values;+  unsigned offset = domain.getNumDimIds();+  domain.getIdValues(offset, domain.getNumDimAndSymbolIds(), &values);++  // Insert the value into the map if there isn't one.+  for (int i = 0, e = values.size(); i < e; i++) {+    mlir::Value value = values[i];+    auto lb = domain.getConstantLowerBound(i + offset);+    auto ub = domain.getConstantUpperBound(i + offset);+    LLVM_DEBUG(llvm::dbgs() << "Bound for parameter i=" << i << " : [" << lb+                            << ", " << ub << ")\n");++    auto it = paramMap.find(value);+    if (it == paramMap.end()) {+      paramMap[value] = DomainParameter(paramMap.size(), lb, ub);+    }+  }+}++/// Build the mapping from position to parameters.+void buildPosToParamMap(llvm::DenseMap<mlir::Value, DomainParameter> &paramMap,+                        SmallVectorImpl<mlir::Value> &posToParam) {+  unsigned numParam = paramMap.size();+  posToParam.reserve(numParam);++  for (auto const &it : paramMap)+    posToParam.push_back(it.first);++  // TODO: Is there a more efficient way?+  llvm::sort(posToParam.begin(), posToParam.end(),+             [&](const mlir::Value &L, const mlir::Value &R) {+               return paramMap[L].pos < paramMap[R].pos;+             });+}++/// Create a context relation from parameters and add it to the scop.+void addContextToScop(llvm::DenseMap<mlir::Value, DomainParameter> &paramMap,+                      OslScop &scop) {+  unsigned numParams = paramMap.size();+  unsigned numCols = 2 + numParams;++  std::vector<std::vector<int64_t>> ctxEqs, ctxInEqs;++  // We assume the context relation is built on inequalities generated by the+  // lower and upper bound of each parameter.+  unsigned i = 0; // The parameter ID.+  ctxInEqs.reserve(numParams * 2);+  for (auto const &it : paramMap) {+    DomainParameter param = it.second;+    assert((param.lb != llvm::None || param.ub != llvm::None) &&+           "At least one of lb and ub should not be None.");++    // Set the lower bound+    if (param.lb != llvm::None) {+      std::vector<int64_t> inEq(numCols - 1, 0);+      inEq[i] = 1;                              // The pos of the parameter.+      inEq[numCols - 2] = -param.lb.getValue(); // The constant.+      ctxInEqs.push_back(inEq);+    }+    // Set the upper bound+    if (param.ub != llvm::None) {+      std::vector<int64_t> inEq(numCols - 1, 0);+      inEq[i] = -1;                            // The pos of the parameter.+      inEq[numCols - 2] = param.ub.getValue(); // The constant.+      ctxInEqs.push_back(inEq);+    }++    i++;+  }++  // Assuming no equality.+  unsigned numRows = ctxInEqs.size();+  scop.addRelation(0, OSL_TYPE_CONTEXT, numRows, numCols, 0, 0, 0, numParams,+                   ctxEqs, ctxInEqs);+}++/// Swap the posA^th identifier with the posB^th identifier.+/// TODO: This is from AffineStructure.h, should be turned into a publicly+/// available API.+void swapId(FlatAffineConstraints *A, unsigned posA, unsigned posB) {+  assert(posA < A->getNumIds() && "invalid position A");+  assert(posB < A->getNumIds() && "invalid position B");++  if (posA == posB)+    return;++  for (unsigned r = 0, e = A->getNumInequalities(); r < e; r++) {+    std::swap(A->atIneq(r, posA), A->atIneq(r, posB));+  }+  for (unsigned r = 0, e = A->getNumEqualities(); r < e; r++) {+    std::swap(A->atEq(r, posA), A->atEq(r, posB));+  }+  std::swap(A->getId(posA), A->getId(posB));+}++/// Update the parameter (symbol) section of the domain constraints. We need to+/// make sure that all domains share the same set of parameters and they are+/// located at the same positions.+void updateDomainParams(+    FlatAffineConstraints &domain, SmallVectorImpl<mlir::Value> &posToParam,+    llvm::DenseMap<mlir::Value, DomainParameter> &paramMap) {+  unsigned offset = domain.getNumDimIds();+  unsigned numParams = paramMap.size();++  for (unsigned pos = 0; pos < numParams; pos++) {+    mlir::Value param = posToParam[pos];++    unsigned posInDomain;+    if (domain.findId(param, &posInDomain)) {+      // If there is such param in the domain, we should check whether its+      // position is right.+      if (posInDomain != offset + pos) {+        // posInDomain should be larger than pos. It is because all parameters+        // located before pos should already be placed correctly.+        assert(posInDomain > pos &&+               "posInDomain should be larger than pos if not equal.");+        swapId(&domain, offset + pos, posInDomain);+      }+    } else {+      domain.addSymbolId(pos, /*id=*/param);+    }+  }+}++} // namespace++namespace {++/// Get a clone of the elements in the equalities or inequalities of+/// FlatAffineConstraints.+void getEqualities(FlatAffineConstraints &cst,+                   std::vector<std::vector<int64_t>> &eqs, bool isEq = true) {+  unsigned numEqualities =+      isEq ? cst.getNumEqualities() : cst.getNumInequalities();+  unsigned numDimIds = cst.getNumDimIds();+  unsigned numLocalIds = cst.getNumLocalIds();+  unsigned numSymbolIds = cst.getNumSymbolIds();++  eqs.resize(numEqualities);+  for (unsigned i = 0; i < numEqualities; i++) {+    auto eq = isEq ? cst.getEquality(i) : cst.getInequality(i);+    unsigned numCols = eq.size();+    eqs[i].resize(numCols);++    // Dims stay at the same positions.+    for (unsigned j = 0; j < numDimIds; j++)+      eqs[i][j] = eq[j];+    // Output local ids before symbols.+    for (unsigned j = 0; j < numLocalIds; j++)+      eqs[i][j + numDimIds] = eq[j + numDimIds + numSymbolIds];+    // Output symbols in the end.+    for (unsigned j = 0; j < numSymbolIds; j++)+      eqs[i][j + numDimIds + numLocalIds] = eq[j + numDimIds];+    eqs[i][numCols - 1] = eq[numCols - 1];+  }+}++/// Gather information from the domain FlatAffineConstraints and put them into+/// the scop as a DOMAIN relation. index gives the statement id.+void addDomainToScop(unsigned index, FlatAffineConstraints &domain,+                     OslScop &scop) {+  // First we clone the equalities and inequalities from the domain constraints.+  std::vector<std::vector<int64_t>> eqs, inEqs;+  getEqualities(domain, eqs);+  getEqualities(domain, inEqs, /*isEq=*/false);++  // Then put them into the scop as a DOMAIN relation.+  scop.addRelation(index + 1, OSL_TYPE_DOMAIN, domain.getNumConstraints(),+                   domain.getNumCols() + 1, domain.getNumDimIds(), 0,+                   domain.getNumLocalIds(), domain.getNumSymbolIds(), eqs,+                   inEqs);+}++} // namespace++namespace {+/// Tree that holds scattering information. This node can represent an induction+/// variable or a statement. A statement is constructed as a leaf node.+class ScatteringTreeNode {+public:+  ScatteringTreeNode(bool isLeaf = false) : isLeaf(isLeaf) {}+  ScatteringTreeNode(mlir::Value iv) : iv(iv), isLeaf(false) {}++  /// Children of the current node.+  std::vector<std::unique_ptr<ScatteringTreeNode>> children;++  /// Mapping from IV to child ID.+  llvm::DenseMap<mlir::Value, unsigned> valueIdMap;++  /// Induction variable.+  mlir::Value iv;++  /// If this node is a statement, then isLeaf is true.+  bool isLeaf;+};++/// Insert a statement characterized by its enclosing operations into a+/// "scattering tree". This is done by iterating through every enclosing for-op+/// from the outermost to the innermost, and we try to traverse the tree by the+/// IVs of these ops. If an IV does not exist, we will insert it into the tree.+/// After that, we insert the current load/store statement into the tree as a+/// leaf. In this progress, we keep track of all the IDs of each child we meet+/// and the final leaf node, which will be used as the scattering.+void insertStatement(ScatteringTreeNode *root,+                     SmallVectorImpl<Operation *> &enclosingOps,+                     SmallVectorImpl<unsigned> &scattering) {+  ScatteringTreeNode *curr = root;++  for (unsigned i = 0, e = enclosingOps.size(); i < e; i++) {+    Operation *op = enclosingOps[i];+    // We only handle for op here.+    // TODO: is it necessary to deal with if?

Yes

kumasento

comment created time in a month

Pull request review commentkumasento/polymer

Emitting OpenSCoP from MLIR affine

+#include "polymer/OslScop.h"++#include "osl/osl.h"++#include "llvm/Support/raw_ostream.h"++#include <vector>++using namespace polymer;++namespace {++/// Create osl_vector from a STL vector. Since the input vector is of type+/// int64_t, we can safely assume the osl_vector we will generate has double

Nit: "double precision" isn't usually used to refer to int64. Precision is a floating point concept.

kumasento

comment created time in a month

Pull request review commentkumasento/polymer

Emitting OpenSCoP from MLIR affine

+// RUN: emit-openscop %s -emit-openscop | FileCheck %s++// Consider local variables in the domain.+// We will make this test valid when the diff D86421 is landed.++#map = affine_map<(d0) -> (d0 floordiv 2)>++func @load_store_if(%A : memref<?xf32>) -> () {+  %c0 = constant 0 : index+  %N = dim %A, %c0 : memref<?xf32>+  %M = affine.apply #map(%N)++  affine.for %i = 0 to %M {+    %0 = affine.load %A[%i] : memref<?xf32>+    affine.store %0, %A[%i + %M] : memref<?xf32>+  }++  return+}++// CHECK: # [File generated by the OpenScop Library 0.9.2]

I wouldn't check for any comments (starting with #), they may change or be omitted. You can keep them as C comments if necessary for readability.

kumasento

comment created time in a month

Pull request review commentkumasento/polymer

Emitting OpenSCoP from MLIR affine

+#include "polymer/EmitOpenScop.h"+#include "polymer/OslScop.h"++#include "mlir/Analysis/AffineAnalysis.h"+#include "mlir/Analysis/AffineStructures.h"+#include "mlir/Analysis/LoopAnalysis.h"+#include "mlir/Analysis/Utils.h"+#include "mlir/Dialect/Affine/IR/AffineOps.h"+#include "mlir/Dialect/Affine/IR/AffineValueMap.h"+#include "mlir/Dialect/Affine/Passes.h"+#include "mlir/Dialect/StandardOps/IR/Ops.h"+#include "mlir/IR/BlockAndValueMapping.h"+#include "mlir/IR/Builders.h"+#include "mlir/IR/Function.h"+#include "mlir/IR/Module.h"+#include "mlir/IR/StandardTypes.h"+#include "mlir/Transforms/LoopUtils.h"+#include "mlir/Transforms/Utils.h"+#include "mlir/Translation.h"++#include "llvm/ADT/DenseMap.h"+#include "llvm/ADT/StringSet.h"+#include "llvm/Support/Debug.h"+#include "llvm/Support/Format.h"+#include "llvm/Support/FormatVariadic.h"+#include "llvm/Support/raw_ostream.h"++#include "osl/osl.h"++#include <memory>++using namespace mlir;+using namespace llvm;+using namespace polymer;++#define DEBUG_TYPE "emit-openscop"++namespace {++/// Extracted data from the domain constraints, will be used as the value of+/// paramMap when generating scop. lb and ub will be used to get the context+/// relation.+struct DomainParameter {+  unsigned pos; // Parameter column position in the constraints.+  llvm::Optional<int64_t> lb, ub; // Lower and upper bounds.++  DomainParameter() : pos(0), lb(llvm::None), ub(llvm::None) {}+  DomainParameter(unsigned pos, llvm::Optional<int64_t> lb,+                  llvm::Optional<int64_t> ub)+      : pos(pos), lb(lb), ub(ub) {}+};++/// Add parameters to the maintained parameter-ID map.+void addParamsToMap(FlatAffineConstraints &domain,+                    llvm::DenseMap<mlir::Value, DomainParameter> &paramMap) {+  // Get symbol values from the domain constraint.+  SmallVector<mlir::Value, 8> values;+  unsigned offset = domain.getNumDimIds();+  domain.getIdValues(offset, domain.getNumDimAndSymbolIds(), &values);++  // Insert the value into the map if there isn't one.+  for (int i = 0, e = values.size(); i < e; i++) {+    mlir::Value value = values[i];+    auto lb = domain.getConstantLowerBound(i + offset);+    auto ub = domain.getConstantUpperBound(i + offset);+    LLVM_DEBUG(llvm::dbgs() << "Bound for parameter i=" << i << " : [" << lb+                            << ", " << ub << ")\n");++    auto it = paramMap.find(value);+    if (it == paramMap.end()) {+      paramMap[value] = DomainParameter(paramMap.size(), lb, ub);+    }+  }+}++/// Build the mapping from position to parameters.+void buildPosToParamMap(llvm::DenseMap<mlir::Value, DomainParameter> &paramMap,+                        SmallVectorImpl<mlir::Value> &posToParam) {+  unsigned numParam = paramMap.size();+  posToParam.reserve(numParam);++  for (auto const &it : paramMap)+    posToParam.push_back(it.first);++  // TODO: Is there a more efficient way?+  llvm::sort(posToParam.begin(), posToParam.end(),+             [&](const mlir::Value &L, const mlir::Value &R) {+               return paramMap[L].pos < paramMap[R].pos;+             });+}++/// Create a context relation from parameters and add it to the scop.+void addContextToScop(llvm::DenseMap<mlir::Value, DomainParameter> &paramMap,+                      OslScop &scop) {+  unsigned numParams = paramMap.size();+  unsigned numCols = 2 + numParams;++  std::vector<std::vector<int64_t>> ctxEqs, ctxInEqs;++  // We assume the context relation is built on inequalities generated by the+  // lower and upper bound of each parameter.+  unsigned i = 0; // The parameter ID.+  ctxInEqs.reserve(numParams * 2);+  for (auto const &it : paramMap) {+    DomainParameter param = it.second;+    assert((param.lb != llvm::None || param.ub != llvm::None) &&+           "At least one of lb and ub should not be None.");++    // Set the lower bound+    if (param.lb != llvm::None) {+      std::vector<int64_t> inEq(numCols - 1, 0);+      inEq[i] = 1;                              // The pos of the parameter.+      inEq[numCols - 2] = -param.lb.getValue(); // The constant.+      ctxInEqs.push_back(inEq);+    }+    // Set the upper bound+    if (param.ub != llvm::None) {+      std::vector<int64_t> inEq(numCols - 1, 0);+      inEq[i] = -1;                            // The pos of the parameter.+      inEq[numCols - 2] = param.ub.getValue(); // The constant.+      ctxInEqs.push_back(inEq);+    }++    i++;+  }++  // Assuming no equality.+  unsigned numRows = ctxInEqs.size();+  scop.addRelation(0, OSL_TYPE_CONTEXT, numRows, numCols, 0, 0, 0, numParams,+                   ctxEqs, ctxInEqs);+}++/// Swap the posA^th identifier with the posB^th identifier.+/// TODO: This is from AffineStructure.h, should be turned into a publicly+/// available API.+void swapId(FlatAffineConstraints *A, unsigned posA, unsigned posB) {+  assert(posA < A->getNumIds() && "invalid position A");+  assert(posB < A->getNumIds() && "invalid position B");++  if (posA == posB)+    return;++  for (unsigned r = 0, e = A->getNumInequalities(); r < e; r++) {+    std::swap(A->atIneq(r, posA), A->atIneq(r, posB));+  }+  for (unsigned r = 0, e = A->getNumEqualities(); r < e; r++) {+    std::swap(A->atEq(r, posA), A->atEq(r, posB));+  }+  std::swap(A->getId(posA), A->getId(posB));+}++/// Update the parameter (symbol) section of the domain constraints. We need to+/// make sure that all domains share the same set of parameters and they are+/// located at the same positions.+void updateDomainParams(+    FlatAffineConstraints &domain, SmallVectorImpl<mlir::Value> &posToParam,+    llvm::DenseMap<mlir::Value, DomainParameter> &paramMap) {+  unsigned offset = domain.getNumDimIds();+  unsigned numParams = paramMap.size();++  for (unsigned pos = 0; pos < numParams; pos++) {+    mlir::Value param = posToParam[pos];++    unsigned posInDomain;+    if (domain.findId(param, &posInDomain)) {+      // If there is such param in the domain, we should check whether its+      // position is right.+      if (posInDomain != offset + pos) {+        // posInDomain should be larger than pos. It is because all parameters+        // located before pos should already be placed correctly.+        assert(posInDomain > pos &&+               "posInDomain should be larger than pos if not equal.");+        swapId(&domain, offset + pos, posInDomain);+      }+    } else {+      domain.addSymbolId(pos, /*id=*/param);+    }+  }+}++} // namespace++namespace {++/// Get a clone of the elements in the equalities or inequalities of+/// FlatAffineConstraints.+void getEqualities(FlatAffineConstraints &cst,+                   std::vector<std::vector<int64_t>> &eqs, bool isEq = true) {+  unsigned numEqualities =+      isEq ? cst.getNumEqualities() : cst.getNumInequalities();+  unsigned numDimIds = cst.getNumDimIds();+  unsigned numLocalIds = cst.getNumLocalIds();+  unsigned numSymbolIds = cst.getNumSymbolIds();++  eqs.resize(numEqualities);+  for (unsigned i = 0; i < numEqualities; i++) {+    auto eq = isEq ? cst.getEquality(i) : cst.getInequality(i);+    unsigned numCols = eq.size();+    eqs[i].resize(numCols);++    // Dims stay at the same positions.+    for (unsigned j = 0; j < numDimIds; j++)+      eqs[i][j] = eq[j];+    // Output local ids before symbols.+    for (unsigned j = 0; j < numLocalIds; j++)+      eqs[i][j + numDimIds] = eq[j + numDimIds + numSymbolIds];+    // Output symbols in the end.+    for (unsigned j = 0; j < numSymbolIds; j++)+      eqs[i][j + numDimIds + numLocalIds] = eq[j + numDimIds];+    eqs[i][numCols - 1] = eq[numCols - 1];+  }+}++/// Gather information from the domain FlatAffineConstraints and put them into+/// the scop as a DOMAIN relation. index gives the statement id.+void addDomainToScop(unsigned index, FlatAffineConstraints &domain,+                     OslScop &scop) {+  // First we clone the equalities and inequalities from the domain constraints.+  std::vector<std::vector<int64_t>> eqs, inEqs;+  getEqualities(domain, eqs);+  getEqualities(domain, inEqs, /*isEq=*/false);++  // Then put them into the scop as a DOMAIN relation.+  scop.addRelation(index + 1, OSL_TYPE_DOMAIN, domain.getNumConstraints(),+                   domain.getNumCols() + 1, domain.getNumDimIds(), 0,+                   domain.getNumLocalIds(), domain.getNumSymbolIds(), eqs,+                   inEqs);+}++} // namespace++namespace {+/// Tree that holds scattering information. This node can represent an induction+/// variable or a statement. A statement is constructed as a leaf node.+class ScatteringTreeNode {+public:+  ScatteringTreeNode(bool isLeaf = false) : isLeaf(isLeaf) {}+  ScatteringTreeNode(mlir::Value iv) : iv(iv), isLeaf(false) {}++  /// Children of the current node.+  std::vector<std::unique_ptr<ScatteringTreeNode>> children;++  /// Mapping from IV to child ID.+  llvm::DenseMap<mlir::Value, unsigned> valueIdMap;++  /// Induction variable.+  mlir::Value iv;++  /// If this node is a statement, then isLeaf is true.+  bool isLeaf;+};++/// Insert a statement characterized by its enclosing operations into a+/// "scattering tree". This is done by iterating through every enclosing for-op+/// from the outermost to the innermost, and we try to traverse the tree by the+/// IVs of these ops. If an IV does not exist, we will insert it into the tree.+/// After that, we insert the current load/store statement into the tree as a+/// leaf. In this progress, we keep track of all the IDs of each child we meet+/// and the final leaf node, which will be used as the scattering.+void insertStatement(ScatteringTreeNode *root,+                     SmallVectorImpl<Operation *> &enclosingOps,

Prefer ArrayRef if you don't need to change the size of the vector

kumasento

comment created time in a month

Pull request review commentkumasento/polymer

Emitting OpenSCoP from MLIR affine

+#include "polymer/EmitOpenScop.h"+#include "polymer/OslScop.h"++#include "mlir/Analysis/AffineAnalysis.h"+#include "mlir/Analysis/AffineStructures.h"+#include "mlir/Analysis/LoopAnalysis.h"+#include "mlir/Analysis/Utils.h"+#include "mlir/Dialect/Affine/IR/AffineOps.h"+#include "mlir/Dialect/Affine/IR/AffineValueMap.h"+#include "mlir/Dialect/Affine/Passes.h"+#include "mlir/Dialect/StandardOps/IR/Ops.h"+#include "mlir/IR/BlockAndValueMapping.h"+#include "mlir/IR/Builders.h"+#include "mlir/IR/Function.h"+#include "mlir/IR/Module.h"+#include "mlir/IR/StandardTypes.h"+#include "mlir/Transforms/LoopUtils.h"+#include "mlir/Transforms/Utils.h"+#include "mlir/Translation.h"++#include "llvm/ADT/DenseMap.h"+#include "llvm/ADT/StringSet.h"+#include "llvm/Support/Debug.h"+#include "llvm/Support/Format.h"+#include "llvm/Support/FormatVariadic.h"+#include "llvm/Support/raw_ostream.h"++#include "osl/osl.h"++#include <memory>++using namespace mlir;+using namespace llvm;+using namespace polymer;++#define DEBUG_TYPE "emit-openscop"++namespace {++/// Extracted data from the domain constraints, will be used as the value of+/// paramMap when generating scop. lb and ub will be used to get the context+/// relation.+struct DomainParameter {+  unsigned pos; // Parameter column position in the constraints.+  llvm::Optional<int64_t> lb, ub; // Lower and upper bounds.++  DomainParameter() : pos(0), lb(llvm::None), ub(llvm::None) {}+  DomainParameter(unsigned pos, llvm::Optional<int64_t> lb,+                  llvm::Optional<int64_t> ub)+      : pos(pos), lb(lb), ub(ub) {}+};++/// Add parameters to the maintained parameter-ID map.+void addParamsToMap(FlatAffineConstraints &domain,+                    llvm::DenseMap<mlir::Value, DomainParameter> &paramMap) {+  // Get symbol values from the domain constraint.+  SmallVector<mlir::Value, 8> values;+  unsigned offset = domain.getNumDimIds();+  domain.getIdValues(offset, domain.getNumDimAndSymbolIds(), &values);++  // Insert the value into the map if there isn't one.+  for (int i = 0, e = values.size(); i < e; i++) {+    mlir::Value value = values[i];+    auto lb = domain.getConstantLowerBound(i + offset);+    auto ub = domain.getConstantUpperBound(i + offset);+    LLVM_DEBUG(llvm::dbgs() << "Bound for parameter i=" << i << " : [" << lb+                            << ", " << ub << ")\n");++    auto it = paramMap.find(value);+    if (it == paramMap.end()) {+      paramMap[value] = DomainParameter(paramMap.size(), lb, ub);+    }+  }+}++/// Build the mapping from position to parameters.+void buildPosToParamMap(llvm::DenseMap<mlir::Value, DomainParameter> &paramMap,+                        SmallVectorImpl<mlir::Value> &posToParam) {+  unsigned numParam = paramMap.size();+  posToParam.reserve(numParam);++  for (auto const &it : paramMap)+    posToParam.push_back(it.first);++  // TODO: Is there a more efficient way?+  llvm::sort(posToParam.begin(), posToParam.end(),+             [&](const mlir::Value &L, const mlir::Value &R) {+               return paramMap[L].pos < paramMap[R].pos;+             });+}++/// Create a context relation from parameters and add it to the scop.+void addContextToScop(llvm::DenseMap<mlir::Value, DomainParameter> &paramMap,+                      OslScop &scop) {+  unsigned numParams = paramMap.size();+  unsigned numCols = 2 + numParams;++  std::vector<std::vector<int64_t>> ctxEqs, ctxInEqs;++  // We assume the context relation is built on inequalities generated by the+  // lower and upper bound of each parameter.+  unsigned i = 0; // The parameter ID.+  ctxInEqs.reserve(numParams * 2);+  for (auto const &it : paramMap) {+    DomainParameter param = it.second;+    assert((param.lb != llvm::None || param.ub != llvm::None) &&+           "At least one of lb and ub should not be None.");++    // Set the lower bound+    if (param.lb != llvm::None) {+      std::vector<int64_t> inEq(numCols - 1, 0);+      inEq[i] = 1;                              // The pos of the parameter.+      inEq[numCols - 2] = -param.lb.getValue(); // The constant.+      ctxInEqs.push_back(inEq);+    }+    // Set the upper bound+    if (param.ub != llvm::None) {+      std::vector<int64_t> inEq(numCols - 1, 0);+      inEq[i] = -1;                            // The pos of the parameter.+      inEq[numCols - 2] = param.ub.getValue(); // The constant.+      ctxInEqs.push_back(inEq);+    }++    i++;+  }++  // Assuming no equality.+  unsigned numRows = ctxInEqs.size();+  scop.addRelation(0, OSL_TYPE_CONTEXT, numRows, numCols, 0, 0, 0, numParams,+                   ctxEqs, ctxInEqs);+}++/// Swap the posA^th identifier with the posB^th identifier.+/// TODO: This is from AffineStructure.h, should be turned into a publicly+/// available API.+void swapId(FlatAffineConstraints *A, unsigned posA, unsigned posB) {+  assert(posA < A->getNumIds() && "invalid position A");+  assert(posB < A->getNumIds() && "invalid position B");++  if (posA == posB)+    return;++  for (unsigned r = 0, e = A->getNumInequalities(); r < e; r++) {+    std::swap(A->atIneq(r, posA), A->atIneq(r, posB));+  }+  for (unsigned r = 0, e = A->getNumEqualities(); r < e; r++) {+    std::swap(A->atEq(r, posA), A->atEq(r, posB));+  }+  std::swap(A->getId(posA), A->getId(posB));+}++/// Update the parameter (symbol) section of the domain constraints. We need to+/// make sure that all domains share the same set of parameters and they are+/// located at the same positions.+void updateDomainParams(+    FlatAffineConstraints &domain, SmallVectorImpl<mlir::Value> &posToParam,+    llvm::DenseMap<mlir::Value, DomainParameter> &paramMap) {+  unsigned offset = domain.getNumDimIds();+  unsigned numParams = paramMap.size();++  for (unsigned pos = 0; pos < numParams; pos++) {+    mlir::Value param = posToParam[pos];++    unsigned posInDomain;+    if (domain.findId(param, &posInDomain)) {+      // If there is such param in the domain, we should check whether its+      // position is right.+      if (posInDomain != offset + pos) {+        // posInDomain should be larger than pos. It is because all parameters+        // located before pos should already be placed correctly.+        assert(posInDomain > pos &&+               "posInDomain should be larger than pos if not equal.");+        swapId(&domain, offset + pos, posInDomain);+      }+    } else {+      domain.addSymbolId(pos, /*id=*/param);+    }+  }+}++} // namespace++namespace {++/// Get a clone of the elements in the equalities or inequalities of+/// FlatAffineConstraints.+void getEqualities(FlatAffineConstraints &cst,+                   std::vector<std::vector<int64_t>> &eqs, bool isEq = true) {+  unsigned numEqualities =+      isEq ? cst.getNumEqualities() : cst.getNumInequalities();+  unsigned numDimIds = cst.getNumDimIds();+  unsigned numLocalIds = cst.getNumLocalIds();+  unsigned numSymbolIds = cst.getNumSymbolIds();++  eqs.resize(numEqualities);+  for (unsigned i = 0; i < numEqualities; i++) {+    auto eq = isEq ? cst.getEquality(i) : cst.getInequality(i);+    unsigned numCols = eq.size();+    eqs[i].resize(numCols);++    // Dims stay at the same positions.+    for (unsigned j = 0; j < numDimIds; j++)+      eqs[i][j] = eq[j];+    // Output local ids before symbols.+    for (unsigned j = 0; j < numLocalIds; j++)+      eqs[i][j + numDimIds] = eq[j + numDimIds + numSymbolIds];+    // Output symbols in the end.+    for (unsigned j = 0; j < numSymbolIds; j++)+      eqs[i][j + numDimIds + numLocalIds] = eq[j + numDimIds];+    eqs[i][numCols - 1] = eq[numCols - 1];+  }+}++/// Gather information from the domain FlatAffineConstraints and put them into+/// the scop as a DOMAIN relation. index gives the statement id.+void addDomainToScop(unsigned index, FlatAffineConstraints &domain,+                     OslScop &scop) {+  // First we clone the equalities and inequalities from the domain constraints.+  std::vector<std::vector<int64_t>> eqs, inEqs;+  getEqualities(domain, eqs);+  getEqualities(domain, inEqs, /*isEq=*/false);++  // Then put them into the scop as a DOMAIN relation.+  scop.addRelation(index + 1, OSL_TYPE_DOMAIN, domain.getNumConstraints(),+                   domain.getNumCols() + 1, domain.getNumDimIds(), 0,+                   domain.getNumLocalIds(), domain.getNumSymbolIds(), eqs,+                   inEqs);+}++} // namespace++namespace {+/// Tree that holds scattering information. This node can represent an induction+/// variable or a statement. A statement is constructed as a leaf node.+class ScatteringTreeNode {+public:+  ScatteringTreeNode(bool isLeaf = false) : isLeaf(isLeaf) {}+  ScatteringTreeNode(mlir::Value iv) : iv(iv), isLeaf(false) {}++  /// Children of the current node.+  std::vector<std::unique_ptr<ScatteringTreeNode>> children;++  /// Mapping from IV to child ID.+  llvm::DenseMap<mlir::Value, unsigned> valueIdMap;++  /// Induction variable.+  mlir::Value iv;++  /// If this node is a statement, then isLeaf is true.+  bool isLeaf;+};++/// Insert a statement characterized by its enclosing operations into a+/// "scattering tree". This is done by iterating through every enclosing for-op+/// from the outermost to the innermost, and we try to traverse the tree by the+/// IVs of these ops. If an IV does not exist, we will insert it into the tree.+/// After that, we insert the current load/store statement into the tree as a+/// leaf. In this progress, we keep track of all the IDs of each child we meet+/// and the final leaf node, which will be used as the scattering.+void insertStatement(ScatteringTreeNode *root,+                     SmallVectorImpl<Operation *> &enclosingOps,+                     SmallVectorImpl<unsigned> &scattering) {+  ScatteringTreeNode *curr = root;++  for (unsigned i = 0, e = enclosingOps.size(); i < e; i++) {+    Operation *op = enclosingOps[i];+    // We only handle for op here.+    // TODO: is it necessary to deal with if?+    if (auto forOp = dyn_cast<AffineForOp>(op)) {+      SmallVector<mlir::Value, 4> indices;+      extractForInductionVars(forOp, &indices);++      for (auto iv : indices) {+        auto it = curr->valueIdMap.find(iv);+        if (it != curr->valueIdMap.end()) {+          // Add a new element to the scattering.+          scattering.push_back(it->second);+          // move to the next IV along the tree.+          curr = curr->children[it->second].get();+        } else {+          // No existing node for such IV is found, create a new one.+          auto node = std::make_unique<ScatteringTreeNode>(iv);++          // Then insert the newly created node into the children set, update+          // the value to child ID map, and move the cursor to this new node.+          curr->children.push_back(std::move(node));+          unsigned valueId = curr->children.size() - 1;+          curr->valueIdMap[iv] = valueId;+          scattering.push_back(valueId);+          curr = curr->children.back().get();+        }+      }+    }+  }++  // Append the leaf node for statement+  auto leaf = std::make_unique<ScatteringTreeNode>(/*isLeaf=*/true);+  curr->children.push_back(std::move(leaf));+  scattering.push_back(curr->children.size() - 1);+}++void addScatteringToScop(unsigned index, ArrayRef<unsigned> scattering,+                         FlatAffineConstraints &domain, OslScop &scop) {+  // Elements (N of them) in `scattering` are constants, and there are IVs+  // interleaved them. Therefore, we have 2N - 1 number of scattering+  // equalities.+  unsigned numScatteringEqualities = scattering.size() * 2 - 1;+  // Columns include new scattering dimensions and those from the domain.+  unsigned numScatteringCols =+      numScatteringEqualities + domain.getNumCols() + 1;++  // Create equalities and inequalities.+  std::vector<std::vector<int64_t>> eqs, inEqs;++  // Initialize contents for equalities.+  eqs.resize(numScatteringEqualities);+  for (unsigned j = 0; j < numScatteringEqualities; j++) {+    eqs[j].resize(numScatteringCols - 1);++    // Initializing scattering dimensions by setting the diagonal to -1.+    for (unsigned k = 0; k < numScatteringEqualities; k++)+      eqs[j][k] = -static_cast<int64_t>(k == j);++    // Relating the loop IVs to the scattering dimensions. If it's the odd+    // equality, set its scattering dimension to the loop IV; otherwise, it's+    // scattering dimension will be set in the following constant section.+    for (unsigned k = 0; k < domain.getNumDimIds(); k++)+      eqs[j][k + numScatteringEqualities] = (j % 2) ? (k == (j / 2)) : 0;++    // TODO: consider the parameters that may appear in the scattering+    // dimension.+    for (unsigned k = 0; k < domain.getNumLocalIds() + domain.getNumSymbolIds();+         k++)+      eqs[j][k + numScatteringEqualities + domain.getNumDimIds()] = 0;++    // Relating the constants (the last column) to the scattering dimensions.+    eqs[j][numScatteringCols - 2] = (j % 2) ? 0 : scattering[j / 2];+  }++  // Then put them into the scop as a SCATTERING relation.+  scop.addRelation(index + 1, OSL_TYPE_SCATTERING, numScatteringEqualities,+                   numScatteringCols, numScatteringEqualities,+                   domain.getNumDimIds(), domain.getNumLocalIds(),+                   domain.getNumSymbolIds(), eqs, inEqs);+}+} // namespace++namespace {++/// Generate the access relation and add it to the scop.+void addAccessToScop(unsigned index, unsigned memrefId, bool isRead,+                     ArrayRef<SmallVector<int64_t, 8>> flatExprs,+                     FlatAffineConstraints &domain, OslScop &scop) {+  // Number of equalities equals to the number of enclosing loop indices+  // plus 1 (the array itself).+  unsigned numAccessEqualities = domain.getNumDimIds() + 1;+  unsigned numAccessCols = domain.getNumCols() + numAccessEqualities + 1;+  unsigned numDimIds = domain.getNumDimIds();+  unsigned numSymbolIds = domain.getNumSymbolIds();+  unsigned numLocalIds = domain.getNumLocalIds();++  // Create equalities and inequalities.+  std::vector<std::vector<int64_t>> eqs, inEqs;++  eqs.resize(numAccessEqualities);+  for (unsigned i = 0; i < numAccessEqualities; i++) {+    // We leave alone the first column that indicates whether it is an equality+    // or not.+    eqs[i].resize(numAccessCols - 1);++    // The first section of a diagonal square matrix that points which axis the+    // current access relation is working on.+    for (unsigned j = 0; j < numAccessEqualities; j++)+      eqs[i][j] = -static_cast<int64_t>(i == j);++    // Set up the relation betwene the memref access position and the loop IVs.+    if (i == 0) {+      // The first row sets the array ID to the memref ID.+      for (unsigned j = 0; j < domain.getNumCols() - 1; j++)+        eqs[i][j + numAccessEqualities] = 0;+      eqs[i][numAccessCols - 2] = memrefId;+    } else {+      unsigned numFlatExprCols = flatExprs[i - 1].size();+      // Put the coefficients in the flat exprs into the access relation.+      // Note that numFlatExprCols may be smaller than the number of columns in+      // the domain constraints, mainly because the flatExprs are not aligned+      // with the position in domain constraints. Therefore, for those cases+      // that numFlatExprCols equals to the number of columns in the domain+      // constraint, we take a special approach to handle the mis-placed local+      // IDs; otherwise, we just place what in the flatExpr into the access+      // equalities.+      // TODO: properly handle local vars in the access equalities.+      if (numFlatExprCols == numDimIds + numLocalIds + numSymbolIds + 1) {+        // Input dims.+        for (unsigned j = 0; j < numDimIds; j++)+          eqs[i][j + numAccessEqualities] = flatExprs[i - 1][j];+        // Local dims.+        for (unsigned j = 0; j < numLocalIds; j++)+          eqs[i][j + numAccessEqualities + numDimIds] =+              flatExprs[i - 1][j + numDimIds + numSymbolIds];+        // Parameters.+        for (unsigned j = 0; j < numSymbolIds; j++)+          eqs[i][j + numAccessEqualities + numDimIds + numLocalIds] =+              flatExprs[i - 1][j + numDimIds];+        // Constant.+        eqs[i][numAccessEqualities + numFlatExprCols - 1] =+            flatExprs[i - 1][numFlatExprCols - 1];+      } else {+        for (unsigned j = 0; j < numFlatExprCols; j++)+          eqs[i][j + numAccessEqualities] = flatExprs[i - 1][j];+      }+    }+  }++  // Then put them into the scop as a ACCESS relation.+  scop.addRelation(index + 1, isRead ? OSL_TYPE_READ : OSL_TYPE_WRITE,+                   numAccessEqualities, numAccessCols, numAccessEqualities,+                   domain.getNumDimIds(), domain.getNumLocalIds(),+                   domain.getNumSymbolIds(), eqs, inEqs);+}+} // namespace++namespace {+/// This class maintains the state of a working emitter.+class OpenScopEmitterState {+public:+  explicit OpenScopEmitterState(raw_ostream &os) : os(os) {}++  /// The stream to emit to.+  raw_ostream &os;++  bool encounteredError = false;+  unsigned currentIdent = 0; // TODO: may not need this.++private:+  OpenScopEmitterState(const OpenScopEmitterState &) = delete;+  void operator=(const OpenScopEmitterState &) = delete;+};++/// Base class for various OpenScop emitters.+class OpenScopEmitterBase {+public:+  explicit OpenScopEmitterBase(OpenScopEmitterState &state)+      : state(state), os(state.os) {}++  InFlightDiagnostic emitError(Operation *op, const Twine &message) {+    state.encounteredError = true;+    return op->emitError(message);+  }++  InFlightDiagnostic emitOpError(Operation *op, const Twine &message) {+    state.encounteredError = true;+    return op->emitOpError(message);+  }++  /// All of the mutable state we are maintaining.+  OpenScopEmitterState &state;++  /// The stream to emit to.+  raw_ostream &os;++private:+  OpenScopEmitterBase(const OpenScopEmitterBase &) = delete;+  void operator=(const OpenScopEmitterBase &) = delete;+};++/// Emit OpenScop representation from an MLIR module.+class ModuleEmitter : public OpenScopEmitterBase {+public:+  explicit ModuleEmitter(OpenScopEmitterState &state)+      : OpenScopEmitterBase(state) {}++  /// Emit OpenScop definitions for all functions in the given module.+  void emitMLIRModule(ModuleOp module);++private:+  /// Emit a OpenScop definition for a single function.+  LogicalResult emitFuncOp(FuncOp func);+};++LogicalResult ModuleEmitter::emitFuncOp(mlir::FuncOp func) {+  // Initialize ja new Scop per FuncOp.+  OslScop scop;++  // We iterate through every operations in the function and extrat load/store

Typos: every operation, extract

kumasento

comment created time in a month

Pull request review commentkumasento/polymer

Emitting OpenSCoP from MLIR affine

+#include "polymer/EmitOpenScop.h"+#include "polymer/OslScop.h"++#include "mlir/Analysis/AffineAnalysis.h"+#include "mlir/Analysis/AffineStructures.h"+#include "mlir/Analysis/LoopAnalysis.h"+#include "mlir/Analysis/Utils.h"+#include "mlir/Dialect/Affine/IR/AffineOps.h"+#include "mlir/Dialect/Affine/IR/AffineValueMap.h"+#include "mlir/Dialect/Affine/Passes.h"+#include "mlir/Dialect/StandardOps/IR/Ops.h"+#include "mlir/IR/BlockAndValueMapping.h"+#include "mlir/IR/Builders.h"+#include "mlir/IR/Function.h"+#include "mlir/IR/Module.h"+#include "mlir/IR/StandardTypes.h"+#include "mlir/Transforms/LoopUtils.h"+#include "mlir/Transforms/Utils.h"+#include "mlir/Translation.h"++#include "llvm/ADT/DenseMap.h"+#include "llvm/ADT/StringSet.h"+#include "llvm/Support/Debug.h"+#include "llvm/Support/Format.h"+#include "llvm/Support/FormatVariadic.h"+#include "llvm/Support/raw_ostream.h"++#include "osl/osl.h"++#include <memory>++using namespace mlir;+using namespace llvm;+using namespace polymer;++#define DEBUG_TYPE "emit-openscop"++namespace {++/// Extracted data from the domain constraints, will be used as the value of+/// paramMap when generating scop. lb and ub will be used to get the context+/// relation.+struct DomainParameter {+  unsigned pos; // Parameter column position in the constraints.+  llvm::Optional<int64_t> lb, ub; // Lower and upper bounds.++  DomainParameter() : pos(0), lb(llvm::None), ub(llvm::None) {}+  DomainParameter(unsigned pos, llvm::Optional<int64_t> lb,+                  llvm::Optional<int64_t> ub)+      : pos(pos), lb(lb), ub(ub) {}+};++/// Add parameters to the maintained parameter-ID map.+void addParamsToMap(FlatAffineConstraints &domain,+                    llvm::DenseMap<mlir::Value, DomainParameter> &paramMap) {+  // Get symbol values from the domain constraint.+  SmallVector<mlir::Value, 8> values;+  unsigned offset = domain.getNumDimIds();+  domain.getIdValues(offset, domain.getNumDimAndSymbolIds(), &values);++  // Insert the value into the map if there isn't one.+  for (int i = 0, e = values.size(); i < e; i++) {+    mlir::Value value = values[i];+    auto lb = domain.getConstantLowerBound(i + offset);+    auto ub = domain.getConstantUpperBound(i + offset);+    LLVM_DEBUG(llvm::dbgs() << "Bound for parameter i=" << i << " : [" << lb+                            << ", " << ub << ")\n");++    auto it = paramMap.find(value);+    if (it == paramMap.end()) {+      paramMap[value] = DomainParameter(paramMap.size(), lb, ub);+    }+  }+}++/// Build the mapping from position to parameters.+void buildPosToParamMap(llvm::DenseMap<mlir::Value, DomainParameter> &paramMap,+                        SmallVectorImpl<mlir::Value> &posToParam) {+  unsigned numParam = paramMap.size();+  posToParam.reserve(numParam);++  for (auto const &it : paramMap)+    posToParam.push_back(it.first);++  // TODO: Is there a more efficient way?+  llvm::sort(posToParam.begin(), posToParam.end(),+             [&](const mlir::Value &L, const mlir::Value &R) {+               return paramMap[L].pos < paramMap[R].pos;+             });+}++/// Create a context relation from parameters and add it to the scop.+void addContextToScop(llvm::DenseMap<mlir::Value, DomainParameter> &paramMap,+                      OslScop &scop) {+  unsigned numParams = paramMap.size();+  unsigned numCols = 2 + numParams;++  std::vector<std::vector<int64_t>> ctxEqs, ctxInEqs;++  // We assume the context relation is built on inequalities generated by the+  // lower and upper bound of each parameter.+  unsigned i = 0; // The parameter ID.+  ctxInEqs.reserve(numParams * 2);+  for (auto const &it : paramMap) {+    DomainParameter param = it.second;+    assert((param.lb != llvm::None || param.ub != llvm::None) &&+           "At least one of lb and ub should not be None.");++    // Set the lower bound+    if (param.lb != llvm::None) {+      std::vector<int64_t> inEq(numCols - 1, 0);+      inEq[i] = 1;                              // The pos of the parameter.+      inEq[numCols - 2] = -param.lb.getValue(); // The constant.+      ctxInEqs.push_back(inEq);+    }+    // Set the upper bound+    if (param.ub != llvm::None) {+      std::vector<int64_t> inEq(numCols - 1, 0);+      inEq[i] = -1;                            // The pos of the parameter.+      inEq[numCols - 2] = param.ub.getValue(); // The constant.+      ctxInEqs.push_back(inEq);+    }++    i++;+  }++  // Assuming no equality.+  unsigned numRows = ctxInEqs.size();+  scop.addRelation(0, OSL_TYPE_CONTEXT, numRows, numCols, 0, 0, 0, numParams,+                   ctxEqs, ctxInEqs);+}++/// Swap the posA^th identifier with the posB^th identifier.+/// TODO: This is from AffineStructure.h, should be turned into a publicly

Please send a patch upstream :)

kumasento

comment created time in a month

Pull request review commentkumasento/polymer

Emitting OpenSCoP from MLIR affine

+#include "polymer/EmitOpenScop.h"+#include "polymer/OslScop.h"++#include "mlir/Analysis/AffineAnalysis.h"+#include "mlir/Analysis/AffineStructures.h"+#include "mlir/Analysis/LoopAnalysis.h"+#include "mlir/Analysis/Utils.h"+#include "mlir/Dialect/Affine/IR/AffineOps.h"+#include "mlir/Dialect/Affine/IR/AffineValueMap.h"+#include "mlir/Dialect/Affine/Passes.h"+#include "mlir/Dialect/StandardOps/IR/Ops.h"+#include "mlir/IR/BlockAndValueMapping.h"+#include "mlir/IR/Builders.h"+#include "mlir/IR/Function.h"+#include "mlir/IR/Module.h"+#include "mlir/IR/StandardTypes.h"+#include "mlir/Transforms/LoopUtils.h"+#include "mlir/Transforms/Utils.h"+#include "mlir/Translation.h"++#include "llvm/ADT/DenseMap.h"+#include "llvm/ADT/StringSet.h"+#include "llvm/Support/Debug.h"+#include "llvm/Support/Format.h"+#include "llvm/Support/FormatVariadic.h"+#include "llvm/Support/raw_ostream.h"++#include "osl/osl.h"++#include <memory>++using namespace mlir;+using namespace llvm;+using namespace polymer;++#define DEBUG_TYPE "emit-openscop"++namespace {++/// Extracted data from the domain constraints, will be used as the value of+/// paramMap when generating scop. lb and ub will be used to get the context+/// relation.+struct DomainParameter {+  unsigned pos; // Parameter column position in the constraints.+  llvm::Optional<int64_t> lb, ub; // Lower and upper bounds.++  DomainParameter() : pos(0), lb(llvm::None), ub(llvm::None) {}+  DomainParameter(unsigned pos, llvm::Optional<int64_t> lb,+                  llvm::Optional<int64_t> ub)+      : pos(pos), lb(lb), ub(ub) {}+};++/// Add parameters to the maintained parameter-ID map.+void addParamsToMap(FlatAffineConstraints &domain,+                    llvm::DenseMap<mlir::Value, DomainParameter> &paramMap) {+  // Get symbol values from the domain constraint.+  SmallVector<mlir::Value, 8> values;+  unsigned offset = domain.getNumDimIds();+  domain.getIdValues(offset, domain.getNumDimAndSymbolIds(), &values);++  // Insert the value into the map if there isn't one.+  for (int i = 0, e = values.size(); i < e; i++) {+    mlir::Value value = values[i];+    auto lb = domain.getConstantLowerBound(i + offset);+    auto ub = domain.getConstantUpperBound(i + offset);+    LLVM_DEBUG(llvm::dbgs() << "Bound for parameter i=" << i << " : [" << lb+                            << ", " << ub << ")\n");++    auto it = paramMap.find(value);+    if (it == paramMap.end()) {+      paramMap[value] = DomainParameter(paramMap.size(), lb, ub);+    }+  }+}++/// Build the mapping from position to parameters.+void buildPosToParamMap(llvm::DenseMap<mlir::Value, DomainParameter> &paramMap,+                        SmallVectorImpl<mlir::Value> &posToParam) {+  unsigned numParam = paramMap.size();+  posToParam.reserve(numParam);++  for (auto const &it : paramMap)+    posToParam.push_back(it.first);++  // TODO: Is there a more efficient way?

Resize the vector to the largest position, then something like

for (auto key : paramMap)
  posToParam[paramMap[key].pos]] = key;

This assumes paramMap contains domain parameters with all contiguous positions from 0 to N, but it appears to me that so does the logic of this function. This is worth documenting, and may be even asserting under #ifndef NDEBUG if necessary.

kumasento

comment created time in a month

Pull request review commentkumasento/polymer

Emitting OpenSCoP from MLIR affine

+#include "polymer/EmitOpenScop.h"+#include "polymer/OslScop.h"++#include "mlir/Analysis/AffineAnalysis.h"+#include "mlir/Analysis/AffineStructures.h"+#include "mlir/Analysis/LoopAnalysis.h"+#include "mlir/Analysis/Utils.h"+#include "mlir/Dialect/Affine/IR/AffineOps.h"+#include "mlir/Dialect/Affine/IR/AffineValueMap.h"+#include "mlir/Dialect/Affine/Passes.h"+#include "mlir/Dialect/StandardOps/IR/Ops.h"+#include "mlir/IR/BlockAndValueMapping.h"+#include "mlir/IR/Builders.h"+#include "mlir/IR/Function.h"+#include "mlir/IR/Module.h"+#include "mlir/IR/StandardTypes.h"+#include "mlir/Transforms/LoopUtils.h"+#include "mlir/Transforms/Utils.h"+#include "mlir/Translation.h"++#include "llvm/ADT/DenseMap.h"+#include "llvm/ADT/StringSet.h"+#include "llvm/Support/Debug.h"+#include "llvm/Support/Format.h"+#include "llvm/Support/FormatVariadic.h"+#include "llvm/Support/raw_ostream.h"++#include "osl/osl.h"++#include <memory>++using namespace mlir;+using namespace llvm;+using namespace polymer;++#define DEBUG_TYPE "emit-openscop"++namespace {++/// Extracted data from the domain constraints, will be used as the value of+/// paramMap when generating scop. lb and ub will be used to get the context+/// relation.+struct DomainParameter {+  unsigned pos; // Parameter column position in the constraints.+  llvm::Optional<int64_t> lb, ub; // Lower and upper bounds.++  DomainParameter() : pos(0), lb(llvm::None), ub(llvm::None) {}+  DomainParameter(unsigned pos, llvm::Optional<int64_t> lb,+                  llvm::Optional<int64_t> ub)+      : pos(pos), lb(lb), ub(ub) {}+};++/// Add parameters to the maintained parameter-ID map.+void addParamsToMap(FlatAffineConstraints &domain,+                    llvm::DenseMap<mlir::Value, DomainParameter> &paramMap) {+  // Get symbol values from the domain constraint.+  SmallVector<mlir::Value, 8> values;+  unsigned offset = domain.getNumDimIds();+  domain.getIdValues(offset, domain.getNumDimAndSymbolIds(), &values);++  // Insert the value into the map if there isn't one.+  for (int i = 0, e = values.size(); i < e; i++) {+    mlir::Value value = values[i];+    auto lb = domain.getConstantLowerBound(i + offset);+    auto ub = domain.getConstantUpperBound(i + offset);+    LLVM_DEBUG(llvm::dbgs() << "Bound for parameter i=" << i << " : [" << lb+                            << ", " << ub << ")\n");++    auto it = paramMap.find(value);+    if (it == paramMap.end()) {+      paramMap[value] = DomainParameter(paramMap.size(), lb, ub);+    }+  }+}++/// Build the mapping from position to parameters.+void buildPosToParamMap(llvm::DenseMap<mlir::Value, DomainParameter> &paramMap,+                        SmallVectorImpl<mlir::Value> &posToParam) {+  unsigned numParam = paramMap.size();+  posToParam.reserve(numParam);++  for (auto const &it : paramMap)+    posToParam.push_back(it.first);++  // TODO: Is there a more efficient way?+  llvm::sort(posToParam.begin(), posToParam.end(),+             [&](const mlir::Value &L, const mlir::Value &R) {+               return paramMap[L].pos < paramMap[R].pos;+             });+}++/// Create a context relation from parameters and add it to the scop.+void addContextToScop(llvm::DenseMap<mlir::Value, DomainParameter> &paramMap,+                      OslScop &scop) {+  unsigned numParams = paramMap.size();+  unsigned numCols = 2 + numParams;++  std::vector<std::vector<int64_t>> ctxEqs, ctxInEqs;++  // We assume the context relation is built on inequalities generated by the+  // lower and upper bound of each parameter.+  unsigned i = 0; // The parameter ID.+  ctxInEqs.reserve(numParams * 2);+  for (auto const &it : paramMap) {+    DomainParameter param = it.second;+    assert((param.lb != llvm::None || param.ub != llvm::None) &&+           "At least one of lb and ub should not be None.");++    // Set the lower bound+    if (param.lb != llvm::None) {+      std::vector<int64_t> inEq(numCols - 1, 0);+      inEq[i] = 1;                              // The pos of the parameter.+      inEq[numCols - 2] = -param.lb.getValue(); // The constant.+      ctxInEqs.push_back(inEq);+    }+    // Set the upper bound+    if (param.ub != llvm::None) {+      std::vector<int64_t> inEq(numCols - 1, 0);+      inEq[i] = -1;                            // The pos of the parameter.+      inEq[numCols - 2] = param.ub.getValue(); // The constant.+      ctxInEqs.push_back(inEq);+    }++    i++;+  }++  // Assuming no equality.+  unsigned numRows = ctxInEqs.size();+  scop.addRelation(0, OSL_TYPE_CONTEXT, numRows, numCols, 0, 0, 0, numParams,+                   ctxEqs, ctxInEqs);+}++/// Swap the posA^th identifier with the posB^th identifier.+/// TODO: This is from AffineStructure.h, should be turned into a publicly+/// available API.+void swapId(FlatAffineConstraints *A, unsigned posA, unsigned posB) {+  assert(posA < A->getNumIds() && "invalid position A");+  assert(posB < A->getNumIds() && "invalid position B");++  if (posA == posB)+    return;++  for (unsigned r = 0, e = A->getNumInequalities(); r < e; r++) {

Avoid trivial braces here and below (the conditional above actually does). Or always use braces, but let's be consistent about this.

kumasento

comment created time in a month

Pull request review commentkumasento/polymer

Emitting OpenSCoP from MLIR affine

+#include "polymer/EmitOpenScop.h"+#include "polymer/OslScop.h"++#include "mlir/Analysis/AffineAnalysis.h"+#include "mlir/Analysis/AffineStructures.h"+#include "mlir/Analysis/LoopAnalysis.h"+#include "mlir/Analysis/Utils.h"+#include "mlir/Dialect/Affine/IR/AffineOps.h"+#include "mlir/Dialect/Affine/IR/AffineValueMap.h"+#include "mlir/Dialect/Affine/Passes.h"+#include "mlir/Dialect/StandardOps/IR/Ops.h"+#include "mlir/IR/BlockAndValueMapping.h"+#include "mlir/IR/Builders.h"+#include "mlir/IR/Function.h"+#include "mlir/IR/Module.h"+#include "mlir/IR/StandardTypes.h"+#include "mlir/Transforms/LoopUtils.h"+#include "mlir/Transforms/Utils.h"+#include "mlir/Translation.h"++#include "llvm/ADT/DenseMap.h"+#include "llvm/ADT/StringSet.h"+#include "llvm/Support/Debug.h"+#include "llvm/Support/Format.h"+#include "llvm/Support/FormatVariadic.h"+#include "llvm/Support/raw_ostream.h"++#include "osl/osl.h"++#include <memory>++using namespace mlir;+using namespace llvm;+using namespace polymer;++#define DEBUG_TYPE "emit-openscop"++namespace {++/// Extracted data from the domain constraints, will be used as the value of+/// paramMap when generating scop. lb and ub will be used to get the context+/// relation.+struct DomainParameter {+  unsigned pos; // Parameter column position in the constraints.+  llvm::Optional<int64_t> lb, ub; // Lower and upper bounds.++  DomainParameter() : pos(0), lb(llvm::None), ub(llvm::None) {}+  DomainParameter(unsigned pos, llvm::Optional<int64_t> lb,+                  llvm::Optional<int64_t> ub)+      : pos(pos), lb(lb), ub(ub) {}+};++/// Add parameters to the maintained parameter-ID map.+void addParamsToMap(FlatAffineConstraints &domain,+                    llvm::DenseMap<mlir::Value, DomainParameter> &paramMap) {+  // Get symbol values from the domain constraint.+  SmallVector<mlir::Value, 8> values;+  unsigned offset = domain.getNumDimIds();+  domain.getIdValues(offset, domain.getNumDimAndSymbolIds(), &values);++  // Insert the value into the map if there isn't one.+  for (int i = 0, e = values.size(); i < e; i++) {+    mlir::Value value = values[i];+    auto lb = domain.getConstantLowerBound(i + offset);+    auto ub = domain.getConstantUpperBound(i + offset);+    LLVM_DEBUG(llvm::dbgs() << "Bound for parameter i=" << i << " : [" << lb+                            << ", " << ub << ")\n");++    auto it = paramMap.find(value);+    if (it == paramMap.end()) {+      paramMap[value] = DomainParameter(paramMap.size(), lb, ub);+    }+  }+}++/// Build the mapping from position to parameters.+void buildPosToParamMap(llvm::DenseMap<mlir::Value, DomainParameter> &paramMap,

Here and below, if the function does not intend to modify its arguments, they should be const references rather than just references.

kumasento

comment created time in a month

Pull request review commentkumasento/polymer

Emitting OpenSCoP from MLIR affine

+#ifndef POLYMER_EMITOPENSCOP_H+#define POLYMER_EMITOPENSCOP_H++#include "mlir/Support/LLVM.h"

Is this include necessary?

kumasento

comment created time in a month

Pull request review commentkumasento/polymer

Emitting OpenSCoP from MLIR affine

+#include "polymer/EmitOpenScop.h"+#include "polymer/OslScop.h"++#include "mlir/Analysis/AffineAnalysis.h"+#include "mlir/Analysis/AffineStructures.h"+#include "mlir/Analysis/LoopAnalysis.h"+#include "mlir/Analysis/Utils.h"+#include "mlir/Dialect/Affine/IR/AffineOps.h"+#include "mlir/Dialect/Affine/IR/AffineValueMap.h"+#include "mlir/Dialect/Affine/Passes.h"+#include "mlir/Dialect/StandardOps/IR/Ops.h"+#include "mlir/IR/BlockAndValueMapping.h"+#include "mlir/IR/Builders.h"+#include "mlir/IR/Function.h"+#include "mlir/IR/Module.h"+#include "mlir/IR/StandardTypes.h"+#include "mlir/Transforms/LoopUtils.h"+#include "mlir/Transforms/Utils.h"+#include "mlir/Translation.h"++#include "llvm/ADT/DenseMap.h"+#include "llvm/ADT/StringSet.h"+#include "llvm/Support/Debug.h"+#include "llvm/Support/Format.h"+#include "llvm/Support/FormatVariadic.h"+#include "llvm/Support/raw_ostream.h"++#include "osl/osl.h"++#include <memory>++using namespace mlir;+using namespace llvm;+using namespace polymer;++#define DEBUG_TYPE "emit-openscop"++namespace {++/// Extracted data from the domain constraints, will be used as the value of+/// paramMap when generating scop. lb and ub will be used to get the context+/// relation.+struct DomainParameter {+  unsigned pos; // Parameter column position in the constraints.+  llvm::Optional<int64_t> lb, ub; // Lower and upper bounds.++  DomainParameter() : pos(0), lb(llvm::None), ub(llvm::None) {}+  DomainParameter(unsigned pos, llvm::Optional<int64_t> lb,+                  llvm::Optional<int64_t> ub)+      : pos(pos), lb(lb), ub(ub) {}+};++/// Add parameters to the maintained parameter-ID map.+void addParamsToMap(FlatAffineConstraints &domain,+                    llvm::DenseMap<mlir::Value, DomainParameter> &paramMap) {+  // Get symbol values from the domain constraint.+  SmallVector<mlir::Value, 8> values;+  unsigned offset = domain.getNumDimIds();+  domain.getIdValues(offset, domain.getNumDimAndSymbolIds(), &values);++  // Insert the value into the map if there isn't one.+  for (int i = 0, e = values.size(); i < e; i++) {+    mlir::Value value = values[i];+    auto lb = domain.getConstantLowerBound(i + offset);+    auto ub = domain.getConstantUpperBound(i + offset);+    LLVM_DEBUG(llvm::dbgs() << "Bound for parameter i=" << i << " : [" << lb+                            << ", " << ub << ")\n");++    auto it = paramMap.find(value);+    if (it == paramMap.end()) {

Nit: LLVM style says one should elide braces around trivial statements.

kumasento

comment created time in a month

Pull request review commentkumasento/polymer

Emitting OpenSCoP from MLIR affine

+#include "polymer/EmitOpenScop.h"+#include "polymer/OslScop.h"++#include "mlir/Analysis/AffineAnalysis.h"+#include "mlir/Analysis/AffineStructures.h"+#include "mlir/Analysis/LoopAnalysis.h"+#include "mlir/Analysis/Utils.h"+#include "mlir/Dialect/Affine/IR/AffineOps.h"+#include "mlir/Dialect/Affine/IR/AffineValueMap.h"+#include "mlir/Dialect/Affine/Passes.h"+#include "mlir/Dialect/StandardOps/IR/Ops.h"+#include "mlir/IR/BlockAndValueMapping.h"+#include "mlir/IR/Builders.h"+#include "mlir/IR/Function.h"+#include "mlir/IR/Module.h"+#include "mlir/IR/StandardTypes.h"+#include "mlir/Transforms/LoopUtils.h"+#include "mlir/Transforms/Utils.h"+#include "mlir/Translation.h"++#include "llvm/ADT/DenseMap.h"+#include "llvm/ADT/StringSet.h"+#include "llvm/Support/Debug.h"+#include "llvm/Support/Format.h"+#include "llvm/Support/FormatVariadic.h"+#include "llvm/Support/raw_ostream.h"++#include "osl/osl.h"++#include <memory>++using namespace mlir;+using namespace llvm;+using namespace polymer;++#define DEBUG_TYPE "emit-openscop"++namespace {++/// Extracted data from the domain constraints, will be used as the value of+/// paramMap when generating scop. lb and ub will be used to get the context+/// relation.+struct DomainParameter {+  unsigned pos; // Parameter column position in the constraints.+  llvm::Optional<int64_t> lb, ub; // Lower and upper bounds.++  DomainParameter() : pos(0), lb(llvm::None), ub(llvm::None) {}+  DomainParameter(unsigned pos, llvm::Optional<int64_t> lb,+                  llvm::Optional<int64_t> ub)+      : pos(pos), lb(lb), ub(ub) {}+};++/// Add parameters to the maintained parameter-ID map.+void addParamsToMap(FlatAffineConstraints &domain,+                    llvm::DenseMap<mlir::Value, DomainParameter> &paramMap) {+  // Get symbol values from the domain constraint.+  SmallVector<mlir::Value, 8> values;+  unsigned offset = domain.getNumDimIds();+  domain.getIdValues(offset, domain.getNumDimAndSymbolIds(), &values);++  // Insert the value into the map if there isn't one.+  for (int i = 0, e = values.size(); i < e; i++) {+    mlir::Value value = values[i];+    auto lb = domain.getConstantLowerBound(i + offset);+    auto ub = domain.getConstantUpperBound(i + offset);+    LLVM_DEBUG(llvm::dbgs() << "Bound for parameter i=" << i << " : [" << lb+                            << ", " << ub << ")\n");++    auto it = paramMap.find(value);

DenseMap has a try_emplace method for this purpose.

kumasento

comment created time in a month

Pull request review commentkumasento/polymer

Emitting OpenSCoP from MLIR affine

+#include "polymer/EmitOpenScop.h"+#include "polymer/OslScop.h"++#include "mlir/Analysis/AffineAnalysis.h"+#include "mlir/Analysis/AffineStructures.h"+#include "mlir/Analysis/LoopAnalysis.h"+#include "mlir/Analysis/Utils.h"+#include "mlir/Dialect/Affine/IR/AffineOps.h"+#include "mlir/Dialect/Affine/IR/AffineValueMap.h"+#include "mlir/Dialect/Affine/Passes.h"+#include "mlir/Dialect/StandardOps/IR/Ops.h"+#include "mlir/IR/BlockAndValueMapping.h"+#include "mlir/IR/Builders.h"+#include "mlir/IR/Function.h"+#include "mlir/IR/Module.h"+#include "mlir/IR/StandardTypes.h"+#include "mlir/Transforms/LoopUtils.h"+#include "mlir/Transforms/Utils.h"+#include "mlir/Translation.h"++#include "llvm/ADT/DenseMap.h"+#include "llvm/ADT/StringSet.h"+#include "llvm/Support/Debug.h"+#include "llvm/Support/Format.h"+#include "llvm/Support/FormatVariadic.h"+#include "llvm/Support/raw_ostream.h"++#include "osl/osl.h"++#include <memory>++using namespace mlir;+using namespace llvm;+using namespace polymer;++#define DEBUG_TYPE "emit-openscop"++namespace {++/// Extracted data from the domain constraints, will be used as the value of+/// paramMap when generating scop. lb and ub will be used to get the context+/// relation.+struct DomainParameter {+  unsigned pos; // Parameter column position in the constraints.+  llvm::Optional<int64_t> lb, ub; // Lower and upper bounds.

Given the using namespace clauses above, there is no need to prefix anything with llvm:: or mlir:: in this file.

kumasento

comment created time in a month

Pull request review commentkumasento/polymer

Emitting OpenSCoP from MLIR affine

+#include "polymer/EmitOpenScop.h"+#include "polymer/OslScop.h"++#include "mlir/Analysis/AffineAnalysis.h"+#include "mlir/Analysis/AffineStructures.h"+#include "mlir/Analysis/LoopAnalysis.h"+#include "mlir/Analysis/Utils.h"+#include "mlir/Dialect/Affine/IR/AffineOps.h"+#include "mlir/Dialect/Affine/IR/AffineValueMap.h"+#include "mlir/Dialect/Affine/Passes.h"+#include "mlir/Dialect/StandardOps/IR/Ops.h"+#include "mlir/IR/BlockAndValueMapping.h"+#include "mlir/IR/Builders.h"+#include "mlir/IR/Function.h"+#include "mlir/IR/Module.h"+#include "mlir/IR/StandardTypes.h"+#include "mlir/Transforms/LoopUtils.h"+#include "mlir/Transforms/Utils.h"+#include "mlir/Translation.h"++#include "llvm/ADT/DenseMap.h"+#include "llvm/ADT/StringSet.h"+#include "llvm/Support/Debug.h"+#include "llvm/Support/Format.h"+#include "llvm/Support/FormatVariadic.h"+#include "llvm/Support/raw_ostream.h"++#include "osl/osl.h"++#include <memory>++using namespace mlir;+using namespace llvm;+using namespace polymer;++#define DEBUG_TYPE "emit-openscop"++namespace {++/// Extracted data from the domain constraints, will be used as the value of+/// paramMap when generating scop. lb and ub will be used to get the context+/// relation.+struct DomainParameter {+  unsigned pos; // Parameter column position in the constraints.

///< will make it a proper doxygen comment.

kumasento

comment created time in a month

Pull request review commentkumasento/polymer

Emitting OpenSCoP from MLIR affine

+#ifndef POLYMER_EMITOPENSCOP_H

All header and source files need a license header.

kumasento

comment created time in a month

Pull request review commentkumasento/polymer

Emitting OpenSCoP from MLIR affine

+#ifndef POLYMER_OSLSCOP_H+#define POLYMER_OSLSCOP_H++#include <cassert>+#include <cstdint>+#include <vector>++struct osl_scop;++namespace polymer {++/// A wrapper for the osl_scop struct in the openscop library.+class OslScop {+public:+  OslScop();+  ~OslScop();++  /// Print the content of the Scop to the stdout.+  void print();++  /// Validate whether the scop is well-formed.+  bool validate();++  /// Simply create a new statement in the linked list scop->statement.+  void createStatement();++  /// Create a new relation and initialize its contents. The new relation will+  /// be created under the scop member.+  /// The target here is an index:+  /// 1) if it's 0, then it means the context;+  /// 2) otherwise, if it is a positive number, it corresponds to a statement of+  /// id=(target-1).+  void addRelation(int target, int type, int numRows, int numCols,+                   int numOutputDims, int numInputDims, int numLocalDims,+                   int numParams, std::vector<std::vector<int64_t>> &eqs,+                   std::vector<std::vector<int64_t>> &inEqs);++private:+  struct osl_scop *scop;

No need for struct in C++.

kumasento

comment created time in a month

Pull request review commentkumasento/polymer

Emitting OpenSCoP from MLIR affine

+#ifndef POLYMER_OSLSCOP_H+#define POLYMER_OSLSCOP_H++#include <cassert>+#include <cstdint>+#include <vector>++struct osl_scop;++namespace polymer {++/// A wrapper for the osl_scop struct in the openscop library.+class OslScop {+public:+  OslScop();+  ~OslScop();++  /// Print the content of the Scop to the stdout.+  void print();++  /// Validate whether the scop is well-formed.+  bool validate();++  /// Simply create a new statement in the linked list scop->statement.+  void createStatement();++  /// Create a new relation and initialize its contents. The new relation will+  /// be created under the scop member.+  /// The target here is an index:+  /// 1) if it's 0, then it means the context;+  /// 2) otherwise, if it is a positive number, it corresponds to a statement of+  /// id=(target-1).+  void addRelation(int target, int type, int numRows, int numCols,+                   int numOutputDims, int numInputDims, int numLocalDims,+                   int numParams, std::vector<std::vector<int64_t>> &eqs,

Vectors-of-vectors are generally bad for cache behavior because of reallocation/copying of the outer vector.

I'm fine if this file is not intended to depend on any LLVM API and thus uses std::vector, but if it's okay for it to depend on LLVM, I'd much rather use ArrayRef so we can accept other things than vectors without allocaiton/copy.

kumasento

comment created time in a month

Pull request review commentkumasento/polymer

Emitting OpenSCoP from MLIR affine

+#ifndef POLYMER_OSLSCOP_H+#define POLYMER_OSLSCOP_H++#include <cassert>+#include <cstdint>+#include <vector>++struct osl_scop;++namespace polymer {++/// A wrapper for the osl_scop struct in the openscop library.+class OslScop {+public:+  OslScop();+  ~OslScop();++  /// Print the content of the Scop to the stdout.+  void print();++  /// Validate whether the scop is well-formed.+  bool validate();++  /// Simply create a new statement in the linked list scop->statement.+  void createStatement();++  /// Create a new relation and initialize its contents. The new relation will+  /// be created under the scop member.+  /// The target here is an index:+  /// 1) if it's 0, then it means the context;+  /// 2) otherwise, if it is a positive number, it corresponds to a statement of

I'd prefer separate methods or an overload with tag (empty named structs) as argument to magic numbers.

kumasento

comment created time in a month

Pull request review commentkumasento/polymer

Emitting OpenSCoP from MLIR affine

+cmake_minimum_required(VERSION 3.10)++if(POLICY CMP0068)+  cmake_policy(SET CMP0068 NEW)+  set(CMAKE_BUILD_WITH_INSTALL_NAME_DIR ON)+endif()++if(POLICY CMP0075)+  cmake_policy(SET CMP0075 NEW)+endif()++if(POLICY CMP0077)+  cmake_policy(SET CMP0077 NEW)+endif()

It would be helpful to add a comment explaining what these policies do.

kumasento

comment created time in a month

Pull request review commentkumasento/polymer

Emitting OpenSCoP from MLIR affine

+#ifndef POLYMER_OSLSCOP_H+#define POLYMER_OSLSCOP_H++#include <cassert>+#include <cstdint>+#include <vector>++struct osl_scop;++namespace polymer {++/// A wrapper for the osl_scop struct in the openscop library.+class OslScop {+public:+  OslScop();+  ~OslScop();++  /// Print the content of the Scop to the stdout.+  void print();++  /// Validate whether the scop is well-formed.+  bool validate();++  /// Simply create a new statement in the linked list scop->statement.+  void createStatement();++  /// Create a new relation and initialize its contents. The new relation will+  /// be created under the scop member.+  /// The target here is an index:+  /// 1) if it's 0, then it means the context;+  /// 2) otherwise, if it is a positive number, it corresponds to a statement of+  /// id=(target-1).+  void addRelation(int target, int type, int numRows, int numCols,+                   int numOutputDims, int numInputDims, int numLocalDims,+                   int numParams, std::vector<std::vector<int64_t>> &eqs,+                   std::vector<std::vector<int64_t>> &inEqs);

const vector & ?

kumasento

comment created time in a month

Pull request review commentkumasento/polymer

Emitting OpenSCoP from MLIR affine

 cd pluto && git submodule init && git submodule update make make test ```++Build this project:++```+mkdir build+cd build+cmake .. -DCMAKE_BUILD_TYPE=DEBUG -DMLIR_DIR=./llvm/build/lib/cmake/mlir -DLLVM_DIR=./llvm/build/lib/cmake/llvm -DLLVM_ENABLE_ASSERTIONS=ON  -G "Ninja"

This looks like Polymer is configured against a build directory of MLIR. It will be hard to distribute this way. I'd consider installing MLIR from source and making sure Polymer can use it.

kumasento

comment created time in a month

Pull request review commentkumasento/polymer

Emitting OpenSCoP from MLIR affine

+cmake_minimum_required(VERSION 3.10)++if(POLICY CMP0068)+  cmake_policy(SET CMP0068 NEW)+  set(CMAKE_BUILD_WITH_INSTALL_NAME_DIR ON)+endif()++if(POLICY CMP0075)+  cmake_policy(SET CMP0075 NEW)+endif()++if(POLICY CMP0077)+  cmake_policy(SET CMP0077 NEW)+endif()++project(polymer LANGUAGES CXX C)++set(CMAKE_CXX_STANDARD 14)+set(CMAKE_CXX_STANDARD_REQUIRED YES)++find_package(MLIR REQUIRED CONFIG)++message(STATUS "Using MLIRConfig.cmake in: ${MLIR_DIR}")+message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}")++set(LLVM_RUNTIME_OUTPUT_INTDIR ${CMAKE_BINARY_DIR}/bin)+set(LLVM_LIBRARY_OUTPUT_INTDIR ${CMAKE_BINARY_DIR}/lib)+set(MLIR_BINARY_DIR ${CMAKE_BINARY_DIR})++# Define the default arguments to use with 'lit', and an option for the user to+# override.+set(LIT_ARGS_DEFAULT "-sv")+if (MSVC_IDE OR XCODE)+  set(LIT_ARGS_DEFAULT "${LIT_ARGS_DEFAULT} --no-progress-bar")+endif()+set(LLVM_LIT_ARGS "${LIT_ARGS_DEFAULT}" CACHE STRING "Default options for lit")++# ------------------------------------------------- Dependencies+# OpenScop+# We should prevent the main CMakeLists.txt file in openscop to change the +# BUILD_SHARED_LIBS flag. +set(BUILD_SHARED_LIBS FALSE)+# This should add a new library osl.+add_subdirectory(openscop)+include_directories(${CMAKE_SOURCE_DIR}/openscop/include)

It's a good practice to quote all path expressions (some lines below do use quotes) in case there is whitespace or other special symbols involved.

kumasento

comment created time in a month

Pull request review commentkumasento/polymer

Emitting OpenSCoP from MLIR affine

 *.exe *.out *.app++# CMake+build/++# Editor+.vscode/

Ultra-nit: I'd configure the editor to add the trailing newline, most editors do and you'll risk spurious whitespace-only changes to the file if somebody else touches it.

kumasento

comment created time in a month

PullRequestReviewEvent
PullRequestReviewEvent

push eventllvm/llvm-project

Mars Saxman

commit sha d34df52377fda5452a8c244a8378957eaed66700

Implement FPToUI and UIToFP ops in standard dialect Add the unsigned complements to the existing FPToSI and SIToFP operations in the standard dialect, with one-to-one lowerings to the corresponding LLVM operations. Reviewed By: ftynse Differential Revision: https://reviews.llvm.org/D85557

view details

push time in a month

push eventllvm/llvm-project

Alex Zinenko

commit sha da562974628017ae92c451ca064fea5b59ad71a4

[mlir] expose standard attributes to C API Provide C API for MLIR standard attributes. Since standard attributes live under lib/IR in core MLIR, place the C APIs in the IR library as well (standard ops will go in a separate library). Affine map and integer set attributes are only exposed as placeholder types with IsA support due to the lack of C APIs for the corresponding types. Integer and floating point attribute APIs expecting APInt and APFloat are not exposed pending decision on how to support APInt and APFloat. Reviewed By: stellaraccident Differential Revision: https://reviews.llvm.org/D86143

view details

push time in a month

push eventllvm/llvm-project

Alex Zinenko

commit sha 0f95e73190c9a555c2917a2963eab128c4ba5395

[mlir] fix build after llvm made ElementCount constructor private The original patch (264afb9e6aebc98c353644dd0700bec808501cab) did not update subprojects.

view details

push time in a month

push eventllvm/llvm-project

Alex Zinenko

commit sha 74f577845e8174e255688589d845d43eacf3923f

[mlir] expose standard types to C API Provide C API for MLIR standard types. Since standard types live under lib/IR in core MLIR, place the C APIs in the IR library as well (standard ops will go into a separate library). This also defines a placeholder for affine maps that are necessary to construct a memref, but are not yet exposed to the C API. Reviewed By: stellaraccident Differential Revision: https://reviews.llvm.org/D86094

view details

push time in a month

push eventllvm/llvm-project

Alex Zinenko

commit sha 674f2df4fe0b6af901fc7c7e8bd3fb37e1e8516c

[mlir] Fix printing of unranked memrefs in non-default memory space The type printer was ignoring the memory space on unranked memrefs. Reviewed By: rriddle Differential Revision: https://reviews.llvm.org/D86096

view details

push time in a month

push eventllvm/llvm-project

Alex Zinenko

commit sha 47d185784d1b82da66da01fe59c05975d455f222

[mlir] Provide LLVMType::getPrimitiveSizeInBits This function is available on llvm::Type and has been used by some clients of the LLVM dialect before the transition. Implement the MLIR counterpart. Reviewed By: schweitz Differential Revision: https://reviews.llvm.org/D85847

view details

push time in a month

push eventllvm/llvm-project

Alex Zinenko

commit sha 9c4825ce282d30ea108e6371c15cb692060ff4f3

[mlir] do not use llvm.cmpxchg with floats According to the LLVM Language Reference, 'cmpxchg' accepts integer or pointer types. Several MLIR tests were using it with floats as it appears possible to programmatically construct and print such an instruction, but it cannot be parsed back. Use integers instead. Depends On D85899 Reviewed By: flaub, rriddle Differential Revision: https://reviews.llvm.org/D85900

view details

push time in a month

push eventllvm/llvm-project

Alex Zinenko

commit sha 168213f91c571352c56f432573513cba3f9ba61b

[mlir] Move data layout from LLVMDialect to module Op attributes Legacy implementation of the LLVM dialect in MLIR contained an instance of llvm::Module as it was required to parse LLVM IR types. The access to the data layout of this module was exposed to the users for convenience, but in practice this layout has always been the default one obtained by parsing an empty layout description string. Current implementation of the dialect no longer relies on wrapping LLVM IR types, but it kept an instance of DataLayout for compatibility. This effectively forces a single data layout to be used across all modules in a given MLIR context, which is not desirable. Remove DataLayout from the LLVM dialect and attach it as a module attribute instead. Since MLIR does not yet have support for data layouts, use the LLVM DataLayout in string form with verification inside MLIR. Introduce the layout when converting a module to the LLVM dialect and keep the default "" description for compatibility. This approach should be replaced with a proper MLIR-based data layout when it becomes available, but provides an immediate solution to compiling modules with different layouts, e.g. for GPUs. This removes the need for LLVMDialectImpl, which is also removed. Depends On D85650 Reviewed By: aartbik Differential Revision: https://reviews.llvm.org/D85652

view details

push time in a month

push eventllvm/llvm-project

Alex Zinenko

commit sha 874aef875d0cd04b33f25bb71b534cbb0d6220ae

[llvm] support graceful failure of DataLayout parsing Existing implementation always aborts on syntax errors in a DataLayout description. While this is meaningful for consuming textual IR modules, it is inconvenient for users that may need fine-grained control over the layout from, e.g., command-line options. Propagate errors through the parsing functions and only abort in the top-level parsing function instead. Reviewed By: mehdi_amini Differential Revision: https://reviews.llvm.org/D85650

view details

push time in a month

push eventwsmoses/MLIR-GPU

William S. Moses

commit sha c66e57c3f3ca8fc14f6185a9d30814f440f363c8

Fix Pet installation

view details

push time in a month

PR merged wsmoses/MLIR-GPU

Reviewers
Fix Pet installation
+1 -0

0 comment

1 changed file

wsmoses

pr closed time in a month

push eventllvm/llvm-project

Alex Zinenko

commit sha 339eba0805fb73da5cc3d29eb7cd1085306db54e

[mlir] do not emit bitcasts between structs in StandardToLLVM The convresion of memref cast operaitons from the Standard dialect to the LLVM dialect has been emitting bitcasts from a struct type to itself. Beyond being useless, such casts are invalid as bitcast does not operate on aggregate types. This kept working by accident because LLVM IR bitcast construction API skips the construction if types are equal before it verifies that the types are acceptable in a bitcast. Do not emit such bitcasts, the memref cast that only adds/erases size information is in fact a noop on the current descriptor as it always contains dynamic values for all sizes. Reviewed By: pifon2a Differential Revision: https://reviews.llvm.org/D85899

view details

push time in 2 months

push eventllvm/llvm-project

Alex Zinenko

commit sha 215c2df6478f65233f15f8978d4dd692fe60c757

[mlir] Mention mandatory RFC process for changes in Standard dialect We have been asking for this systematically, mention it in the documentation. Reviewed By: mehdi_amini Differential Revision: https://reviews.llvm.org/D85902

view details

push time in 2 months

push eventllvm/llvm-project

Alex Zinenko

commit sha 321aa19ec8ede62325b7e07d3fef4d12859275ab

[mlir] Expose printing functions in C API Provide printing functions for most IR objects in C API (except Region that does not have a `print` function, and Module that is expected to be printed as Operation instead). The printing is based on a callback that is called with chunks of the string representation and forwarded user-defined data. Reviewed By: stellaraccident, Jing, mehdi_amini Differential Revision: https://reviews.llvm.org/D85748

view details

push time in 2 months

push eventllvm/llvm-project

Alex Zinenko

commit sha af838584ec5bfd3487ea86a54966a27f73b7e113

[mlir] use intptr_t in C API Using intptr_t is a consensus for MLIR C API, but the change was missing from 75f239e9756b (that was using unsigned initially) due to a misrebase. Reviewed By: stellaraccident, mehdi_amini Differential Revision: https://reviews.llvm.org/D85751

view details

push time in 2 months

push eventllvm/llvm-project

Alex Zinenko

commit sha bae1517266bf4ce85a32390323fd463f28ae9d0c

[mlir] Add verification to LLVM dialect types Now that LLVM dialect types are implemented directly in the dialect, we can use MLIR hooks for verifying type construction invariants. Implement the verifiers and use them in the parser. Reviewed By: rriddle Differential Revision: https://reviews.llvm.org/D85663

view details

push time in 2 months

push eventllvm/llvm-project

Jing Pu

commit sha 69eb7e36aa3c71997811054bb31d4546b08bfff0

Free the memory allocated by mlirOperationStateAddXXX methods in mlirOperationCreate. Previously, the memory leaks on heap. Since the MlirOperationState is not intended to be used again after mlirOperationCreate, the patch simplify frees the memory in mlirOperationCreate instead of creating any new API. Reviewed By: ftynse Differential Revision: https://reviews.llvm.org/D85629

view details

push time in 2 months

push eventllvm/llvm-project

Alex Zinenko

commit sha 87a89e0f7753711ef5f2741a1494e7a44da99d21

[mlir] Remove llvm::LLVMContext and llvm::Module from mlir::LLVMDialectImpl Original modeling of LLVM IR types in the MLIR LLVM dialect had been wrapping LLVM IR types and therefore required the LLVMContext in which they were created to outlive them, which was solved by placing the LLVMContext inside the dialect and thus having the lifetime of MLIRContext. This has led to numerous issues caused by the lack of thread-safety of LLVMContext and the need to re-create LLVM IR modules, obtained by translating from MLIR, in different LLVM contexts to enable parallel compilation. Similarly, llvm::Module had been introduced to keep track of identified structure types that could not be modeled properly. A recent series of commits changed the modeling of LLVM IR types in the MLIR LLVM dialect so that it no longer wraps LLVM IR types and has no dependence on LLVMContext and changed the ownership model of the translated LLVM IR modules. Remove LLVMContext and LLVM modules from the implementation of MLIR LLVM dialect and clean up the remaining uses. The only part of LLVM IR that remains necessary for the LLVM dialect is the data layout. It should be moved from the dialect level to the module level and replaced with an MLIR-based representation to remove the dependency of the LLVMDialect on LLVM IR library. Reviewed By: rriddle Differential Revision: https://reviews.llvm.org/D85445

view details

push time in 2 months

push eventllvm/llvm-project

Alex Zinenko

commit sha 16b02253778caf1723b63c8ee482bb68ccae0a90

[mlir] do not require LLVMDialect in conversion from LLVM IR Historically, LLVMDialect has been required in the conversion from LLVM IR in order to be able to construct types. This is no longer necessary with the new type model and the dialect can be replaced with a local LLVM context. Reviewed By: rriddle, mehdi_amini Differential Revision: https://reviews.llvm.org/D85444

view details

push time in 2 months

push eventllvm/llvm-project

Alex Zinenko

commit sha db1c197bf8247d8dced41ae2f579168c7b54d9ef

[mlir] take LLVMContext in MLIR-to-LLVM-IR translation Due to the original type system implementation, LLVMDialect in MLIR contains an LLVMContext in which the relevant objects (types, metadata) are created. When an MLIR module using the LLVM dialect (and related intrinsic-based dialects NVVM, ROCDL, AVX512) is converted to LLVM IR, it could only live in the LLVMContext owned by the dialect. The type system no longer relies on the LLVMContext, so this limitation can be removed. Instead, translation functions now take a reference to an LLVMContext in which the LLVM IR module should be constructed. The caller of the translation functions is responsible for ensuring the same LLVMContext is not used concurrently as the translation no longer uses a dialect-wide context lock. As an additional bonus, this change removes the need to recreate the LLVM IR module in a different LLVMContext through printing and parsing back, decreasing the compilation overhead in JIT and GPU-kernel-to-blob passes. Reviewed By: rriddle, mehdi_amini Differential Revision: https://reviews.llvm.org/D85443

view details

push time in 2 months

push eventllvm/llvm-project

Alex Zinenko

commit sha 5446ec8507080ac075274a30c7cf25652d9a860f

[mlir] take MLIRContext instead of LLVMDialect in getters of LLVMType's Historical modeling of the LLVM dialect types had been wrapping LLVM IR types and therefore needed access to the instance of LLVMContext stored in the LLVMDialect. The new modeling does not rely on that and only needs the MLIRContext that is used for uniquing, similarly to other MLIR types. Change LLVMType::get<Kind>Ty functions to take `MLIRContext *` instead of `LLVMDialect *` as first argument. This brings the code base closer to completely removing the dependence on LLVMContext from the LLVMDialect, together with additional support for thread-safety of its use. Depends On D85371 Reviewed By: rriddle Differential Revision: https://reviews.llvm.org/D85372

view details

push time in 2 months

push eventllvm/llvm-project

Alex Zinenko

commit sha d3a9807674c1d7000bd5ec4028be399c81cbd098

[mlir] Remove most uses of LLVMDialect::getModule This prepares for the removal of llvm::Module and LLVMContext from the mlir::LLVMDialect. Reviewed By: rriddle Differential Revision: https://reviews.llvm.org/D85371

view details

push time in 2 months

push eventllvm/llvm-project

Alex Zinenko

commit sha b2ab375d1f08ab0faecc20f0340cb972f31010a7

[mlir] use the new stateful LLVM type translator by default Previous type model in the LLVM dialect did not support identified structure types properly and therefore could use stateless translations implemented as free functions. The new model supports identified structs and must keep track of the identified structure types present in the target context (LLVMContext or MLIRContext) to avoid creating duplicate structs due to LLVM's type auto-renaming. Expose the stateful type translation classes and use them during translation, storing the state as part of ModuleTranslation. Drop the test type translation mechanism that is no longer necessary and update the tests to exercise type translation as part of the main translation flow. Update the code in vector-to-LLVM dialect conversion that relied on stateless translation to use the new class in a stateless manner. Reviewed By: rriddle Differential Revision: https://reviews.llvm.org/D85297

view details

push time in 2 months

push eventllvm/llvm-project

Vincent Zhao

commit sha b727cfed5e765d099f47e6785f962695408977c0

[MLIR][LinAlg] Use AnyTypeOf for LinalgOperand for better error msg. Previously, `LinalgOperand` is defined with `Type<Or<..,>>`, which produces not very readable error messages when it is not matched, e.g., ``` 'linalg.generic' op operand #0 must be anonymous_326, but got .... ``` It is simply because the `description` property is not properly set. This diff switches to use `AnyTypeOf` for `LinalgOperand`, which automatically generates a description based on the allowed types provided. As a result, the error message now becomes: ``` 'linalg.generic' op operand #0 must be ranked tensor of any type values or strided memref of any type values, but got ... ``` Which is clearer and more informative. Reviewed By: nicolasvasilache Differential Revision: https://reviews.llvm.org/D84428

view details

push time in 2 months

push eventllvm/llvm-project

Alex Zinenko

commit sha 75f239e9756b157f209412268c5a50a69b1a4e74

[mlir] Initial version of C APIs Introduce an initial version of C API for MLIR core IR components: Value, Type, Attribute, Operation, Region, Block, Location. These APIs allow for both inspection and creation of the IR in the generic form and intended for wrapping in high-level library- and language-specific constructs. At this point, there is no stability guarantee provided for the API. Reviewed By: stellaraccident, lattner Differential Revision: https://reviews.llvm.org/D83310

view details

push time in 2 months

push eventllvm/llvm-project

Alex Zinenko

commit sha 4e491570b5ecff17d3ac7cf6dbb328d379cd4fb6

[mlir] Remove LLVMTypeTestDialect This dialect was introduced during the bring-up of the new LLVM dialect type system for testing purposes. The main LLVM dialect now uses the new type system and the test dialect is no longer necessary, so remove it. Reviewed By: rriddle Differential Revision: https://reviews.llvm.org/D85224

view details

push time in 2 months

push eventllvm/llvm-project

Alex Zinenko

commit sha bdb9295664aa2ea0ee195505a0ca78ea8e34e657

[mlir] Fix convert-to-llvmir.mlir test broken due to syntax change The syntax of the LLVM dialect types changed between the time the code was written and it was submitted, leading to a test failure. Update the syntax.

view details

push time in 2 months

push eventllvm/llvm-project

Arpith C. Jacob

commit sha fab4b59961aa35109861493dfe071979d56b4360

[mlir] Conversion of ViewOp with memory space to LLVM. Handle the case where the ViewOp takes in a memref that has an memory space. Reviewed By: ftynse, bondhugula, nicolasvasilache Differential Revision: https://reviews.llvm.org/D85048

view details

push time in 2 months

push eventllvm/llvm-project

Alex Zinenko

commit sha cb9f9df5f8239e291a62934b0f64eb795b26d84a

[mlir] Fix GCC5 compilation problem in MLIR->LLVM type translation GCC5 seems to dislike generic lambdas calling a method of the class containing the lambda without explicit `this`.

view details

push time in 2 months

push eventllvm/llvm-project

Alex Zinenko

commit sha ec1f4e7c3b17656658c9cf49c33bc06c4bc747c2

[mlir] switch the modeling of LLVM types to use the new mechanism A new first-party modeling for LLVM IR types in the LLVM dialect has been developed in parallel to the existing modeling based on wrapping LLVM `Type *` instances. It resolves the long-standing problem of modeling identified structure types, including recursive structures, and enables future removal of LLVMContext and related locking mechanisms from LLVMDialect. This commit only switches the modeling by (a) renaming LLVMTypeNew to LLVMType, (b) removing the old implementaiton of LLVMType, and (c) updating the tests. It is intentionally minimal. Separate commits will remove the infrastructure built for the transition and update API uses where appropriate. Depends On D85020 Reviewed By: rriddle Differential Revision: https://reviews.llvm.org/D85021

view details

push time in 2 months

push eventllvm/llvm-project

Alex Zinenko

commit sha 6abd7e2e622bc7eabdb673a7815f6673523a1e94

[mlir] provide same APIs as existing LLVMType in the new LLVM type modeling These are intended to smoothen the transition and may be removed in the future in favor of more MLIR-compatible APIs. They intentionally have the same semantics as the existing functions, which must remain stable until the transition is complete. Depends On D85019 Reviewed By: nicolasvasilache Differential Revision: https://reviews.llvm.org/D85020

view details

push time in 2 months

push eventllvm/llvm-project

Alex Zinenko

commit sha d4fbbab2e494a59480096a257136ed2b75d07e87

[mlir] translate types between MLIR LLVM dialect and LLVM IR With new LLVM dialect type modeling, the dialect types no longer wrap LLVM IR types. Therefore, they need to be translated to and from LLVM IR during export and import. Introduce the relevant functionality for translating types. It is currently exercised by an ad-hoc type translation roundtripping test that will be subsumed by the actual translation test when the type system transition is complete. Depends On D84339 Reviewed By: herhut Differential Revision: https://reviews.llvm.org/D85019

view details

push time in 2 months

push eventllvm/llvm-project

Alex Zinenko

commit sha 0c40af6b594f6eb2dcd43cdb2bc2f4584ec8ca15

[mlir] First-party modeling of LLVM types The current modeling of LLVM IR types in MLIR is based on the LLVMType class that wraps a raw `llvm::Type *` and delegates uniquing, printing and parsing to LLVM itself. This model makes thread-safe type manipulation hard and is being progressively replaced with a cleaner MLIR model that replicates the type system. Introduce a set of classes reflecting the LLVM IR type system in MLIR instead of wrapping the existing types. These are currently introduced as separate classes without affecting the dialect flow, and are exercised through a test dialect. Once feature parity is reached, the old implementation will be gradually substituted with the new one. Depends On D84171 Reviewed By: rriddle Differential Revision: https://reviews.llvm.org/D84339

view details

push time in 2 months

push eventllvm/llvm-project

Jakub Lichman

commit sha eef1bfb2d219191cee16ee24efbf2d204488696c

[mlir][Linalg] Conv {1,2,3}D ops defined with TC syntax Replaced definition of named ND ConvOps with tensor comprehension syntax which reduces boilerplate code significantly. Furthermore, new ops to support TF convolutions added (without strides and dilations). Reviewed By: nicolasvasilache Differential Revision: https://reviews.llvm.org/D84628

view details

push time in 2 months

push eventllvm/llvm-project

Jakub Lichman

commit sha 1aaf8aa53d694309087b322861038130490bdd5e

[mlir][Linalg] Conv1D, Conv2D and Conv3D added as named ops This commit is part of a greater project which aims to add full end-to-end support for convolutions inside mlir. The reason behind having conv ops for each rank rather than having one generic ConvOp is to enable better optimizations for every N-D case which reflects memory layout of input/kernel buffers better and simplifies code as well. We expect plain linalg.conv to be progressively retired. Reviewed By: ftynse Differential Revision: https://reviews.llvm.org/D83879

view details

push time in 2 months

push eventllvm/llvm-project

Alex Zinenko

commit sha aec38c619dfa1c41b6b0f6c52a31d221ac108b6b

[mlir] LLVMType: make getUnderlyingType private The current modeling of LLVM IR types in MLIR is based on the LLVMType class that wraps a raw `llvm::Type *` and delegates uniquing, printing and parsing to LLVM itself. This is model makes thread-safe type manipulation hard and is being progressively replaced with a cleaner MLIR model that replicates the type system. In the new model, LLVMType will no longer have an underlying LLVM IR type. Restrict access to this type in the current model in preparation for the change. Reviewed By: nicolasvasilache Differential Revision: https://reviews.llvm.org/D84389

view details

push time in 2 months

push eventllvm/llvm-project

lorenzo chelini

commit sha 946be75b9ec131519837e85487fc3e8bf475d001

[MLIR][Linalg] Retire C++ DotOp in favor of a linalg-ods-gen'd op - replace DotOp, now that DRR rules have been dropped. - Capture arguments mismatch in the parser. The number of parsed arguments must equal the number of expected arguments. Reviewed By: ftynse, nicolasvasilache Differential Revision: https://reviews.llvm.org/D82952

view details

push time in 2 months

push eventllvm/llvm-project

Alex Zinenko

commit sha a51829913dba28dae603fdcdddd242c7e20192a1

[mlir] Support for mutable types Introduce support for mutable storage in the StorageUniquer infrastructure. This makes MLIR have key-value storage instead of just uniqued key storage. A storage instance now contains a unique immutable key and a mutable value, both stored in the arena allocator that belongs to the context. This is a preconditio for supporting recursive types that require delayed initialization, in particular LLVM structure types. The functionality is exercised in the test pass with trivial self-recursive type. So far, recursive types can only be printed in parsed in a closed type system. Removing this restriction is left for future work. Differential Revision: https://reviews.llvm.org/D84171

view details

push time in 2 months

push eventllvm/llvm-project

Jakub Lichman

commit sha 20c3386f4a0bc39aa33512dcad024c678e89f9c4

[mlir][Linalg] emitLoopRanges and emitLoopRangesWithSymbols merged into one Right now there is a branching for 2 functions based on whether target map has symbols or not. In this commit these functions are merged into one. Furthermore, emitting does not require inverse and map applying as it computes the correct Range in a single step and thus reduces unnecessary overhead. Differential Revision: https://reviews.llvm.org/D83756

view details

push time in 2 months

push eventllvm/llvm-project

Jakub Lichman

commit sha 919922b0c20e99de51bb0d06c98a5fc2fa5feec4

[mlir] Added verification check for linalg.conv to ensure memrefs are of rank > 2 linalg.conv does not support memrefs with rank smaller than 3 as stated here: https://www.tensorflow.org/versions/r2.0/api_docs/python/tf/nn/convolution However it does not verify it and thus crashes with "LLVM ERROR: out of memory" error for 1D case and "nWin > 0 && "expected at least one window dimension"" assertion for 2D case. This commit adds check for that in the verification method. Differential Revision: https://reviews.llvm.org/D84317

view details

push time in 2 months

push eventllvm/llvm-project

Jakub Lichman

commit sha e4dd964df0164651f1804612ad41582fb801607f

[mlir] Loop bounds inference in linalg.generic op improved to support bounds for convolution Loop bound inference is right now very limited as it supports only permutation maps and thus it is impossible to implement convolution with linalg.generic as it requires more advanced loop bound inference. This commits solves it for the convolution case. Depends On D83158 Differential Revision: https://reviews.llvm.org/D83191

view details

push time in 2 months

push eventwsmoses/MLIR-GPU

Alex Zinenko

commit sha 41c33e81510d08f943f22c24be38e0218591c22c

Add ISL and PET as LLVM subprojects, configure the build This revamps the build system to avoid the bootstrapping flow that was previously necessary to compile mlir-pet. ISL and PET are now embedded into the LLVM build system, and are added as submodules. The build system reuses some code from the Polly subproject, which is attributed accordingly.

view details

push time in 2 months

push eventllvm/llvm-project

Alex Zinenko

commit sha aa84e6e579bedffda43f8ebffa553cf277947e96

[mlir] Fix undefined behavior in Linalg utils getViewSizes The utility function getViewSizes in Linalg has been recently updated to support a different form of Linalg operations. In doing so, the code looking like `smallvector.push_back(smallvector[i])` was introduced. Unlike std vectors, this can lead to undefined behavior if the vector must grow upon insertion: `smallvector[i]` returns a reference to the element, `push_back` takes a const reference to the element, and then grows the vector storage before accessing the referenced value. After the resize, the reference may become dangling, which leads to undefined behavior detected by ASAN as use-after-free. Work around the issue by forcing the value to be copied by putting it into a temporary variable.

view details

push time in 2 months

push eventllvm/llvm-project

Jakub Lichman

commit sha f9c8febc522c2d26a44d4881f015e0e11e4f9167

[mlir] Added support for symbols inside linalg.generic and map concatenation This commit adds functionality needed for implementation of convolutions with linalg.generic op. Since linalg.generic right now expects indexing maps to be just permutations, offset indexing needed in convolutions is not possible. Therefore in this commit we address the issue by adding support for symbols inside indexing maps which enables more advanced indexing. The upcoming commit will solve the problem of computing loop bounds from such maps. Differential Revision: https://reviews.llvm.org/D83158

view details

push time in 2 months

push eventllvm/llvm-project

Alex Zinenko

commit sha ebbdecdd57160fb4816b365f79c1185d68a94c0f

[mlir] Support translating function linkage between MLIR and LLVM IR Linkage support is already present in the LLVM dialect, and is being translated for globals other than functions. Translation support has been missing for functions because their conversion goes through a different code path than other globals. Differential Revision: https://reviews.llvm.org/D84149

view details

push time in 2 months

push eventwsmoses/MLIR-GPU

Alex Zinenko

commit sha 8cdbd771ae6f8f852d6bb3568aa758e4f0b0c49a

Add ISL and PET as LLVM subprojects, configure the build This revamps the build system to avoid the bootstrapping flow that was previously necessary to compile mlir-pet. ISL and PET are now embedded into the LLVM build system, and are both present in the source tree. Both are licensed under MIT license. ISL is already present in LLVM as part of Polly. Parts of Polly build scripts are reused and credited appropriately.

view details

push time in 3 months

Pull request review commentLoopTactics/mlir

[PET-to-MLIR] added symbolic bounds with test case

 LogicalResult MLIRCodegen::verifyModule() {   return success(); } -AffineForOp MLIRCodegen::createLoop(int upperBound, int lowerBound, int step) {-  auto loop = builder_.create<AffineForOp>(builder_.getUnknownLoc(), upperBound,-                                           lowerBound, step);+AffineForOp MLIRCodegen::createLoop(int lb, int ub, int step) {++  auto loop =+      builder_.create<AffineForOp>(builder_.getUnknownLoc(), lb, ub, step);   loop.getBody()->clear();   builder_.setInsertionPointToStart(loop.getBody());   builder_.create<AffineTerminatorOp>(builder_.getUnknownLoc());   builder_.setInsertionPointToStart(loop.getBody());++  return loop;+}++AffineForOp MLIRCodegen::createLoop(int lb, std::string ub_id, int step) {++  Value ub;+  if (failed(this->getLoopTable().find(ub_id, ub)))+    llvm_unreachable("Couldn't find the bound in the loop table.");++  auto lbMap = AffineMap::getConstantMap(lb, builder_.getContext());+  auto ubMap = AffineMap::getMultiDimIdentityMap(1, builder_.getContext());++  ValueRange ubOperands = ValueRange(ub);+  ValueRange lbOperands = {};+  auto loop = builder_.create<AffineForOp>(builder_.getUnknownLoc(), lbOperands,+                                           lbMap, ubOperands, ubMap, step);+  loop.getBody()->clear();++  builder_.setInsertionPointToStart(loop.getBody());+  builder_.create<AffineTerminatorOp>(builder_.getUnknownLoc());+  builder_.setInsertionPointToStart(loop.getBody());

Why is this necessary? The builder of AffineForOp will create an AffineTerminatorOp for you, and this code erases it and creates it back again... Was this cargo-culted from the tutorial?

Komisarczyk

comment created time in 3 months

push eventllvm/llvm-project

Alex Zinenko

commit sha 4ab43980450baf3c49bebbc526c6c96c3ed9f06e

[mlir] minor tweaks in standard-to-llvm lowering Fix a typo in the documentation and simplify the condition to drop braces. Addresses post-commit review of https://reviews.llvm.org/D82647.

view details

push time in 3 months

more