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 = rewriter.create<LLVM::ConstantOp>(
161 op->getLoc(), rewriter.getI32Type(), rewriter.getI32IntegerAttr(1));
162 auto zextIndex = zextByOne(op->getLoc(), rewriter, op.getIndex());
163
164 Value arrPtr;
165 if (arrElems == 1 || !llvm::isPowerOf2_64(arrElems)) {
166 // Clamp index to prevent OOB access. We add an extra element to the
167 // array so that OOB access modifies this element, leaving the original
168 // array intact.
169 auto maxIndex = rewriter.create<LLVM::ConstantOp>(
170 op->getLoc(), zextIndex.getType(),
171 rewriter.getI32IntegerAttr(arrElems));
172 zextIndex =
173 rewriter.create<LLVM::UMinOp>(op->getLoc(), zextIndex, maxIndex);
174
175 newArrTy = typeConverter->convertType(
176 hw::ArrayType::get(inputType.getElementType(), arrElems + 1));
177 arrPtr = rewriter.create<LLVM::AllocaOp>(
178 op->getLoc(), LLVM::LLVMPointerType::get(rewriter.getContext()),
179 newArrTy, oneC, /*alignment=*/4);
180 } else {
181 arrPtr = rewriter.create<LLVM::AllocaOp>(
182 op->getLoc(), LLVM::LLVMPointerType::get(rewriter.getContext()),
183 newArrTy, oneC, /*alignment=*/4);
184 }
185
186 rewriter.create<LLVM::StoreOp>(op->getLoc(), adaptor.getInput(), arrPtr);
187
188 auto gep = rewriter.create<LLVM::GEPOp>(
189 op->getLoc(), LLVM::LLVMPointerType::get(rewriter.getContext()),
190 newArrTy, arrPtr, ArrayRef<LLVM::GEPArg>{0, zextIndex});
191
192 rewriter.create<LLVM::StoreOp>(op->getLoc(), adaptor.getElement(), gep);
193 rewriter.replaceOpWithNewOp<LLVM::LoadOp>(op, oldArrTy, arrPtr);
194 return success();
195 }
196};
197} // namespace
198
199namespace {
200/// Convert an ArrayGetOp to the LLVM dialect.
201/// Pattern: array_get(input, index) =>
202/// load(gep(store(input, alloca), zext(index)))
203struct ArrayGetOpConversion : public ConvertOpToLLVMPattern<hw::ArrayGetOp> {
204 using ConvertOpToLLVMPattern<hw::ArrayGetOp>::ConvertOpToLLVMPattern;
205
206 LogicalResult
207 matchAndRewrite(hw::ArrayGetOp op, OpAdaptor adaptor,
208 ConversionPatternRewriter &rewriter) const override {
209
210 Value arrPtr;
211 if (auto load = adaptor.getInput().getDefiningOp<LLVM::LoadOp>()) {
212 // In this case the array was loaded from an existing address, so we can
213 // just grab that address instead of reallocating the array on the stack.
214 arrPtr = load.getAddr();
215 } else {
216 auto oneC = LLVM::ConstantOp::create(
217 rewriter, op->getLoc(), IntegerType::get(rewriter.getContext(), 32),
218 rewriter.getI32IntegerAttr(1));
219 arrPtr = LLVM::AllocaOp::create(
220 rewriter, op->getLoc(),
221 LLVM::LLVMPointerType::get(rewriter.getContext()),
222 adaptor.getInput().getType(), oneC,
223 /*alignment=*/4);
224 LLVM::StoreOp::create(rewriter, op->getLoc(), adaptor.getInput(), arrPtr);
225 }
226
227 auto arrTy = typeConverter->convertType(op.getInput().getType());
228 auto elemTy = typeConverter->convertType(op.getResult().getType());
229 auto zextIndex = zextByOne(op->getLoc(), rewriter, op.getIndex());
230
231 // During the ongoing migration to opaque types, use the constructor that
232 // accepts an element type when the array pointer type is opaque, and
233 // otherwise use the typed pointer constructor.
234 auto gep = LLVM::GEPOp::create(
235 rewriter, op->getLoc(),
236 LLVM::LLVMPointerType::get(rewriter.getContext()), arrTy, arrPtr,
237 ArrayRef<LLVM::GEPArg>{0, zextIndex});
238 rewriter.replaceOpWithNewOp<LLVM::LoadOp>(op, elemTy, gep);
239
240 return success();
241 }
242};
243} // namespace
244
245namespace {
246/// Convert an ArraySliceOp to the LLVM dialect.
247/// Pattern: array_slice(input, lowIndex) =>
248/// load(bitcast(gep(store(input, alloca), zext(lowIndex))))
249struct ArraySliceOpConversion
250 : public ConvertOpToLLVMPattern<hw::ArraySliceOp> {
251 using ConvertOpToLLVMPattern<hw::ArraySliceOp>::ConvertOpToLLVMPattern;
252
253 LogicalResult
254 matchAndRewrite(hw::ArraySliceOp op, OpAdaptor adaptor,
255 ConversionPatternRewriter &rewriter) const override {
256
257 auto dstTy = typeConverter->convertType(op.getDst().getType());
258
259 auto oneC =
260 LLVM::ConstantOp::create(rewriter, op->getLoc(), rewriter.getI32Type(),
261 rewriter.getI32IntegerAttr(1));
262
263 auto arrPtr = LLVM::AllocaOp::create(
264 rewriter, op->getLoc(),
265 LLVM::LLVMPointerType::get(rewriter.getContext()),
266 adaptor.getInput().getType(), oneC,
267 /*alignment=*/4);
268
269 LLVM::StoreOp::create(rewriter, op->getLoc(), adaptor.getInput(), arrPtr);
270
271 auto zextIndex = zextByOne(op->getLoc(), rewriter, op.getLowIndex());
272
273 // During the ongoing migration to opaque types, use the constructor that
274 // accepts an element type when the array pointer type is opaque, and
275 // otherwise use the typed pointer constructor.
276 auto gep = LLVM::GEPOp::create(
277 rewriter, op->getLoc(),
278 LLVM::LLVMPointerType::get(rewriter.getContext()), dstTy, arrPtr,
279 ArrayRef<LLVM::GEPArg>{0, zextIndex});
280
281 rewriter.replaceOpWithNewOp<LLVM::LoadOp>(op, dstTy, gep);
282
283 return success();
284 }
285};
286} // namespace
287
288//===----------------------------------------------------------------------===//
289// Insertion operations conversion
290//===----------------------------------------------------------------------===//
291
292namespace {
293/// Convert a StructInjectOp to LLVM dialect.
294/// Pattern: struct_inject(input, index, value) =>
295/// insertvalue(input, value, index)
296struct StructInjectOpConversion
297 : public ConvertOpToLLVMPattern<hw::StructInjectOp> {
298 using ConvertOpToLLVMPattern<hw::StructInjectOp>::ConvertOpToLLVMPattern;
299
300 LogicalResult
301 matchAndRewrite(hw::StructInjectOp op, OpAdaptor adaptor,
302 ConversionPatternRewriter &rewriter) const override {
303
305 op.getInput().getType(), op.getFieldIndex());
306
307 rewriter.replaceOpWithNewOp<LLVM::InsertValueOp>(
308 op, adaptor.getInput(), adaptor.getNewValue(), fieldIndex);
309
310 return success();
311 }
312};
313} // namespace
314
315//===----------------------------------------------------------------------===//
316// Concat operations conversion
317//===----------------------------------------------------------------------===//
318
319namespace {
320/// Lower an ArrayConcatOp operation to the LLVM dialect.
321struct ArrayConcatOpConversion
322 : public ConvertOpToLLVMPattern<hw::ArrayConcatOp> {
323 using ConvertOpToLLVMPattern<hw::ArrayConcatOp>::ConvertOpToLLVMPattern;
324
325 LogicalResult
326 matchAndRewrite(hw::ArrayConcatOp op, OpAdaptor adaptor,
327 ConversionPatternRewriter &rewriter) const override {
328
329 hw::ArrayType arrTy = cast<hw::ArrayType>(op.getResult().getType());
330 Type resultTy = typeConverter->convertType(arrTy);
331
332 Value arr = LLVM::UndefOp::create(rewriter, op->getLoc(), resultTy);
333
334 // Attention: j is hardcoded for little endian machines.
335 size_t j = op.getInputs().size() - 1, k = 0;
336
337 for (size_t i = 0, e = arrTy.getNumElements(); i < e; ++i) {
338 Value element = LLVM::ExtractValueOp::create(rewriter, op->getLoc(),
339 adaptor.getInputs()[j], k);
340 arr =
341 LLVM::InsertValueOp::create(rewriter, op->getLoc(), arr, element, i);
342
343 ++k;
344 if (k >=
345 cast<hw::ArrayType>(op.getInputs()[j].getType()).getNumElements()) {
346 k = 0;
347 --j;
348 }
349 }
350
351 rewriter.replaceOp(op, arr);
352 return success();
353 }
354};
355} // namespace
356
357//===----------------------------------------------------------------------===//
358// Bitwise conversions
359//===----------------------------------------------------------------------===//
360
361namespace {
362/// Lower an ArrayConcatOp operation to the LLVM dialect.
363/// Pattern: hw.bitcast(input) ==> load(bitcast_ptr(store(input, alloca)))
364/// This is necessary because we cannot bitcast aggregate types directly in
365/// LLVMIR.
366struct BitcastOpConversion : public ConvertOpToLLVMPattern<hw::BitcastOp> {
367 using ConvertOpToLLVMPattern<hw::BitcastOp>::ConvertOpToLLVMPattern;
368
369 LogicalResult
370 matchAndRewrite(hw::BitcastOp op, OpAdaptor adaptor,
371 ConversionPatternRewriter &rewriter) const override {
372
373 Type resultTy = typeConverter->convertType(op.getResult().getType());
374
375 auto oneC = rewriter.createOrFold<LLVM::ConstantOp>(
376 op->getLoc(), rewriter.getI32Type(), rewriter.getI32IntegerAttr(1));
377
378 auto ptr = LLVM::AllocaOp::create(
379 rewriter, op->getLoc(),
380 LLVM::LLVMPointerType::get(rewriter.getContext()),
381 adaptor.getInput().getType(), oneC,
382 /*alignment=*/4);
383
384 LLVM::StoreOp::create(rewriter, op->getLoc(), adaptor.getInput(), ptr);
385
386 rewriter.replaceOpWithNewOp<LLVM::LoadOp>(op, resultTy, ptr);
387
388 return success();
389 }
390};
391} // namespace
392
393//===----------------------------------------------------------------------===//
394// Value creation conversions
395//===----------------------------------------------------------------------===//
396
397namespace {
398struct HWConstantOpConversion : public ConvertToLLVMPattern {
399 explicit HWConstantOpConversion(MLIRContext *ctx,
400 LLVMTypeConverter &typeConverter)
401 : ConvertToLLVMPattern(hw::ConstantOp::getOperationName(), ctx,
402 typeConverter) {}
403
404 LogicalResult
405 matchAndRewrite(Operation *op, ArrayRef<Value> operand,
406 ConversionPatternRewriter &rewriter) const override {
407 // Get the ConstOp.
408 auto constOp = cast<hw::ConstantOp>(op);
409 // Get the converted llvm type.
410 auto intType = typeConverter->convertType(constOp.getValueAttr().getType());
411 // Replace the operation with an llvm constant op.
412 rewriter.replaceOpWithNewOp<LLVM::ConstantOp>(op, intType,
413 constOp.getValueAttr());
414
415 return success();
416 }
417};
418} // namespace
419
420namespace {
421/// Convert an ArrayCreateOp with dynamic elements to the LLVM dialect. An
422/// equivalent and initialized llvm dialect array type is generated.
423struct HWDynamicArrayCreateOpConversion
424 : public ConvertOpToLLVMPattern<hw::ArrayCreateOp> {
425 using ConvertOpToLLVMPattern<hw::ArrayCreateOp>::ConvertOpToLLVMPattern;
426
427 LogicalResult
428 matchAndRewrite(hw::ArrayCreateOp op, OpAdaptor adaptor,
429 ConversionPatternRewriter &rewriter) const override {
430 auto arrayTy = typeConverter->convertType(op->getResult(0).getType());
431 assert(arrayTy);
432
433 Value arr = LLVM::UndefOp::create(rewriter, op->getLoc(), arrayTy);
434 for (size_t i = 0, e = op.getInputs().size(); i < e; ++i) {
435 Value input =
436 adaptor
438 op.getResult().getType(), i)];
439 arr = LLVM::InsertValueOp::create(rewriter, op->getLoc(), arr, input, i);
440 }
441
442 rewriter.replaceOp(op, arr);
443 return success();
444 }
445};
446} // namespace
447
448namespace {
449
450/// Convert an ArrayCreateOp with constant elements to the LLVM dialect. An
451/// equivalent and initialized llvm dialect array type is generated.
452class AggregateConstantOpConversion
453 : public ConvertOpToLLVMPattern<hw::AggregateConstantOp> {
454 using ConvertOpToLLVMPattern<hw::AggregateConstantOp>::ConvertOpToLLVMPattern;
455
456 bool containsArrayAndStructAggregatesOnly(Type type) const;
457
458 bool isMultiDimArrayOfIntegers(Type type,
459 SmallVectorImpl<int64_t> &dims) const;
460
461 void flatten(Type type, Attribute attr,
462 SmallVectorImpl<Attribute> &output) const;
463
464 Value constructAggregate(OpBuilder &builder,
465 const TypeConverter &typeConverter, Location loc,
466 Type type, Attribute data) const;
467
468public:
469 explicit AggregateConstantOpConversion(
470 LLVMTypeConverter &typeConverter,
471 DenseMap<std::pair<Type, ArrayAttr>, LLVM::GlobalOp>
472 &constAggregateGlobalsMap,
473 Namespace &globals)
474 : ConvertOpToLLVMPattern(typeConverter),
475 constAggregateGlobalsMap(constAggregateGlobalsMap), globals(globals) {}
476
477 LogicalResult
478 matchAndRewrite(hw::AggregateConstantOp op, OpAdaptor adaptor,
479 ConversionPatternRewriter &rewriter) const override;
480
481private:
482 DenseMap<std::pair<Type, ArrayAttr>, LLVM::GlobalOp>
483 &constAggregateGlobalsMap;
484 Namespace &globals;
485};
486} // namespace
487
488namespace {
489/// Convert a StructCreateOp operation to the LLVM dialect. An equivalent and
490/// initialized llvm dialect struct type is generated.
491struct HWStructCreateOpConversion
492 : public ConvertOpToLLVMPattern<hw::StructCreateOp> {
493 using ConvertOpToLLVMPattern<hw::StructCreateOp>::ConvertOpToLLVMPattern;
494
495 LogicalResult
496 matchAndRewrite(hw::StructCreateOp op, OpAdaptor adaptor,
497 ConversionPatternRewriter &rewriter) const override {
498
499 auto resTy = typeConverter->convertType(op.getResult().getType());
500
501 Value tup = LLVM::UndefOp::create(rewriter, op->getLoc(), resTy);
502 for (size_t i = 0, e = cast<LLVM::LLVMStructType>(resTy).getBody().size();
503 i < e; ++i) {
504 Value input =
506 op.getResult().getType(), i)];
507 tup = LLVM::InsertValueOp::create(rewriter, op->getLoc(), tup, input, i);
508 }
509
510 rewriter.replaceOp(op, tup);
511 return success();
512 }
513};
514} // namespace
515
516//===----------------------------------------------------------------------===//
517// Pattern implementations
518//===----------------------------------------------------------------------===//
519
520bool AggregateConstantOpConversion::containsArrayAndStructAggregatesOnly(
521 Type type) const {
522 if (auto intType = dyn_cast<IntegerType>(type))
523 return true;
524
525 if (auto arrTy = dyn_cast<hw::ArrayType>(type))
526 return containsArrayAndStructAggregatesOnly(arrTy.getElementType());
527
528 if (auto structTy = dyn_cast<hw::StructType>(type)) {
529 SmallVector<Type> innerTypes;
530 structTy.getInnerTypes(innerTypes);
531 return llvm::all_of(innerTypes, [&](auto ty) {
532 return containsArrayAndStructAggregatesOnly(ty);
533 });
534 }
535
536 return false;
537}
538
539bool AggregateConstantOpConversion::isMultiDimArrayOfIntegers(
540 Type type, SmallVectorImpl<int64_t> &dims) const {
541 if (auto intType = dyn_cast<IntegerType>(type))
542 return true;
543
544 if (auto arrTy = dyn_cast<hw::ArrayType>(type)) {
545 dims.push_back(arrTy.getNumElements());
546 return isMultiDimArrayOfIntegers(arrTy.getElementType(), dims);
547 }
548
549 return false;
550}
551
552void AggregateConstantOpConversion::flatten(
553 Type type, Attribute attr, SmallVectorImpl<Attribute> &output) const {
554 if (isa<IntegerType>(type)) {
555 assert(isa<IntegerAttr>(attr));
556 output.push_back(attr);
557 return;
558 }
559
560 auto arrAttr = cast<ArrayAttr>(attr);
561 for (size_t i = 0, e = arrAttr.size(); i < e; ++i) {
562 auto element =
564
565 flatten(cast<hw::ArrayType>(type).getElementType(), element, output);
566 }
567}
568
569Value AggregateConstantOpConversion::constructAggregate(
570 OpBuilder &builder, const TypeConverter &typeConverter, Location loc,
571 Type type, Attribute data) const {
572 Type llvmType = typeConverter.convertType(type);
573
574 auto getElementType = [](Type type, size_t index) {
575 if (auto arrTy = dyn_cast<hw::ArrayType>(type)) {
576 return arrTy.getElementType();
577 }
578
579 assert(isa<hw::StructType>(type));
580 auto structTy = cast<hw::StructType>(type);
581 SmallVector<Type> innerTypes;
582 structTy.getInnerTypes(innerTypes);
583 return innerTypes[index];
584 };
585
586 return TypeSwitch<Type, Value>(type)
587 .Case<IntegerType>([&](auto ty) {
588 return LLVM::ConstantOp::create(builder, loc, cast<TypedAttr>(data));
589 })
590 .Case<hw::ArrayType, hw::StructType>([&](auto ty) {
591 Value aggVal = LLVM::UndefOp::create(builder, loc, llvmType);
592 auto arrayAttr = cast<ArrayAttr>(data);
593 for (size_t i = 0, e = arrayAttr.size(); i < e; ++i) {
594 size_t currIdx =
596 Attribute input = arrayAttr[currIdx];
597 Type elementType = getElementType(ty, currIdx);
598
599 Value element = constructAggregate(builder, typeConverter, loc,
600 elementType, input);
601 aggVal =
602 LLVM::InsertValueOp::create(builder, loc, aggVal, element, i);
603 }
604
605 return aggVal;
606 });
607}
608
609LogicalResult AggregateConstantOpConversion::matchAndRewrite(
610 hw::AggregateConstantOp op, OpAdaptor adaptor,
611 ConversionPatternRewriter &rewriter) const {
612 Type aggregateType = op.getResult().getType();
613
614 // TODO: Only arrays and structs supported at the moment.
615 if (!containsArrayAndStructAggregatesOnly(aggregateType))
616 return failure();
617
618 auto llvmTy = typeConverter->convertType(op.getResult().getType());
619 auto typeAttrPair = std::make_pair(aggregateType, adaptor.getFields());
620
621 if (!constAggregateGlobalsMap.count(typeAttrPair) ||
622 !constAggregateGlobalsMap[typeAttrPair]) {
623 auto ipSave = rewriter.saveInsertionPoint();
624
625 Operation *parent = op->getParentOp();
626 while (!isa<mlir::ModuleOp>(parent->getParentOp())) {
627 parent = parent->getParentOp();
628 }
629
630 rewriter.setInsertionPoint(parent);
631
632 // Create a global region for this static array.
633 auto name = globals.newName("_aggregate_const_global");
634
635 SmallVector<int64_t> dims;
636 if (isMultiDimArrayOfIntegers(aggregateType, dims)) {
637 SmallVector<Attribute> ints;
638 flatten(aggregateType, adaptor.getFields(), ints);
639 assert(!ints.empty());
640 auto shapedType = RankedTensorType::get(
641 dims, cast<IntegerAttr>(ints.front()).getType());
642 auto denseAttr = DenseElementsAttr::get(shapedType, ints);
643
644 constAggregateGlobalsMap[typeAttrPair] =
645 LLVM::GlobalOp::create(rewriter, op.getLoc(), llvmTy, true,
646 LLVM::Linkage::Internal, name, denseAttr);
647 } else {
648 auto global =
649 LLVM::GlobalOp::create(rewriter, op.getLoc(), llvmTy, false,
650 LLVM::Linkage::Internal, name, Attribute());
651 Block *blk = new Block();
652 global.getInitializerRegion().push_back(blk);
653 rewriter.setInsertionPointToStart(blk);
654
655 Value aggregate =
656 constructAggregate(rewriter, *typeConverter, op.getLoc(),
657 aggregateType, adaptor.getFields());
658 LLVM::ReturnOp::create(rewriter, op.getLoc(), aggregate);
659 constAggregateGlobalsMap[typeAttrPair] = global;
660 }
661
662 rewriter.restoreInsertionPoint(ipSave);
663 }
664
665 // Get the global array address and load it to return an array value.
666 auto addr = LLVM::AddressOfOp::create(rewriter, op->getLoc(),
667 constAggregateGlobalsMap[typeAttrPair]);
668 rewriter.replaceOpWithNewOp<LLVM::LoadOp>(op, llvmTy, addr);
669
670 return success();
671}
672
673//===----------------------------------------------------------------------===//
674// Type conversions
675//===----------------------------------------------------------------------===//
676
677static Type convertArrayType(hw::ArrayType type, LLVMTypeConverter &converter) {
678 auto elementTy = converter.convertType(type.getElementType());
679 return LLVM::LLVMArrayType::get(elementTy, type.getNumElements());
680}
681
682static Type convertStructType(hw::StructType type,
683 LLVMTypeConverter &converter) {
684 llvm::SmallVector<Type, 8> elements;
685 mlir::SmallVector<mlir::Type> types;
686 type.getInnerTypes(types);
687
688 for (int i = 0, e = types.size(); i < e; ++i)
689 elements.push_back(converter.convertType(
691
692 return LLVM::LLVMStructType::getLiteral(&converter.getContext(), elements);
693}
694
695//===----------------------------------------------------------------------===//
696// Pass initialization
697//===----------------------------------------------------------------------===//
698
699namespace {
700struct HWToLLVMLoweringPass
701 : public circt::impl::ConvertHWToLLVMBase<HWToLLVMLoweringPass> {
702 void runOnOperation() override;
703};
704} // namespace
705
707 LLVMTypeConverter &converter, RewritePatternSet &patterns,
708 Namespace &globals,
709 DenseMap<std::pair<Type, ArrayAttr>, LLVM::GlobalOp>
710 &constAggregateGlobalsMap) {
711 MLIRContext *ctx = converter.getDialect()->getContext();
712
713 // Value creation conversion patterns.
714 patterns.add<HWConstantOpConversion>(ctx, converter);
715 patterns.add<HWDynamicArrayCreateOpConversion, HWStructCreateOpConversion>(
716 converter);
717 patterns.add<AggregateConstantOpConversion>(
718 converter, constAggregateGlobalsMap, globals);
719
720 // Bitwise conversion patterns.
721 patterns.add<BitcastOpConversion>(converter);
722
723 // Extraction operation conversion patterns.
724 patterns.add<ArrayInjectOpConversion, ArrayGetOpConversion,
725 ArraySliceOpConversion, ArrayConcatOpConversion,
726 StructExplodeOpConversion, StructExtractOpConversion,
727 StructInjectOpConversion>(converter);
728}
729
730void circt::populateHWToLLVMTypeConversions(LLVMTypeConverter &converter) {
731 converter.addConversion(
732 [&](hw::ArrayType arr) { return convertArrayType(arr, converter); });
733 converter.addConversion(
734 [&](hw::StructType tup) { return convertStructType(tup, converter); });
735}
736
737void HWToLLVMLoweringPass::runOnOperation() {
738 DenseMap<std::pair<Type, ArrayAttr>, LLVM::GlobalOp> constAggregateGlobalsMap;
739 Namespace globals;
740 SymbolCache cache;
741 cache.addDefinitions(getOperation());
742 globals.add(cache);
743
744 RewritePatternSet patterns(&getContext());
745 auto converter = mlir::LLVMTypeConverter(&getContext());
747
748 LLVMConversionTarget target(getContext());
749 target.addIllegalDialect<hw::HWDialect>();
750
751 // Setup the conversion.
753 constAggregateGlobalsMap);
754
755 // Apply the partial conversion.
756 ConversionConfig config;
757 config.allowPatternRollback = false;
758 if (failed(applyPartialConversion(getOperation(), target, std::move(patterns),
759 config)))
760 signalPassFailure();
761}
762
763/// Create an HW to LLVM conversion pass.
764std::unique_ptr<OperationPass<ModuleOp>> circt::createConvertHWToLLVMPass() {
765 return std::make_unique<HWToLLVMLoweringPass>();
766}
assert(baseType &&"element must be base type")
MlirType elementType
Definition CHIRRTL.cpp:29
static Type convertStructType(hw::StructType type, LLVMTypeConverter &converter)
Definition HWToLLVM.cpp:682
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:677
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:764
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