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