CIRCT 22.0.0git
Loading...
Searching...
No Matches
HWToLLVM.cpp
Go to the documentation of this file.
1//===- HWToLLVM.cpp - HW to LLVM Conversion Pass --------------------------===//
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//
9// This is the main HW to LLVM Conversion Pass Implementation.
10//
11//===----------------------------------------------------------------------===//
12
15#include "circt/Support/LLVM.h"
17#include "mlir/Conversion/LLVMCommon/ConversionTarget.h"
18#include "mlir/Conversion/LLVMCommon/Pattern.h"
19#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
20#include "mlir/Pass/Pass.h"
21#include "mlir/Transforms/DialectConversion.h"
22#include "llvm/ADT/TypeSwitch.h"
23
24namespace circt {
25#define GEN_PASS_DEF_CONVERTHWTOLLVM
26#include "circt/Conversion/Passes.h.inc"
27} // namespace circt
28
29using namespace mlir;
30using namespace circt;
31
32//===----------------------------------------------------------------------===//
33// Endianess Converter
34//===----------------------------------------------------------------------===//
35
36uint32_t
38 uint32_t index) {
39 // This is hardcoded for little endian machines for now.
40 return TypeSwitch<Type, uint32_t>(type)
41 .Case<hw::ArrayType>(
42 [&](hw::ArrayType ty) { return ty.getNumElements() - index - 1; })
43 .Case<hw::StructType>([&](hw::StructType ty) {
44 return ty.getElements().size() - index - 1;
45 });
46}
47
48uint32_t
50 StringRef fieldName) {
51 auto fieldIter = type.getElements();
52 size_t index = 0;
53
54 for (const auto *iter = fieldIter.begin(); iter != fieldIter.end(); ++iter) {
55 if (iter->name == fieldName) {
57 }
58 ++index;
59 }
60
61 // Verifier of StructExtractOp has to ensure that the field name is indeed
62 // present.
63 llvm_unreachable("Field name attribute of hw::StructExtractOp invalid");
64 return 0;
65}
66
67//===----------------------------------------------------------------------===//
68// Helpers
69//===----------------------------------------------------------------------===//
70
71/// Create a zext operation by one bit on the given value. This is useful when
72/// passing unsigned indexes to a GEP instruction, which treats indexes as
73/// signed values, to avoid unexpected "sign overflows".
74static Value zextByOne(Location loc, ConversionPatternRewriter &rewriter,
75 Value value) {
76 auto valueTy = value.getType();
77 auto zextTy = IntegerType::get(valueTy.getContext(),
78 valueTy.getIntOrFloatBitWidth() + 1);
79 return LLVM::ZExtOp::create(rewriter, loc, zextTy, value);
80}
81
82//===----------------------------------------------------------------------===//
83// Extraction operation conversions
84//===----------------------------------------------------------------------===//
85
86namespace {
87/// Convert a StructExplodeOp to the LLVM dialect.
88/// Pattern: struct_explode(input) =>
89/// struct_extract(input, structElements_index(index)) ...
90struct StructExplodeOpConversion
91 : public ConvertOpToLLVMPattern<hw::StructExplodeOp> {
92 using ConvertOpToLLVMPattern<hw::StructExplodeOp>::ConvertOpToLLVMPattern;
93
94 LogicalResult
95 matchAndRewrite(hw::StructExplodeOp op, OpAdaptor adaptor,
96 ConversionPatternRewriter &rewriter) const override {
97
98 SmallVector<Value> replacements;
99
100 for (size_t i = 0,
101 e = cast<LLVM::LLVMStructType>(adaptor.getInput().getType())
102 .getBody()
103 .size();
104 i < e; ++i)
105
106 replacements.push_back(LLVM::ExtractValueOp::create(
107 rewriter, op->getLoc(), adaptor.getInput(),
109 op.getInput().getType(), i)));
110
111 rewriter.replaceOp(op, replacements);
112 return success();
113 }
114};
115} // namespace
116
117namespace {
118/// Convert a StructExtractOp to LLVM dialect.
119/// Pattern: struct_extract(input, fieldname) =>
120/// extractvalue(input, fieldname_to_index(fieldname))
121struct StructExtractOpConversion
122 : public ConvertOpToLLVMPattern<hw::StructExtractOp> {
123 using ConvertOpToLLVMPattern<hw::StructExtractOp>::ConvertOpToLLVMPattern;
124
125 LogicalResult
126 matchAndRewrite(hw::StructExtractOp op, OpAdaptor adaptor,
127 ConversionPatternRewriter &rewriter) const override {
128
130 op.getInput().getType(), op.getFieldIndex());
131 rewriter.replaceOpWithNewOp<LLVM::ExtractValueOp>(op, adaptor.getInput(),
132 fieldIndex);
133 return success();
134 }
135};
136} // namespace
137
138namespace {
139/// Convert an ArrayInjectOp to the LLVM dialect.
140/// Pattern: array_inject(input, element, index) =>
141/// store(gep(store(input, alloca), zext(index)), element)
142/// load(alloca)
143struct ArrayInjectOpConversion
144 : public ConvertOpToLLVMPattern<hw::ArrayInjectOp> {
145 using ConvertOpToLLVMPattern<hw::ArrayInjectOp>::ConvertOpToLLVMPattern;
146
147 LogicalResult
148 matchAndRewrite(hw::ArrayInjectOp op, OpAdaptor adaptor,
149 ConversionPatternRewriter &rewriter) const override {
150 auto inputType = cast<hw::ArrayType>(op.getInput().getType());
151 auto oldArrTy = adaptor.getInput().getType();
152 auto newArrTy = oldArrTy;
153 const size_t arrElems = inputType.getNumElements();
154
155 if (arrElems == 0) {
156 rewriter.replaceOp(op, adaptor.getInput());
157 return success();
158 }
159
160 auto oneC =
161 LLVM::ConstantOp::create(rewriter, op->getLoc(), rewriter.getI32Type(),
162 rewriter.getI32IntegerAttr(1));
163 auto zextIndex = zextByOne(op->getLoc(), rewriter, op.getIndex());
164
165 Value arrPtr;
166 if (arrElems == 1 || !llvm::isPowerOf2_64(arrElems)) {
167 // Clamp index to prevent OOB access. We add an extra element to the
168 // array so that OOB access modifies this element, leaving the original
169 // array intact.
170 auto maxIndex =
171 LLVM::ConstantOp::create(rewriter, op->getLoc(), zextIndex.getType(),
172 rewriter.getI32IntegerAttr(arrElems));
173 zextIndex =
174 LLVM::UMinOp::create(rewriter, op->getLoc(), zextIndex, maxIndex);
175
176 newArrTy = typeConverter->convertType(
177 hw::ArrayType::get(inputType.getElementType(), arrElems + 1));
178 arrPtr = LLVM::AllocaOp::create(
179 rewriter, op->getLoc(),
180 LLVM::LLVMPointerType::get(rewriter.getContext()), newArrTy, oneC,
181 /*alignment=*/4);
182 } else {
183 arrPtr = LLVM::AllocaOp::create(
184 rewriter, op->getLoc(),
185 LLVM::LLVMPointerType::get(rewriter.getContext()), newArrTy, oneC,
186 /*alignment=*/4);
187 }
188
189 LLVM::StoreOp::create(rewriter, op->getLoc(), adaptor.getInput(), arrPtr);
190
191 auto gep = LLVM::GEPOp::create(
192 rewriter, op->getLoc(),
193 LLVM::LLVMPointerType::get(rewriter.getContext()), newArrTy, arrPtr,
194 ArrayRef<LLVM::GEPArg>{0, zextIndex});
195
196 LLVM::StoreOp::create(rewriter, op->getLoc(), adaptor.getElement(), gep);
197 rewriter.replaceOpWithNewOp<LLVM::LoadOp>(op, oldArrTy, arrPtr);
198 return success();
199 }
200};
201} // namespace
202
203namespace {
204/// Convert an ArrayGetOp to the LLVM dialect.
205/// Pattern: array_get(input, index) =>
206/// load(gep(store(input, alloca), zext(index)))
207struct ArrayGetOpConversion : public ConvertOpToLLVMPattern<hw::ArrayGetOp> {
208 using ConvertOpToLLVMPattern<hw::ArrayGetOp>::ConvertOpToLLVMPattern;
209
210 LogicalResult
211 matchAndRewrite(hw::ArrayGetOp op, OpAdaptor adaptor,
212 ConversionPatternRewriter &rewriter) const override {
213
214 Value arrPtr;
215 if (auto load = adaptor.getInput().getDefiningOp<LLVM::LoadOp>()) {
216 // In this case the array was loaded from an existing address, so we can
217 // just grab that address instead of reallocating the array on the stack.
218 arrPtr = load.getAddr();
219 } else {
220 auto oneC = LLVM::ConstantOp::create(
221 rewriter, op->getLoc(), IntegerType::get(rewriter.getContext(), 32),
222 rewriter.getI32IntegerAttr(1));
223 arrPtr = LLVM::AllocaOp::create(
224 rewriter, op->getLoc(),
225 LLVM::LLVMPointerType::get(rewriter.getContext()),
226 adaptor.getInput().getType(), oneC,
227 /*alignment=*/4);
228 LLVM::StoreOp::create(rewriter, op->getLoc(), adaptor.getInput(), arrPtr);
229 }
230
231 auto arrTy = typeConverter->convertType(op.getInput().getType());
232 auto elemTy = typeConverter->convertType(op.getResult().getType());
233 auto zextIndex = zextByOne(op->getLoc(), rewriter, op.getIndex());
234
235 // During the ongoing migration to opaque types, use the constructor that
236 // accepts an element type when the array pointer type is opaque, and
237 // otherwise use the typed pointer constructor.
238 auto gep = LLVM::GEPOp::create(
239 rewriter, op->getLoc(),
240 LLVM::LLVMPointerType::get(rewriter.getContext()), arrTy, arrPtr,
241 ArrayRef<LLVM::GEPArg>{0, zextIndex});
242 rewriter.replaceOpWithNewOp<LLVM::LoadOp>(op, elemTy, gep);
243
244 return success();
245 }
246};
247} // namespace
248
249namespace {
250/// Convert an ArraySliceOp to the LLVM dialect.
251/// Pattern: array_slice(input, lowIndex) =>
252/// load(bitcast(gep(store(input, alloca), zext(lowIndex))))
253struct ArraySliceOpConversion
254 : public ConvertOpToLLVMPattern<hw::ArraySliceOp> {
255 using ConvertOpToLLVMPattern<hw::ArraySliceOp>::ConvertOpToLLVMPattern;
256
257 LogicalResult
258 matchAndRewrite(hw::ArraySliceOp op, OpAdaptor adaptor,
259 ConversionPatternRewriter &rewriter) const override {
260
261 auto dstTy = typeConverter->convertType(op.getDst().getType());
262
263 auto oneC =
264 LLVM::ConstantOp::create(rewriter, op->getLoc(), rewriter.getI32Type(),
265 rewriter.getI32IntegerAttr(1));
266
267 auto arrPtr = LLVM::AllocaOp::create(
268 rewriter, op->getLoc(),
269 LLVM::LLVMPointerType::get(rewriter.getContext()),
270 adaptor.getInput().getType(), oneC,
271 /*alignment=*/4);
272
273 LLVM::StoreOp::create(rewriter, op->getLoc(), adaptor.getInput(), arrPtr);
274
275 auto zextIndex = zextByOne(op->getLoc(), rewriter, op.getLowIndex());
276
277 // During the ongoing migration to opaque types, use the constructor that
278 // accepts an element type when the array pointer type is opaque, and
279 // otherwise use the typed pointer constructor.
280 auto gep = LLVM::GEPOp::create(
281 rewriter, op->getLoc(),
282 LLVM::LLVMPointerType::get(rewriter.getContext()), dstTy, arrPtr,
283 ArrayRef<LLVM::GEPArg>{0, zextIndex});
284
285 rewriter.replaceOpWithNewOp<LLVM::LoadOp>(op, dstTy, gep);
286
287 return success();
288 }
289};
290} // namespace
291
292//===----------------------------------------------------------------------===//
293// Insertion operations conversion
294//===----------------------------------------------------------------------===//
295
296namespace {
297/// Convert a StructInjectOp to LLVM dialect.
298/// Pattern: struct_inject(input, index, value) =>
299/// insertvalue(input, value, index)
300struct StructInjectOpConversion
301 : public ConvertOpToLLVMPattern<hw::StructInjectOp> {
302 using ConvertOpToLLVMPattern<hw::StructInjectOp>::ConvertOpToLLVMPattern;
303
304 LogicalResult
305 matchAndRewrite(hw::StructInjectOp op, OpAdaptor adaptor,
306 ConversionPatternRewriter &rewriter) const override {
307
309 op.getInput().getType(), op.getFieldIndex());
310
311 rewriter.replaceOpWithNewOp<LLVM::InsertValueOp>(
312 op, adaptor.getInput(), adaptor.getNewValue(), fieldIndex);
313
314 return success();
315 }
316};
317} // namespace
318
319//===----------------------------------------------------------------------===//
320// Concat operations conversion
321//===----------------------------------------------------------------------===//
322
323namespace {
324/// Lower an ArrayConcatOp operation to the LLVM dialect.
325struct ArrayConcatOpConversion
326 : public ConvertOpToLLVMPattern<hw::ArrayConcatOp> {
327 using ConvertOpToLLVMPattern<hw::ArrayConcatOp>::ConvertOpToLLVMPattern;
328
329 LogicalResult
330 matchAndRewrite(hw::ArrayConcatOp op, OpAdaptor adaptor,
331 ConversionPatternRewriter &rewriter) const override {
332
333 hw::ArrayType arrTy = cast<hw::ArrayType>(op.getResult().getType());
334 Type resultTy = typeConverter->convertType(arrTy);
335
336 Value arr = LLVM::UndefOp::create(rewriter, op->getLoc(), resultTy);
337
338 // Attention: j is hardcoded for little endian machines.
339 size_t j = op.getInputs().size() - 1, k = 0;
340
341 for (size_t i = 0, e = arrTy.getNumElements(); i < e; ++i) {
342 Value element = LLVM::ExtractValueOp::create(rewriter, op->getLoc(),
343 adaptor.getInputs()[j], k);
344 arr =
345 LLVM::InsertValueOp::create(rewriter, op->getLoc(), arr, element, i);
346
347 ++k;
348 if (k >=
349 cast<hw::ArrayType>(op.getInputs()[j].getType()).getNumElements()) {
350 k = 0;
351 --j;
352 }
353 }
354
355 rewriter.replaceOp(op, arr);
356 return success();
357 }
358};
359} // namespace
360
361//===----------------------------------------------------------------------===//
362// Bitwise conversions
363//===----------------------------------------------------------------------===//
364
365namespace {
366/// Lower an ArrayConcatOp operation to the LLVM dialect.
367/// Pattern: hw.bitcast(input) ==> load(bitcast_ptr(store(input, alloca)))
368/// This is necessary because we cannot bitcast aggregate types directly in
369/// LLVMIR.
370struct BitcastOpConversion : public ConvertOpToLLVMPattern<hw::BitcastOp> {
371 using ConvertOpToLLVMPattern<hw::BitcastOp>::ConvertOpToLLVMPattern;
372
373 LogicalResult
374 matchAndRewrite(hw::BitcastOp op, OpAdaptor adaptor,
375 ConversionPatternRewriter &rewriter) const override {
376
377 Type resultTy = typeConverter->convertType(op.getResult().getType());
378
379 auto oneC = rewriter.createOrFold<LLVM::ConstantOp>(
380 op->getLoc(), rewriter.getI32Type(), rewriter.getI32IntegerAttr(1));
381
382 auto ptr = LLVM::AllocaOp::create(
383 rewriter, op->getLoc(),
384 LLVM::LLVMPointerType::get(rewriter.getContext()),
385 adaptor.getInput().getType(), oneC,
386 /*alignment=*/4);
387
388 LLVM::StoreOp::create(rewriter, op->getLoc(), adaptor.getInput(), ptr);
389
390 rewriter.replaceOpWithNewOp<LLVM::LoadOp>(op, resultTy, ptr);
391
392 return success();
393 }
394};
395} // namespace
396
397//===----------------------------------------------------------------------===//
398// Value creation conversions
399//===----------------------------------------------------------------------===//
400
401namespace {
402struct HWConstantOpConversion : public ConvertToLLVMPattern {
403 explicit HWConstantOpConversion(MLIRContext *ctx,
404 LLVMTypeConverter &typeConverter)
405 : ConvertToLLVMPattern(hw::ConstantOp::getOperationName(), ctx,
406 typeConverter) {}
407
408 LogicalResult
409 matchAndRewrite(Operation *op, ArrayRef<Value> operand,
410 ConversionPatternRewriter &rewriter) const override {
411 // Get the ConstOp.
412 auto constOp = cast<hw::ConstantOp>(op);
413 // Get the converted llvm type.
414 auto intType = typeConverter->convertType(constOp.getValueAttr().getType());
415 // Replace the operation with an llvm constant op.
416 rewriter.replaceOpWithNewOp<LLVM::ConstantOp>(op, intType,
417 constOp.getValueAttr());
418
419 return success();
420 }
421};
422} // namespace
423
424namespace {
425/// Convert an ArrayCreateOp with dynamic elements to the LLVM dialect. An
426/// equivalent and initialized llvm dialect array type is generated.
427struct HWDynamicArrayCreateOpConversion
428 : public ConvertOpToLLVMPattern<hw::ArrayCreateOp> {
429 using ConvertOpToLLVMPattern<hw::ArrayCreateOp>::ConvertOpToLLVMPattern;
430
431 LogicalResult
432 matchAndRewrite(hw::ArrayCreateOp op, OpAdaptor adaptor,
433 ConversionPatternRewriter &rewriter) const override {
434 auto arrayTy = typeConverter->convertType(op->getResult(0).getType());
435 assert(arrayTy);
436
437 Value arr = LLVM::UndefOp::create(rewriter, op->getLoc(), arrayTy);
438 for (size_t i = 0, e = op.getInputs().size(); i < e; ++i) {
439 Value input =
440 adaptor
442 op.getResult().getType(), i)];
443 arr = LLVM::InsertValueOp::create(rewriter, op->getLoc(), arr, input, i);
444 }
445
446 rewriter.replaceOp(op, arr);
447 return success();
448 }
449};
450} // namespace
451
452namespace {
453
454/// Convert an ArrayCreateOp with constant elements to the LLVM dialect. An
455/// equivalent and initialized llvm dialect array type is generated.
456class AggregateConstantOpConversion
457 : public ConvertOpToLLVMPattern<hw::AggregateConstantOp> {
458 using ConvertOpToLLVMPattern<hw::AggregateConstantOp>::ConvertOpToLLVMPattern;
459
460 bool containsArrayAndStructAggregatesOnly(Type type) const;
461
462 bool isMultiDimArrayOfIntegers(Type type,
463 SmallVectorImpl<int64_t> &dims) const;
464
465 void flatten(Type type, Attribute attr,
466 SmallVectorImpl<Attribute> &output) const;
467
468 Value constructAggregate(OpBuilder &builder,
469 const TypeConverter &typeConverter, Location loc,
470 Type type, Attribute data) const;
471
472public:
473 explicit AggregateConstantOpConversion(
474 LLVMTypeConverter &typeConverter,
475 DenseMap<std::pair<Type, ArrayAttr>, LLVM::GlobalOp>
476 &constAggregateGlobalsMap,
477 Namespace &globals)
478 : ConvertOpToLLVMPattern(typeConverter),
479 constAggregateGlobalsMap(constAggregateGlobalsMap), globals(globals) {}
480
481 LogicalResult
482 matchAndRewrite(hw::AggregateConstantOp op, OpAdaptor adaptor,
483 ConversionPatternRewriter &rewriter) const override;
484
485private:
486 DenseMap<std::pair<Type, ArrayAttr>, LLVM::GlobalOp>
487 &constAggregateGlobalsMap;
488 Namespace &globals;
489};
490} // namespace
491
492namespace {
493/// Convert a StructCreateOp operation to the LLVM dialect. An equivalent and
494/// initialized llvm dialect struct type is generated.
495struct HWStructCreateOpConversion
496 : public ConvertOpToLLVMPattern<hw::StructCreateOp> {
497 using ConvertOpToLLVMPattern<hw::StructCreateOp>::ConvertOpToLLVMPattern;
498
499 LogicalResult
500 matchAndRewrite(hw::StructCreateOp op, OpAdaptor adaptor,
501 ConversionPatternRewriter &rewriter) const override {
502
503 auto resTy = typeConverter->convertType(op.getResult().getType());
504
505 Value tup = LLVM::UndefOp::create(rewriter, op->getLoc(), resTy);
506 for (size_t i = 0, e = cast<LLVM::LLVMStructType>(resTy).getBody().size();
507 i < e; ++i) {
508 Value input =
510 op.getResult().getType(), i)];
511 tup = LLVM::InsertValueOp::create(rewriter, op->getLoc(), tup, input, i);
512 }
513
514 rewriter.replaceOp(op, tup);
515 return success();
516 }
517};
518} // namespace
519
520//===----------------------------------------------------------------------===//
521// Pattern implementations
522//===----------------------------------------------------------------------===//
523
524bool AggregateConstantOpConversion::containsArrayAndStructAggregatesOnly(
525 Type type) const {
526 if (auto intType = dyn_cast<IntegerType>(type))
527 return true;
528
529 if (auto arrTy = dyn_cast<hw::ArrayType>(type))
530 return containsArrayAndStructAggregatesOnly(arrTy.getElementType());
531
532 if (auto structTy = dyn_cast<hw::StructType>(type)) {
533 SmallVector<Type> innerTypes;
534 structTy.getInnerTypes(innerTypes);
535 return llvm::all_of(innerTypes, [&](auto ty) {
536 return containsArrayAndStructAggregatesOnly(ty);
537 });
538 }
539
540 return false;
541}
542
543bool AggregateConstantOpConversion::isMultiDimArrayOfIntegers(
544 Type type, SmallVectorImpl<int64_t> &dims) const {
545 if (auto intType = dyn_cast<IntegerType>(type))
546 return true;
547
548 if (auto arrTy = dyn_cast<hw::ArrayType>(type)) {
549 dims.push_back(arrTy.getNumElements());
550 return isMultiDimArrayOfIntegers(arrTy.getElementType(), dims);
551 }
552
553 return false;
554}
555
556void AggregateConstantOpConversion::flatten(
557 Type type, Attribute attr, SmallVectorImpl<Attribute> &output) const {
558 if (isa<IntegerType>(type)) {
559 assert(isa<IntegerAttr>(attr));
560 output.push_back(attr);
561 return;
562 }
563
564 auto arrAttr = cast<ArrayAttr>(attr);
565 for (size_t i = 0, e = arrAttr.size(); i < e; ++i) {
566 auto element =
568
569 flatten(cast<hw::ArrayType>(type).getElementType(), element, output);
570 }
571}
572
573Value AggregateConstantOpConversion::constructAggregate(
574 OpBuilder &builder, const TypeConverter &typeConverter, Location loc,
575 Type type, Attribute data) const {
576 Type llvmType = typeConverter.convertType(type);
577
578 auto getElementType = [](Type type, size_t index) {
579 if (auto arrTy = dyn_cast<hw::ArrayType>(type)) {
580 return arrTy.getElementType();
581 }
582
583 assert(isa<hw::StructType>(type));
584 auto structTy = cast<hw::StructType>(type);
585 SmallVector<Type> innerTypes;
586 structTy.getInnerTypes(innerTypes);
587 return innerTypes[index];
588 };
589
590 return TypeSwitch<Type, Value>(type)
591 .Case<IntegerType>([&](auto ty) {
592 return LLVM::ConstantOp::create(builder, loc, cast<TypedAttr>(data));
593 })
594 .Case<hw::ArrayType, hw::StructType>([&](auto ty) {
595 Value aggVal = LLVM::UndefOp::create(builder, loc, llvmType);
596 auto arrayAttr = cast<ArrayAttr>(data);
597 for (size_t i = 0, e = arrayAttr.size(); i < e; ++i) {
598 size_t currIdx =
600 Attribute input = arrayAttr[currIdx];
601 Type elementType = getElementType(ty, currIdx);
602
603 Value element = constructAggregate(builder, typeConverter, loc,
604 elementType, input);
605 aggVal =
606 LLVM::InsertValueOp::create(builder, loc, aggVal, element, i);
607 }
608
609 return aggVal;
610 });
611}
612
613LogicalResult AggregateConstantOpConversion::matchAndRewrite(
614 hw::AggregateConstantOp op, OpAdaptor adaptor,
615 ConversionPatternRewriter &rewriter) const {
616 Type aggregateType = op.getResult().getType();
617
618 // TODO: Only arrays and structs supported at the moment.
619 if (!containsArrayAndStructAggregatesOnly(aggregateType))
620 return failure();
621
622 auto llvmTy = typeConverter->convertType(op.getResult().getType());
623 auto typeAttrPair = std::make_pair(aggregateType, adaptor.getFields());
624
625 if (!constAggregateGlobalsMap.count(typeAttrPair) ||
626 !constAggregateGlobalsMap[typeAttrPair]) {
627 auto ipSave = rewriter.saveInsertionPoint();
628
629 Operation *parent = op->getParentOp();
630 while (!isa<mlir::ModuleOp>(parent->getParentOp())) {
631 parent = parent->getParentOp();
632 }
633
634 rewriter.setInsertionPoint(parent);
635
636 // Create a global region for this static array.
637 auto name = globals.newName("_aggregate_const_global");
638
639 SmallVector<int64_t> dims;
640 if (isMultiDimArrayOfIntegers(aggregateType, dims)) {
641 SmallVector<Attribute> ints;
642 flatten(aggregateType, adaptor.getFields(), ints);
643 assert(!ints.empty());
644 auto shapedType = RankedTensorType::get(
645 dims, cast<IntegerAttr>(ints.front()).getType());
646 auto denseAttr = DenseElementsAttr::get(shapedType, ints);
647
648 constAggregateGlobalsMap[typeAttrPair] =
649 LLVM::GlobalOp::create(rewriter, op.getLoc(), llvmTy, true,
650 LLVM::Linkage::Internal, name, denseAttr);
651 } else {
652 auto global =
653 LLVM::GlobalOp::create(rewriter, op.getLoc(), llvmTy, false,
654 LLVM::Linkage::Internal, name, Attribute());
655 Block *blk = new Block();
656 global.getInitializerRegion().push_back(blk);
657 rewriter.setInsertionPointToStart(blk);
658
659 Value aggregate =
660 constructAggregate(rewriter, *typeConverter, op.getLoc(),
661 aggregateType, adaptor.getFields());
662 LLVM::ReturnOp::create(rewriter, op.getLoc(), aggregate);
663 constAggregateGlobalsMap[typeAttrPair] = global;
664 }
665
666 rewriter.restoreInsertionPoint(ipSave);
667 }
668
669 // Get the global array address and load it to return an array value.
670 auto addr = LLVM::AddressOfOp::create(rewriter, op->getLoc(),
671 constAggregateGlobalsMap[typeAttrPair]);
672 rewriter.replaceOpWithNewOp<LLVM::LoadOp>(op, llvmTy, addr);
673
674 return success();
675}
676
677//===----------------------------------------------------------------------===//
678// Type conversions
679//===----------------------------------------------------------------------===//
680
681static Type convertArrayType(hw::ArrayType type, LLVMTypeConverter &converter) {
682 auto elementTy = converter.convertType(type.getElementType());
683 return LLVM::LLVMArrayType::get(elementTy, type.getNumElements());
684}
685
686static Type convertStructType(hw::StructType type,
687 LLVMTypeConverter &converter) {
688 llvm::SmallVector<Type, 8> elements;
689 mlir::SmallVector<mlir::Type> types;
690 type.getInnerTypes(types);
691
692 for (int i = 0, e = types.size(); i < e; ++i)
693 elements.push_back(converter.convertType(
695
696 return LLVM::LLVMStructType::getLiteral(&converter.getContext(), elements);
697}
698
699//===----------------------------------------------------------------------===//
700// Pass initialization
701//===----------------------------------------------------------------------===//
702
703namespace {
704struct HWToLLVMLoweringPass
705 : public circt::impl::ConvertHWToLLVMBase<HWToLLVMLoweringPass> {
706 void runOnOperation() override;
707};
708} // namespace
709
711 LLVMTypeConverter &converter, RewritePatternSet &patterns,
712 Namespace &globals,
713 DenseMap<std::pair<Type, ArrayAttr>, LLVM::GlobalOp>
714 &constAggregateGlobalsMap) {
715 MLIRContext *ctx = converter.getDialect()->getContext();
716
717 // Value creation conversion patterns.
718 patterns.add<HWConstantOpConversion>(ctx, converter);
719 patterns.add<HWDynamicArrayCreateOpConversion, HWStructCreateOpConversion>(
720 converter);
721 patterns.add<AggregateConstantOpConversion>(
722 converter, constAggregateGlobalsMap, globals);
723
724 // Bitwise conversion patterns.
725 patterns.add<BitcastOpConversion>(converter);
726
727 // Extraction operation conversion patterns.
728 patterns.add<ArrayInjectOpConversion, ArrayGetOpConversion,
729 ArraySliceOpConversion, ArrayConcatOpConversion,
730 StructExplodeOpConversion, StructExtractOpConversion,
731 StructInjectOpConversion>(converter);
732}
733
734void circt::populateHWToLLVMTypeConversions(LLVMTypeConverter &converter) {
735 converter.addConversion(
736 [&](hw::ArrayType arr) { return convertArrayType(arr, converter); });
737 converter.addConversion(
738 [&](hw::StructType tup) { return convertStructType(tup, converter); });
739}
740
741void HWToLLVMLoweringPass::runOnOperation() {
742 DenseMap<std::pair<Type, ArrayAttr>, LLVM::GlobalOp> constAggregateGlobalsMap;
743 Namespace globals;
744 SymbolCache cache;
745 cache.addDefinitions(getOperation());
746 globals.add(cache);
747
748 RewritePatternSet patterns(&getContext());
749 auto converter = mlir::LLVMTypeConverter(&getContext());
751
752 LLVMConversionTarget target(getContext());
753 target.addIllegalDialect<hw::HWDialect>();
754
755 // Setup the conversion.
757 constAggregateGlobalsMap);
758
759 // Apply the partial conversion.
760 ConversionConfig config;
761 config.allowPatternRollback = false;
762 if (failed(applyPartialConversion(getOperation(), target, std::move(patterns),
763 config)))
764 signalPassFailure();
765}
766
767/// Create an HW to LLVM conversion pass.
768std::unique_ptr<OperationPass<ModuleOp>> circt::createConvertHWToLLVMPass() {
769 return std::make_unique<HWToLLVMLoweringPass>();
770}
assert(baseType &&"element must be base type")
MlirType elementType
Definition CHIRRTL.cpp:29
static Type convertStructType(hw::StructType type, LLVMTypeConverter &converter)
Definition HWToLLVM.cpp:686
static Value zextByOne(Location loc, ConversionPatternRewriter &rewriter, Value value)
Create a zext operation by one bit on the given value.
Definition HWToLLVM.cpp:74
static Type convertArrayType(hw::ArrayType type, LLVMTypeConverter &converter)
Definition HWToLLVM.cpp:681
A namespace that is used to store existing names and generate new names in some scope within the IR.
Definition Namespace.h:30
void add(mlir::ModuleOp module)
Definition Namespace.h:48
void addDefinitions(mlir::Operation *top)
Populate the symbol cache with all symbol-defining operations within the 'top' operation.
Definition SymCache.cpp:23
Default symbol cache implementation; stores associations between names (StringAttr's) to mlir::Operat...
Definition SymCache.h:85
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
void populateHWToLLVMConversionPatterns(mlir::LLVMTypeConverter &converter, RewritePatternSet &patterns, Namespace &globals, DenseMap< std::pair< Type, ArrayAttr >, mlir::LLVM::GlobalOp > &constAggregateGlobalsMap)
Get the HW to LLVM conversion patterns.
std::unique_ptr< OperationPass< ModuleOp > > createConvertHWToLLVMPass()
Create an HW to LLVM conversion pass.
Definition HWToLLVM.cpp:768
void populateHWToLLVMTypeConversions(mlir::LLVMTypeConverter &converter)
Get the HW to LLVM type conversions.
Definition hw.py:1
static uint32_t convertToLLVMEndianess(Type type, uint32_t index)
Convert an index into a HW ArrayType or StructType to LLVM Endianess.
Definition HWToLLVM.cpp:37
static uint32_t llvmIndexOfStructField(hw::StructType type, StringRef fieldName)
Get the index of a specific StructType field in the LLVM lowering of the StructType.
Definition HWToLLVM.cpp:49