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