diff options
Diffstat (limited to 'compiler/loco/include')
62 files changed, 5241 insertions, 0 deletions
diff --git a/compiler/loco/include/loco.h b/compiler/loco/include/loco.h new file mode 100644 index 000000000..5cc4487ea --- /dev/null +++ b/compiler/loco/include/loco.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 __LOCO_H__ +#define __LOCO_H__ + +#include "loco/IR/Graph.h" +#include "loco/IR/Algorithm.h" +#include "loco/IR/Verifier.h" + +#include "loco/IR/PermutingCodec.h" + +#endif // __LOCO_H__ diff --git a/compiler/loco/include/loco/ADT/AnnotatedItem.h b/compiler/loco/include/loco/ADT/AnnotatedItem.h new file mode 100644 index 000000000..be0d9ac1d --- /dev/null +++ b/compiler/loco/include/loco/ADT/AnnotatedItem.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 __LOCO_ADT_ANNOTATED_ITEM_H__ +#define __LOCO_ADT_ANNOTATED_ITEM_H__ + +#include <map> +#include <memory> +#include <typeindex> + +namespace loco +{ + +template <typename Annotation> class AnnotatedItem +{ +public: + AnnotatedItem() = default; + +public: + virtual ~AnnotatedItem() = default; + +public: + /** + * @brief Retrieve a stored annotation of type T + * + * @note This method returns nullptr if annotation does not exist + */ + template <typename T> const T *annot(void) const + { + // TODO Insert static_assert(T derives Annotation); + + auto it = _attrs.find(typeid(T)); + + if (it == _attrs.end()) + { + return nullptr; + } + + // TODO Insert null check + return dynamic_cast<T *>(it->second.get()); + } + + /** + * @brief Attach or remove a new annotation of type T + * + * @note annot<T>(nullptr) removes an attached annotation if it exists + */ + template <typename T> void annot(std::unique_ptr<T> &&p) + { + // TODO: Insert static_assert(T derives Annotation); + + if (p == nullptr) + { + _attrs.erase(typeid(T)); + } + else + { + // TODO: assert(_attribs.find(typeid(T)) == _attribs.end()); + _attrs[typeid(T)] = std::move(p); + } + } + +private: + std::map<std::type_index, std::unique_ptr<Annotation>> _attrs; +}; + +} // namespace loco + +#endif // __LOCO_ADT_ANNOTATED_ITEM_H__ diff --git a/compiler/loco/include/loco/ADT/ObjectPool.h b/compiler/loco/include/loco/ADT/ObjectPool.h new file mode 100644 index 000000000..3f3a25c16 --- /dev/null +++ b/compiler/loco/include/loco/ADT/ObjectPool.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 __LOCO_ADT_OBJECT_POOL_H__ +#define __LOCO_ADT_OBJECT_POOL_H__ + +#include <algorithm> +#include <memory> +#include <vector> + +namespace loco +{ + +/** + * @brief Object Pool + * @note ObjectPool owns registered objects. + */ +template <typename T> class ObjectPool +{ +public: + virtual ~ObjectPool() = default; + +public: + /// @brief Return the number of objects + uint32_t size(void) const { return _pool.size(); } + + /// @brief Access N-th object + T *at(uint32_t n) const { return _pool.at(n).get(); } + +protected: + /// @brief Take the ownership of a given object and returns its raw pointer + template <typename U> U *take(std::unique_ptr<U> &&o) + { + auto res = o.get(); + _pool.emplace_back(std::move(o)); + return res; + } + + /** + * @brief Erase an object from the pool + * + * erase(p) returns false if p does not belong to this object pool. + */ + bool erase(T *ptr) + { + auto pred = [ptr](const std::unique_ptr<T> &o) { return o.get() == ptr; }; + auto it = std::find_if(_pool.begin(), _pool.end(), pred); + + if (it == _pool.end()) + { + return false; + } + + _pool.erase(it); + return true; + } + +private: + std::vector<std::unique_ptr<T>> _pool; +}; + +} // namespace loco + +#endif // __LOCO_ADT_OBJECT_POOL_H__ diff --git a/compiler/loco/include/loco/IR/Algorithm.h b/compiler/loco/include/loco/IR/Algorithm.h new file mode 100644 index 000000000..f7812e85d --- /dev/null +++ b/compiler/loco/include/loco/IR/Algorithm.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 __LOCO_IR_ALGORITHM_H__ +#define __LOCO_IR_ALGORITHM_H__ + +#include "loco/IR/Node.h" + +#include <set> +#include <vector> + +namespace loco +{ + +/** + * @brief Generate postorder traversal sequence starting from "roots" + * + * HOW TO USE + * + * for (auto node : postorder_traversal(...)) + * { + * ... node->do_something() ... + * } + * + */ +std::vector<loco::Node *> postorder_traversal(const std::vector<loco::Node *> &roots); + +/** + * @brief Enumerate all the nodes required to compute "roots" + */ +std::set<loco::Node *> active_nodes(const std::vector<loco::Node *> &roots); + +} // namespace loco + +#endif // __LOCO_IR_ALGORITHM_H__ diff --git a/compiler/loco/include/loco/IR/BiasShape.h b/compiler/loco/include/loco/IR/BiasShape.h new file mode 100644 index 000000000..037b0873e --- /dev/null +++ b/compiler/loco/include/loco/IR/BiasShape.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 __LOCO_IR_BIAS_SHAPE_H__ +#define __LOCO_IR_BIAS_SHAPE_H__ + +#include "loco/IR/Dimension.h" + +namespace loco +{ + +/** + * \brief Bias Shape + */ +class BiasShape final +{ +public: + BiasShape() = default; + +public: + const Dimension &length(void) const { return _length; } + Dimension &length(void) { return _length; } + +private: + Dimension _length; +}; + +} // namespace loco + +#endif // __LOCO_IR_BIAS_SHAPE_H__ diff --git a/compiler/loco/include/loco/IR/CanonicalDialect.h b/compiler/loco/include/loco/IR/CanonicalDialect.h new file mode 100644 index 000000000..940d29a59 --- /dev/null +++ b/compiler/loco/include/loco/IR/CanonicalDialect.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 __LOCO_IR_CANONICAL_DIALECT_H__ +#define __LOCO_IR_CANONICAL_DIALECT_H__ + +#include "loco/IR/Dialect.h" + +namespace loco +{ + +/** + * @brief A singleton for Canonical Dialect + * + * CanonicalDialect serves as an in-memory unique identifier. + */ +class CanonicalDialect final : public Dialect +{ +private: + CanonicalDialect(); + +public: + CanonicalDialect(const CanonicalDialect &) = delete; + CanonicalDialect(CanonicalDialect &&) = delete; + +public: + static Dialect *get(void); +}; + +} // namespace loco + +#endif // __LOCO_IR_CANONICAL_DIALECT_H__ diff --git a/compiler/loco/include/loco/IR/CanonicalNode.h b/compiler/loco/include/loco/IR/CanonicalNode.h new file mode 100644 index 000000000..2dcc02e5d --- /dev/null +++ b/compiler/loco/include/loco/IR/CanonicalNode.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 __LOCO_IR_CANONICAL_NODE_H__ +#define __LOCO_IR_CANONICAL_NODE_H__ + +#include "loco/IR/CanonicalNodeDecl.h" +#include "loco/IR/CanonicalNodeImpl.h" + +#endif // __LOCO_IR_CANONICAL_NODE_H__ diff --git a/compiler/loco/include/loco/IR/CanonicalNodeDecl.h b/compiler/loco/include/loco/IR/CanonicalNodeDecl.h new file mode 100644 index 000000000..872edbb3e --- /dev/null +++ b/compiler/loco/include/loco/IR/CanonicalNodeDecl.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 __LOCO_IR_CANONICAL_NODE_DECL_H__ +#define __LOCO_IR_CANONICAL_NODE_DECL_H__ + +#include "loco/IR/Node.h" +#include "loco/IR/Dialect.h" +#include "loco/IR/CanonicalOpcode.h" +#include "loco/IR/CanonicalNodeVisitor.forward.h" + +namespace loco +{ + +struct CanonicalNode : public Node +{ + virtual ~CanonicalNode() = default; + + const Dialect *dialect(void) const final; + virtual CanonicalOpcode opcode(void) const = 0; + + template <typename T> T accept(CanonicalNodeVisitorBase<T> *) const; + template <typename T> T accept(CanonicalNodeMutableVisitorBase<T> *); +}; + +template <CanonicalOpcode Code, template <typename T> class... Mixins> +struct CanonicalNodeDef : public virtual CanonicalNode, public Mixins<CanonicalNode>... +{ + virtual ~CanonicalNodeDef() = default; + + uint32_t opnum(void) const final { return static_cast<uint32_t>(Code); } + CanonicalOpcode opcode(void) const final { return Code; } +}; + +} // namespace loco + +#endif // __LOCO_IR_CANONICAL_NODE_H__ diff --git a/compiler/loco/include/loco/IR/CanonicalNodeImpl.h b/compiler/loco/include/loco/IR/CanonicalNodeImpl.h new file mode 100644 index 000000000..73aa4caa5 --- /dev/null +++ b/compiler/loco/include/loco/IR/CanonicalNodeImpl.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 __LOCO_IR_CANONICAL_NODE_IMPL_H__ +#define __LOCO_IR_CANONICAL_NODE_IMPL_H__ + +#include "loco/IR/Nodes.h" +#include "loco/IR/CanonicalNodeVisitor.h" + +#include <stdexcept> + +namespace loco +{ + +template <typename T> T CanonicalNode::accept(CanonicalNodeVisitorBase<T> *v) const +{ + switch (this->opcode()) + { +#define CANONICAL_NODE(OPCODE, CLASS) \ + case CanonicalOpcode::OPCODE: \ + return v->visit(dynamic_cast<const CLASS *>(this)); + +#include "CanonicalNodes.lst" +#undef CANONICAL_NODE + default: + break; + } + + throw std::runtime_error{"NYI"}; +} + +template <typename T> T CanonicalNode::accept(CanonicalNodeMutableVisitorBase<T> *v) +{ + switch (this->opcode()) + { +#define CANONICAL_NODE(OPCODE, CLASS) \ + case CanonicalOpcode::OPCODE: \ + return v->visit(dynamic_cast<CLASS *>(this)); + +#include "CanonicalNodes.lst" +#undef CANONICAL_NODE + default: + break; + } + + throw std::runtime_error{"NYI"}; +} + +} // namespace loco + +#endif // __LOCO_IR_CANONICAL_NODE_IMPL_H__ diff --git a/compiler/loco/include/loco/IR/CanonicalNodeVisitor.forward.h b/compiler/loco/include/loco/IR/CanonicalNodeVisitor.forward.h new file mode 100644 index 000000000..425d77997 --- /dev/null +++ b/compiler/loco/include/loco/IR/CanonicalNodeVisitor.forward.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 __LOCO_IR_CANONICAL_NODE_VISITOR_FORWARD_H__ +#define __LOCO_IR_CANONICAL_NODE_VISITOR_FORWARD_H__ + +namespace loco +{ + +// NOTE These forward declarations SHOULD BE aligned with "CanonicalNodeVisitor.h" +template <typename T> struct CanonicalNodeVisitorBase; +template <typename T> struct CanonicalNodeMutableVisitorBase; + +} // namespace loco + +#endif // __LOCO_IR_CANONICAL_NODE_VISITOR_FORWARD_H__ diff --git a/compiler/loco/include/loco/IR/CanonicalNodeVisitor.h b/compiler/loco/include/loco/IR/CanonicalNodeVisitor.h new file mode 100644 index 000000000..b9ffd5472 --- /dev/null +++ b/compiler/loco/include/loco/IR/CanonicalNodeVisitor.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 __LOCO_IR_CANONICAL_NODE_VISITOR_H__ +#define __LOCO_IR_CANONICAL_NODE_VISITOR_H__ + +#include "loco/IR/Nodes.h" + +#include <stdexcept> + +namespace loco +{ + +/** + * DO NOT use this class. Use CanonicalNodeVisitor instead. + */ +template <typename T> struct CanonicalNodeVisitorBase +{ + virtual ~CanonicalNodeVisitorBase() = default; + +#define CANONICAL_NODE(OPCODE, CLASS) virtual T visit(const CLASS *) = 0; +#include "CanonicalNodes.lst" +#undef CANONICAL_NODE +}; + +template <typename T> struct CanonicalNodeVisitor : public CanonicalNodeVisitorBase<T> +{ + virtual ~CanonicalNodeVisitor() = default; + +#define CANONICAL_NODE(OPCODE, CLASS) \ + virtual T visit(const CLASS *node) { return visit(static_cast<const Node *>(node)); } +#include "CanonicalNodes.lst" +#undef CANONICAL_NODE + + /// @brief Default fallback + virtual T visit(const Node *) { throw std::runtime_error{"Not implemented, yet"}; } +}; + +/** + * DO NOT use this class. Use CanonicalNodeMutableVisitor instead. + */ +template <typename T> struct CanonicalNodeMutableVisitorBase +{ + virtual ~CanonicalNodeMutableVisitorBase() = default; + +#define CANONICAL_NODE(OPCODE, CLASS) virtual T visit(CLASS *) = 0; +#include "CanonicalNodes.lst" +#undef CANONICAL_NODE +}; + +template <typename T> struct CanonicalNodeMutableVisitor : public CanonicalNodeMutableVisitorBase<T> +{ + virtual ~CanonicalNodeMutableVisitor() = default; + +#define CANONICAL_NODE(OPCODE, CLASS) \ + virtual T visit(CLASS *node) { return visit(static_cast<Node *>(node)); } +#include "CanonicalNodes.lst" +#undef CANONICAL_NODE + + /// @brief Default fallback + virtual T visit(Node *) { throw std::runtime_error{"Not implemented, yet"}; } +}; + +} // namespace loco + +#endif // __LOCO_IR_CANONICAL_NODE_VISITOR_H__ diff --git a/compiler/loco/include/loco/IR/CanonicalNodes.lst b/compiler/loco/include/loco/IR/CanonicalNodes.lst new file mode 100644 index 000000000..527856fbe --- /dev/null +++ b/compiler/loco/include/loco/IR/CanonicalNodes.lst @@ -0,0 +1,49 @@ +#ifndef CANONICAL_NODE +#error "Define CANONICAL_NODE" +#endif // CANONICAL_NODE + +// +// PLEASE SORT NODE DECLS IN ALPHABETICAL ORDER +// + +// CANONICAL_NODE(OPCODE, CLASS) +CANONICAL_NODE(AvgPool2D, AvgPool2D) +CANONICAL_NODE(BiasDecode, BiasDecode) +CANONICAL_NODE(BiasEncode, BiasEncode) +CANONICAL_NODE(ConstGen, ConstGen) +CANONICAL_NODE(Conv2D, Conv2D) +CANONICAL_NODE(DepthwiseConv2D, DepthwiseConv2D) +CANONICAL_NODE(DepthwiseFilterDecode, DepthwiseFilterDecode) +CANONICAL_NODE(DepthwiseFilterEncode, DepthwiseFilterEncode) +CANONICAL_NODE(EltwiseAdd, EltwiseAdd) +CANONICAL_NODE(EltwiseDiv, EltwiseDiv) +CANONICAL_NODE(EltwiseMax, EltwiseMax) +CANONICAL_NODE(EltwiseMul, EltwiseMul) +CANONICAL_NODE(EltwiseSqrt, EltwiseSqrt) +CANONICAL_NODE(EltwiseSub, EltwiseSub) +CANONICAL_NODE(FeatureBiasAdd, BiasAdd<Domain::Feature>) +CANONICAL_NODE(FeatureDecode, FeatureDecode) +CANONICAL_NODE(FeatureEncode, FeatureEncode) +CANONICAL_NODE(FilterDecode, FilterDecode) +CANONICAL_NODE(FilterEncode, FilterEncode) +CANONICAL_NODE(FixedReshape, Reshape<ReshapeType::Fixed>) +CANONICAL_NODE(Forward, Forward) +CANONICAL_NODE(MaxPool2D, MaxPool2D) +// WARN Push may be excluded from canoncial dialect in the future +CANONICAL_NODE(Push, Push) +// WARN Pull may be excluded from canoncial dialect in the future +CANONICAL_NODE(Pull, Pull) +CANONICAL_NODE(ReLU, ReLU) +CANONICAL_NODE(ReLU6, ReLU6) +CANONICAL_NODE(Tanh, Tanh) +CANONICAL_NODE(TensorConcat, TensorConcat) +CANONICAL_NODE(TensorConstantPad, TensorConstantPad) +CANONICAL_NODE(TensorBiasAdd, BiasAdd<Domain::Tensor>) +CANONICAL_NODE(TensorBroadcast, TensorBroadcast) +CANONICAL_NODE(TensorReduce, TensorReduce) +CANONICAL_NODE(TensorTranspose, TensorTranspose) +CANONICAL_NODE(TensorSoftmax, Softmax<Domain::Tensor>) +CANONICAL_NODE(TransposedConv2D, TransposedConv2D) +CANONICAL_NODE(MatrixEncode, MatrixEncode) +CANONICAL_NODE(MatrixDecode, MatrixDecode) +CANONICAL_NODE(MatMul, MatMul) diff --git a/compiler/loco/include/loco/IR/CanonicalOpcode.h b/compiler/loco/include/loco/IR/CanonicalOpcode.h new file mode 100644 index 000000000..58aa7de6d --- /dev/null +++ b/compiler/loco/include/loco/IR/CanonicalOpcode.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 __LOCO_IR_CANONICAL_OPCODE_H__ +#define __LOCO_IR_CANONICAL_OPCODE_H__ + +namespace loco +{ + +/** + * @brief Canonical Node Opcode + * + * WARNING The order is subject to change. DO NOT serialize this value. + */ +enum class CanonicalOpcode +{ +#define CANONICAL_NODE(OPCODE, CLASS) OPCODE, +#include "CanonicalNodes.lst" +#undef CANONICAL_NODE +}; + +} // namespace loco + +#endif // __LOCO_IR_CANONICAL_OPCODE_H__ diff --git a/compiler/loco/include/loco/IR/DataType.h b/compiler/loco/include/loco/IR/DataType.h new file mode 100644 index 000000000..b07022bf5 --- /dev/null +++ b/compiler/loco/include/loco/IR/DataType.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 __LOCO_IR_DATA_TYPE_H__ +#define __LOCO_IR_DATA_TYPE_H__ + +namespace loco +{ + +/** + * @brief "scalar" value type + */ +enum class DataType +{ + Unknown, // Unknown type (serves as a default value) + + U8, // 8-bit unsigned integer + U16, // 16-bit unsigned integer + U32, // 32-bit unsigned integer + U64, // 64-bit unsigned integer + + S8, // 8-bit signed integer + S16, // 16-bit signed integer + S32, // 32-bit signed integer + S64, // 64-bit signed integer + + FLOAT16, // IEEE 16-bit floating-point + FLOAT32, // IEEE 32-bit floating-point + FLOAT64, // IEEE 64-bit floating-point + + // WARNING the size of Bool may vary for NN frameworks + // TODO we need to find a way to resolve this issue + BOOL, // Boolean +}; + +} // namespace loco + +#endif // __LOCO_IR_DATA_TYPE_H__ diff --git a/compiler/loco/include/loco/IR/DataTypeTraits.h b/compiler/loco/include/loco/IR/DataTypeTraits.h new file mode 100644 index 000000000..c4479e545 --- /dev/null +++ b/compiler/loco/include/loco/IR/DataTypeTraits.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 __LOCO_IR_DATA_TYPE_TRAITS_H__ +#define __LOCO_IR_DATA_TYPE_TRAITS_H__ + +#include "loco/IR/DataType.h" + +#include <cassert> +#include <cstdint> + +namespace loco +{ + +/** + * @brief C++ scalar type corresponding to each DataType + */ +template <DataType DT> struct DataTypeImpl +{ + // using Type = ... +}; + +// TODO Support other enum values +template <> struct DataTypeImpl<DataType::S8> +{ + // Use C++ int8_t type for 8bit integer + using Type = int8_t; +}; + +template <> struct DataTypeImpl<DataType::U8> +{ + // Use C++ uint8_t type for unsigned 8bit integer + using Type = uint8_t; +}; + +template <> struct DataTypeImpl<DataType::S32> +{ + // Use C++ int32_t type for 32bit integer + using Type = int32_t; +}; + +template <> struct DataTypeImpl<DataType::FLOAT32> +{ + // Use C++ float type for IEEE 32-bit floating-point numbers + using Type = float; +}; + +/** + * @brief Returns the size of the data type. + * @note If you need the size at compile time, use `sizeof(typename DataTypeImpl<DT>::Type)`. + */ +inline uint32_t size(DataType data_type) +{ + switch (data_type) + { + case DataType::S8: + return sizeof(DataTypeImpl<DataType::S8>::Type); + case DataType::U8: + return sizeof(DataTypeImpl<DataType::U8>::Type); + case DataType::S32: + return sizeof(DataTypeImpl<DataType::S32>::Type); + case DataType::FLOAT32: + return sizeof(DataTypeImpl<DataType::FLOAT32>::Type); + default: + // TODO Support remaining data types. + assert(false); + return UINT32_MAX; // Avoid compiler warning. + } +} + +} // namespace loco + +#endif // __LOCO_IR_DATA_TYPE_TRAITS_H__ diff --git a/compiler/loco/include/loco/IR/DepthwiseFilterAxis.h b/compiler/loco/include/loco/IR/DepthwiseFilterAxis.h new file mode 100644 index 000000000..eb4650ec9 --- /dev/null +++ b/compiler/loco/include/loco/IR/DepthwiseFilterAxis.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 __LOCO_IR_DEPTHWISE_FILTER_AXIS_H__ +#define __LOCO_IR_DEPTHWISE_FILTER_AXIS_H__ + +namespace loco +{ + +enum class DepthwiseFilterAxis +{ + Depth, + Multiplier, + Height, + Width +}; + +} // namespace loco + +#endif // __LOCO_IR_DEPTHWISE_FILTER_AXIS_H__ diff --git a/compiler/loco/include/loco/IR/DepthwiseFilterCodec.h b/compiler/loco/include/loco/IR/DepthwiseFilterCodec.h new file mode 100644 index 000000000..0d9286b46 --- /dev/null +++ b/compiler/loco/include/loco/IR/DepthwiseFilterCodec.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 __LOCO_IR_DEPTHWISE_FILTER_CODEC_H__ +#define __LOCO_IR_DEPTHWISE_FILTER_CODEC_H__ + +#include "loco/IR/DepthwiseFilterShape.h" +#include "loco/IR/DepthwiseFilterIndex.h" + +#include "loco/IR/TensorShape.h" +#include "loco/IR/TensorIndex.h" + +namespace loco +{ + +/** + * @brief Describe how to build a depthwise convolution filter from a tensor + * + * Let us assume that "enc" is a depthwise filter encoder. + * + * Given a tensor "inp" and its shape "inp.shape", "enc" builds a depthwise filter + * "out" as follows: + * + * for each valid filter_index for enc.shape(inp.shape) + * out.at(filter_index) = inp.at(enc.value(filter_index)) + */ +struct DepthwiseFilterEncoder +{ + virtual ~DepthwiseFilterEncoder() = default; + + virtual DepthwiseFilterShape shape(const TensorShape &shape) const = 0; + virtual TensorIndex value(const DepthwiseFilterIndex &index) const = 0; +}; + +/** + * @brief Describe how to build a tensor from a depthwise convolution filter + * + * Let us assume that "dec" is a depthwise filter decoder. + * + * Given a depthwise filter "inp" and its shape "inp.shape", "dec" builds a tensor + * "out" as follows: + * + * for each valid tensor_index for dec.shape(inp.shape) + * out.at(tensor_index) = inp.at(dec.value(tensor_index)) + */ +struct DepthwiseFilterDecoder +{ + virtual ~DepthwiseFilterDecoder() = default; + + virtual TensorShape shape(const DepthwiseFilterShape &shape) const = 0; + virtual DepthwiseFilterIndex value(const TensorIndex &index) const = 0; +}; + +} // namespace loco + +#endif // __LOCO_IR_DEPTHWISE_FILTER_CODEC_H__ diff --git a/compiler/loco/include/loco/IR/DepthwiseFilterIndex.h b/compiler/loco/include/loco/IR/DepthwiseFilterIndex.h new file mode 100644 index 000000000..884e50a56 --- /dev/null +++ b/compiler/loco/include/loco/IR/DepthwiseFilterIndex.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 __LOCO_IR_DEPTHWISE_FILTER_INDEX_H__ +#define __LOCO_IR_DEPTHWISE_FILTER_INDEX_H__ + +#include <cstdint> + +namespace loco +{ + +/** + * @brief DepthwiseFilter Index + * + * DepthwiseFilter Index indicates an "element" in a given Depthwise convolution filter. + * + * Assume there is a filter K where KS denotes its shape (of DepthwiseFilterShape type). + * + * Then, any valid filter index I satisfies the following invariants: + * - 0 <= I.channel() < KS.depth() + * - 0 <= I.nth() < KS.multiplier() + * - 0 <= I.row() < KS.height() + * - 0 <= I.column() < KS.width() + */ +class DepthwiseFilterIndex final +{ +public: + DepthwiseFilterIndex() = default; + +public: + const uint32_t &channel(void) const { return _channel; } + uint32_t &channel(void) { return _channel; } + + const uint32_t &nth(void) const { return _nth; } + uint32_t &nth(void) { return _nth; } + + const uint32_t &row(void) const { return _row; } + uint32_t &row(void) { return _row; } + + const uint32_t &column(void) const { return _column; } + uint32_t &column(void) { return _column; } + +private: + uint32_t _channel = 0; + uint32_t _nth = 0; + uint32_t _row = 0; + uint32_t _column = 0; +}; + +} // namespace loco + +#endif // __LOCO_IR_DEPTHWISE_FILTER_INDEX_H__ diff --git a/compiler/loco/include/loco/IR/DepthwiseFilterShape.h b/compiler/loco/include/loco/IR/DepthwiseFilterShape.h new file mode 100644 index 000000000..eb1a1e335 --- /dev/null +++ b/compiler/loco/include/loco/IR/DepthwiseFilterShape.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 __LOCO_IR_DEPTHWISE_FILTER_SHAPE_H__ +#define __LOCO_IR_DEPTHWISE_FILTER_SHAPE_H__ + +#include "loco/IR/Dimension.h" + +namespace loco +{ + +/** + * @brief DepthwiseFilter Shape + * + * This class describes the shape of depthwise filter, which is an input of depthwise 2D + * convolutional operation. + * + * depth() refers to expected channel depth of matching input + * multiplier() refers to number of traverse for one input + * height() refers to the height of 2D weights + * width() refers to the width of 2D weights + */ +class DepthwiseFilterShape final +{ +public: + DepthwiseFilterShape() = default; + +public: + const Dimension &depth(void) const { return _depth; } + Dimension &depth(void) { return _depth; } + + const Dimension &multiplier(void) const { return _multiplier; } + Dimension &multiplier(void) { return _multiplier; } + + const Dimension &height(void) const { return _height; } + Dimension &height(void) { return _height; } + + const Dimension &width(void) const { return _width; } + Dimension &width(void) { return _width; } + +private: + Dimension _depth; + Dimension _multiplier; + Dimension _height; + Dimension _width; +}; + +} // namespace loco + +#endif // __LOCO_IR_DEPTHWISE_FILTER_SHAPE_H__ diff --git a/compiler/loco/include/loco/IR/Dialect.h b/compiler/loco/include/loco/IR/Dialect.h new file mode 100644 index 000000000..b8942bfb4 --- /dev/null +++ b/compiler/loco/include/loco/IR/Dialect.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 __LOCO_IR_DIALECT_H__ +#define __LOCO_IR_DIALECT_H__ + +#include "loco/IR/DialectService.h" + +#include <map> +#include <memory> +#include <typeindex> +#include <typeinfo> + +namespace loco +{ + +/** + * @brief Dialect interface + * + * Each dialect implementation is expected to have static "get" method + * which returns "const Dialect *" value. + */ +class Dialect +{ +public: + virtual ~Dialect() = default; + +protected: + template <typename ConcreteService> void service(std::unique_ptr<ConcreteService> &&s) + { + _services[typeid(ConcreteService)] = std::move(s); + } + +public: + template <typename ConcreteService> ConcreteService *service(void) const + { + auto it = _services.find(typeid(ConcreteService)); + + if (it == _services.end()) + { + return nullptr; + } + + return dynamic_cast<ConcreteService *>(it->second.get()); + } + +private: + std::map<std::type_index, std::unique_ptr<DialectService>> _services; +}; + +} // namespace loco + +#endif // __LOCO_IR_DIALECT_H__ diff --git a/compiler/loco/include/loco/IR/DialectService.h b/compiler/loco/include/loco/IR/DialectService.h new file mode 100644 index 000000000..54a3fac74 --- /dev/null +++ b/compiler/loco/include/loco/IR/DialectService.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 __LOCO_IR_DIALECT_SERVICE_H__ +#define __LOCO_IR_DIALECT_SERVICE_H__ + +namespace loco +{ + +/** + * @brief Dialect Service interface + * + * Every service that each dialect exposes should inherit this interface. + */ +struct DialectService +{ + virtual ~DialectService() = default; +}; + +} // namespace loco + +#endif // __LOCO_IR_DIALECT_SERVICE_H__ diff --git a/compiler/loco/include/loco/IR/Dimension.h b/compiler/loco/include/loco/IR/Dimension.h new file mode 100644 index 000000000..7b5d5943f --- /dev/null +++ b/compiler/loco/include/loco/IR/Dimension.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 __LOCO_IR_DIMENSION_H__ +#define __LOCO_IR_DIMENSION_H__ + +#include <cstdint> + +namespace loco +{ + +/** + * @brief The value of one dimension in a tensor shape + * @note The value may be unknown + */ +class Dimension final +{ +private: + enum class Kind + { + Known, + Unknown + }; + +public: + // @brief Construct an "unknown" dimension + Dimension() = default; + + // @brief Construct a "known" dimension + Dimension(uint32_t value) { set(value); } + +public: + // @brief Return whether the value is known (or not) + bool known(void) const { return _kind == Kind::Known; } + + // @brief Return the value + // @note This value is meaningful only for known dimension + uint32_t value(void) const { return _value; } + + void set(uint32_t value) + { + _kind = Kind::Known; + _value = value; + } + + void unset(void) + { + _kind = Kind::Unknown; + _value = 0; + } + +private: + Kind _kind{Kind::Unknown}; + uint32_t _value{0}; +}; + +/** + * @brief Equality operator between two Dimensions + * + * @note Refer to the definition of equality of dimemsion at + * https://www.tensorflow.org/api_docs/python/tf/Dimension#__eq__ + */ +bool operator==(const Dimension &, const Dimension &); +bool operator==(const Dimension &, uint32_t); +bool operator==(uint32_t, const Dimension &); + +// @brief Make an "unknown" dimension +Dimension make_dimension(void); + +} // namespace loco + +#endif // __LOCO_IR_DIMENSION_H__ diff --git a/compiler/loco/include/loco/IR/Domain.h b/compiler/loco/include/loco/IR/Domain.h new file mode 100644 index 000000000..823bc1833 --- /dev/null +++ b/compiler/loco/include/loco/IR/Domain.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 __LOCO_IR_DOMAIN_H__ +#define __LOCO_IR_DOMAIN_H__ + +namespace loco +{ + +/** + * @brief Describe the kind of (N-dimensional) loco values + * + * loco is an intermediate representation for neural network compiler, which mainly focuses on + * N-dimensional values (usually referred to as Tensor). + * + * There are several special cases for N-dimensional values according to its usage. For example, + * vision community often refers to 4D array as "FeatureMap". + * + * It is definitely possible to represent all of these special cases using Tensor, but that scheme + * may introduces some confusion (e.g. NCHW vs NHWC issue). + * + * loco distinguishes these special cases from Tensor in order to reduce such confusion. + * + * This "Domain" enum class enumerates all of these special cases that loco supports. + */ +enum class Domain +{ + Unknown, + Tensor, + Feature, + Filter, /* 2D Convolution Filter */ + DepthwiseFilter, /* Depthwise 2D Convolution Filter */ + Bias, + Matrix, + /* ... */ +}; + +} // namespace loco + +#endif // __LOCO_IR_DOMAIN_H__ diff --git a/compiler/loco/include/loco/IR/FeatureAxis.h b/compiler/loco/include/loco/IR/FeatureAxis.h new file mode 100644 index 000000000..cf020edd2 --- /dev/null +++ b/compiler/loco/include/loco/IR/FeatureAxis.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 __LOCO_IR_FEATURE_AXIS_H__ +#define __LOCO_IR_FEATURE_AXIS_H__ + +namespace loco +{ + +enum class FeatureAxis +{ + Count, + Depth, + Height, + Width +}; + +} // namespace loco + +#endif // __LOCO_IR_FEATURE_AXIS_H__ diff --git a/compiler/loco/include/loco/IR/FeatureCodec.h b/compiler/loco/include/loco/IR/FeatureCodec.h new file mode 100644 index 000000000..93094e13a --- /dev/null +++ b/compiler/loco/include/loco/IR/FeatureCodec.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 __LOCO_IR_FEATURE_CODEC_H__ +#define __LOCO_IR_FEATURE_CODEC_H__ + +#include "loco/IR/FeatureShape.h" +#include "loco/IR/FeatureIndex.h" + +#include "loco/IR/TensorShape.h" +#include "loco/IR/TensorIndex.h" + +#include <memory> + +namespace loco +{ + +/** + * @brief Decribe how to build a (convolution) feature map from a tensor + * + * Let us assume that "enc" is a feature encoder. + * + * Given a tensor "inp" and its shape "inp.shape", "enc" builds a feature map + * "out" as follows: + * + * for each valid feature index (referred to as feature_idx below) for enc.shape(inp.shape) + * out.at(feature_index) = inp.at(enc.value(feature_index)) + */ +struct FeatureEncoder +{ + virtual ~FeatureEncoder() = default; + + virtual FeatureShape shape(const TensorShape &shape) const = 0; + virtual TensorIndex value(const FeatureIndex &index) const = 0; + + virtual std::unique_ptr<FeatureEncoder> clone(void) const = 0; +}; + +/** + * @brief Describe how to build a tensor from a (convolution) feature map + * + * Let us assume that "dec" is a feature decoder. + * + * Given a feature map "inp" and its shape "inp.shape", "dec" builds a tensor + * "out" as follows: + * + * for each valid tensor index (referred to as tensor_index below) for dec.shape(inp.shape) + * out.at(tensor_index) = inp.at(dec.value(tensor_index)) + * + * NOTE "inp" is a feature value and "out" is a tensor value in this example. + */ +struct FeatureDecoder +{ + virtual ~FeatureDecoder() = default; + + virtual TensorShape shape(const FeatureShape &) const = 0; + virtual FeatureIndex value(const TensorIndex &) const = 0; + + virtual std::unique_ptr<FeatureDecoder> clone(void) const = 0; +}; + +} // namespace loco + +#endif // __LOCO_IR_FEATURE_CODEC_H__ diff --git a/compiler/loco/include/loco/IR/FeatureIndex.h b/compiler/loco/include/loco/IR/FeatureIndex.h new file mode 100644 index 000000000..007f94491 --- /dev/null +++ b/compiler/loco/include/loco/IR/FeatureIndex.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 __LOCO_IR_FEATURE_INDEX_H__ +#define __LOCO_IR_FEATURE_INDEX_H__ + +#include <cstdint> + +namespace loco +{ + +/** + * \brief Feature Index + * + * Feature Index indicates an "element" in a given feature map. + * + * Let us assume that there is a feature map F and S denotes its shape (of FeatureShape type). + * + * Then, any valid feature index I satisfies the following invariants: + * - 0 <= I.batch() < S.count() + * - 0 <= I.channel() < S.depth() + * - 0 <= I.row() < S.height() + * - 0 <= I.column() < S.width() + */ +class FeatureIndex final +{ +public: + FeatureIndex() = default; + +public: + const uint32_t &batch(void) const { return _batch; } + uint32_t &batch(void) { return _batch; } + + const uint32_t &channel(void) const { return _channel; } + uint32_t &channel(void) { return _channel; } + + const uint32_t &row(void) const { return _row; } + uint32_t &row(void) { return _row; } + + const uint32_t &column(void) const { return _column; } + uint32_t &column(void) { return _column; } + +private: + uint32_t _batch = 0; + uint32_t _channel = 0; + uint32_t _row = 0; + uint32_t _column = 0; +}; + +} // namespace loco + +#endif // __LOCO_IR_FEATURE_INDEX_H__ diff --git a/compiler/loco/include/loco/IR/FeatureShape.h b/compiler/loco/include/loco/IR/FeatureShape.h new file mode 100644 index 000000000..d09a2b2b8 --- /dev/null +++ b/compiler/loco/include/loco/IR/FeatureShape.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 __LOCO_IR_FEATURE_SHAPE_H__ +#define __LOCO_IR_FEATURE_SHAPE_H__ + +#include "loco/IR/Dimension.h" + +namespace loco +{ + +/** + * \brief Feature Map Shape + * + * This class describes the shape of feature maps, which serves as the input/output of 2D + * convolutional operations (e.g. Convolution). + * + * Each feature map is a collection of 3D features conceptually. + * Each feature has depth, height, width. + * + * count() refers to the number of features in a feature map + * depth() refers to the depth of features in a given feature map + * height() refers to the height of features in a given feature map + * width() refers to the width of features in a given feature map + */ +class FeatureShape final +{ +public: + FeatureShape() = default; + +public: + const Dimension &count(void) const { return _count; } + Dimension &count(void) { return _count; } + + const Dimension &depth(void) const { return _depth; } + Dimension &depth(void) { return _depth; } + + const Dimension &height(void) const { return _height; } + Dimension &height(void) { return _height; } + + const Dimension &width(void) const { return _width; } + Dimension &width(void) { return _width; } + +private: + Dimension _count; + Dimension _depth; + Dimension _height; + Dimension _width; +}; + +} // namespace loco + +#endif // __LOCO_IR_FEATURE_SHAPE_H__ diff --git a/compiler/loco/include/loco/IR/FilterAxis.h b/compiler/loco/include/loco/IR/FilterAxis.h new file mode 100644 index 000000000..269e2aecc --- /dev/null +++ b/compiler/loco/include/loco/IR/FilterAxis.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 __LOCO_IR_FILTER_AXIS_H__ +#define __LOCO_IR_FILTER_AXIS_H__ + +namespace loco +{ + +enum class FilterAxis +{ + Count, + Depth, + Height, + Width +}; + +} // namespace loco + +#endif // __LOCO_IR_FILTER_AXIS_H__ diff --git a/compiler/loco/include/loco/IR/FilterCodec.h b/compiler/loco/include/loco/IR/FilterCodec.h new file mode 100644 index 000000000..3ff548d6d --- /dev/null +++ b/compiler/loco/include/loco/IR/FilterCodec.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 __LOCO_IR_FILTER_CODEC_H__ +#define __LOCO_IR_FILTER_CODEC_H__ + +#include "loco/IR/FilterShape.h" +#include "loco/IR/FilterIndex.h" + +#include "loco/IR/TensorShape.h" +#include "loco/IR/TensorIndex.h" + +namespace loco +{ + +/** + * @brief Decribe how to build a (convolution) filter from a tensor + * + * Let us assume that "enc" is a filter encoder. + * + * Given a tensor "inp" and its shape "inp.shape", "enc" builds a filter + * "out" as follows: + * + * for each valid filter index (referred to as filter_index below) for enc.shape(inp.shape) + * out.at(filter_index) = inp.at(enc.value(filter_index)) + */ +struct FilterEncoder +{ + virtual ~FilterEncoder() = default; + + virtual FilterShape shape(const TensorShape &shape) const = 0; + virtual TensorIndex value(const FilterIndex &index) const = 0; +}; + +/** + * @brief Decribe how to build a a tensor from a filter + */ +struct FilterDecoder +{ + virtual ~FilterDecoder() = default; + + virtual TensorShape shape(const FilterShape &shape) const = 0; + virtual FilterIndex value(const TensorIndex &index) const = 0; +}; + +} // namespace loco + +#endif // __LOCO_IR_FILTER_CODEC_H__ diff --git a/compiler/loco/include/loco/IR/FilterIndex.h b/compiler/loco/include/loco/IR/FilterIndex.h new file mode 100644 index 000000000..5765ea764 --- /dev/null +++ b/compiler/loco/include/loco/IR/FilterIndex.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 __LOCO_IR_FILTER_INDEX_H__ +#define __LOCO_IR_FILTER_INDEX_H__ + +#include <cstdint> + +namespace loco +{ + +/** + * \brief Filter Index + * + * Filter Index indicates an "element" in a given (convolutional) filter. + * + * Let us assume that there is a filter K where KS denotes its shape (of FilterShape type). + * + * Then, any valid filter index I satisfies the following invariants: + * - 0 <= I.nth() < KS.count() + * - 0 <= I.channel() < KS.depth() + * - 0 <= I.row() < KS.height() + * - 0 <= I.column() < KS.width() + */ +class FilterIndex final +{ +public: + FilterIndex() = default; + +public: + const uint32_t &nth(void) const { return _nth; } + uint32_t &nth(void) { return _nth; } + + const uint32_t &channel(void) const { return _channel; } + uint32_t &channel(void) { return _channel; } + + const uint32_t &row(void) const { return _row; } + uint32_t &row(void) { return _row; } + + const uint32_t &column(void) const { return _column; } + uint32_t &column(void) { return _column; } + +private: + uint32_t _nth = 0; + uint32_t _channel = 0; + uint32_t _row = 0; + uint32_t _column = 0; +}; + +} // namespace loco + +#endif // __LOCO_IR_FILTER_INDEX_H__ diff --git a/compiler/loco/include/loco/IR/FilterShape.h b/compiler/loco/include/loco/IR/FilterShape.h new file mode 100644 index 000000000..00e44892a --- /dev/null +++ b/compiler/loco/include/loco/IR/FilterShape.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 __LOCO_IR_FILTER_SHAPE_H__ +#define __LOCO_IR_FILTER_SHAPE_H__ + +#include "loco/IR/Dimension.h" + +namespace loco +{ + +/** + * \brief Filter Shape + * + * This class describes the shape of filter, which is an input of 2D + * convolutional operations (e.g. Convolution). + * + * count() refers to the number of 3D weight in a filter + * depth() refers to the depth of 3D weights + * height() refers to the height of 3D weights + * width() refers to the width of 3D weights + * + * NOTE + * + * The definition of FilterShape is almost same as that of FeatureShape, but loco + * distinguishes FeatureShape and FilterShape in class-level in order to prevent + * potential errors by type check. + */ +class FilterShape final +{ +public: + FilterShape() = default; + +public: + const Dimension &count(void) const { return _count; } + Dimension &count(void) { return _count; } + + const Dimension &depth(void) const { return _depth; } + Dimension &depth(void) { return _depth; } + + const Dimension &height(void) const { return _height; } + Dimension &height(void) { return _height; } + + const Dimension &width(void) const { return _width; } + Dimension &width(void) { return _width; } + +private: + Dimension _count; + Dimension _depth; + Dimension _height; + Dimension _width; +}; + +} // namespace loco + +#endif // __LOCO_IR_FILTER_SHAPE_H__ diff --git a/compiler/loco/include/loco/IR/Graph.forward.h b/compiler/loco/include/loco/IR/Graph.forward.h new file mode 100644 index 000000000..2a43be93a --- /dev/null +++ b/compiler/loco/include/loco/IR/Graph.forward.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 __LOCO_IR_GRAPH_FORWARD_H__ +#define __LOCO_IR_GRAPH_FORWARD_H__ + +namespace loco +{ + +// This forward declaration SHOULD BE aligned with the actual declaration in "Graph.h". +class Graph; + +} // namespace loco + +#endif // __LOCO_IR_GRAPH_FORWARD_H__ diff --git a/compiler/loco/include/loco/IR/Graph.h b/compiler/loco/include/loco/IR/Graph.h new file mode 100644 index 000000000..a820aba91 --- /dev/null +++ b/compiler/loco/include/loco/IR/Graph.h @@ -0,0 +1,284 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 __LOCO_IR_GRAPH_H__ +#define __LOCO_IR_GRAPH_H__ + +#include "loco/IR/DataType.h" +// TODO Include "Node.h" instead +#include "loco/IR/Nodes.h" +#include "loco/IR/NodePool.h" +#include "loco/IR/GraphInputIndex.h" +#include "loco/IR/GraphOutputIndex.h" + +#include "loco/ADT/ObjectPool.h" + +#include <initializer_list> +#include <set> +#include <string> +#include <memory> +#include <vector> + +namespace loco +{ + +// TODO Introduce Named trait +enum class Trait +{ + // Any "DataTyped" class has the following methods + // - DataType dtype(void) const; + // - void dtype(const DataType &value); + DataTyped, + // Any "TensorShaped" class has the following methods + // - const TensorShape *shape(void) const; + // - void shape(std::unique_ptr<TensorShape> &&); + // - void shape(std::initializer_list<Dimension> &&); + // + // TODO Rename NodeMixin::TensorShape as NodeMixin::NDShape + TensorShaped, +}; + +template <Trait T> class Mixin; + +// TODO Re-implement NodeMixin<NodeTrait::DataType> using this mixin +template <> class Mixin<Trait::DataTyped> +{ +public: + Mixin() = default; + +public: + const DataType &dtype(void) const { return _dtype; } + void dtype(const DataType &value) { _dtype = value; } + +private: + DataType _dtype = DataType::Unknown; +}; + +template <> class Mixin<Trait::TensorShaped> +{ +public: + Mixin() = default; + +public: + const TensorShape *shape(void) const { return _shape.get(); } + void shape(std::unique_ptr<TensorShape> &&shape) { _shape = std::move(shape); } + void shape(std::initializer_list<Dimension> dims); + +private: + std::unique_ptr<TensorShape> _shape = nullptr; +}; + +/** + * @brief Trait for elements with name + */ +class NamedEntity +{ +public: + const std::string &name(void) const { return _name; } + void name(const std::string &name) { _name = name; } + +/// If new interface methods are added to this class they also will need to +/// be added in `using` of this macro to get them visible from inherited classes +#define LOCO_NAMED_ENTITY_EXPOSE using NamedEntity::name + +private: + std::string _name; +}; + +/** + * @brief Graph-level Input Metadata + */ +class GraphInput final : private NamedEntity, + public Mixin<Trait::DataTyped>, + public Mixin<Trait::TensorShaped> +{ +public: + LOCO_NAMED_ENTITY_EXPOSE; + + // TODO Use GraphInputIndex (instead of uint32_t) + GraphInput(uint32_t index) : _index{index} + { + // DO NOTHING + } + + GraphInput(const GraphInput &) = delete; + GraphInput(GraphInput &&) = delete; + + ~GraphInput() = default; + +public: + GraphInputIndex index(void) const { return _index; } + +private: + uint32_t _index; +}; + +/** + * @brief Graph-level Output Metadata + */ +class GraphOutput final : private NamedEntity, + public Mixin<Trait::DataTyped>, + public Mixin<Trait::TensorShaped> +{ +public: + LOCO_NAMED_ENTITY_EXPOSE; + + // TODO Use GraphOutputIndex (instead of uint32_t) + GraphOutput(uint32_t index) : _index{index} + { + // DO NOTHING + } + + GraphOutput(const GraphOutput &) = delete; + GraphOutput(GraphOutput &&) = delete; + + ~GraphOutput() = default; + +public: + GraphOutputIndex index(void) const { return _index; } + +private: + uint32_t _index; +}; + +/** + * @brief A neural network graph + */ +class Graph final : public NamedEntity +{ +public: + /** + * @brief Node Pool + * + * This alias confines the impact of changes to loco internals. + * + * TODO Remove this alias + */ + using NodeContext = NodePool; + + /** + * @brief Object Pool with Simple Factory Method + * + * TODO Remove this unused class + */ + template <typename T> struct SimpleFactoryObjectPool : public ObjectPool<T> + { + virtual ~SimpleFactoryObjectPool() = default; + + T *create(void) + { + std::unique_ptr<T> ptr{new T}; + return ObjectPool<T>::take(std::move(ptr)); + } + }; + + /** + * @brief GraphInput Pool + */ + struct InputContext final : public ObjectPool<GraphInput> + { + GraphInput *create(void); + }; + + /** + * @brief GraphOutput Pool + */ + struct OutputContext final : public ObjectPool<GraphOutput> + { + GraphOutput *create(void); + }; + +public: + Graph() + { + // Associate "NodeContext" and the current "Graph" + _node_ctx.graph(this); + } + + // Copy/Move is not allowed for Graph + Graph(const Graph &) = delete; + Graph(Graph &&) = delete; + + ~Graph() = default; + +public: + NodeContext *nodes(void) { return &_node_ctx; } + const NodeContext *nodes(void) const { return &_node_ctx; } + InputContext *inputs(void) { return &_input_ctx; } + const InputContext *inputs(void) const { return &_input_ctx; } + OutputContext *outputs(void) { return &_output_ctx; } + const OutputContext *outputs(void) const { return &_output_ctx; } + +private: + NodeContext _node_ctx; + InputContext _input_ctx; + OutputContext _output_ctx; +}; + +struct GraphInputIndexQueryService : public DialectService +{ + virtual ~GraphInputIndexQueryService() = default; + + /** + * @brief Check whether a given node is associated with any Graph-level input + */ + virtual bool associated(const Node *node) const = 0; + + /** + * Exceptions + * - index SHOULD throw std::invalid_argument exception if a given node is not associated with + * any input (i.e. assocaited above returns false). + */ + virtual GraphInputIndex index(const Node *node) const = 0; +}; + +std::vector<Node *> input_nodes(const Graph *); + +struct GraphOutputIndexQueryService : public DialectService +{ + virtual ~GraphOutputIndexQueryService() = default; + + /** + * @brief Check whether a given node is associated with any Graph-level output + */ + virtual bool associated(const Node *node) const = 0; + + /** + * Exceptions + * - index SHOULD throw std::invalid_argument exception if a given node is not associated with + * any output (i.e. assocaited above returns false). + */ + virtual GraphOutputIndex index(const Node *node) const = 0; +}; + +// TODO Use "const Graph *" +std::vector<Node *> output_nodes(Graph *); + +/** + * @brief Enumerate all the nodes in a given graph + * + * NOTE This method returns std::set<Node *> unlike input_nodes and output_nodes. + * + * Please use traverse algorithms that "Algorithm.h" provides (such as postorder_traversal) + * if order is relevant for implementation. + */ +std::set<Node *> all_nodes(Graph *); + +std::unique_ptr<Graph> make_graph(void); + +} // namespace loco + +#endif // __LOCO_IR_GRAPH_H__ diff --git a/compiler/loco/include/loco/IR/GraphInputIndex.h b/compiler/loco/include/loco/IR/GraphInputIndex.h new file mode 100644 index 000000000..3c7ae98ef --- /dev/null +++ b/compiler/loco/include/loco/IR/GraphInputIndex.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 __LOCO_IR_GRAPH_INPUT_INDEX_H__ +#define __LOCO_IR_GRAPH_INPUT_INDEX_H__ + +#include <cstdint> + +namespace loco +{ + +using GraphInputIndex = uint32_t; + +} // namespace loco + +#endif // __LOCO_IR_GRAPH_INPUT_INDEX_H__ diff --git a/compiler/loco/include/loco/IR/GraphOutputIndex.h b/compiler/loco/include/loco/IR/GraphOutputIndex.h new file mode 100644 index 000000000..3231cbd95 --- /dev/null +++ b/compiler/loco/include/loco/IR/GraphOutputIndex.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 __LOCO_IR_GRAPH_OUTPUT_INDEX_H__ +#define __LOCO_IR_GRAPH_OUTPUT_INDEX_H__ + +#include <cstdint> + +namespace loco +{ + +using GraphOutputIndex = uint32_t; + +} // namespace loco + +#endif // __LOCO_IR_GRAPH_OUTPUT_INDEX_H__ diff --git a/compiler/loco/include/loco/IR/MatrixAxis.h b/compiler/loco/include/loco/IR/MatrixAxis.h new file mode 100644 index 000000000..8a1689bb3 --- /dev/null +++ b/compiler/loco/include/loco/IR/MatrixAxis.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 __LOCO_IR_MATRIX_AXIS_H__ +#define __LOCO_IR_MATRIX_AXIS_H__ + +namespace loco +{ + +enum class MatrixAxis +{ + Height, + Width +}; + +} // namespace loco + +#endif // __LOCO_IR_MATRIX_AXIS_H__ diff --git a/compiler/loco/include/loco/IR/MatrixCodec.h b/compiler/loco/include/loco/IR/MatrixCodec.h new file mode 100644 index 000000000..40312641a --- /dev/null +++ b/compiler/loco/include/loco/IR/MatrixCodec.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 __LOCO_IR_MATRIX_CODEC_H__ +#define __LOCO_IR_MATRIX_CODEC_H__ + +#include "loco/IR/MatrixShape.h" +#include "loco/IR/MatrixIndex.h" + +#include "loco/IR/TensorShape.h" +#include "loco/IR/TensorIndex.h" + +#include <memory> + +namespace loco +{ + +/** + * @brief Decribe how to build a matrix from a tensor + * + * Let us assume that "enc" is a matrix encoder. + * + * Given a tensor "inp" and its shape "inp.shape", "enc" builds a matrix + * "out" as follows: + * + * for each valid matrix index (referred to as matrix_idx below) for enc.shape(inp.shape) + * out.at(matrix_index) = inp.at(enc.value(matrix_index)) + */ +struct MatrixEncoder +{ + virtual ~MatrixEncoder() = default; + + virtual MatrixShape shape(const TensorShape &shape) const = 0; + virtual TensorIndex value(const MatrixIndex &index) const = 0; +}; + +/** + * @brief Describe how to build a tensor from a matrix + * + * Let us assume that "dec" is a matrix decoder. + * + * Given a matrix "inp" and its shape "inp.shape", "dec" builds a tensor + * "out" as follows: + * + * for each valid tensor index (referred to as tensor_index below) for dec.shape(inp.shape) + * out.at(tensor_index) = inp.at(dec.value(tensor_index)) + * + * NOTE "inp" is a matrix value and "out" is a tensor value in this example. + */ +struct MatrixDecoder +{ + virtual ~MatrixDecoder() = default; + + virtual TensorShape shape(const MatrixShape &) const = 0; + virtual MatrixIndex value(const TensorIndex &) const = 0; +}; + +} // namespace loco + +#endif // __LOCO_IR_MATRIX_CODEC_H__ diff --git a/compiler/loco/include/loco/IR/MatrixIndex.h b/compiler/loco/include/loco/IR/MatrixIndex.h new file mode 100644 index 000000000..eb6d65580 --- /dev/null +++ b/compiler/loco/include/loco/IR/MatrixIndex.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 __LOCO_IR_MATRIX_INDEX_H__ +#define __LOCO_IR_MATRIX_INDEX_H__ + +#include <cstdint> + +namespace loco +{ + +/** + * @brief Matrix Index + * + * Matrix Index indicates an "element" in a given Matrix + * + * Let us assume that there is a Matrix F and S denotes its shape (of MatrixShape type). + * + * Then, any valid Matrix index I satisfies the following invariants: + * - 0 <= I.row() < S.height() + * - 0 <= I.column() < S.width() + */ +class MatrixIndex final +{ +public: + MatrixIndex() = default; + +public: + const uint32_t &row(void) const { return _row; } + uint32_t &row(void) { return _row; } + + const uint32_t &column(void) const { return _column; } + uint32_t &column(void) { return _column; } + +private: + uint32_t _row = 0; + uint32_t _column = 0; +}; + +} // namespace loco + +#endif // __LOCO_IR_MATRIX_INDEX_H__ diff --git a/compiler/loco/include/loco/IR/MatrixShape.h b/compiler/loco/include/loco/IR/MatrixShape.h new file mode 100644 index 000000000..512691beb --- /dev/null +++ b/compiler/loco/include/loco/IR/MatrixShape.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 __LOCO_IR_MATRIX_SHAPE_H__ +#define __LOCO_IR_MATRIX_SHAPE_H__ + +#include "loco/IR/Dimension.h" + +namespace loco +{ + +/** + * @brief Matrix Shape + * + * This class describes the shape of matrix, which serves as the input/output of + * matrix operations (e.g. Matrix Multiplication). + * + * Each matrix is a collection of 2D features conceptually. + * Each matrix has height, width. + * + * height() refers to the height of matrix in a given matrix + * width() refers to the width of matrix in a given matrix + */ +class MatrixShape final +{ +public: + MatrixShape() = default; + +public: + const Dimension &height(void) const { return _height; } + Dimension &height(void) { return _height; } + + const Dimension &width(void) const { return _width; } + Dimension &width(void) { return _width; } + +private: + Dimension _height; + Dimension _width; +}; + +} // namespace loco + +#endif // __LOCO_IR_MATRIX_SHAPE_H__ diff --git a/compiler/loco/include/loco/IR/Node.forward.h b/compiler/loco/include/loco/IR/Node.forward.h new file mode 100644 index 000000000..425b28aff --- /dev/null +++ b/compiler/loco/include/loco/IR/Node.forward.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 __LOCO_IR_NODE_FORWARD_H__ +#define __LOCO_IR_NODE_FORWARD_H__ + +namespace loco +{ + +// NOTE This forward declaration SHOULD BE aligned with Node delcaration in "Node.h" +class Node; + +} // namespace loco + +#endif // __LOCO_IR_NODE_FORWARD_H__ diff --git a/compiler/loco/include/loco/IR/Node.h b/compiler/loco/include/loco/IR/Node.h new file mode 100644 index 000000000..ef0bf238d --- /dev/null +++ b/compiler/loco/include/loco/IR/Node.h @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 __LOCO_IR_NODE_H__ +#define __LOCO_IR_NODE_H__ + +#include "loco/ADT/AnnotatedItem.h" + +#include "loco/IR/Use.h" +#include "loco/IR/Dialect.h" +#include "loco/IR/NodePool.forward.h" +#include "loco/IR/Graph.forward.h" + +#include <array> +#include <memory> +#include <set> + +namespace loco +{ + +/** + * @brief Extensible Node Metadata + */ +struct NodeAnnotation +{ + virtual ~NodeAnnotation() = default; +}; + +enum class SubstQualifier +{ + Default, // Replace all the occurrences as "Use" (by default) +}; + +template <SubstQualifier Q> class Subst; + +/** + * @brief Logical unit of computation + */ +class Node : public AnnotatedItem<NodeAnnotation> +{ +public: + friend class Use; + friend class Subst<SubstQualifier::Default>; + friend class NodePool; + friend std::set<Node *> succs(const Node *node); + +public: + Node() = default; + + Node(const Node &) = delete; + Node(Node &&) = delete; + + virtual ~Node(); + +public: + Graph *graph(void) { return _graph; } + const Graph *graph(void) const { return _graph; } + +private: + /** + * @brief Set associated "Graph" + * + * @note Only "NodePool" class is permitted to invoke this private method. + */ + void graph(Graph *g) { _graph = g; } + +public: + /** + * @brief Return "Dialect" identifier that this node belongs to + * + * dialect() SHOULD return a valid pointer. + */ + virtual const Dialect *dialect(void) const = 0; + + virtual uint32_t opnum(void) const = 0; + +public: + /// @brief Return the number of arguments + virtual uint32_t arity(void) const = 0; + + /// @brief Access N-th argument node + virtual Node *arg(uint32_t N) const = 0; + + /** + * @brief Drop all the reference of arguments + * + * arg(n) SHOULD return nullptr for every valid n after drop() call. + */ + virtual void drop(void) = 0; + +private: + /** + * @brief Associated Graph + * + * May be nullptr if no associated Graph exists. + */ + Graph *_graph = nullptr; + + /** + * @brief The edges to a node that uses this node as its argument + * + * @note "succs" function below accesses this private field. + */ + std::set<Use *> _uses; +}; + +/// @brief Enumerate all the predecessors of a given node +std::set<Node *> preds(const Node *node); +/// @brief Enumerate all the successors of a given node +std::set<Node *> succs(const Node *node); + +/** + * @brief A helper for below "replace" helper + */ +template <> class Subst<SubstQualifier::Default> +{ +public: + friend Subst<SubstQualifier::Default> replace(Node *node); + +private: + explicit Subst(Node *from); + +public: + void with(Node *into) const; + +private: + Node *_from; +}; + +Subst<SubstQualifier::Default> replace(Node *node); + +} // namespace loco + +#endif // __LOCO_IR_NODE_H__ diff --git a/compiler/loco/include/loco/IR/NodeMixins.h b/compiler/loco/include/loco/IR/NodeMixins.h new file mode 100644 index 000000000..f0e34b0ba --- /dev/null +++ b/compiler/loco/include/loco/IR/NodeMixins.h @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 __LOCO_IR_NODE_MIXINS_H__ +#define __LOCO_IR_NODE_MIXINS_H__ + +#include "loco/IR/Node.h" +#include "loco/IR/DataType.h" +#include "loco/IR/Dimension.h" + +#include <vector> +#include <initializer_list> + +namespace loco +{ + +enum class NodeTrait +{ + DataType, + // Nodes with TensorShape trait will provide the following methods: + // - rank() + // - rank(value) + // - dim() + // - dim(value) + // - shape({...}) + TensorShape, +}; + +template <NodeTrait T> class NodeMixin; + +template <> class NodeMixin<NodeTrait::DataType> +{ +public: + NodeMixin() = default; + +public: + const DataType &dtype(void) const { return _dtype; } + void dtype(const DataType &dtype) { _dtype = dtype; } + +private: + /// @brief Data type + DataType _dtype{DataType::Unknown}; +}; + +template <> class NodeMixin<NodeTrait::TensorShape> +{ +public: + NodeMixin() = default; + +public: + uint32_t rank(void) const { return _dims.size(); } + void rank(uint32_t value) { _dims.resize(value); } + + const Dimension &dim(uint32_t axis) const { return _dims.at(axis); } + Dimension &dim(uint32_t axis) { return _dims.at(axis); } + + void shape(std::initializer_list<uint32_t> dims) + { + rank(dims.size()); + + uint32_t axis = 0; + for (auto d : dims) + { + dim(axis++) = d; + } + } + +private: + /// @brief Data shape (as tensor) + std::vector<Dimension> _dims; +}; + +template <unsigned N> struct FixedArity +{ + template <typename Base> class Mixin : public virtual Base + { + public: + Mixin() + { + for (uint32_t n = 0; n < N; ++n) + { + _args[n] = std::unique_ptr<Use>{new Use{this}}; + } + } + + virtual ~Mixin() = default; + + public: + unsigned arity(void) const final { return N; } + + Node *arg(uint32_t n) const final { return _args.at(n)->node(); } + + void drop(void) final + { + for (uint32_t n = 0; n < N; ++n) + { + _args.at(n)->node(nullptr); + } + } + + protected: + // This API allows inherited classes to access "_args" field. + Use *at(unsigned n) const { return _args.at(n).get(); } + + private: + std::array<std::unique_ptr<Use>, N> _args{}; + }; +}; + +template <NodeTrait Trait> struct With +{ + template <typename Base> struct Mixin : public virtual Base, public NodeMixin<Trait> + { + // DO NOTHING + }; +}; + +} // namespace loco + +#endif // __LOCO_IR_NODE_MIXINS_H__ diff --git a/compiler/loco/include/loco/IR/NodePool.forward.h b/compiler/loco/include/loco/IR/NodePool.forward.h new file mode 100644 index 000000000..87bf01311 --- /dev/null +++ b/compiler/loco/include/loco/IR/NodePool.forward.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 __LOCO_IR_NODE_POOL_FORWARD_H__ +#define __LOCO_IR_NODE_POOL_FORWARD_H__ + +namespace loco +{ + +// This forward declaration SHOULD BE aligned with the actual declaration in "NodePool.h". +class NodePool; + +} // namespace loco + +#endif // __LOCO_IR_NODE_POOL_FORWARD_H__ diff --git a/compiler/loco/include/loco/IR/NodePool.h b/compiler/loco/include/loco/IR/NodePool.h new file mode 100644 index 000000000..4db4caae3 --- /dev/null +++ b/compiler/loco/include/loco/IR/NodePool.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 __LOCO_IR_NODE_POOL_H__ +#define __LOCO_IR_NODE_POOL_H__ + +#include "loco/IR/Node.h" +#include "loco/IR/Graph.forward.h" + +#include "loco/ADT/ObjectPool.h" + +namespace loco +{ + +class NodePool final : public ObjectPool<Node> +{ +public: + friend class Graph; + +public: + ~NodePool(); + +public: + template <typename Derived, typename... Args> Derived *create(Args &&... args) + { + std::unique_ptr<Derived> ptr{new Derived(std::forward<Args>(args)...)}; + ptr->graph(_graph); + return ObjectPool<Node>::take<Derived>(std::move(ptr)); + } + + void destroy(Node *node) + { + if (!ObjectPool<Node>::erase(node)) + { + throw std::invalid_argument{"node"}; + } + } + +private: + /// Only "Graph" is permitted to invoke this private method. + void graph(Graph *g) { _graph = g; } + +private: + Graph *_graph = nullptr; +}; + +} // namespace loco + +#endif // __LOCO_IR_NODE_POOL_H__ diff --git a/compiler/loco/include/loco/IR/NodeShape.h b/compiler/loco/include/loco/IR/NodeShape.h new file mode 100644 index 000000000..5eefd3c19 --- /dev/null +++ b/compiler/loco/include/loco/IR/NodeShape.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 __LOCO_IR_NODE_SHAPE_H__ +#define __LOCO_IR_NODE_SHAPE_H__ + +#include "loco/IR/Domain.h" + +#include "loco/IR/BiasShape.h" +#include "loco/IR/DepthwiseFilterShape.h" +#include "loco/IR/FeatureShape.h" +#include "loco/IR/FilterShape.h" +#include "loco/IR/MatrixShape.h" +#include "loco/IR/TensorShape.h" + +#include <vector> + +namespace loco +{ + +class NodeShape final +{ +public: + NodeShape() = default; + +public: + NodeShape(const BiasShape &shape) { set(shape); } + NodeShape(const DepthwiseFilterShape &shape) { set(shape); } + NodeShape(const FeatureShape &shape) { set(shape); } + NodeShape(const FilterShape &shape) { set(shape); } + NodeShape(const MatrixShape &shape) { set(shape); } + NodeShape(const TensorShape &shape) { set(shape); } + +public: + const Domain &domain(void) const { return _domain; } + +public: + void set(const BiasShape &); + void set(const DepthwiseFilterShape &); + void set(const FeatureShape &); + void set(const FilterShape &); + void set(const MatrixShape &); + void set(const TensorShape &); + +public: + template <typename ShapeType> ShapeType as(void) const; + +private: + Domain _domain = Domain::Unknown; + std::vector<Dimension> _dims; +}; + +bool operator==(const NodeShape &lhs, const NodeShape &rhs); + +} // namespace loco + +#endif // __LOCO_IR_NODE_SHAPE_H__ diff --git a/compiler/loco/include/loco/IR/Nodes.h b/compiler/loco/include/loco/IR/Nodes.h new file mode 100644 index 000000000..9aac48b6e --- /dev/null +++ b/compiler/loco/include/loco/IR/Nodes.h @@ -0,0 +1,1123 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 __LOCO_IR_NODES_H__ +#define __LOCO_IR_NODES_H__ + +#include "loco/IR/Node.h" +#include "loco/IR/Use.h" +#include "loco/IR/Domain.h" +#include "loco/IR/DataType.h" +#include "loco/IR/DataTypeTraits.h" +#include "loco/IR/Dimension.h" +#include "loco/IR/Window.h" +#include "loco/IR/Stride.h" +#include "loco/IR/Padding2D.h" +#include "loco/IR/PaddingND.h" +#include "loco/IR/TensorAxis.h" +#include "loco/IR/TensorAxisSet.h" +#include "loco/IR/FeatureCodec.h" +#include "loco/IR/FilterCodec.h" +#include "loco/IR/DepthwiseFilterCodec.h" +#include "loco/IR/MatrixCodec.h" +#include "loco/IR/NodeMixins.h" +#include "loco/IR/CanonicalNodeDecl.h" +#include "loco/IR/GraphInputIndex.h" +#include "loco/IR/GraphOutputIndex.h" + +namespace loco +{ + +class Graph; +class GraphInput; +class GraphOutput; + +/** + * @brief Make a value visible to user + */ +class Push /* to user */ final + : public CanonicalNodeDef<CanonicalOpcode::Push, FixedArity<1>::Mixin> +{ +public: + Push() = default; + +public: + Node *from(void) const { return at(0)->node(); } + void from(Node *node) { at(0)->node(node); } + +public: + void index(const GraphOutputIndex &index); + + /** + * @brief Get associated output index + * + * The behavior of this method is undefined when "index" is not set before. + * + * NOTE This method intentionally returns "GraphOutputIndex" instead of "const GraphOutputIndex &" + * not to expose the internal implementation details. + */ + GraphOutputIndex index(void) const; + + /** + * @brief Check whether index is initialized + * + * NOTE "indexed" method does not validate whether index is in a valid range + */ + bool indexed(void) const { return _index != -1; } + +private: + int64_t _index = -1; // Uninitialized +}; + +void link(GraphOutput *, Push *push); + +/// @brief Find a Push node with a given output index +Push *push_node(Graph *g, const GraphOutputIndex &index); + +/** + * @brief Create a value from user data + */ +class Pull /* from user */ final + : public CanonicalNodeDef<CanonicalOpcode::Pull, FixedArity<0>::Mixin, + With<NodeTrait::TensorShape>::Mixin> +{ +public: + Pull() = default; + +public: + void index(const GraphInputIndex &index); + + /** + * @brief Get associated input index + * + * The behavior of this method is undefined when "index" is not set before. + * + * NOTE This method intentionally returns "GraphInputIndex" instead of "const GraphInputIndex &" + * not to expose the internal implementation details. + */ + GraphInputIndex index(void) const; + + /** + * @brief Check whether index is initialized + * + * NOTE "indexed" method does not validate whether index is in a valid range + */ + bool indexed(void) const { return _index != -1; } + +public: + void dtype(const DataType &d); + DataType dtype(void) const; + +private: + int64_t _index = -1; // Uninitialized + + /** + * @brief Locally cached data type attribute + * + * TODO Remove this cache once all the clients are updated + */ + DataType _dtype = DataType::Unknown; +}; + +void link(GraphInput *, Pull *pull); + +/// @brief Find a Pull node with a given input index +Pull *pull_node(Graph *g, const GraphInputIndex &index); + +/** + * @brief Create a new value identical to its input + * + * This node may encode memory transfer (such as CPU -> GPU or GPU -> CPU) + */ +class Forward final : public CanonicalNodeDef<CanonicalOpcode::Forward, FixedArity<1>::Mixin> +{ +public: + Forward() = default; + +public: + Node *input(void) const { return at(0)->node(); } + void input(Node *node) { at(0)->node(node); } +}; + +/** + * @brief Create a new value that rectifies its input + */ +class ReLU final : public CanonicalNodeDef<CanonicalOpcode::ReLU, FixedArity<1>::Mixin> +{ +public: + ReLU() = default; + +public: + Node *input(void) const { return at(0)->node(); } + void input(Node *node) { at(0)->node(node); } +}; + +/** + * @brief Create a new value that rectifies its input capping the units at 6. + */ +class ReLU6 final : public CanonicalNodeDef<CanonicalOpcode::ReLU6, FixedArity<1>::Mixin> +{ +public: + ReLU6() = default; + +public: + Node *input(void) const { return at(0)->node(); } + void input(Node *node) { at(0)->node(node); } +}; + +/** + * @brief Create a new value that rectifies its input by tanh + */ +class Tanh final : public CanonicalNodeDef<CanonicalOpcode::Tanh, FixedArity<1>::Mixin> +{ +public: + Tanh() = default; + +public: + Node *input(void) const { return at(0)->node(); } + void input(Node *node) { at(0)->node(node); } +}; + +/** + * @brief Create a value from constant byte array + * + * @note ConstGen assumes "lexical memory layout". + * + * Let us assume that a 'ConstGen' generates a constant tensor of shape "S". + * for each valid index I, the corresponding value comes from offset(S, I) + * where the implementation of "offset" is given as follows: + * + * uint32_t stride(TensorShape shape, uint32_t axis) { + * uint32_t res = 1; + * for (uint32_t n = rank(shape) - 1; n > axis; --n) { res *= shape.dim(n); } + * return res; + * } + * + * uint32_t offset(TensorShape shape, TensorIndex index) { + * uint32_t res = 0; + * for (uint32_t n = 0; n < rank(shape); ++n) { res += index.at(n) * stride(shape, n); } + * return res; + * } + */ +class ConstGen final + : public CanonicalNodeDef<CanonicalOpcode::ConstGen, FixedArity<0>::Mixin, + With<NodeTrait::DataType>::Mixin, With<NodeTrait::TensorShape>::Mixin> +{ +public: + ConstGen() = default; + +public: + /** + * @brief Return the number of reserved elements + * @note This method returns the number of ELEMENT (not BYTE). + */ + template <DataType DT> uint32_t size(void) const; + + /** + * @brief Adjust the number of reserved elements + */ + template <DataType DT> void size(uint32_t size); + + /** + * @brief Get the element at a given position + * @require at(n) is valid only when n < size() + */ + template <DataType DT> const typename DataTypeImpl<DT>::Type &at(uint32_t n) const; + + /** + * @brief Update the element at a given position + * @require at(n) is valid only when n < size() + */ + template <DataType DT> typename DataTypeImpl<DT>::Type &at(uint32_t n); + +private: + /// @brief Data + std::vector<uint8_t> _data; +}; + +/** + * @brief 2D Max Pooling + * + * MaxPool2D takes as input a feature map, and produces another feature map + * + * --- + * Any valid MaxPool2D nodes SHOULD satisfy the following conditions. + * + * Let us define several helper functions that takes a MaxPool2D nodes first: + * - IFM_DOMAIN returns the domain of its input + * - IFM_H returns the height of its input. + * - IFM_W returns the width of its input. + * - PAD_T returns the top padding required over its input + * - PAD_B returns the bottom padding required over its input + * - PAD_L returns the left padding required over its input + * - PAD_R returns the right padding required over its input + * - WIN_H returns the height of its receptive field. + * - WIN_W returns the width of its receptive field. + * - STRIDE_H returns the vertical(= on height) stride. + * - STRIDE_W returns the horizontal(= on width) stride. + * + * Condition 1 + * Statement + * + * A valid MaxPool2D node M SHOULD satisfy the following condition: + * - IFM_DOMAIN(M) == Feature + * + * Motivation + * + * There are many possible ways to encode a feature map as a tensor. + * - e.g. NCHW/NHWC/... + * + * In order to give some freedom on memory layout to backend, loco requires a feature map + * value to be explicitly encoded via FeatureEncode. + * + * Condition 2: + * Statement + * + * A valid MaxPool2D node M SHOULD satisfy the following conditions: + * - (IFM_H(M) + PAD_T(M) + PAD_B(M) - WIN_H(M)) % STRIDE_H(M) == 0 + * - (IFM_W(M) + PAD_L(M) + PAD_R(M) - WIN_W(M)) % STRIDE_W(M) == 0 + * + * Motivation + * + * The output shape may differ for each NN framework when these conditions do not hold. + * + * In order to mitigate such a difference among NN frameworks, loco requires these conditions + * for MaxPool2D nodes. + * + * This means that each frontend implementation SHOULD insert appropriate padding/trimming node + * before/after MaxPool2D node according to the semantics of the corresponding NN framework. + * --- + */ +class MaxPool2D final : public CanonicalNodeDef<CanonicalOpcode::MaxPool2D, FixedArity<1>::Mixin> +{ +public: + Node *ifm(void) const { return at(0)->node(); } + void ifm(Node *node) { at(0)->node(node); } + +public: + const Padding2D *pad(void) const { return &_pad; } + Padding2D *pad(void) { return &_pad; } + +public: + const Window<2> *window(void) const { return &_window; } + Window<2> *window(void) { return &_window; } + +public: + const Stride<2> *stride(void) const { return &_stride; } + Stride<2> *stride(void) { return &_stride; } + +private: + // Pad + Padding2D _pad; + // Window + Window<2> _window; + // Stride + Stride<2> _stride; +}; + +/** + * @brief 2D Average Pooling + * + * @note Follows MaxPool2D (TODO: describe difference) + */ +class AvgPool2D final : public CanonicalNodeDef<CanonicalOpcode::AvgPool2D, FixedArity<1>::Mixin> +{ +public: + enum class Convention + { + Unknown, + // Use the number of elements in each receptive field as a divisor + Full, + // Use the number of valid (non-padding) elements in each receptive field as a divisor + Valid + }; + +public: + Node *ifm(void) const { return at(0)->node(); } + void ifm(Node *node) { at(0)->node(node); } + +public: + Convention convention(void) const { return _convention; } + void convention(const Convention &convention) { _convention = convention; } + +public: + const Padding2D *pad(void) const { return &_pad; } + Padding2D *pad(void) { return &_pad; } + +public: + const Window<2> *window(void) const { return &_window; } + Window<2> *window(void) { return &_window; } + +public: + const Stride<2> *stride(void) const { return &_stride; } + Stride<2> *stride(void) { return &_stride; } + +private: + Convention _convention = Convention::Unknown; + Padding2D _pad; + Window<2> _window; + Stride<2> _stride; +}; + +/** + * @brief Create a feature map from a tensor + */ +class FeatureEncode final + : public CanonicalNodeDef<CanonicalOpcode::FeatureEncode, FixedArity<1>::Mixin> +{ +public: + Node *input(void) const { return at(0)->node(); } + void input(Node *node) { at(0)->node(node); } + +public: + FeatureEncoder *encoder(void) const { return _enc.get(); } + void encoder(std::unique_ptr<FeatureEncoder> &&enc) { _enc = std::move(enc); } + +private: + /// @note "encoder" is mandatory + std::unique_ptr<FeatureEncoder> _enc{nullptr}; +}; + +/** + * @brief Create a tensor from a feature map + */ +class FeatureDecode final + : public CanonicalNodeDef<CanonicalOpcode::FeatureDecode, FixedArity<1>::Mixin> +{ +public: + Node *input(void) const { return at(0)->node(); } + void input(Node *node) { at(0)->node(node); } + +public: + FeatureDecoder *decoder(void) const { return _dec.get(); } + void decoder(std::unique_ptr<FeatureDecoder> &&dec) { _dec = std::move(dec); } + +private: + /// @NOTE "decoder" is mandatory + std::unique_ptr<FeatureDecoder> _dec{nullptr}; +}; + +/** + * @brief Create a filter from a tensor + */ +class FilterEncode final + : public CanonicalNodeDef<CanonicalOpcode::FilterEncode, FixedArity<1>::Mixin> +{ +public: + Node *input(void) const { return at(0)->node(); } + void input(Node *node) { at(0)->node(node); } + +public: + FilterEncoder *encoder(void) const { return _enc.get(); } + void encoder(std::unique_ptr<FilterEncoder> &&enc) { _enc = std::move(enc); } + +private: + /// @note "encoder" is mandatory + std::unique_ptr<FilterEncoder> _enc{nullptr}; +}; + +/** + * @brief Create a tensor from a filter + */ +class FilterDecode final + : public CanonicalNodeDef<CanonicalOpcode::FilterDecode, FixedArity<1>::Mixin> +{ +public: + Node *input(void) const { return at(0)->node(); } + void input(Node *node) { at(0)->node(node); } + +public: + FilterDecoder *decoder(void) const { return _dec.get(); } + void decoder(std::unique_ptr<FilterDecoder> &&dec) { _dec = std::move(dec); } + +private: + /// @note "decoder" is mandatory + std::unique_ptr<FilterDecoder> _dec{nullptr}; +}; + +/** + * @brief Create a depthwise filter from a tensor + */ +class DepthwiseFilterEncode final + : public CanonicalNodeDef<CanonicalOpcode::DepthwiseFilterEncode, FixedArity<1>::Mixin> +{ +public: + Node *input(void) const { return at(0)->node(); } + void input(Node *node) { at(0)->node(node); } + +public: + DepthwiseFilterEncoder *encoder(void) const { return _enc.get(); } + void encoder(std::unique_ptr<DepthwiseFilterEncoder> &&enc) { _enc = std::move(enc); } + +private: + /// @note "encoder" is mandatory + std::unique_ptr<DepthwiseFilterEncoder> _enc{nullptr}; +}; + +/** + * @brief Create a tensor from a depthwise filter + */ +class DepthwiseFilterDecode final + : public CanonicalNodeDef<CanonicalOpcode::DepthwiseFilterDecode, FixedArity<1>::Mixin> +{ +public: + Node *input(void) const { return at(0)->node(); } + void input(Node *node) { at(0)->node(node); } + +public: + DepthwiseFilterDecoder *decoder(void) const { return _dec.get(); } + void decoder(std::unique_ptr<DepthwiseFilterDecoder> &&dec) { _dec = std::move(dec); } + +private: + /// @note "decoder" is mandatory + std::unique_ptr<DepthwiseFilterDecoder> _dec{nullptr}; +}; + +enum class ReshapeType +{ + Fixed, // shape is known at compile time + // Add another type for a case when shape is not known at compile time +}; + +template <ReshapeType RT> class Reshape; + +/** + * @brief Reshape a tensor to another tensor whose shape is known at compile time + * + * @note This class reshapes the shape of an input tensor to _shape. + * Each dimension of _shape should be known at compile time. + * Any dimension of _shape should be greater than 0. + * + * Interpreter or runtime should lexicographically copy an input tensor into an output tensor. + * For example, values of an input tesor of shape [2, 2, 2, 2] will be copied into an output + * tensor of new shape [4, 4] like the following: + * input[0, 0, 0, 0] => output [0, 0] + * input[0, 0, 0, 1] => output [0, 1] + * input[0, 0, 1, 0] => output [0, 2] + * ... + * input[1, 1, 1, 1] => output [3, 3] + */ +template <> +class Reshape<ReshapeType::Fixed> final + : public CanonicalNodeDef<CanonicalOpcode::FixedReshape, FixedArity<1>::Mixin, + With<NodeTrait::TensorShape>::Mixin> +{ +public: + Node *input(void) const { return at(0)->node(); } + void input(Node *node) { at(0)->node(node); } +}; + +using FixedReshape = Reshape<ReshapeType::Fixed>; + +/** + * @brief Concatenate two tensors + * + * Given an axis, TensorConcat takes as input two tensors and produces a tensor + * concatenated along the given axis. + */ +class TensorConcat final + : public CanonicalNodeDef<CanonicalOpcode::TensorConcat, FixedArity<2>::Mixin> +{ +public: + Node *lhs(void) const { return at(0)->node(); } + void lhs(Node *node) { at(0)->node(node); } + + Node *rhs(void) const { return at(1)->node(); } + void rhs(Node *node) { at(1)->node(node); } + +public: + uint32_t axis(void) const { return _axis; } + void axis(uint32_t val) { _axis = val; } + +private: + // Axis + uint32_t _axis{0}; +}; + +/** + * @brief 2D Spatial Convolution + */ +class Conv2D final : public CanonicalNodeDef<CanonicalOpcode::Conv2D, FixedArity<2>::Mixin> +{ +public: + Node *ifm(void) const { return at(0)->node(); } + void ifm(Node *node) { at(0)->node(node); } + + Node *ker(void) const { return at(1)->node(); } + void ker(Node *node) { at(1)->node(node); } + +public: + const Padding2D *pad(void) const { return &_pad; } + Padding2D *pad(void) { return &_pad; } + +public: + const Stride<2> *stride(void) const { return &_stride; } + Stride<2> *stride(void) { return &_stride; } + +private: + Padding2D _pad; + Stride<2> _stride; + + // TODO Support "Dilation" +}; + +/** + * @brief Depthwise 2D Convolution + */ +class DepthwiseConv2D final + : public CanonicalNodeDef<CanonicalOpcode::DepthwiseConv2D, FixedArity<2>::Mixin> +{ +public: + Node *ifm(void) const { return at(0)->node(); } + void ifm(Node *node) { at(0)->node(node); } + + Node *ker(void) const { return at(1)->node(); } + void ker(Node *node) { at(1)->node(node); } + +public: + const Padding2D *pad(void) const { return &_pad; } + Padding2D *pad(void) { return &_pad; } + +public: + const Stride<2> *stride(void) const { return &_stride; } + Stride<2> *stride(void) { return &_stride; } + +private: + Padding2D _pad; + Stride<2> _stride; + + // TODO Support "Dilation" +}; + +/** + * @brief Reduce type functions + */ +enum class ReduceFunc +{ + Mean, // ReduceMean + // TODO Support other reduce operations +}; + +/** + * @brief Computes ReduceFunc operations for Tensor domain + * @note All the reduce functions always keep dimensions + */ +class TensorReduce final + : public CanonicalNodeDef<CanonicalOpcode::TensorReduce, FixedArity<1>::Mixin> +{ +public: + Node *input(void) const { return at(0)->node(); } + void input(Node *node) { at(0)->node(node); } + +public: + const TensorAxisSet *axes(void) const { return &_axes; } + TensorAxisSet *axes(void) { return &_axes; } + +public: + ReduceFunc func(void) const { return _func; } + void func(ReduceFunc func) { _func = func; } + +private: + TensorAxisSet _axes; + ReduceFunc _func; +}; + +/** + * @brief 2D Transposed Convolution + * + * @note TransposedConv2D have a few important conventions that IR users should + * understand and follow, so please check below notice carefully. + * + * + * 1. What is 'input' and 'output' + * + * For loco canonical TransposedConv2D, 'input' and 'output' mean actual input + * and output node of TransposedConv2D node. Be careful that some other + * frameworks may use opposite sense, especially TensorFlow which is inspired by + * backpropagation of convolution. + * For example, loco::TransposedConv2D::ifm() means actual input feature map + * node that is sourced into TransposedConv2D. + * + * 2. How to read kernel representation + * + * TransposedConv2D::ker() should be a node of Filter domain. Following is what + * each FilterAxis means as a kernel of TransposedConv2D: + * - FilterAxis::Height : kernel's height + * - FilterAxis::Width : kernel's width + * - FilterAxis::Depth : IFM's channel depth + * - FilterAxis::Count : OFM's channel depth + * TODO We may refactor FilterAxis as follow to reduce ambiguity: + * - FilterAxis::Height -> FilterAxis::H + * - FilterAxis::Width -> FilterAxis::W + * - FilterAxis::Depth -> FilterAxis::I + * - FilterAxis::Count -> FilterAxis::O + * + * + * 3. Tight fit rule + * + * TransposedConv2D have no information about its output shape. Instead, it + * always satisfy following 'tight fit' rule for horizontal and vertical + * dimension: + * + * O = S * ( I - 1 ) + F - P + * + * where + * O: output size + * S: stride + * I: input size + * F: effective kernal(filter) size + * P: whole pad size (= front + rear pad) + * + * With this, output shape is uniquely determined by all inputs and attributes. + */ +class TransposedConv2D final + : public CanonicalNodeDef<CanonicalOpcode::TransposedConv2D, FixedArity<2>::Mixin> +{ +public: + Node *ifm(void) const { return at(0)->node(); } + void ifm(Node *node) { at(0)->node(node); } + + Node *ker(void) const { return at(1)->node(); } + void ker(Node *node) { at(1)->node(node); } + +public: + const Padding2D *pad(void) const { return &_pad; } + Padding2D *pad(void) { return &_pad; } + +public: + const Stride<2> *stride(void) const { return &_stride; } + Stride<2> *stride(void) { return &_stride; } + +private: + Padding2D _pad; + Stride<2> _stride; + + // TODO Support "Dilation" +}; + +/** + * @brief Computes softmax activations + */ +template <Domain D> class Softmax; + +/** +* @brief Computes softmax activations for Tensor domain +*/ +template <> +class Softmax<Domain::Tensor> final + : public CanonicalNodeDef<CanonicalOpcode::TensorSoftmax, FixedArity<1>::Mixin> +{ +public: + Softmax() = default; + +public: + Node *input(void) const { return at(0)->node(); } + void input(Node *node) { return at(0)->node(node); } + + uint32_t axis(void) const { return _axis; } + void axis(uint32_t axis) { _axis = axis; } + +private: + uint32_t _axis = 0; +}; + +using TensorSoftmax = Softmax<Domain::Tensor>; + +/** + * @brief Create a "Tensor" from a "Bias" + */ +class BiasDecode final : public CanonicalNodeDef<CanonicalOpcode::BiasDecode, FixedArity<1>::Mixin> +{ +public: + BiasDecode() = default; + +public: + Node *input(void) const { return at(0)->node(); } + void input(Node *node) { at(0)->node(node); } +}; + +/** + * @brief Create a "Bias" from a "Tensor" + * + * BiasEncode currently requires a rank-1 tensor as its input. + */ +class BiasEncode final : public CanonicalNodeDef<CanonicalOpcode::BiasEncode, FixedArity<1>::Mixin> +{ +public: + BiasEncode() = default; + +public: + Node *input(void) const { return at(0)->node(); } + void input(Node *node) { at(0)->node(node); } +}; + +/** + * @brief Produce a value of domain D from an input value (of domain D) and a bias + */ +template <Domain D> class BiasAdd; + +/** + * @brief Add Tensor and Bias + * + * for each valid tensor index I + * out(I) = value(I) + bias(I.at(axis)) + */ +template <> +class BiasAdd<Domain::Tensor> final + : public CanonicalNodeDef<CanonicalOpcode::TensorBiasAdd, FixedArity<2>::Mixin> +{ +public: + BiasAdd() = default; + +public: + Node *value(void) const { return at(0)->node(); } + void value(Node *node) { return at(0)->node(node); } + + Node *bias(void) const { return at(1)->node(); } + void bias(Node *node) { return at(1)->node(node); } + + uint32_t axis(void) const { return _axis; } + void axis(uint32_t axis) { _axis = axis; } + +private: + uint32_t _axis = 0; +}; + +// +// Alias for external users +// +// loco::TensorBiasAdd +// vs. +// loco::BiasAdd<loco::Domain::Tensor> +// +using TensorBiasAdd = BiasAdd<Domain::Tensor>; + +/** + * @brief Add Feature and Bias along "depth" axis + * + * for each valid feature index (b, ch, row, col) + * out(b, ch, row, col) = value(b, ch, row, col) + bias(ch) + */ +template <> +class BiasAdd<Domain::Feature> final + : public CanonicalNodeDef<CanonicalOpcode::FeatureBiasAdd, FixedArity<2>::Mixin> +{ +public: + BiasAdd() = default; + +public: + Node *value(void) const { return at(0)->node(); } + void value(Node *node) { return at(0)->node(node); } + + Node *bias(void) const { return at(1)->node(); } + void bias(Node *node) { return at(1)->node(node); } +}; + +using FeatureBiasAdd = BiasAdd<Domain::Feature>; + +/** + * @brief Pads a tensor with constant value + * + * Pads a input tensor according to the padding with constant value. + * + * The dimension of each axis n of the output is + * output.dim(n) = padding.front(n) + input.dim(n) + padding.back(n) + * + * For example, input tensor of shape [1, 2] with + * + * padding.front(0) = 1; + * padding.back(0) = 2; + * + * padding.front(1) = 3; + * padding.back(1) = 4; + * + * will be a output tensor of shape + * [padding.front(0) + 1 + padding.back(0), padding.front(1) + 2 + padding.back(1)] = [4,9]. + */ +class TensorConstantPad final + : public CanonicalNodeDef<CanonicalOpcode::TensorConstantPad, FixedArity<2>::Mixin> +{ +public: + Node *input(void) const { return at(0)->node(); } + void input(Node *node) { at(0)->node(node); } + + Node *constant(void) const { return at(1)->node(); } + void constant(Node *node) { at(1)->node(node); } + +public: + const PaddingND *padding(void) const { return &_padding; } + PaddingND *padding(void) { return &_padding; } + +private: + PaddingND _padding; +}; + +/** + * @brief Elementwise Add lhs and rhs + */ +class EltwiseAdd final : public CanonicalNodeDef<CanonicalOpcode::EltwiseAdd, FixedArity<2>::Mixin> +{ +public: + EltwiseAdd() = default; + +public: + Node *lhs(void) const { return at(0)->node(); } + void lhs(Node *node) { return at(0)->node(node); } + + Node *rhs(void) const { return at(1)->node(); } + void rhs(Node *node) { return at(1)->node(node); } +}; + +/** + * @brief Elementwise Maximum of lhs and rhs + * + * o = (l > r) ? l : r (element-wise) + */ +class EltwiseMax final : public CanonicalNodeDef<CanonicalOpcode::EltwiseMax, FixedArity<2>::Mixin> +{ +public: + EltwiseMax() = default; + +public: + Node *lhs(void) const { return at(0)->node(); } + void lhs(Node *node) { return at(0)->node(node); } + + Node *rhs(void) const { return at(1)->node(); } + void rhs(Node *node) { return at(1)->node(node); } +}; + +/** + * @brief Elementwise Mul lhs and rhs + */ +class EltwiseMul final : public CanonicalNodeDef<CanonicalOpcode::EltwiseMul, FixedArity<2>::Mixin> +{ +public: + EltwiseMul() = default; + +public: + Node *lhs(void) const { return at(0)->node(); } + void lhs(Node *node) { return at(0)->node(node); } + + Node *rhs(void) const { return at(1)->node(); } + void rhs(Node *node) { return at(1)->node(node); } +}; + +/** + * @brief Elementwise Sub lhs and rhs + */ +class EltwiseSub final : public CanonicalNodeDef<CanonicalOpcode::EltwiseSub, FixedArity<2>::Mixin> +{ +public: + EltwiseSub() = default; + +public: + Node *lhs(void) const { return at(0)->node(); } + void lhs(Node *node) { return at(0)->node(node); } + + Node *rhs(void) const { return at(1)->node(); } + void rhs(Node *node) { return at(1)->node(node); } +}; + +/** + * @brief Elementwise Div lhs and rhs + */ +class EltwiseDiv final : public CanonicalNodeDef<CanonicalOpcode::EltwiseDiv, FixedArity<2>::Mixin> +{ +public: + EltwiseDiv() = default; + +public: + Node *lhs(void) const { return at(0)->node(); } + void lhs(Node *node) { return at(0)->node(node); } + + Node *rhs(void) const { return at(1)->node(); } + void rhs(Node *node) { return at(1)->node(node); } +}; + +/** + * @brief Elementwise Sqrt of input + */ +class EltwiseSqrt final + : public CanonicalNodeDef<CanonicalOpcode::EltwiseSqrt, FixedArity<1>::Mixin> +{ +public: + EltwiseSqrt() = default; + +public: + Node *input(void) const { return at(0)->node(); } + void input(Node *node) { at(0)->node(node); } +}; + +/** + * @brief Duplicate elements along specified axes + * + * TensorBroadcast takes a tensor and produces another tensor with the same rank but HIGHER + * dimensionality. + * + * To create such a tensor. TensorBroadcast duplicates the element along the specified axes. + * + * It is possible to control the degree of duplication with a partial map from TensorAxis to + * Dimension. + * + * TODO Explain the constraints (The dimension of inputs for specified axes SHOULD BE 1). + * TODO Explain the operation semantics + */ +class TensorBroadcast final + : public CanonicalNodeDef<CanonicalOpcode::TensorBroadcast, FixedArity<1>::Mixin> +{ +public: + TensorBroadcast() = default; + +public: + Node *input(void) const { return at(0)->node(); } + void input(Node *node) { at(0)->node(node); } + +public: + class Mapping final + { + public: + Mapping() = default; + + public: + bool defined(const TensorAxis &axis) const; + + const Dimension &dim(const TensorAxis &axis) const; + Dimension &dim(const TensorAxis &axis); + + private: + std::map<TensorAxis, Dimension> _content; + }; + + Mapping *mapping(void) { return &_mapping; } + const Mapping *mapping(void) const { return &_mapping; } + +private: + Mapping _mapping; +}; + +/** + * @brief Create Matrix from Tensor + * + * MatrixEncode currently requires a rank-2 Tensor as its input. + */ +class MatrixEncode final + : public CanonicalNodeDef<CanonicalOpcode::MatrixEncode, FixedArity<1>::Mixin> +{ +public: + MatrixEncode() = default; + +public: + Node *input(void) const { return at(0)->node(); } + void input(Node *node) { at(0)->node(node); } + +public: + MatrixEncoder *encoder(void) const { return _enc.get(); } + void encoder(std::unique_ptr<MatrixEncoder> &&enc) { _enc = std::move(enc); } + +private: + /// @note "encoder" is mandatory + std::unique_ptr<MatrixEncoder> _enc{nullptr}; +}; + +/** + * @brief Create Tensor from Matrix + * + * MatrixDecode currently requires a Matrix as its input. + */ +class MatrixDecode final + : public CanonicalNodeDef<CanonicalOpcode::MatrixDecode, FixedArity<1>::Mixin> +{ +public: + MatrixDecode() = default; + +public: + Node *input(void) const { return at(0)->node(); } + void input(Node *node) { at(0)->node(node); } + +public: + MatrixDecoder *decoder(void) const { return _dec.get(); } + void decoder(std::unique_ptr<MatrixDecoder> &&dec) { _dec = std::move(dec); } + +private: + /// @note "decoder" is mandatory + std::unique_ptr<MatrixDecoder> _dec{nullptr}; +}; + +/** + * @brief Matrix Multiplication lhs and rhs + * + * LHS and RHS must be on Matrix domain + */ +class MatMul final : public CanonicalNodeDef<CanonicalOpcode::MatMul, FixedArity<2>::Mixin> +{ +public: + MatMul() = default; + +public: + Node *lhs(void) const { return at(0)->node(); } + void lhs(Node *node) { return at(0)->node(node); } + + Node *rhs(void) const { return at(1)->node(); } + void rhs(Node *node) { return at(1)->node(node); } +}; + +/** + * @brief Permute an input + * + * In the following case, + * + * output = loco::TensorTranspose(input) + * + * perm()->axis(output's axis) = input's axis + * + * Input and output belong to tensor domain. + */ +class TensorTranspose final + : public CanonicalNodeDef<CanonicalOpcode::TensorTranspose, FixedArity<1>::Mixin> +{ +public: + TensorTranspose() = default; + +public: + Node *input(void) const { return at(0)->node(); } + void input(Node *node) { return at(0)->node(node); } + + class Perm final + { + public: + Perm() = default; + + public: + uint32_t size() const { return _vals.size(); } + void size(uint32_t size) { _vals.resize(size); } + + const TensorAxis &axis(TensorAxis n) const { return _vals[n]; } + TensorAxis &axis(TensorAxis n) { return _vals[n]; } + + private: + std::vector<TensorAxis> _vals; + }; + + Perm *perm(void) { return &_perm; } + const Perm *perm(void) const { return &_perm; } + +private: + Perm _perm; +}; + +} // namespace loco + +#endif // __LOCO_IR_NODES_H__ diff --git a/compiler/loco/include/loco/IR/Padding2D.h b/compiler/loco/include/loco/IR/Padding2D.h new file mode 100644 index 000000000..30557a891 --- /dev/null +++ b/compiler/loco/include/loco/IR/Padding2D.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 __LOCO_IR_PADDING2D_H__ +#define __LOCO_IR_PADDING2D_H__ + +#include <cstdint> + +namespace loco +{ + +class Padding2D final +{ +public: + Padding2D() : _top{0}, _bottom{0}, _left{0}, _right{0} + { + // DO NOTHING + } + +public: + Padding2D(uint32_t top, uint32_t bottom, uint32_t left, uint32_t right) + : _top{top}, _bottom{bottom}, _left{left}, _right{right} + { + // DO NOTHING + } + +public: + uint32_t top(void) const { return _top; } + void top(uint32_t value) { _top = value; } + +public: + uint32_t bottom(void) const { return _bottom; } + void bottom(uint32_t value) { _bottom = value; } + +public: + uint32_t left(void) const { return _left; } + void left(uint32_t value) { _left = value; } + +public: + uint32_t right(void) const { return _right; } + void right(uint32_t value) { _right = value; } + +private: + uint32_t _top; + uint32_t _bottom; + uint32_t _left; + uint32_t _right; +}; + +} // namespace loco + +#endif // __LOCO_IR_PADDING2D_H__ diff --git a/compiler/loco/include/loco/IR/PaddingND.h b/compiler/loco/include/loco/IR/PaddingND.h new file mode 100644 index 000000000..59be73943 --- /dev/null +++ b/compiler/loco/include/loco/IR/PaddingND.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 __LOCO_IR_PADDINGND_H__ +#define __LOCO_IR_PADDINGND_H__ + +#include <cstdint> +#include <vector> + +namespace loco +{ + +/** + * This class indicates how many pads to add before(front) and after(back) the contents of + * tensor in that dimension. + */ +class PaddingND final +{ + +public: + const uint32_t &front(uint32_t dim) const { return _front.at(dim); } + uint32_t &front(uint32_t dim) { return _front.at(dim); } + +public: + const uint32_t &back(uint32_t dim) const { return _back.at(dim); } + uint32_t &back(uint32_t dim) { return _back.at(dim); } + +public: + uint32_t rank(void) const { return _front.size(); } + void rank(uint32_t s) + { + _front.resize(s); + _back.resize(s); + } + +private: + std::vector<uint32_t> _front; + std::vector<uint32_t> _back; +}; + +} // namespace loco + +#endif // __LOCO_IR_PADDINGND_H__ diff --git a/compiler/loco/include/loco/IR/PermutingCodec.h b/compiler/loco/include/loco/IR/PermutingCodec.h new file mode 100644 index 000000000..60b05dcbb --- /dev/null +++ b/compiler/loco/include/loco/IR/PermutingCodec.h @@ -0,0 +1,421 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 __LOCO_IR_PERMUTING_CODEC_H__ +#define __LOCO_IR_PERMUTING_CODEC_H__ + +#include "loco/IR/Domain.h" + +#include "loco/IR/FeatureAxis.h" +#include "loco/IR/FeatureCodec.h" +#include "loco/IR/FilterAxis.h" +#include "loco/IR/FilterCodec.h" +#include "loco/IR/DepthwiseFilterAxis.h" +#include "loco/IR/DepthwiseFilterCodec.h" +#include "loco/IR/MatrixAxis.h" +#include "loco/IR/MatrixCodec.h" +#include "loco/IR/TensorAxis.h" + +#include <map> + +namespace loco +{ + +template <Domain D> class Permutation; +template <Domain D> class PermutingEncoder; +template <Domain D> class PermutingDecoder; + +/** + * @brief Mapping between Feature/Tensor Axis + */ +template <> class Permutation<Domain::Feature> +{ +public: + Permutation() = default; + +public: + /** + * @brief Return whether a tensor axis is specified for a given feature axis + * + * This method does not validate the corresponding value. + */ + bool mapped(const FeatureAxis &axis_f) const; + + /** + * @brief Get the tensor axis corresponding to a given feature axis + * + * This method works correclty only when feature axis is mapped before. + */ + TensorAxis axis(const FeatureAxis &axis_f) const; + + /** + * @brief Set the tensor axis corresponding to a given feature axis + */ + TensorAxis &axis(const FeatureAxis &axis_f); + + TensorAxis operator[](const FeatureAxis &axis_f) const { return axis(axis_f); } + TensorAxis &operator[](const FeatureAxis &axis_f) { return axis(axis_f); } + +private: + std::map<FeatureAxis, TensorAxis> _map; +}; + +template <> class PermutingEncoder<Domain::Feature> final : public FeatureEncoder +{ +public: + PermutingEncoder() = default; + +public: + PermutingEncoder(const Permutation<Domain::Feature> &perm) : _perm{perm} + { + // DO NOTHING + } + +public: + bool valid(void) const; + +public: + FeatureShape shape(const TensorShape &tensor_shape) const override; + TensorIndex value(const FeatureIndex &index) const override; + + std::unique_ptr<FeatureEncoder> clone(void) const override; + +public: + const Permutation<Domain::Feature> *perm(void) const { return &_perm; } + Permutation<Domain::Feature> *perm(void) { return &_perm; } + void perm(const Permutation<Domain::Feature> &p) { _perm = p; } + +private: + Permutation<Domain::Feature> _perm; +}; + +template <> class PermutingDecoder<Domain::Feature> final : public FeatureDecoder +{ +public: + PermutingDecoder() = default; + +public: + PermutingDecoder(const Permutation<Domain::Feature> &perm) : _perm{perm} + { + // DO NOTHING + } + +public: + bool valid(void) const; + +public: + TensorShape shape(const FeatureShape &tensor_shape) const override; + FeatureIndex value(const TensorIndex &index) const override; + + std::unique_ptr<FeatureDecoder> clone(void) const override; + +public: + const Permutation<Domain::Feature> *perm(void) const { return &_perm; } + Permutation<Domain::Feature> *perm(void) { return &_perm; } + void perm(const Permutation<Domain::Feature> &p) { _perm = p; } + +private: + Permutation<Domain::Feature> _perm; +}; + +/** + * @brief Mapping between Filter/Tensor Axis + */ +template <> class Permutation<Domain::Filter> +{ +public: + Permutation() = default; + +public: + /** + * @brief Return whether a given filter axis has a corresponding tensor axis + * + * This method does not validate the corresponding value. + */ + bool mapped(const FilterAxis &axis_f) const; + + /** + * @brief Get the tensor axis corresponding to a given filter axis + * + * This method works correctly only for mapped filter axes. + */ + const TensorAxis &axis(const FilterAxis &axis_f) const; + + /** + * @brief Set the tensor axis corresponding to a given filter axis + */ + TensorAxis &axis(const FilterAxis &axis_f); + + TensorAxis operator[](const FilterAxis &axis_f) const { return axis(axis_f); } + TensorAxis &operator[](const FilterAxis &axis_f) { return axis(axis_f); } + +private: + std::map<FilterAxis, TensorAxis> _map; +}; + +/** + * @brief Permutation-based Tensor-to-Filter converter + */ +template <> class PermutingEncoder<Domain::Filter> final : public FilterEncoder +{ +public: + PermutingEncoder() = default; + +public: + explicit PermutingEncoder(const Permutation<Domain::Filter> &perm) : _perm{perm} + { + // DO NOTHING + } + +public: + bool valid(void) const; + +public: + FilterShape shape(const TensorShape &tensor_shape) const override; + TensorIndex value(const FilterIndex &index) const override; + +public: + const Permutation<Domain::Filter> *perm(void) const { return &_perm; } + Permutation<Domain::Filter> *perm(void) { return &_perm; } + void perm(const Permutation<Domain::Filter> &p) { _perm = p; } + +private: + Permutation<Domain::Filter> _perm; +}; + +/** + * @brief Permutation-based Filter-to-Tensor converter + */ +template <> class PermutingDecoder<Domain::Filter> final : public FilterDecoder +{ +public: + PermutingDecoder() = default; + +public: + explicit PermutingDecoder(const Permutation<Domain::Filter> &perm) : _perm{perm} + { + // DO NOTHING + } + +public: + bool valid(void) const; + +public: + TensorShape shape(const FilterShape &tensor_shape) const override; + FilterIndex value(const TensorIndex &index) const override; + +public: + const Permutation<Domain::Filter> *perm(void) const { return &_perm; } + Permutation<Domain::Filter> *perm(void) { return &_perm; } + void perm(const Permutation<Domain::Filter> &p) { _perm = p; } + +private: + Permutation<Domain::Filter> _perm; +}; + +/** + * @brief Mapping between DepthwiseFilter/Tensor Axis + */ +template <> class Permutation<Domain::DepthwiseFilter> +{ +public: + Permutation() = default; + +public: + /** + * @brief Return whether a given depthwise filter axis has a corresponding tensor axis + * + * This method does not validate the corresponding value. + */ + bool mapped(const DepthwiseFilterAxis &axis_f) const; + + /** + * @brief Get the tensor axis corresponding to a given depthwise filter axis + * + * This method works correctly only for mapped depthwise filter axes. + */ + const TensorAxis &axis(const DepthwiseFilterAxis &axis_f) const; + + /** + * @brief Set the tensor axis corresponding to a given depthwise filter axis + */ + TensorAxis &axis(const DepthwiseFilterAxis &axis_f); + + TensorAxis operator[](const DepthwiseFilterAxis &axis_f) const { return axis(axis_f); } + TensorAxis &operator[](const DepthwiseFilterAxis &axis_f) { return axis(axis_f); } + +private: + std::map<DepthwiseFilterAxis, TensorAxis> _map; +}; + +/** + * @brief Permutation-based Tensor-to-DepthwiseFilter converter + */ +template <> class PermutingEncoder<Domain::DepthwiseFilter> final : public DepthwiseFilterEncoder +{ +public: + PermutingEncoder() = default; + +public: + PermutingEncoder(const Permutation<Domain::DepthwiseFilter> &perm) : _perm{perm} + { + // DO NOTHING + } + +public: + bool valid(void) const; + +public: + DepthwiseFilterShape shape(const TensorShape &tensor_shape) const override; + TensorIndex value(const DepthwiseFilterIndex &index) const override; + +public: + const Permutation<Domain::DepthwiseFilter> *perm(void) const { return &_perm; } + Permutation<Domain::DepthwiseFilter> *perm(void) { return &_perm; } + void perm(const Permutation<Domain::DepthwiseFilter> &p) { _perm = p; } + +private: + Permutation<Domain::DepthwiseFilter> _perm; +}; + +/** + * @brief Permutation-based DepthwiseFilter-to-Tensor converter + */ +template <> class PermutingDecoder<Domain::DepthwiseFilter> final : public DepthwiseFilterDecoder +{ +public: + PermutingDecoder() = default; + +public: + PermutingDecoder(const Permutation<Domain::DepthwiseFilter> &perm) : _perm{perm} + { + // DO NOTHING + } + +public: + bool valid(void) const; + +public: + TensorShape shape(const DepthwiseFilterShape &shape) const override; + DepthwiseFilterIndex value(const TensorIndex &index) const override; + +public: + const Permutation<Domain::DepthwiseFilter> *perm(void) const { return &_perm; } + Permutation<Domain::DepthwiseFilter> *perm(void) { return &_perm; } + void perm(const Permutation<Domain::DepthwiseFilter> &p) { _perm = p; } + +private: + Permutation<Domain::DepthwiseFilter> _perm; +}; + +/** + * @brief Mapping between Matrix/Tensor Axis + */ +template <> class Permutation<Domain::Matrix> +{ +public: + Permutation() = default; + +public: + /** + * @brief Return whether a given matrix axis has a corresponding tensor axis + * + * This method does not validate the corresponding value. + */ + bool mapped(const MatrixAxis &axis_f) const; + + /** + * @brief Get the tensor axis corresponding to a given matrix axis + * + * This method works correctly only for mapped matrix axes. + */ + TensorAxis axis(const MatrixAxis &axis_f) const; + + /** + * @brief Set the tensor axis corresponding to a given matrix axis + */ + TensorAxis &axis(const MatrixAxis &axis_f); + + TensorAxis operator[](const MatrixAxis &axis_f) const { return axis(axis_f); } + TensorAxis &operator[](const MatrixAxis &axis_f) { return axis(axis_f); } + +private: + std::map<MatrixAxis, TensorAxis> _map; +}; + +/** + * @brief Permutation-based Tensor-to-Matrix converter + */ +template <> class PermutingEncoder<Domain::Matrix> final : public MatrixEncoder +{ +public: + PermutingEncoder() = default; + +public: + PermutingEncoder(const Permutation<Domain::Matrix> &perm) : _perm{perm} + { + // DO NOTHING + } + +public: + bool valid(void) const; + +public: + MatrixShape shape(const TensorShape &tensor_shape) const override; + TensorIndex value(const MatrixIndex &index) const override; + +public: + const Permutation<Domain::Matrix> *perm(void) const { return &_perm; } + Permutation<Domain::Matrix> *perm(void) { return &_perm; } + void perm(const Permutation<Domain::Matrix> &p) { _perm = p; } + +private: + Permutation<Domain::Matrix> _perm; +}; + +/** + * @brief Permutation-based Matrix-to-Tensor converter + */ +template <> class PermutingDecoder<Domain::Matrix> final : public MatrixDecoder +{ +public: + PermutingDecoder() = default; + +public: + PermutingDecoder(const Permutation<Domain::Matrix> &perm) : _perm{perm} + { + // DO NOTHING + } + +public: + bool valid(void) const; + +public: + TensorShape shape(const MatrixShape &tensor_shape) const override; + MatrixIndex value(const TensorIndex &index) const override; + +public: + const Permutation<Domain::Matrix> *perm(void) const { return &_perm; } + Permutation<Domain::Matrix> *perm(void) { return &_perm; } + void perm(const Permutation<Domain::Matrix> &p) { _perm = p; } + +private: + Permutation<Domain::Matrix> _perm; +}; + +} // namespace loco + +#endif // __LOCO_IR_PERMUTING_CODEC_H__ diff --git a/compiler/loco/include/loco/IR/Stride.h b/compiler/loco/include/loco/IR/Stride.h new file mode 100644 index 000000000..eb9d47115 --- /dev/null +++ b/compiler/loco/include/loco/IR/Stride.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 __LOCO_IR_STRIDE_H__ +#define __LOCO_IR_STRIDE_H__ + +#include <cstdint> + +namespace loco +{ + +/** + * @brief Stride configuration for N-dimensional spatial operations + */ +template <unsigned N> class Stride; + +/** + * @brief Stride configuration for 2D spatial operations + */ +template <> class Stride<2> final +{ +public: + uint32_t vertical(void) const { return _vertical; } + void vertical(uint32_t value) { _vertical = value; } + +public: + uint32_t horizontal(void) const { return _horizontal; } + void horizontal(uint32_t value) { _horizontal = value; } + +private: + uint32_t _vertical = 1; + uint32_t _horizontal = 1; +}; + +} // namespace loco + +#endif // __LOCO_IR_STRIDE_H__ diff --git a/compiler/loco/include/loco/IR/TensorAxis.h b/compiler/loco/include/loco/IR/TensorAxis.h new file mode 100644 index 000000000..c41da512e --- /dev/null +++ b/compiler/loco/include/loco/IR/TensorAxis.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 __LOCO_IR_TENSOR_AXIS_H__ +#define __LOCO_IR_TENSOR_AXIS_H__ + +#include <cstdint> + +namespace loco +{ + +using TensorAxis = uint32_t; + +} // namespace loco + +#endif // __LOCO_IR_TENSOR_AXIS_H__ diff --git a/compiler/loco/include/loco/IR/TensorAxisSet.h b/compiler/loco/include/loco/IR/TensorAxisSet.h new file mode 100644 index 000000000..240dcc556 --- /dev/null +++ b/compiler/loco/include/loco/IR/TensorAxisSet.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 __LOCO_IR_TENSOR_AXIS_SET_H__ +#define __LOCO_IR_TENSOR_AXIS_SET_H__ + +#include "loco/IR/TensorAxis.h" + +#include <set> + +namespace loco +{ + +class TensorAxisSet final +{ +public: + TensorAxisSet() = default; + +public: + bool defined(const TensorAxis &axis) const { return _axes.find(axis) != _axes.end(); } + void insert(const TensorAxis &axis) { _axes.insert(axis); } + +private: + std::set<TensorAxis> _axes; +}; + +} // namespace loco + +#endif // __LOCO_IR_TENSOR_AXIS_SET_H__ diff --git a/compiler/loco/include/loco/IR/TensorIndex.h b/compiler/loco/include/loco/IR/TensorIndex.h new file mode 100644 index 000000000..8f2385104 --- /dev/null +++ b/compiler/loco/include/loco/IR/TensorIndex.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 __LOCO_IR_TENSOR_INDEX_H__ +#define __LOCO_IR_TENSOR_INDEX_H__ + +#include <nncc/core/ADT/tensor/Index.h> + +namespace loco +{ + +// TODO Remove dependencies on angkor +using TensorIndex = nncc::core::ADT::tensor::Index; + +} // namespace loco + +#endif // __LOCO_IR_TENSOR_INDEX_H__ diff --git a/compiler/loco/include/loco/IR/TensorShape.h b/compiler/loco/include/loco/IR/TensorShape.h new file mode 100644 index 000000000..af1066d52 --- /dev/null +++ b/compiler/loco/include/loco/IR/TensorShape.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 __LOCO_IR_TENSOR_SHAPE_H__ +#define __LOCO_IR_TENSOR_SHAPE_H__ + +#include "loco/IR/Dimension.h" + +#include <initializer_list> +#include <vector> + +namespace loco +{ + +class TensorShape +{ +public: + TensorShape() = default; + TensorShape(std::initializer_list<Dimension> dims) : _dims(dims.begin(), dims.end()) {} + +public: + uint32_t rank(void) const { return _dims.size(); } + void rank(uint32_t r) { _dims.resize(r); } + + const Dimension &dim(uint32_t axis) const { return _dims.at(axis); } + Dimension &dim(uint32_t axis) { return _dims.at(axis); } + +private: + std::vector<Dimension> _dims; +}; + +/** + * @brief Return the number of elements in a tensor of given shape + * + * NOTE 1. + * + * "volume" returns 1 if the rank is 0. + * + * NOTE 2. + * + * "caller" SHOULD pass a valid shape that has no unknown dimension. + * - The behavior of "volume" on invalid is undefined. + * + */ +uint32_t element_count(const loco::TensorShape *tensor_shape); + +} // namespace loco + +#endif // __LOCO_IR_TENSOR_SHAPE_H__ diff --git a/compiler/loco/include/loco/IR/Use.h b/compiler/loco/include/loco/IR/Use.h new file mode 100644 index 000000000..a4db924e4 --- /dev/null +++ b/compiler/loco/include/loco/IR/Use.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 __LOCO_IR_USE_H__ +#define __LOCO_IR_USE_H__ + +#include "loco/IR/Node.forward.h" + +namespace loco +{ + +/** + * @brief The edge between a node definition and its user. + * + * Note that this "Use" denotes **one** edge between a node and its users, + * and thus there are unique node and user for each Use. + * + * There will be multiple "Use" edges for the same node if there are multiple + * users. + * + * This class design is heavily inspired from "Use" class in LLVM. + */ +class Use final +{ +public: + /** + * @brief Construct Use with its user + * @note user SHOULD BE set on construction. + */ + Use(Node *user) : _user{user} + { + // DO NOTHING + } + + Use(const Use &) = delete; + Use(Use &&) = delete; + + ~Use() + { + // Unlink itself from the node + node(nullptr); + } + +public: + Node *node(void) const { return _node; } + void node(Node *node); + +public: + Node *user(void) const { return _user; } + +private: + Node *_node{nullptr}; + Node *_user{nullptr}; +}; + +} // namespace loco + +#endif // __LOCO_IR_USE_H__ diff --git a/compiler/loco/include/loco/IR/Verifier.h b/compiler/loco/include/loco/IR/Verifier.h new file mode 100644 index 000000000..8ff85e16f --- /dev/null +++ b/compiler/loco/include/loco/IR/Verifier.h @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 __LOCO_IR_VERIFIER_H__ +#define __LOCO_IR_VERIFIER_H__ + +#include "loco/IR/Graph.h" + +#include <memory> + +namespace loco +{ + +/** + * @brief Possible error categories + * + * This enum class enumerates all the possible validation failure reasons. + * + * WARN DO NOT serialize this code. The tag value is subject to change. + */ +enum class ErrorCategory +{ + MissingArgument, + /* TO BE ADDED */ +}; + +/** + * @brief The details of each error + */ +template <ErrorCategory Code> class ErrorDetail; + +/** + * @brief The details of MissingArgument error + */ +template <> class ErrorDetail<ErrorCategory::MissingArgument> +{ +public: + ErrorDetail(loco::Node *node, uint32_t index) : _node{node}, _index{index} + { + // DO NOTHING + } + +public: + /// @brief The node with missing arguments + loco::Node *node(void) const { return _node; } + /// @brief The missing argument index + uint32_t index(void) const { return _index; } + +private: + loco::Node *_node; + uint32_t _index; +}; + +/** + * @brief Error listener interface + * + * DOo NOT inherit this interface. Use DefaultErrorListener instead. + */ +struct IErrorListener +{ + virtual ~IErrorListener() = default; + + virtual void notify(const ErrorDetail<ErrorCategory::MissingArgument> &) = 0; +}; + +/** + * @brief Error listener (with default implementation) + */ +struct ErrorListener : public IErrorListener +{ + virtual ~ErrorListener() = default; + + void notify(const ErrorDetail<ErrorCategory::MissingArgument> &) override { return; } +}; + +/** + * @brief Validate a loco graph + * + * "valid" returns true if a given graph has no error. + * + * NOTE Given a valid(non-null) listener, "valid" notifies error details to the listener. + */ +bool valid(Graph *g, std::unique_ptr<ErrorListener> &&l = nullptr); + +} // namespace loco + +#endif // __LOCO_IR_VERIFIER_H__ diff --git a/compiler/loco/include/loco/IR/Window.h b/compiler/loco/include/loco/IR/Window.h new file mode 100644 index 000000000..604fea868 --- /dev/null +++ b/compiler/loco/include/loco/IR/Window.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 __LOCO_IR_WINDOW_H__ +#define __LOCO_IR_WINDOW_H__ + +#include <cstdint> + +namespace loco +{ + +/** + * @brief ND Receptive Field Shape + * + * Window<N> describes the shape of N-dimensional receptive field. + */ +template <unsigned N> class Window; + +/** + * @brief 2D Receptive Field Shape + */ +template <> class Window<2> final +{ +public: + uint32_t vertical(void) const { return _vertical; } + void vertical(uint32_t value) { _vertical = value; } + +public: + uint32_t horizontal(void) const { return _horizontal; } + void horizontal(uint32_t value) { _horizontal = value; } + +private: + uint32_t _vertical = 1; + uint32_t _horizontal = 1; +}; + +} // namespace loco + +#endif // __LOCO_IR_WINDOW_H__ diff --git a/compiler/loco/include/loco/Service/CanonicalShapeInferenceRule.h b/compiler/loco/include/loco/Service/CanonicalShapeInferenceRule.h new file mode 100644 index 000000000..cd3bed405 --- /dev/null +++ b/compiler/loco/include/loco/Service/CanonicalShapeInferenceRule.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 __LOCO_SERVICE_CANONICAL_SHAPE_INFERENCE_RULE_H__ +#define __LOCO_SERVICE_CANONICAL_SHAPE_INFERENCE_RULE_H__ + +#include "loco/Service/ShapeInferenceRule.h" + +namespace loco +{ + +/** + * @brief Shape inference rule for canonical dialect + */ +struct CanonicalShapeInferenceRule final : public ShapeInferenceRule +{ + bool support(const API &ver) const final; + bool recognize(const Dialect *) const final; + bool infer(const Node *, NodeShape &) const final; + void infer(const Context *, const Node *, Sink *) const final; +}; + +} // namespace loco + +#endif // __LOCO_SERVICE_CANONICAL_SHAPE_INFERENCE_RULE_H__ diff --git a/compiler/loco/include/loco/Service/MultiDialectShapeInferenceRule.h b/compiler/loco/include/loco/Service/MultiDialectShapeInferenceRule.h new file mode 100644 index 000000000..1a6c85b42 --- /dev/null +++ b/compiler/loco/include/loco/Service/MultiDialectShapeInferenceRule.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 __LOCO_SERVICE_MULTI_DIALECT_SHAPE_INFERENCE_RULE_H__ +#define __LOCO_SERVICE_MULTI_DIALECT_SHAPE_INFERENCE_RULE_H__ + +#include "loco/Service/ShapeInferenceRule.h" + +#include <map> + +namespace loco +{ + +/** + * @brief Shape inference rule for multiple dialects + */ +class MultiDialectShapeInferenceRule final : public ShapeInferenceRule +{ +public: + bool recognize(const Dialect *) const final; + bool infer(const Node *, NodeShape &) const final; + + /// @brief Bind a specific rule to a Dialect + MultiDialectShapeInferenceRule &bind(const Dialect *d, const ShapeInferenceRule *rule); + +private: + std::map<const Dialect *, const ShapeInferenceRule *> _rules; +}; + +} // namespace loco + +#endif // __LOCO_SERVICE_MULTI_DIALECT_SHAPE_INFERENCE_RULE_H__ diff --git a/compiler/loco/include/loco/Service/ShapeInference.h b/compiler/loco/include/loco/Service/ShapeInference.h new file mode 100644 index 000000000..f7bc5d4d6 --- /dev/null +++ b/compiler/loco/include/loco/Service/ShapeInference.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 __LOCO_SERVICE_SHAPE_INFERENCE_H__ +#define __LOCO_SERVICE_SHAPE_INFERENCE_H__ + +#include "loco/Service/ShapeInferenceRule.h" +#include "loco/IR/Graph.h" + +/** + * @file This file implements dialect-agnostic shape inference framework + * + * HOW TO USE: + * + * loco::Graph *g = ...; + * loco::ShapeInferenceRule *rule = ...; + * loco::apply(rule).to(g); + * + */ +namespace loco +{ + +class ShapeInferenceSession +{ +public: + ShapeInferenceSession(const ShapeInferenceRule *rule) : _rule{rule} + { + // DO NOTHING + } + +public: + bool to(Graph *g) const; + +private: + const ShapeInferenceRule *_rule; +}; + +inline ShapeInferenceSession apply(ShapeInferenceRule *r) { return ShapeInferenceSession{r}; } + +struct ShapeInference +{ + static bool known(const Node *); + static NodeShape get(const Node *); + static void erase(Node *); +}; + +inline bool shape_known(const Node *node) { return ShapeInference::known(node); } +inline NodeShape shape_get(const Node *node) { return ShapeInference::get(node); } +inline void shape_erase(Node *node) { ShapeInference::erase(node); } + +} // namespace loco + +#endif // __LOCO_SERVICE_SHAPE_INFERENCE_H__ diff --git a/compiler/loco/include/loco/Service/ShapeInferenceRule.h b/compiler/loco/include/loco/Service/ShapeInferenceRule.h new file mode 100644 index 000000000..889f0b6b2 --- /dev/null +++ b/compiler/loco/include/loco/Service/ShapeInferenceRule.h @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 __LOCO_SERVICE_SHAPE_INFERENCE_RULE_H__ +#define __LOCO_SERVICE_SHAPE_INFERENCE_RULE_H__ + +#include "loco/IR/Domain.h" +#include "loco/IR/Dialect.h" +#include "loco/IR/Node.h" +#include "loco/IR/NodeShape.h" + +namespace loco +{ + +struct ShapeInferenceRule +{ + virtual ~ShapeInferenceRule() = default; + + enum class API + { + /** + * API v1 + * + * This API uses "shape_get" method to query the shape of other nodes. + */ + V1, + + /** + * API v2 + * + * This API uses a given context (defined below) to query the shape of other nodes. + */ + V2, + }; + + /// @brief Check whether a given API is available or not + virtual bool support(const API &api) const + { + // To be backward compatible + return api == API::V1; + } + + /// @brief Return true if this rule recognizes a given dialect + virtual bool recognize(const Dialect *) const = 0; + + /** + * @brief Infer node's shape + * + * WARNING!! + * + * Implementation SHOULD return true only when it succeeds in inference! + * + */ + virtual bool infer(const Node *, NodeShape &) const = 0; + + // + // API v2 + // + struct Context + { + virtual ~Context() = default; + + virtual bool known(const Node *node) const = 0; + virtual NodeShape get(const Node *node) const = 0; + }; + + struct Sink + { + virtual ~Sink() = default; + + // TODO Add methods for error reporting + + // Each ShapeInferenceRule SHOULD invoke one of okay and fail before it returns + virtual void okay(const NodeShape &) = 0; + virtual void fail(void) = 0; + }; + + // WARNING! Invoke this method only when API v2 is supported + virtual void infer(const Context *, const Node *, Sink *) const; +}; + +} // namespace loco + +#endif // __LOCO_SERVICE_SHAPE_INFERENCE_RULE_H__ diff --git a/compiler/loco/include/loco/Service/TypeInference.h b/compiler/loco/include/loco/Service/TypeInference.h new file mode 100644 index 000000000..c2ce1a4c7 --- /dev/null +++ b/compiler/loco/include/loco/Service/TypeInference.h @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 __LOCO_SERVICE_TYPE_INFERENCE_H__ +#define __LOCO_SERVICE_TYPE_INFERENCE_H__ + +#include "loco/IR/DataType.h" + +#include "loco/IR/Node.h" +#include "loco/IR/Dialect.h" +#include "loco/IR/Graph.h" + +#include <map> + +/** + * @file This file implements dialect-agnostic type inference framework. + * + * HOW TO USE: + * + * loco::Graph *g = ...; + * loco::TypeInferenceRule *rule = ...; + * loco::apply(rule).to(g); + * + */ +namespace loco +{ + +struct TypeInferenceRule +{ + virtual ~TypeInferenceRule() = default; + + /// @brief Return true if this rule recognizes a given dialect + virtual bool recognize(const Dialect *) const = 0; + + /** + * Framework guarantees the followings: + * + * 1. Framework tries to infer the data type of each node only after the data type of all of + * its valid (= non-nullptr) argument nodes is inferred. + * 2. The result of preceding "infer" is accessible through below dtype_get method. + * - This holds only when preceding "infer" returns true. + */ + virtual bool infer(const Node *, DataType &) const = 0; +}; + +/** + * @brief Type Inference Rule for Canonical Dialect + */ +struct CanonicalTypeInferenceRule final : public TypeInferenceRule +{ + bool recognize(const Dialect *) const final; + bool infer(const Node *, DataType &) const final; +}; + +/** + * @brief Type Inference Rule for multiple dialects + */ +class MultiDialectTypeInferenceRule final : public TypeInferenceRule +{ +public: + bool recognize(const Dialect *) const final; + bool infer(const Node *, DataType &) const final; + + /// @brief Bind a specific rule to a Dialect + MultiDialectTypeInferenceRule &bind(const Dialect *d, const TypeInferenceRule *rule); + +private: + std::map<const Dialect *, const TypeInferenceRule *> _rules; +}; + +class TypeInferenceSession +{ +public: + TypeInferenceSession(const TypeInferenceRule *rule) : _rule{rule} + { + // DO NOTHING + } + +public: + bool to(Graph *g) const; + +private: + const TypeInferenceRule *_rule; +}; + +inline TypeInferenceSession apply(TypeInferenceRule *r) { return TypeInferenceSession{r}; } + +struct TypeInference +{ + static bool known(const Node *); + static DataType get(const Node *); + static void erase(Node *); +}; + +inline bool dtype_known(const Node *node) { return TypeInference::known(node); } +inline DataType dtype_get(const Node *node) { return TypeInference::get(node); } +inline void dtype_erase(Node *node) { TypeInference::erase(node); } + +} // namespace loco + +#endif // __LOCO_SERVICE_TYPE_INFERENCE_H__ |