CIRCT 22.0.0git
Loading...
Searching...
No Matches
LLHDOps.cpp
Go to the documentation of this file.
1//===----------------------------------------------------------------------===//
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
12#include "mlir/IR/Attributes.h"
13#include "mlir/IR/BuiltinTypes.h"
14#include "mlir/IR/Matchers.h"
15#include "mlir/IR/PatternMatch.h"
16#include "mlir/IR/Region.h"
17#include "mlir/IR/Types.h"
18#include "mlir/IR/Value.h"
19#include "llvm/ADT/ArrayRef.h"
20#include "llvm/ADT/SmallVector.h"
21
22using namespace circt;
23using namespace mlir;
24using namespace llhd;
25
26unsigned circt::llhd::getLLHDTypeWidth(Type type) {
27 if (auto sig = dyn_cast<RefType>(type))
28 type = sig.getNestedType();
29 if (auto array = dyn_cast<hw::ArrayType>(type))
30 return array.getNumElements();
31 if (auto tup = dyn_cast<hw::StructType>(type))
32 return tup.getElements().size();
33 return type.getIntOrFloatBitWidth();
34}
35
37 if (auto sig = dyn_cast<RefType>(type))
38 type = sig.getNestedType();
39 if (auto array = dyn_cast<hw::ArrayType>(type))
40 return array.getElementType();
41 return type;
42}
43
44//===----------------------------------------------------------------------===//
45// ConstantTimeOp
46//===----------------------------------------------------------------------===//
47
48OpFoldResult llhd::ConstantTimeOp::fold(FoldAdaptor adaptor) {
49 assert(adaptor.getOperands().empty() && "const has no operands");
50 return getValueAttr();
51}
52
53void llhd::ConstantTimeOp::build(OpBuilder &builder, OperationState &result,
54 uint64_t time, const StringRef &timeUnit,
55 unsigned delta, unsigned epsilon) {
56 auto *ctx = builder.getContext();
57 auto attr = TimeAttr::get(ctx, time, timeUnit, delta, epsilon);
58 return build(builder, result, TimeType::get(ctx), attr);
59}
60
61//===----------------------------------------------------------------------===//
62// SignalOp
63//===----------------------------------------------------------------------===//
64
65static Value getValueAtIndex(OpBuilder &builder, Location loc, Value val,
66 unsigned index) {
67 return TypeSwitch<Type, Value>(val.getType())
68 .Case<hw::StructType>([&](hw::StructType ty) -> Value {
69 return hw::StructExtractOp::create(builder, loc, val,
70 ty.getElements()[index].name);
71 })
72 .Case<hw::ArrayType>([&](hw::ArrayType ty) -> Value {
73 Value idx = hw::ConstantOp::create(
74 builder, loc,
75 builder.getIntegerType(llvm::Log2_64_Ceil(ty.getNumElements())),
76 index);
77 return hw::ArrayGetOp::create(builder, loc, val, idx);
78 });
79}
80
81void SignalOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
82 if (getName() && !getName()->empty())
83 setNameFn(getResult(), *getName());
84}
85
86SmallVector<DestructurableMemorySlot> SignalOp::getDestructurableSlots() {
87 auto type = getType().getNestedType();
88
89 auto destructurable = llvm::dyn_cast<DestructurableTypeInterface>(type);
90 if (!destructurable)
91 return {};
92
93 auto destructuredType = destructurable.getSubelementIndexMap();
94 if (!destructuredType)
95 return {};
96
97 return {DestructurableMemorySlot{{getResult(), type}, *destructuredType}};
98}
99
100DenseMap<Attribute, MemorySlot> SignalOp::destructure(
101 const DestructurableMemorySlot &slot,
102 const SmallPtrSetImpl<Attribute> &usedIndices, OpBuilder &builder,
103 SmallVectorImpl<DestructurableAllocationOpInterface> &newAllocators) {
104 assert(slot.ptr == getResult());
105 builder.setInsertionPointAfter(*this);
106
107 auto destructurableType =
108 cast<DestructurableTypeInterface>(getType().getNestedType());
109 DenseMap<Attribute, MemorySlot> slotMap;
110 SmallVector<std::pair<unsigned, Type>> indices;
111 for (auto attr : usedIndices) {
112 assert(isa<IntegerAttr>(attr));
113 auto elemType = destructurableType.getTypeAtIndex(attr);
114 assert(elemType && "used index must exist");
115 indices.push_back({cast<IntegerAttr>(attr).getInt(), elemType});
116 }
117
118 llvm::sort(indices, [](auto a, auto b) { return a.first < b.first; });
119
120 for (auto [index, type] : indices) {
121 Value init = getValueAtIndex(builder, getLoc(), getInit(), index);
122 auto sigOp = SignalOp::create(builder, getLoc(), getNameAttr(), init);
123 newAllocators.push_back(sigOp);
124 slotMap.try_emplace<MemorySlot>(
125 IntegerAttr::get(IndexType::get(getContext()), index),
126 {sigOp.getResult(), type});
127 }
128
129 return slotMap;
130}
131
132std::optional<DestructurableAllocationOpInterface>
133SignalOp::handleDestructuringComplete(const DestructurableMemorySlot &slot,
134 OpBuilder &builder) {
135 assert(slot.ptr == getResult());
136 this->erase();
137 return std::nullopt;
138}
139
140//===----------------------------------------------------------------------===//
141// SigExtractOp
142//===----------------------------------------------------------------------===//
143
144template <class Op>
145static OpFoldResult foldSigPtrExtractOp(Op op, ArrayRef<Attribute> operands) {
146
147 if (!operands[1])
148 return nullptr;
149
150 // llhd.sig.extract(input, 0) with inputWidth == resultWidth => input
151 if (op.getResultWidth() == op.getInputWidth() &&
152 cast<IntegerAttr>(operands[1]).getValue().isZero())
153 return op.getInput();
154
155 return nullptr;
156}
157
158OpFoldResult llhd::SigExtractOp::fold(FoldAdaptor adaptor) {
159 return foldSigPtrExtractOp(*this, adaptor.getOperands());
160}
161
162//===----------------------------------------------------------------------===//
163// SigArraySliceOp
164//===----------------------------------------------------------------------===//
165
166OpFoldResult llhd::SigArraySliceOp::fold(FoldAdaptor adaptor) {
167 auto lowIndex = dyn_cast_or_null<IntegerAttr>(adaptor.getLowIndex());
168 if (!lowIndex)
169 return {};
170
171 // llhd.sig.array_slice(input, 0) with inputWidth == resultWidth => input
172 if (getResultWidth() == getInputWidth() && lowIndex.getValue().isZero())
173 return getInput();
174
175 return {};
176}
177
178template <class Op>
179static LogicalResult canonicalizeSigPtrArraySliceOp(Op op,
180 PatternRewriter &rewriter) {
181 IntegerAttr indexAttr;
182 if (!matchPattern(op.getLowIndex(), m_Constant(&indexAttr)))
183 return failure();
184
185 // llhd.sig.array_slice(llhd.sig.array_slice(target, a), b)
186 // => llhd.sig.array_slice(target, a+b)
187 IntegerAttr a;
188 if (matchPattern(op.getInput(),
189 m_Op<Op>(matchers::m_Any(), m_Constant(&a)))) {
190 auto sliceOp = op.getInput().template getDefiningOp<Op>();
191 rewriter.modifyOpInPlace(op, [&]() {
192 op.getInputMutable().assign(sliceOp.getInput());
193 Value newIndex = hw::ConstantOp::create(
194 rewriter, op->getLoc(), a.getValue() + indexAttr.getValue());
195 op.getLowIndexMutable().assign(newIndex);
196 });
197
198 return success();
199 }
200
201 return failure();
202}
203
204LogicalResult llhd::SigArraySliceOp::canonicalize(llhd::SigArraySliceOp op,
205 PatternRewriter &rewriter) {
206 return canonicalizeSigPtrArraySliceOp(op, rewriter);
207}
208
209//===----------------------------------------------------------------------===//
210// SigArrayGetOp
211//===----------------------------------------------------------------------===//
212
213bool SigArrayGetOp::canRewire(const DestructurableMemorySlot &slot,
214 SmallPtrSetImpl<Attribute> &usedIndices,
215 SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
216 const DataLayout &dataLayout) {
217 if (slot.ptr != getInput())
218 return false;
219 APInt idx;
220 if (!matchPattern(getIndex(), m_ConstantInt(&idx)))
221 return false;
222 auto index =
223 IntegerAttr::get(IndexType::get(getContext()), idx.getZExtValue());
224 if (!slot.subelementTypes.contains(index))
225 return false;
226 usedIndices.insert(index);
227 mustBeSafelyUsed.emplace_back<MemorySlot>(
228 {getResult(), cast<RefType>(getResult().getType()).getNestedType()});
229 return true;
230}
231
232DeletionKind SigArrayGetOp::rewire(const DestructurableMemorySlot &slot,
233 DenseMap<Attribute, MemorySlot> &subslots,
234 OpBuilder &builder,
235 const DataLayout &dataLayout) {
236 APInt idx;
237 bool result = matchPattern(getIndex(), m_ConstantInt(&idx));
238 (void)result;
239 assert(result);
240 auto index =
241 IntegerAttr::get(IndexType::get(getContext()), idx.getZExtValue());
242 auto it = subslots.find(index);
243 assert(it != subslots.end());
244 replaceAllUsesWith(it->getSecond().ptr);
245 return DeletionKind::Delete;
246}
247
248LogicalResult SigArrayGetOp::ensureOnlySafeAccesses(
249 const MemorySlot &slot, SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
250 const DataLayout &dataLayout) {
251 return success();
252}
253
254//===----------------------------------------------------------------------===//
255// SigStructExtractOp
256//===----------------------------------------------------------------------===//
257
258LogicalResult llhd::SigStructExtractOp::inferReturnTypes(
259 MLIRContext *context, std::optional<Location> loc, ValueRange operands,
260 DictionaryAttr attrs, mlir::OpaqueProperties properties,
261 mlir::RegionRange regions, SmallVectorImpl<Type> &results) {
262 typename SigStructExtractOp::Adaptor adaptor(operands, attrs, properties,
263 regions);
264 Type type = cast<hw::StructType>(
265 cast<RefType>(adaptor.getInput().getType()).getNestedType())
266 .getFieldType(adaptor.getField());
267 if (!type) {
268 context->getDiagEngine().emit(loc.value_or(UnknownLoc()),
269 DiagnosticSeverity::Error)
270 << "invalid field name specified";
271 return failure();
272 }
273 results.push_back(RefType::get(type));
274 return success();
275}
276
277bool SigStructExtractOp::canRewire(
278 const DestructurableMemorySlot &slot,
279 SmallPtrSetImpl<Attribute> &usedIndices,
280 SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
281 const DataLayout &dataLayout) {
282 if (slot.ptr != getInput())
283 return false;
284 auto index =
285 cast<hw::StructType>(cast<RefType>(getInput().getType()).getNestedType())
286 .getFieldIndex(getFieldAttr());
287 if (!index)
288 return false;
289 auto indexAttr = IntegerAttr::get(IndexType::get(getContext()), *index);
290 if (!slot.subelementTypes.contains(indexAttr))
291 return false;
292 usedIndices.insert(indexAttr);
293 mustBeSafelyUsed.emplace_back<MemorySlot>(
294 {getResult(), cast<RefType>(getResult().getType()).getNestedType()});
295 return true;
296}
297
298DeletionKind
299SigStructExtractOp::rewire(const DestructurableMemorySlot &slot,
300 DenseMap<Attribute, MemorySlot> &subslots,
301 OpBuilder &builder, const DataLayout &dataLayout) {
302 auto index =
303 cast<hw::StructType>(cast<RefType>(getInput().getType()).getNestedType())
304 .getFieldIndex(getFieldAttr());
305 assert(index.has_value());
306 auto indexAttr = IntegerAttr::get(IndexType::get(getContext()), *index);
307 auto it = subslots.find(indexAttr);
308 assert(it != subslots.end());
309 replaceAllUsesWith(it->getSecond().ptr);
310 return DeletionKind::Delete;
311}
312
313LogicalResult SigStructExtractOp::ensureOnlySafeAccesses(
314 const MemorySlot &slot, SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
315 const DataLayout &dataLayout) {
316 return success();
317}
318
319//===----------------------------------------------------------------------===//
320// ProbeOp
321//===----------------------------------------------------------------------===//
322
323static void getSortedPtrs(DenseMap<Attribute, MemorySlot> &subslots,
324 SmallVectorImpl<std::pair<unsigned, Value>> &sorted) {
325 for (auto [attr, mem] : subslots) {
326 assert(isa<IntegerAttr>(attr));
327 sorted.push_back({cast<IntegerAttr>(attr).getInt(), mem.ptr});
328 }
329
330 llvm::sort(sorted, [](auto a, auto b) { return a.first < b.first; });
331}
332
333bool ProbeOp::canRewire(const DestructurableMemorySlot &slot,
334 SmallPtrSetImpl<Attribute> &usedIndices,
335 SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
336 const DataLayout &dataLayout) {
337 for (auto [key, _] : slot.subelementTypes)
338 usedIndices.insert(key);
339
340 return isa<hw::StructType, hw::ArrayType>(slot.elemType);
341}
342
343DeletionKind ProbeOp::rewire(const DestructurableMemorySlot &slot,
344 DenseMap<Attribute, MemorySlot> &subslots,
345 OpBuilder &builder, const DataLayout &dataLayout) {
346 SmallVector<std::pair<unsigned, Value>> elements;
347 SmallVector<Value> probed;
348 getSortedPtrs(subslots, elements);
349 for (auto [_, val] : elements)
350 probed.push_back(ProbeOp::create(builder, getLoc(), val));
351
352 Value repl =
353 TypeSwitch<Type, Value>(getType())
354 .Case<hw::StructType>([&](auto ty) {
355 return hw::StructCreateOp::create(builder, getLoc(), getType(),
356 probed);
357 })
358 .Case<hw::ArrayType>([&](auto ty) {
359 return hw::ArrayCreateOp::create(builder, getLoc(), probed);
360 });
361
362 replaceAllUsesWith(repl);
363 return DeletionKind::Delete;
364}
365
366LogicalResult
367ProbeOp::ensureOnlySafeAccesses(const MemorySlot &slot,
368 SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
369 const DataLayout &dataLayout) {
370 return success();
371}
372
373void ProbeOp::getEffects(
374 SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
375 &effects) {
376 if (mayHaveSSADominance(*getOperation()->getParentRegion()))
377 effects.emplace_back(MemoryEffects::Read::get(), &getSignalMutable());
378}
379
380//===----------------------------------------------------------------------===//
381// DriveOp
382//===----------------------------------------------------------------------===//
383
384LogicalResult llhd::DriveOp::fold(FoldAdaptor adaptor,
385 SmallVectorImpl<OpFoldResult> &result) {
386 if (!getEnable())
387 return failure();
388
389 if (matchPattern(getEnable(), m_One())) {
390 getEnableMutable().clear();
391 return success();
392 }
393
394 return failure();
395}
396
397LogicalResult llhd::DriveOp::canonicalize(llhd::DriveOp op,
398 PatternRewriter &rewriter) {
399 if (!op.getEnable())
400 return failure();
401
402 if (matchPattern(op.getEnable(), m_Zero())) {
403 rewriter.eraseOp(op);
404 return success();
405 }
406
407 return failure();
408}
409
410bool DriveOp::canRewire(const DestructurableMemorySlot &slot,
411 SmallPtrSetImpl<Attribute> &usedIndices,
412 SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
413 const DataLayout &dataLayout) {
414 for (auto [key, _] : slot.subelementTypes)
415 usedIndices.insert(key);
416
417 return isa<hw::StructType, hw::ArrayType>(slot.elemType);
418}
419
420DeletionKind DriveOp::rewire(const DestructurableMemorySlot &slot,
421 DenseMap<Attribute, MemorySlot> &subslots,
422 OpBuilder &builder, const DataLayout &dataLayout) {
423 SmallVector<std::pair<unsigned, Value>> driven;
424 getSortedPtrs(subslots, driven);
425
426 for (auto [idx, sig] : driven)
427 DriveOp::create(builder, getLoc(), sig,
428 getValueAtIndex(builder, getLoc(), getValue(), idx),
429 getTime(), getEnable());
430
431 return DeletionKind::Delete;
432}
433
434LogicalResult
435DriveOp::ensureOnlySafeAccesses(const MemorySlot &slot,
436 SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
437 const DataLayout &dataLayout) {
438 return success();
439}
440
441//===----------------------------------------------------------------------===//
442// ProcessOp
443//===----------------------------------------------------------------------===//
444
445LogicalResult ProcessOp::canonicalize(ProcessOp op, PatternRewriter &rewriter) {
446 if (!op.getBody().hasOneBlock())
447 return failure();
448
449 auto &block = op.getBody().front();
450 auto haltOp = dyn_cast<HaltOp>(block.getTerminator());
451 if (!haltOp)
452 return failure();
453
454 if (op.getNumResults() == 0 && block.getOperations().size() == 1) {
455 rewriter.eraseOp(op);
456 return success();
457 }
458
459 // Only constants and halt terminator are expected in a single block.
460 if (!llvm::all_of(block.without_terminator(), [](auto &bodyOp) {
461 return bodyOp.template hasTrait<OpTrait::ConstantLike>();
462 }))
463 return failure();
464
465 auto yieldOperands = haltOp.getYieldOperands();
468 llvm::BitVector operandsToErase(yieldOperands.size());
469
470 for (auto [operandNo, operand] : llvm::enumerate(yieldOperands)) {
471 auto *defOp = operand.getDefiningOp();
472 if (defOp && defOp->hasTrait<OpTrait::ConstantLike>()) {
473 // If the constant is available outside the process, use it directly;
474 // otherwise move it outside.
475 if (!defOp->getParentRegion()->isProperAncestor(&op.getBody())) {
476 defOp->moveBefore(op);
477 }
478 rewriter.replaceAllUsesWith(op.getResult(operandNo), operand);
479 operandsToErase.set(operandNo);
480 continue;
481 }
482
483 // Identify duplicate operands to merge and compute updated result
484 // positions for the process operation.
485 if (!uniqueOperands.contains(operand)) {
486 const auto newPos = uniqueOperands.size();
487 uniqueOperands.insert(std::make_pair(operand, newPos));
488 origToNewPos.insert(std::make_pair(operandNo, newPos));
489 } else {
490 auto firstOccurrencePos = uniqueOperands.lookup(operand);
491 origToNewPos.insert(std::make_pair(operandNo, firstOccurrencePos));
492 operandsToErase.set(operandNo);
493 }
494 }
495
496 const auto countOperandsToErase = operandsToErase.count();
497 if (countOperandsToErase == 0)
498 return failure();
499
500 // Remove the process operation if all its results have been replaced with
501 // constants.
502 if (countOperandsToErase == op.getNumResults()) {
503 rewriter.eraseOp(op);
504 return success();
505 }
506
507 haltOp->eraseOperands(operandsToErase);
508
509 SmallVector<Type> resultTypes = llvm::to_vector(haltOp->getOperandTypes());
510 auto newProcessOp = ProcessOp::create(rewriter, op.getLoc(), resultTypes,
511 op->getOperands(), op->getAttrs());
512 newProcessOp.getBody().takeBody(op.getBody());
513
514 // Update old results with new values, accounting for pruned halt operands.
515 for (auto oldResult : op.getResults()) {
516 auto newResultPos = origToNewPos.find(oldResult.getResultNumber());
517 if (newResultPos == origToNewPos.end())
518 continue;
519 auto newResult = newProcessOp.getResult(newResultPos->getSecond());
520 rewriter.replaceAllUsesWith(oldResult, newResult);
521 }
522
523 rewriter.eraseOp(op);
524 return success();
525}
526
527//===----------------------------------------------------------------------===//
528// CombinationalOp
529//===----------------------------------------------------------------------===//
530
531LogicalResult CombinationalOp::canonicalize(CombinationalOp op,
532 PatternRewriter &rewriter) {
533 // Inline the combinational region if it consists of a single block and
534 // contains no side-effecting operations.
535 if (op.getBody().hasOneBlock() && isMemoryEffectFree(op)) {
536 auto &block = op.getBody().front();
537 auto *terminator = block.getTerminator();
538 rewriter.inlineBlockBefore(&block, op, ValueRange{});
539 rewriter.replaceOp(op, terminator->getOperands());
540 rewriter.eraseOp(terminator);
541 return success();
542 }
543 return failure();
544}
545
546//===----------------------------------------------------------------------===//
547// WaitOp
548//===----------------------------------------------------------------------===//
549
550static LogicalResult verifyYieldResults(Operation *op,
551 ValueRange yieldOperands) {
552 // Determine the result values of the parent.
553 auto *parentOp = op->getParentOp();
554 TypeRange resultTypes = TypeSwitch<Operation *, TypeRange>(parentOp)
555 .Case<ProcessOp, CombinationalOp>(
556 [](auto op) { return op.getResultTypes(); })
557 .Case<FinalOp>([](auto) { return TypeRange{}; });
558
559 // Check that the number of yield operands matches the process.
560 if (yieldOperands.size() != resultTypes.size())
561 return op->emitOpError()
562 << "has " << yieldOperands.size()
563 << " yield operands, but enclosing '" << parentOp->getName()
564 << "' returns " << resultTypes.size();
565
566 // Check that the types match.
567 for (unsigned i = 0; i < yieldOperands.size(); ++i)
568 if (yieldOperands[i].getType() != resultTypes[i])
569 return op->emitError()
570 << "type of yield operand " << i << " ("
571 << yieldOperands[i].getType() << ") does not match enclosing '"
572 << parentOp->getName() << "' result type (" << resultTypes[i]
573 << ")";
574
575 return success();
576}
577
578LogicalResult WaitOp::verify() {
579 return verifyYieldResults(*this, getYieldOperands());
580}
581
582//===----------------------------------------------------------------------===//
583// HaltOp
584//===----------------------------------------------------------------------===//
585
586LogicalResult HaltOp::verify() {
587 return verifyYieldResults(*this, getYieldOperands());
588}
589
590//===----------------------------------------------------------------------===//
591// YieldOp
592//===----------------------------------------------------------------------===//
593
594LogicalResult YieldOp::verify() {
595 return verifyYieldResults(*this, getYieldOperands());
596}
597
598//===----------------------------------------------------------------------===//
599// Auto-Generated Implementations
600//===----------------------------------------------------------------------===//
601
602#define GET_OP_CLASSES
603#include "circt/Dialect/LLHD/IR/LLHD.cpp.inc"
604#include "circt/Dialect/LLHD/IR/LLHDEnums.cpp.inc"
assert(baseType &&"element must be base type")
static Value getValueAtIndex(OpBuilder &builder, Location loc, Value val, unsigned index)
Definition LLHDOps.cpp:65
static void getSortedPtrs(DenseMap< Attribute, MemorySlot > &subslots, SmallVectorImpl< std::pair< unsigned, Value > > &sorted)
Definition LLHDOps.cpp:323
static LogicalResult verifyYieldResults(Operation *op, ValueRange yieldOperands)
Definition LLHDOps.cpp:550
static LogicalResult canonicalizeSigPtrArraySliceOp(Op op, PatternRewriter &rewriter)
Definition LLHDOps.cpp:179
static OpFoldResult foldSigPtrExtractOp(Op op, ArrayRef< Attribute > operands)
Definition LLHDOps.cpp:145
static Location getLoc(DefSlot slot)
Definition Mem2Reg.cpp:216
static InstancePath empty
create(elements)
Definition hw.py:483
create(array_value, idx)
Definition hw.py:450
create(data_type, value)
Definition hw.py:433
create(elements, Type result_type=None)
Definition hw.py:532
create(struct_value, str field_name)
Definition hw.py:556
StringAttr getName(ArrayAttr names, size_t idx)
Return the name at the specified index of the ArrayAttr or null if it cannot be determined.
unsigned getLLHDTypeWidth(Type type)
Definition LLHDOps.cpp:26
Type getLLHDElementType(Type type)
Definition LLHDOps.cpp:36
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
function_ref< void(Value, StringRef)> OpAsmSetValueNameFn
Definition LLVM.h:183