MLIR 24.0.0git
TosaConvertIntegerTypesBitwidth.cpp
Go to the documentation of this file.
1//===- TosaConvertIntegerTypesBitwidth.cpp - Convert TOSA integers --------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
10
15
16#include "llvm/ADT/APInt.h"
17#include "llvm/ADT/DenseMap.h"
18#include "llvm/ADT/SmallVector.h"
19#include "llvm/Support/CommandLine.h"
20#include "llvm/Support/MathExtras.h"
21
22#include <cstddef>
23#include <cstdint>
24#include <optional>
25#include <utility>
26
27namespace llvm::cl {
28template <>
29class parser<std::pair<unsigned, unsigned>>
30 : public basic_parser<std::pair<unsigned, unsigned>> {
31public:
32 parser(Option &option) : basic_parser(option) {}
33
34 bool parse(Option &option, StringRef argName, StringRef arg,
35 std::pair<unsigned, unsigned> &value) {
36 auto [source, destination] = arg.split(':');
37 if (source.getAsInteger(10, value.first) ||
38 destination.getAsInteger(10, value.second))
39 return option.error("expected <source>:<destination> integer bitwidths",
40 argName);
41 return false;
42 }
43
44 StringRef getValueName() const override { return "source:destination"; }
45
46 static void print(raw_ostream &os,
47 const std::pair<unsigned, unsigned> &value) {
48 os << value.first << ":" << value.second;
49 }
50};
51} // namespace llvm::cl
52
53namespace mlir::tosa {
54#define GEN_PASS_DEF_TOSACONVERTINTEGERTYPESBITWIDTHPASS
55#include "mlir/Dialect/Tosa/Transforms/Passes.h.inc"
56} // namespace mlir::tosa
57
58using namespace mlir;
59
60namespace {
61// Convert a validated byte-aligned payload, retaining storage for every
62// element. In particular, each i4 element occupies one byte, not half a byte.
63SmallVector<char> convertRawValues(ArrayRef<char> rawData, IntegerType type,
64 IntegerType convertedType) {
65 const unsigned inputElementSizeInBytes = llvm::divideCeil(type.getWidth(), 8);
66 const unsigned outputElementSizeInBytes = convertedType.getWidth() / 8;
67 const size_t numElements = rawData.size() / inputElementSizeInBytes;
68 SmallVector<char> data(numElements * outputElementSizeInBytes);
69 const auto *input = reinterpret_cast<const uint8_t *>(rawData.data());
70 auto *output = reinterpret_cast<uint8_t *>(data.data());
71 for (size_t i = 0; i < numElements; ++i) {
72 APInt value(inputElementSizeInBytes * 8, 0);
73 llvm::LoadIntFromMemory(value, input + i * inputElementSizeInBytes,
74 inputElementSizeInBytes);
75 // Discard padding bits before interpreting the source sign bit.
76 value = value.trunc(type.getWidth());
77 llvm::StoreIntToMemory(value.sext(convertedType.getWidth()),
78 output + i * outputElementSizeInBytes,
79 outputElementSizeInBytes);
80 }
81 return data;
82}
83
84class IntegerTypeReplacer : public AttrTypeReplacer {
85public:
86 // The mappings must outlive this replacer.
87 explicit IntegerTypeReplacer(const DenseMap<unsigned, unsigned> &mappings) {
88 addReplacement([&mappings](IntegerType type) -> std::optional<Type> {
89 auto mapping = mappings.find(type.getWidth());
90 if (mapping == mappings.end())
91 return std::nullopt;
92 return IntegerType::get(type.getContext(), mapping->second);
93 });
94 addReplacement(
95 [this](IntegerAttr attr) { return skip(convertInteger(attr)); });
96 addReplacement(
97 [this](DenseArrayAttr attr) { return skip(convertDenseArray(attr)); });
98 addReplacement([this](DenseTypedElementsAttr attr) {
99 return skip(convertDenseElements(attr));
100 });
101 addReplacement([this](DenseResourceElementsAttr attr) {
102 return skip(convertDenseResource(attr));
103 });
104 }
105
106 bool hasFailed() const { return conversionFailed; }
107
108private:
109 Attribute convertInteger(IntegerAttr attr) {
110 Type type = attr.getType();
111 Type convertedType = replace(type);
112 if (type == convertedType)
113 return attr;
114 return IntegerAttr::get(
115 convertedType,
116 attr.getValue().sext(cast<IntegerType>(convertedType).getWidth()));
117 }
118
119 Attribute convertDenseArray(DenseArrayAttr attr) {
120 Type type = attr.getElementType();
121 Type convertedType = replace(type);
122 if (type == convertedType)
123 return attr;
124 // Unlike dense elements, arrays always store every element, even splats.
125 const SmallVector<char> data =
126 convertRawValues(attr.getRawData(), cast<IntegerType>(type),
127 cast<IntegerType>(convertedType));
128 return DenseArrayAttr::get(convertedType, attr.size(), data);
129 }
130
131 Attribute convertDenseElements(DenseTypedElementsAttr attr) {
132 auto convertedType = dyn_cast_or_null<ShapedType>(replace(attr.getType()));
133 if (!convertedType)
134 return nullptr;
135
136 Type elementType = attr.getElementType();
137 Type convertedElementType = convertedType.getElementType();
138 if (elementType == convertedElementType) {
139 // reshape is still necessary to handle any encoding changes.
140 return attr.reshape(convertedType);
141 }
142
143 auto convertedIntegerType = dyn_cast<IntegerType>(convertedElementType);
144 if (!convertedIntegerType)
145 return nullptr;
146 return attr
147 .mapValues(convertedElementType,
148 [convertedIntegerType](const APInt &value) {
149 return value.sext(convertedIntegerType.getWidth());
150 })
151 .reshape(convertedType);
152 }
153
154 Attribute convertDenseResource(DenseResourceElementsAttr attr) {
155 ShapedType type = attr.getType();
156 Type elementType = type.getElementType();
157 auto convertedType = dyn_cast_or_null<ShapedType>(replace(type));
158 if (!convertedType)
159 return nullptr;
160 Type convertedElementType = convertedType.getElementType();
161 if (elementType == convertedElementType)
162 return DenseResourceElementsAttr::get(convertedType, attr.getRawHandle());
163 auto convertedIntegerType = dyn_cast<IntegerType>(convertedElementType);
164 if (!convertedIntegerType)
165 return nullptr;
166 auto integerType = cast<IntegerType>(elementType);
167 const AsmResourceBlob *blob = attr.getRawHandle().getBlob();
168 if (!blob)
169 return nullptr;
170 ArrayRef<char> rawData = blob->getData();
171 const unsigned elementSizeInBytes =
172 llvm::divideCeil(integerType.getWidth(), 8);
173 if (rawData.size() % elementSizeInBytes != 0)
174 return nullptr;
175 // Check resource is correct size.
176 if (type.hasStaticShape() &&
177 rawData.size() !=
178 elementSizeInBytes * static_cast<uint64_t>(type.getNumElements()))
179 return nullptr;
180 const SmallVector<char> data =
181 convertRawValues(rawData, integerType, convertedIntegerType);
182 const unsigned alignment = convertedIntegerType.getWidth() / 8;
183 return DenseResourceElementsAttr::get(
184 convertedType, attr.getRawHandle().getKey(),
186 }
187
188 // Payload handlers already converted their subelements.
189 ReplaceFnResult<Attribute> skip(Attribute attr) {
190 // replaceElementsIn ignores null replacements, so track failures here.
191 conversionFailed |= !attr;
192 return std::make_pair(attr, WalkResult::skip());
193 }
194
195 bool conversionFailed = false;
196};
197
198struct TosaConvertIntegerTypesBitwidth
200 TosaConvertIntegerTypesBitwidth> {
201 using Base::Base;
202
203 void runOnOperation() override {
204 if (bitwidthMappings.empty()) {
205 getOperation().emitError(
206 "The 'bitwidth-mappings' option must be specified and non-empty.");
207 return signalPassFailure();
208 }
210 for (auto [source, destination] : bitwidthMappings) {
211 if (!((source == 4 && destination == 8) ||
212 (source == 48 && destination == 64))) {
213 getOperation().emitError("unsupported integer bitwidth mapping ")
214 << source << ":" << destination << ": expected 4:8 or 48:64";
215 return signalPassFailure();
216 }
217 mappings.try_emplace(source, destination);
218 }
219 IntegerTypeReplacer replacer(mappings);
220 replacer.recursivelyReplaceElementsIn(getOperation(), /*replaceAttrs=*/true,
221 /*replaceLocs=*/false,
222 /*replaceTypes=*/true);
223 if (replacer.hasFailed()) {
224 getOperation().emitError("failed to convert integer types or attributes");
225 signalPassFailure();
226 }
227 }
228};
229} // namespace
bool parse(Option &option, StringRef argName, StringRef arg, std::pair< unsigned, unsigned > &value)
static void print(raw_ostream &os, const std::pair< unsigned, unsigned > &value)
ArrayRef< char > getData() const
Return the raw underlying data of this blob.
Definition AsmState.h:145
This is an attribute/type replacer that is naively cached.
static AsmResourceBlob allocateAndCopyWithAlign(ArrayRef< char > data, size_t align, bool dataIsMutable=true)
Create a new heap allocated blob and copy the provided data into it.
Definition AsmState.h:201
static WalkResult skip()
Definition WalkResult.h:48
Include the generated interface declarations.
llvm::DenseMap< KeyT, ValueT, KeyInfoT, BucketT > DenseMap
Definition LLVM.h:123