mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2025-02-11 04:06:20 +00:00
Implement Uday's suggestion to unique attribute lists across instructions,
reducing the memory impact on Operation to one word instead of 3 from an std::vector. Implement Jacques' suggestion to merge OpImpl::Storage into OpImpl::Base. PiperOrigin-RevId: 203426518
This commit is contained in:
parent
1928e20a56
commit
9e0e01b47a
@ -19,6 +19,7 @@
|
||||
#define MLIR_IR_IDENTIFIER_H
|
||||
|
||||
#include "mlir/Support/LLVM.h"
|
||||
#include "llvm/ADT/DenseMapInfo.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
|
||||
namespace mlir {
|
||||
@ -61,6 +62,16 @@ public:
|
||||
|
||||
void print(raw_ostream &os) const;
|
||||
void dump() const;
|
||||
|
||||
static Identifier getEmptyKey() {
|
||||
return Identifier(
|
||||
(const char *)llvm::DenseMapInfo<const void *>::getEmptyKey());
|
||||
}
|
||||
static Identifier getTombstoneKey() {
|
||||
return Identifier(
|
||||
(const char *)llvm::DenseMapInfo<const void *>::getTombstoneKey());
|
||||
}
|
||||
|
||||
private:
|
||||
/// These are the bytes of the string, which is a nul terminated string.
|
||||
const char *pointer;
|
||||
@ -85,6 +96,28 @@ inline bool operator!=(Identifier lhs, StringRef rhs) { return !lhs.is(rhs); }
|
||||
inline bool operator==(StringRef lhs, Identifier rhs) { return rhs.is(lhs); }
|
||||
inline bool operator!=(StringRef lhs, Identifier rhs) { return !rhs.is(lhs); }
|
||||
|
||||
// Make identifiers hashable.
|
||||
inline llvm::hash_code hash_value(Identifier arg) {
|
||||
return llvm::hash_value(arg.str());
|
||||
}
|
||||
|
||||
} // end namespace mlir
|
||||
|
||||
namespace llvm {
|
||||
// Identifiers hash just like pointers, there is no need to hash the bytes.
|
||||
template <> struct DenseMapInfo<mlir::Identifier> {
|
||||
static mlir::Identifier getEmptyKey() {
|
||||
return mlir::Identifier::getEmptyKey();
|
||||
}
|
||||
static mlir::Identifier getTombstoneKey() {
|
||||
return mlir::Identifier::getTombstoneKey();
|
||||
}
|
||||
static unsigned getHashValue(mlir::Identifier Val) {
|
||||
return DenseMapInfo<const void *>::getHashValue(Val.data());
|
||||
}
|
||||
static bool isEqual(mlir::Identifier LHS, mlir::Identifier RHS) {
|
||||
return LHS == RHS;
|
||||
}
|
||||
};
|
||||
} // end namespace llvm
|
||||
#endif
|
||||
|
@ -87,8 +87,9 @@ class OperationInst
|
||||
: public Operation, public Instruction,
|
||||
public llvm::ilist_node_with_parent<OperationInst, BasicBlock> {
|
||||
public:
|
||||
explicit OperationInst(Identifier name, ArrayRef<NamedAttribute> attrs = {})
|
||||
: Operation(name, attrs), Instruction(Kind::Operation) {}
|
||||
explicit OperationInst(Identifier name, ArrayRef<NamedAttribute> attrs,
|
||||
MLIRContext *context)
|
||||
: Operation(name, attrs, context), Instruction(Kind::Operation) {}
|
||||
~OperationInst() {}
|
||||
|
||||
/// Unlink this instruction from its BasicBlock and delete it.
|
||||
|
@ -24,6 +24,7 @@
|
||||
|
||||
namespace mlir {
|
||||
class Attribute;
|
||||
class AttributeListStorage;
|
||||
template <typename OpType> class ConstOpPointer;
|
||||
template <typename OpType> class OpPointer;
|
||||
|
||||
@ -52,20 +53,18 @@ public:
|
||||
// (maybe a dozen or so, but not hundreds or thousands) so we use linear
|
||||
// searches for everything.
|
||||
|
||||
ArrayRef<NamedAttribute> getAttrs() const {
|
||||
return attrs;
|
||||
}
|
||||
ArrayRef<NamedAttribute> getAttrs() const;
|
||||
|
||||
/// Return the specified attribute if present, null otherwise.
|
||||
Attribute *getAttr(Identifier name) const {
|
||||
for (auto elt : attrs)
|
||||
for (auto elt : getAttrs())
|
||||
if (elt.first == name)
|
||||
return elt.second;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Attribute *getAttr(StringRef name) const {
|
||||
for (auto elt : attrs)
|
||||
for (auto elt : getAttrs())
|
||||
if (elt.first.is(name))
|
||||
return elt.second;
|
||||
return nullptr;
|
||||
@ -83,7 +82,7 @@ public:
|
||||
|
||||
/// If the an attribute exists with the specified name, change it to the new
|
||||
/// value. Otherwise, add a new attribute with the specified name/value.
|
||||
void setAttr(Identifier name, Attribute *value);
|
||||
void setAttr(Identifier name, Attribute *value, MLIRContext *context);
|
||||
|
||||
enum class RemoveResult {
|
||||
Removed, NotFound
|
||||
@ -91,7 +90,7 @@ public:
|
||||
|
||||
/// Remove the attribute with the specified name if it exists. The return
|
||||
/// value indicates whether the attribute was present or not.
|
||||
RemoveResult removeAttr(Identifier name);
|
||||
RemoveResult removeAttr(Identifier name, MLIRContext *context);
|
||||
|
||||
/// The getAs methods perform a dynamic cast from an Operation (like
|
||||
/// OperationInst and OperationStmt) to a typed Op like DimOp. This returns
|
||||
@ -112,14 +111,15 @@ public:
|
||||
}
|
||||
|
||||
protected:
|
||||
Operation(Identifier name, ArrayRef<NamedAttribute> attrs);
|
||||
Operation(Identifier name, ArrayRef<NamedAttribute> attrs,
|
||||
MLIRContext *context);
|
||||
~Operation();
|
||||
private:
|
||||
Operation(const Operation&) = delete;
|
||||
void operator=(const Operation&) = delete;
|
||||
|
||||
Identifier name;
|
||||
std::vector<NamedAttribute> attrs;
|
||||
AttributeListStorage *attrs;
|
||||
};
|
||||
|
||||
} // end namespace mlir
|
||||
|
@ -72,9 +72,11 @@ public:
|
||||
|
||||
namespace OpImpl {
|
||||
|
||||
/// Every op should subclass this class to provide the basic storage of the
|
||||
/// Operation*.
|
||||
class Storage {
|
||||
/// This provides public APIs that all operations should have. The template
|
||||
/// argument 'ConcreteType' should be the concrete type by CRTP and the others
|
||||
/// are base classes by the policy pattern.
|
||||
template <typename ConcreteType, typename... Traits>
|
||||
class Base : public Traits... {
|
||||
public:
|
||||
/// Return the operation that this refers to.
|
||||
const Operation *getOperation() const { return state; }
|
||||
@ -88,31 +90,24 @@ public:
|
||||
|
||||
/// If the an attribute exists with the specified name, change it to the new
|
||||
/// value. Otherwise, add a new attribute with the specified name/value.
|
||||
void setAttr(Identifier name, Attribute *value) {
|
||||
state->setAttr(name, value);
|
||||
void setAttr(Identifier name, Attribute *value, MLIRContext *context) {
|
||||
state->setAttr(name, value, context);
|
||||
}
|
||||
|
||||
protected:
|
||||
/// Mutability management is handled by the OpWrapper/OpConstWrapper classes,
|
||||
/// so we can cast it away here.
|
||||
explicit Storage(const Operation *state)
|
||||
: state(const_cast<Operation *>(state)) {}
|
||||
|
||||
private:
|
||||
Operation *state;
|
||||
};
|
||||
|
||||
/// This provides public APIs that all operations should have. The template
|
||||
/// argument 'ConcreteType' should be the concrete type by CRTP and the others
|
||||
/// are base classes by the policy pattern.
|
||||
template <typename ConcreteType, typename... Traits>
|
||||
class Base : public Traits... {
|
||||
public:
|
||||
/// This is the hook used by the AsmPrinter to emit this to the .mlir file.
|
||||
/// Op implementations should provide a print method.
|
||||
static void printAssembly(const Operation *op, raw_ostream &os) {
|
||||
op->getAs<ConcreteType>()->print(os);
|
||||
}
|
||||
|
||||
protected:
|
||||
/// Mutability management is handled by the OpWrapper/OpConstWrapper classes,
|
||||
/// so we can cast it away here.
|
||||
explicit Base(const Operation *state)
|
||||
: state(const_cast<Operation *>(state)) {}
|
||||
|
||||
private:
|
||||
Operation *state;
|
||||
};
|
||||
|
||||
/// This class provides the API for ops that are known to have exactly one
|
||||
|
@ -37,8 +37,7 @@ class OperationSet;
|
||||
/// %2 = addf %0, %1 : f32
|
||||
///
|
||||
class AddFOp
|
||||
: public OpImpl::Storage,
|
||||
public OpImpl::Base<AddFOp, OpImpl::TwoOperands, OpImpl::OneResult> {
|
||||
: public OpImpl::Base<AddFOp, OpImpl::TwoOperands, OpImpl::OneResult> {
|
||||
public:
|
||||
/// Methods for support type inquiry through isa, cast, and dyn_cast.
|
||||
static StringRef getOperationName() { return "addf"; }
|
||||
@ -47,7 +46,7 @@ public:
|
||||
|
||||
private:
|
||||
friend class Operation;
|
||||
explicit AddFOp(const Operation *state) : Storage(state) {}
|
||||
explicit AddFOp(const Operation *state) : Base(state) {}
|
||||
};
|
||||
|
||||
/// The "dim" builtin takes a memref or tensor operand and returns an
|
||||
@ -57,8 +56,7 @@ private:
|
||||
/// %1 = dim %0, 2 : tensor<?x?x?xf32>
|
||||
///
|
||||
class DimOp
|
||||
: public OpImpl::Storage,
|
||||
public OpImpl::Base<DimOp, OpImpl::OneOperand, OpImpl::OneResult> {
|
||||
: public OpImpl::Base<DimOp, OpImpl::OneOperand, OpImpl::OneResult> {
|
||||
public:
|
||||
/// This returns the dimension number that the 'dim' is inspecting.
|
||||
unsigned getIndex() const {
|
||||
@ -72,7 +70,7 @@ public:
|
||||
|
||||
private:
|
||||
friend class Operation;
|
||||
explicit DimOp(const Operation *state) : Storage(state) {}
|
||||
explicit DimOp(const Operation *state) : Base(state) {}
|
||||
};
|
||||
|
||||
/// Install the standard operations in the specified operation set.
|
||||
|
58
mlir/lib/IR/AttributeListStorage.h
Normal file
58
mlir/lib/IR/AttributeListStorage.h
Normal file
@ -0,0 +1,58 @@
|
||||
//===- AttributeListStorage.h - Attr representation for ops -----*- C++ -*-===//
|
||||
//
|
||||
// Copyright 2019 The MLIR Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// =============================================================================
|
||||
|
||||
#ifndef ATTRIBUTELISTSTORAGE_H
|
||||
#define ATTRIBUTELISTSTORAGE_H
|
||||
|
||||
#include "mlir/IR/Operation.h"
|
||||
#include "llvm/Support/TrailingObjects.h"
|
||||
|
||||
namespace mlir {
|
||||
|
||||
class AttributeListStorage final
|
||||
: private llvm::TrailingObjects<AttributeListStorage, NamedAttribute> {
|
||||
friend class llvm::TrailingObjects<AttributeListStorage, NamedAttribute>;
|
||||
|
||||
public:
|
||||
/// Given a list of NamedAttribute's, canonicalize the list (sorting
|
||||
/// by name) and return the unique'd result. Note that the empty list is
|
||||
/// represented with a null pointer.
|
||||
static AttributeListStorage *get(ArrayRef<NamedAttribute> attrs,
|
||||
MLIRContext *context);
|
||||
|
||||
/// Return the element constants for this aggregate constant. These are
|
||||
/// known to all be constants.
|
||||
ArrayRef<NamedAttribute> getElements() const {
|
||||
return {getTrailingObjects<NamedAttribute>(), numElements};
|
||||
}
|
||||
|
||||
private:
|
||||
// This is used by the llvm::TrailingObjects base class.
|
||||
size_t numTrailingObjects(OverloadToken<NamedAttribute>) const {
|
||||
return numElements;
|
||||
}
|
||||
AttributeListStorage() = delete;
|
||||
AttributeListStorage(const AttributeListStorage &) = delete;
|
||||
AttributeListStorage(unsigned numElements) : numElements(numElements) {}
|
||||
|
||||
/// This is the number of attributes.
|
||||
const unsigned numElements;
|
||||
};
|
||||
|
||||
} // end namespace mlir
|
||||
|
||||
#endif
|
@ -16,6 +16,7 @@
|
||||
// =============================================================================
|
||||
|
||||
#include "mlir/IR/MLIRContext.h"
|
||||
#include "AttributeListStorage.h"
|
||||
#include "mlir/IR/AffineExpr.h"
|
||||
#include "mlir/IR/AffineMap.h"
|
||||
#include "mlir/IR/Attributes.h"
|
||||
@ -24,6 +25,7 @@
|
||||
#include "mlir/IR/StandardOps.h"
|
||||
#include "mlir/IR/Types.h"
|
||||
#include "mlir/Support/STLExtras.h"
|
||||
#include "third_party/llvm/llvm/include/llvm/ADT/STLExtras.h"
|
||||
#include "llvm/ADT/DenseSet.h"
|
||||
#include "llvm/ADT/StringMap.h"
|
||||
#include "llvm/Support/Allocator.h"
|
||||
@ -125,6 +127,24 @@ struct ArrayAttrKeyInfo : DenseMapInfo<ArrayAttr*> {
|
||||
return lhs == rhs->getValue();
|
||||
}
|
||||
};
|
||||
|
||||
struct AttributeListKeyInfo : DenseMapInfo<AttributeListStorage *> {
|
||||
// Array attributes are uniqued based on their elements.
|
||||
using KeyTy = ArrayRef<NamedAttribute>;
|
||||
using DenseMapInfo<AttributeListStorage *>::getHashValue;
|
||||
using DenseMapInfo<AttributeListStorage *>::isEqual;
|
||||
|
||||
static unsigned getHashValue(KeyTy key) {
|
||||
return hash_combine_range(key.begin(), key.end());
|
||||
}
|
||||
|
||||
static bool isEqual(const KeyTy &lhs, const AttributeListStorage *rhs) {
|
||||
if (rhs == getEmptyKey() || rhs == getTombstoneKey())
|
||||
return false;
|
||||
return lhs == rhs->getElements();
|
||||
}
|
||||
};
|
||||
|
||||
} // end anonymous namespace.
|
||||
|
||||
|
||||
@ -181,6 +201,9 @@ public:
|
||||
StringMap<StringAttr*> stringAttrs;
|
||||
using ArrayAttrSet = DenseSet<ArrayAttr*, ArrayAttrKeyInfo>;
|
||||
ArrayAttrSet arrayAttrs;
|
||||
using AttributeListSet =
|
||||
DenseSet<AttributeListStorage *, AttributeListKeyInfo>;
|
||||
AttributeListSet attributeLists;
|
||||
|
||||
public:
|
||||
MLIRContextImpl() : identifiers(allocator) {
|
||||
@ -448,6 +471,77 @@ ArrayAttr *ArrayAttr::get(ArrayRef<Attribute*> value, MLIRContext *context) {
|
||||
return *existing.first = result;
|
||||
}
|
||||
|
||||
/// Perform a three-way comparison between the names of the specified
|
||||
/// NamedAttributes.
|
||||
static int compareNamedAttributes(const NamedAttribute *lhs,
|
||||
const NamedAttribute *rhs) {
|
||||
return lhs->first.str().compare(rhs->first.str());
|
||||
}
|
||||
|
||||
/// Given a list of NamedAttribute's, canonicalize the list (sorting
|
||||
/// by name) and return the unique'd result. Note that the empty list is
|
||||
/// represented with a null pointer.
|
||||
AttributeListStorage *AttributeListStorage::get(ArrayRef<NamedAttribute> attrs,
|
||||
MLIRContext *context) {
|
||||
// We need to sort the element list to canonicalize it, but we also don't want
|
||||
// to do a ton of work in the super common case where the element list is
|
||||
// already sorted.
|
||||
SmallVector<NamedAttribute, 8> storage;
|
||||
switch (attrs.size()) {
|
||||
case 0:
|
||||
// An empty list is represented with a null pointer.
|
||||
return nullptr;
|
||||
case 1:
|
||||
// A single element is already sorted.
|
||||
break;
|
||||
case 2:
|
||||
// Don't invoke a general sort for two element case.
|
||||
if (attrs[0].first.str() > attrs[1].first.str()) {
|
||||
storage.push_back(attrs[1]);
|
||||
storage.push_back(attrs[0]);
|
||||
attrs = storage;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// Check to see they are sorted already.
|
||||
bool isSorted = true;
|
||||
for (unsigned i = 0, e = attrs.size() - 1; i != e; ++i) {
|
||||
if (attrs[i].first.str() > attrs[i + 1].first.str()) {
|
||||
isSorted = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// If not, do a general sort.
|
||||
if (!isSorted) {
|
||||
storage.append(attrs.begin(), attrs.end());
|
||||
llvm::array_pod_sort(storage.begin(), storage.end(),
|
||||
compareNamedAttributes);
|
||||
attrs = storage;
|
||||
}
|
||||
}
|
||||
|
||||
// Ok, now that we've canonicalized our attributes, unique them.
|
||||
auto &impl = context->getImpl();
|
||||
|
||||
// Look to see if we already have this.
|
||||
auto existing = impl.attributeLists.insert_as(nullptr, attrs);
|
||||
|
||||
// If we already have it, return that value.
|
||||
if (!existing.second)
|
||||
return *existing.first;
|
||||
|
||||
// Otherwise, allocate a new AttributeListStorage, unique it and return it.
|
||||
auto byteSize =
|
||||
AttributeListStorage::totalSizeToAlloc<NamedAttribute>(attrs.size());
|
||||
auto rawMem = impl.allocator.Allocate(byteSize, alignof(NamedAttribute));
|
||||
|
||||
// Placement initialize the AggregateSymbolicValue.
|
||||
auto result = ::new (rawMem) AttributeListStorage(attrs.size());
|
||||
std::uninitialized_copy(attrs.begin(), attrs.end(),
|
||||
result->getTrailingObjects<NamedAttribute>());
|
||||
return *existing.first = result;
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// AffineMap and AffineExpr uniquing
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
@ -16,10 +16,14 @@
|
||||
// =============================================================================
|
||||
|
||||
#include "mlir/IR/Operation.h"
|
||||
#include "AttributeListStorage.h"
|
||||
using namespace mlir;
|
||||
|
||||
Operation::Operation(Identifier name, ArrayRef<NamedAttribute> attrs)
|
||||
: name(name), attrs(attrs.begin(), attrs.end()) {
|
||||
Operation::Operation(Identifier name, ArrayRef<NamedAttribute> attrs,
|
||||
MLIRContext *context)
|
||||
: name(name) {
|
||||
this->attrs = AttributeListStorage::get(attrs, context);
|
||||
|
||||
#ifndef NDEBUG
|
||||
for (auto elt : attrs)
|
||||
assert(elt.second != nullptr && "Attributes cannot have null entries");
|
||||
@ -29,27 +33,46 @@ Operation::Operation(Identifier name, ArrayRef<NamedAttribute> attrs)
|
||||
Operation::~Operation() {
|
||||
}
|
||||
|
||||
ArrayRef<NamedAttribute> Operation::getAttrs() const {
|
||||
if (!attrs)
|
||||
return {};
|
||||
return attrs->getElements();
|
||||
}
|
||||
|
||||
/// If an attribute exists with the specified name, change it to the new
|
||||
/// value. Otherwise, add a new attribute with the specified name/value.
|
||||
void Operation::setAttr(Identifier name, Attribute *value) {
|
||||
void Operation::setAttr(Identifier name, Attribute *value,
|
||||
MLIRContext *context) {
|
||||
assert(value && "attributes may never be null");
|
||||
auto origAttrs = getAttrs();
|
||||
|
||||
SmallVector<NamedAttribute, 8> newAttrs(origAttrs.begin(), origAttrs.end());
|
||||
|
||||
// If we already have this attribute, replace it.
|
||||
for (auto &elt : attrs)
|
||||
for (auto &elt : newAttrs)
|
||||
if (elt.first == name) {
|
||||
elt.second = value;
|
||||
attrs = AttributeListStorage::get(newAttrs, context);
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise, add it.
|
||||
attrs.push_back({name, value});
|
||||
newAttrs.push_back({name, value});
|
||||
attrs = AttributeListStorage::get(newAttrs, context);
|
||||
}
|
||||
|
||||
/// Remove the attribute with the specified name if it exists. The return
|
||||
/// value indicates whether the attribute was present or not.
|
||||
auto Operation::removeAttr(Identifier name) -> RemoveResult {
|
||||
for (unsigned i = 0, e = attrs.size(); i != e; ++i) {
|
||||
if (attrs[i].first == name) {
|
||||
attrs.erase(attrs.begin()+i);
|
||||
auto Operation::removeAttr(Identifier name, MLIRContext *context)
|
||||
-> RemoveResult {
|
||||
auto origAttrs = getAttrs();
|
||||
for (unsigned i = 0, e = origAttrs.size(); i != e; ++i) {
|
||||
if (origAttrs[i].first == name) {
|
||||
SmallVector<NamedAttribute, 8> newAttrs;
|
||||
newAttrs.reserve(origAttrs.size() - 1);
|
||||
newAttrs.append(origAttrs.begin(), origAttrs.begin() + i);
|
||||
newAttrs.append(origAttrs.begin() + i + 1, origAttrs.end());
|
||||
attrs = AttributeListStorage::get(newAttrs, context);
|
||||
return RemoveResult::Removed;
|
||||
}
|
||||
}
|
||||
|
@ -1296,7 +1296,7 @@ parseCFGOperation(CFGFunctionParserState &functionState) {
|
||||
}
|
||||
|
||||
auto nameId = Identifier::get(name, context);
|
||||
return new OperationInst(nameId, attributes);
|
||||
return new OperationInst(nameId, attributes, context);
|
||||
}
|
||||
|
||||
|
||||
|
@ -89,7 +89,7 @@ bb42: ; CHECK: bb0:
|
||||
; CHECK: "foo"(){a: 1, b: -423, c: [true, false]}
|
||||
"foo"(){a: 1, b: -423, c: [true, false] }
|
||||
|
||||
; CHECK: "foo"(){if: "foo", cfgfunc: [], i123: 7}
|
||||
; CHECK: "foo"(){cfgfunc: [], i123: 7, if: "foo"}
|
||||
"foo"(){if: "foo", cfgfunc: [], i123: 7}
|
||||
|
||||
return
|
||||
|
Loading…
x
Reference in New Issue
Block a user