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